http://us.blackberry.com/devjournals/resources/journals/sep_2006/localization.jsp
Localization is the process by which an application's user interface is
adapted to meet the default language that is currently set on a
BlackBerry® device. The concept of localization is becoming more
important in the world of BlackBerry, as usage expands across the world.
Topics within this article include:
- What is a locale?
- Class ~
ResourceBundle - Class ~
ResourceBundleFamily - What is required?
- Putting it all together
- Step # 1 ~ Add Resource Header
File - Step # 2 ~ Add Resource Content
File - Step # 3 ~ Add resources keys and
values into the resources - Step # 4 ~ Editing application code
to include support for resources
Additional Locales
Resource Projects
the Output Filename
an initialization file
resource projects
There are several benefits to implementing localization within an
application:
- Text translations are more efficient.
- Applications can dynamically retrieve the appropriate text based on the
user's locale.
Localization can be added by using the ResourceBundle and
ResourceBundleFamily classes, which are members of the net.rim.device.api.il8n
package.
In general, the resources for a particular locale are stored within a
ResourceBundle. A ResourceBundleFamily will contain a collection of
ResourceBundle objects, which groups the resources for an application.
What is a locale?
A locale represents a specific geographical, political, or cultural region
such as France, Spain or London.
Note: MIDP applications (MIDlets) that implement JSR-238
for localization will not currently work.
The ResourceBundle and ResourceBundleFamily classes are outlined in the next
two sections.
Class ~ ResourceBundle
Definition:
The net.rim.device.api.il8n.ResourceBundle class defines the base
functionality for locale-specific resource bundles.
protected ResourceBundle(Locale locale)
Constructs a new ResourceBundle instance. The parameter "Locale" is the
locale for this resource bundle.
Returns | Method | Parameter |
---|---|---|
ResourceBundleFamily | getBundle | String name |
Description:
- Retrieves resource bundle family by name
Parameter:
- The name for the resource bundle family
Returns:
- Resource bundle family of the passed name. If the resource bundle family
does not already exist, this method creates it for you.
Returns | Method | Parameter |
---|---|---|
ResourceBundleFamily | getBundle | long bundle String name |
Description:
- Retrieves the resource bundle family by ID. If this bundle family does not
exist, this method creates a new resource bundle family with the provided ID and
name.
Parameters:
- Bundle ID
- Name for the resource bundle family
Returns:
- Resource bundle family of provided ID
- If it doesn't already exist, this method creates it for you
Throws:
- IllegalArgumentException if the name or its hash is not valid
Returns | Method | Parameter |
---|---|---|
ResourceBundleFamily | getBundle | long bundle String name CodeSigningKey key |
Description:
- Retrieves the resource bundle family by ID. If this bundle family does not
exist, this method creates a new resource bundle family with the provided ID and
name and returns it.
Parameters
- Bundle ID
- Name for the resource bundle family
- Code signing key
Returns:
- Resource bundle family of provided ID
- If it doesn't already exist, this method creates it for you
Throws:
- IllegalArgumentException if the name or its hash is not valid
Returns | Method | Parameter |
---|---|---|
ResourceBundleFamily | getFamily | void |
Description:
- Retrieves the bundle's family
Parameter:
- None
Returns:
- Resource bundle family containing this bundle
Returns | Method | Parameter |
---|---|---|
Locale | getLocale | void |
Description:
- Retrieves this bundle's locale
Parameter:
- None
Returns:
- The bundle's locale
- Returns null if the bundle is a bundle family
Returns | Method | Parameter |
---|---|---|
Object | getObject | int key |
Description:
- Retrieves resource object using a key by chaining through parent bundles.
This method simply invokes getObject(key, true). Invoking this method on a
derived class prompts the method to invoke this method on the enclosing bundle
parent if the object is not found in this bundle. If the object is never found
through the entire chain of calls on parent objects, then this method throws an
exception.
Parameter:
- Key for searched-for resource object
Returns:
- Resource object associated with key
Throws:
- MissingResourceException if neither this bundle, or any enclosing parent
bundle, contains the resource associated with the provided key.
Returns | Method | Parameter |
---|---|---|
Object | getObject | int key boolean searchParent |
Description:
- Retrieves resource object by key
Parameters:
- Key for searched-for resource object
- searchParent: if true, and if this bundle does not contain the resource
object, this method looks in the enclosing bundle parent for the object, and
chains outwards through enclosing bundles until either the object is found, or
the outmost bundle is reached. If false, this method stops looking after
searching this bundle.
Returns:
- Resource object associated with key or null if this bundles does not contain
the object. If searchParent was specified as true, then null gets returned only
if this method cannot find the object in this bundle, nor any enclosing parent
bundle.
Returns | Method | Parameter |
---|---|---|
String | getString | int key |
Description:
- Retrieves string form of resource object by key. This method merely casts
the return value of getObject(int) as a String.
Parameter:
- Key for searched-for resource object
Returns:
- String form of resource object
Throws:
- MissingResourceException if neither this bundle, nor any enclosing parent
bundle, contains the resource associated with provided key
Returns | Method | Parameter |
---|---|---|
String[] | getStringArray | int key |
Description:
- Retrieves string array from of a resource object by key. This method merely
casts the return value of getObject(int) as a String array.
Parameter:
- Key for searched-for resource object
Returns:
- String array form of research object
Throws:
- MissingResourceException if neither this bundle, nor any enclosing parent
bundle, contains the resource associated with the provided key.
Returns | Method | Parameter |
---|---|---|
Object | handleGetObject | int key |
Description:
- Retrieves resource object handle by integer key. Subclasses must implement
this method to return the appropriate resource object for a given key.
Parameter:
- Integer key associated with resource object
Returns:
- Resource object associated with integer key
Throws:
- MissingResourceException if the object associated with the key is not found
Class ~ ResourceBundleFamily
Definition:
The net.rim.device.api.il8n.ResourceBundleFamily class is a ResourceBundle
that contains a collection of ResourceBundles. The ResourceBundleFamily extends
ResourceBundle.
Returns | Method | Parameter |
---|---|---|
ResourceBundle | getBundle | Locale locale |
Description:
- Retrieves bundle by locale. This method returns the resource bundle
associated with a locale. If not present, then this method returns the closest
parent resource, and if still not present, returns null.
Parameter:
- Locale owning the bundle
Returns:
- Bundle for provided locale, closest parent bundle, or null if no bundle (or
enclosing bundle) found for locale
Returns | Method | Parameter |
---|---|---|
long | getId | void |
Description:
- Retrieves this bundle family's ID
Parameter:
- None
Overrides:
- getId() in class ResourceBundle
Returns:
- ID for this bundle family
Returns | Method | Parameter |
---|---|---|
String | getName | void |
Description:
- Retrieves this bundle family's name
Parameter:
- None
Returns:
- Name for this bundle family
Returns | Method | Parameter |
---|---|---|
Object | handleGetObject | int key |
Description:
- Retrieves a resource object handle by integer key. This method searches in
this bundle for the object. If it is not found, then it chains back searching
enclosing parent bundles for the object.
Parameter:
- Integer key associated with resource object
Overrides:
- handleGetObject in class ResourceBundle
Returns:
- Resource object associated with integer key
Throws:
- MissingResourceException if the object associated with the key not found
Returns | Method | Parameter |
---|---|---|
boolean | isEmpty | void |
Description:
- Determines if this bundle family contains no resource bundles
Parameter:
- None
Returns:
- True if this resource bundle family contains no resource bundles
Returns | Method | Parameter |
---|---|---|
void | put | Locale locale ResourceBundle bundle |
Description:
- Adds a resource bundle to this bundle family
Parameter:
- Locale for added resource bundle. If the parent locale is not yet present,
this method waits for it. - Resource bundle to add to this family
Throws:
- IllegalStateException if called from the wrong module
- MissingResourceException if the parent resource module is not found
Returns | Method | Parameter |
---|---|---|
boolean | verify | CodeSigningKey key |
Description:
- Determines if all resource bundles in this family are signed with provided
key
Parameter:
- Code Signing key to test against
Returns:
- True if all bundles in this family are signed with provided key. Also
returns true if the passed key is null.
What is required?
Resource files must be added into the application's project space before
implementing ResourceBundle or ResourceBundleFamily. Four file types are
required to add localization to your application:
- Resource header file
This file defines descriptive keys for each localized string. The resource
header file should be the same name as the project with an extension of .rrhExample: AppName.rrh
When the BlackBerry Java™ Development Environment (BlackBerry JDE) builds a
project, it creates a resource interface with the same name as the .rrh file and
appends "Resource".Example: AppNameResource.java
The resource interface file contains references to the constants added under
the resource header file.
Note: The resource interface file is created behind the scenes
and is not accessible in BlackBerry JDE v4.1. In previous BlackBerry JDE
versions the resource interface file was created and placed in the workspace. - Resource content file (root locale)
This file maps resource keys to string values for the root (or global)
locale. It has the same name as the resource header file, except that it ends
with an extension .rrcExample: AppName.rrc
- Resource content file (specific locales)
This resource content file maps resource keys to string values for specific
locales (language and country). These files are given the same name as the
resource header file, followed by an underscore (_) and the language code, and
then optionally followed by another underscore (_) and country code.Example:
- English Locale
AppName_en.rrc
- Great Britain Locale
AppName_en_GB.rrc
- French Locale
AppName_fr.rrc
- Initialization file
This file initializes the resource bundle mechanism. It is only required when
the resources are compiled in a project that is separate from the main
application project.
There is no restriction on how many resource content files can be added.
Please keep in mind that if you want to add Great Britain locale support, you
must add the default language locale - English.
Putting it all together
In general, there are four steps to be completed when adding localization
support:
- Add resource header file
- Add resource content file
- Add resources keys and values into resources created in Steps 1 and 2
- Edit application code to include support for resources
We will start off by adding the root (or global) locale into an application
named LocalizationExApp.java and then will move into adding additional locales.
The LocalizationExApp.java is a simple application that outputs a screen
containing a title and an ObjectChoiceField.
The code for this application is shown below:
package LocalizationExample; import net.rim.device.api.ui.*; import net.rim.device.api.ui.component.*; import net.rim.device.api.ui.container.*; public class LocalizationExApp extends UiApplication { InitialScreen _screen = new InitialScreen(); public static void main(String[] args) { LocalizationExApp theApp = new LocalizationExApp(); theApp.enterEventDispatcher(); } public LocalizationExApp() { pushScreen(_screen); } } final class InitialScreen extends MainScreen { LabelField _title; ObjectChoiceField _choiceField; String[] _options = { "January","February","March","April","May","June", "July","August","September","October","November","December" }; public InitialScreen() { super(); _title = new LabelField("Localization Sample"); setTitle(_title); _choiceField = new ObjectChoiceField("Month", _options); add(_choiceField); } }
The workspace for the application appears as:
For this sample, the screen title and ObjectChoiceField (label and String
array) will be changed to use resource files instead of hard-coded values within
the source code.
Note: The following steps assume that the BlackBerry Java
Development Environment (BlackBerry JDE) v4.0 is running, with your workspace,
project and java source files already loaded.
Step # 1 ~ Add Resource Header File
- In the BlackBerry JDE, select "File", "New"
- Under the "Files" tab, select "Other" from the list of available file
formats - Enter a name in the "File name:" field. The file name should follow the
proper format of the resource header file specified above. For example:[ApplicationName].rrh
- Browse to the location of the .java source files of the application and then
click "OK". The path should appear in the "Location" field.For this sample, the resource header filename is
"LocalizationExApp.rrh", which corresponds to the name of the Java source file
with the extension .rrh. - Click "OK"
- The following prompt will be displayed:
The package name is automatically generated within the "Add package
information:" field. It is identical to the package statement included within
the java source file. For this sample, the package name is
"LocalizationExample". - Ensure that the package name has been populated correctly and then click
"OK". - The resource header file will appear in the text editor containing the
package statement specified in Step # 4Confirm that the package statement appears correctly, and then close the file
(using the X in the top, right hand corner of the window) - Add the resource header file ([AppName].rrh) into the project space. To do
this, right-click on the name of your project, and select "Add File to [Project
Name]". - Browse to the location of the resource header file and select "*.rrh" from
the "Files of Type:" drop-down list. Highlight the resource header file and
select "Open".The resource header file appears beneath the project once added.
Step # 2 ~ Add Resource Content File
- In the BlackBerry JDE, select "File", "New"
- Under the "Files" tab, select "Other" from the list of available file
formatsEnter the name of the root resource content file in the "File name:" field.
This name should correspond to the application name with the extension .rrc
(i.e. [ApplicationName].rrc).The root locale name would be LocalizationExApp.rrc
- Browse to the location of the source files (.java) of the application and
then click "OK". The path should appear in the "Location" field. - Click "Ok"
- The resource content file appears in the text editor without contents.
Please leave this file empty and close the file (using the X in the top/right
hand corner of the window). If prompted to save changes, select "Yes." - Add the resource content file ([AppName].rrc) into the project
To do this, right-click on the name of your project, and select "Add File to
[Project Name]"Browse to the location of the resource content file and select "*.rrc" from
the "Files of Type:" drop-down list. Highlight the resource content file and
select "Open."The resource content file is added beneath the project within the BlackBerry
JDE. The workspace appears as shown below:
Step # 3 ~ Add resources keys and values
into the resources
In the "Files" panel, double-click on the resource header file
(LocalizationExApp.rrh). The Resource Editor window appears and looks similar to
the following window:
The Resource Editor is used to enter the resource keys and values (strings or
string arrays) for each different locale. Since only the root locale (or
resource content file) has been added, the Resource Editor only displays a tab
for "Root" (as seen in the above screen capture). If more than one locale is
added, a tab will be displayed for each one.
Each row under the headers "Keys" and "Values" defines a single resource. A
blank row is also generated at the base of the list after adding a new resource.
The "Keys" column defines the descriptive name for the resource which will be
used in the source code to retrieve the associated (localized) value. The
"Values" column displays the text or content of that particular resource.
In this sample, three resource keys/values will be added.
- Title of the Screen<
(Single Value - String)
- ObjectChoiceField Title
(Single Value - String)
- ObjectChoiceField Values
(Multiple Values - String Array)
In the first row, enter APPLICATION_TITLE in the "Keys" field. In the
corresponding "Values" field, enter the title of the application - "Localization
Sample".
In the second row, enter "MONTH_TITLE" in the "Keys" field. In the
corresponding "Values" field, enter the title for the ObjectChoiceField -
"Months".
In the third row, enter "MONTH_VALUES" in the "Keys" field. The value for
this resource will be different than the first two that were added because this
will become an array of values as opposed to a single string value.
When a new key/value is added, it is considered to be a single value (only
one value) by default. However, a single value resource can be converted into a
multiple value by right clicking on the resource key and select "Convert to
Multiple Values".
Click "Yes" when prompted to change the resource from a single to multiple
value. Then right-click the keys value (MONTH_VALUES) and select "Properties" to
display the "Edit Resource Line" window. This is where you can add/edit/remove
values from this multiple-value resource.
For this resource, the names of the 12 months will be added. In the field
beside the three buttons - "Add", "Edit" and "Remove" - enter "January" and then
click on the "Add" button. This should display "January" in the "Values" box.
After all months have been added, the "Edit Resource Line" window appears
similar to below:
Click "OK" after all values have been entered.
The "Resource Editor" window appears as below:
After all resource keys and values have been entered into the resource editor
file, the only remaining step is to edit the existing Java source file to
include support for localization.
Step # 4 ~ Editing application code to
include support for resources
- Ensure that the proper package is specified at the top of the application
source file. This package reference will determine the location of all resources
included in the project. - Include an import statement at the top of the application source file for
the net.rim.device.api.il8n.* package. This package contains two classes,
ResourceBundle and ResourceBundleFamily, that are used to create
reference/retrieve resources.import net.rim.device.api.il8n.*;
- Create and define a ResourceBundle object.
A resource bundle object will have to be created as it will contain a
reference to all constants that have been added into the resource header/content
file(s).In general, the declaration of a ResourceBundle object would appear as shown
below:ResourceBundle resources = ResourceBundle.getBundle ([Resource Interface].BUNDLE_ID, [Resource Interface].BUNDLE_NAME);
The ResourceBundle object is defined as:
ResourceBundle _resources = ResourceBundle.getBundle (LocalizationExAppResource.BUNDLE_ID, LocalizationExAppResource.BUNDLE_NAME);
The resource interface will have the same name as the resource header file
(.rrh) with "Resource" appended to the end.The getBundle() method will retrieve the appropriate resource bundle family.
The BUNDLE_ID and BUNDLE_NAME are automatically created within the resource
interface when the BlackBerry JDE compiles the application. - Now that the ResourceBundle object has been created, you can retrieve the
resources by replacing each field value with the appropriate resource.The original LabelField definition is replaced with obtaining the value from
a resource by invoking the getString() method and referencing the
APPLICATION_TITLE key value.//LabelField title = new LabelField("Localization Sample"); LabelField title = new LabelField(_resources.getString (LocalizationExAppResource.APPLICATION_TITLE));
We have two parts for the ObjectChoiceField (Title and Values) that will be
replaced with resources:_choiceField = new ObjectChoiceField(_resources.getString (LocalizationExAppResource.MONTH_TITLE), _resources.getStringArray (LocalizationExAppResource.MONTH_VALUES));
The multiple value key resource is retrieved by using the getStringArray()
method.
The updated LocalizationExApp.java file appears as shown
below:
package LocalizationExample; import net.rim.device.api.ui.*; import net.rim.device.api.ui.component.*; import net.rim.device.api.ui.container.*; import net.rim.device.api.i18n.*; public class LocalizationExApp extends UiApplication { InitialScreen _screen = new InitialScreen(); public static void main(String[] args) { LocalizationExApp theApp = new LocalizationExApp(); theApp.enterEventDispatcher(); } public LocalizationExApp() { pushScreen(_screen); } } final class InitialScreen extends MainScreen { LabelField _title; ObjectChoiceField _choiceField; ResourceBundle _resources = ResourceBundle.getBundle (LocalizationExAppResource.BUNDLE_ID, LocalizationExAppResource.BUNDLE_NAME); public InitialScreen() { super(); //LabelField title = new LabelField("Localization Sample"); _title = new LabelField(_resources.getString (LocalizationExAppResource.APPLICATION_TITLE)); setTitle(_title); // Creates a new object choice field, retrieving the field // title (MONTH_TITLE) and a list of months (APP_MONTHS) // from the resource file _choiceField = new ObjectChoiceField (_resources.getString (LocalizationExAppResource.MONTH_TITLE), _resources.getStringArray (LocalizationExAppResource.MONTH_VALUES)); add(_choiceField); } }
Adding Additional Locales
Now that the root locale has been added into the project/application, it is
simply a matter of adding additional resources for different locales.
Working from the sample above, additional locales - Great Britain, French and
Spanish - will be created and added into the same project.
Two extra items will have to be known prior to adding additional locales:
- Language Code
- Country Code
Both the language and country codes follow ISO standards. The language code
follows the IS0-639 standard and the country code follows the ISO-3166 standard.
Including the country code is optional where the language code is mandatory.
The language code, with optional country code, for additional locales will be
added into the sample.
Locale | Language Code | Country Code |
---|---|---|
English, Great Britain | en | GB |
French | fr | --- |
Spanish | es | --- |
Note: We are only using the Country Code for the English,
Great Britain locale in this sample.
Each locale is represented by an associated resource content file. Each file
will be named similar to the root locale, which includes the language and
country code if necessary.
The resource content file names for the three new locales would be as
follows:
- English, Great Britain
LocalizationExApp_en_GB.rrc
- French
LocalizationExApp_fr.rrc
- Spanish
LocalizationExApp_es.rrc
Since we are adding a country code to one of the locales, the project also
requires a resource content file to be added for the basic language of that
specific locale. This means is that since we are adding a locale for English,
Great Britain, we must also add a resource for English.
Four additional resources will be created and added into this sample project:
Local | English |
---|---|
Language Code | en |
Country Code | --- |
Resource Content Filename | LocalizationExApp_en.rrc |
Local | English, Great Britain |
---|---|
Language Code | en |
Country Code | GB |
Resource Content Filename | LocalizationExApp_en_GB.rrc |
Local | French |
---|---|
Language Code | fr |
Country Code | --- |
Resource Content Filename | LocalizationExApp_fr.rrc |
Local | Spanish |
---|---|
Language Code | es |
Country Code | --- |
Resource Content Filename | LocalizationExApp_es.rrc |
The general naming syntax for these additional locales would generally be as
follows:
[ApplicationName]_[LanguageCode]_[Country Code Optional].rrc
[ApplicationName]
- Name of Application Source
[LanguageCode]
- Language Code of the locale
[CountryCode]
- Country Code of the locale (Optional parameter)
Country Code of the locale (Optional parameter)
Now that file naming conventions for the additional locales are understood,
we can move into creating and adding additional locales into the project.
The following steps must be followed to add resource content files. These
steps must be repeated for each resource that is created and added. In this
case, these steps will be repeated four times (English, English GB, French and
Spanish) using the files names defined above.
To create these additional locales, follow the same steps that were described
when creating the root locale above (Step # 2 - Add Resource Content File),
using the file naming convention described earlier.
After adding the locales, the workspace tree structure will appears as shown
below:
Double-click on the resource header file. The resource header file will
create additional tabs for each locale that has been added. This acts as a
centralized location to add resource keys/values. The sample file,
LocalizationExApp.rrh, appears as shown below:
For each locale added, the resource keys that were added into the root locale
will automatically be generated in the additional locales with blank values.
Please note that a root locale is required for any application that will use
localization. The root locale acts as a global locale and will be used by
default if the other locales are not supported on the BlackBerry device.
To add the content or values into each key value for each locale, follow the
steps outlined in Step # 3 - Add resource keys and values into the resources.
In addition to having the resources read by the application, you can also
localize the application name that appears under the icon on the main ribbon of
the BlackBerry device. To do this, right-click on the project name, select
"Properties", and then select the "Resources" tab.
Select the "Title Resource Available" check box. In the "Resource Bundle:"
and "Title Id" drop-down lists, select the correct resource file and key for the
application title.
No extra code is required within your application to support the additional
locales. Once the application has been installed onto a BlackBerry device, the
appropriate locale will be selected by default, based on the language that the
device is set to. To find out the current language on the BlackBerry device,
view Options - Languages.
Note: The wording of "Options - Language" will change based
on the localization of the BlackBerry device.
A More Efficient Way
If multiple locales are being implemented or maintained for an application,
it can be more efficient to add resource files into their own project based on
the locale. This can be done as follows:
The following steps use the sample developed in the previous steps, but all
resource files were removed from the project before worked was started.
1) Create Resource Projects
Add a new project into the existing workspace for each resource bundle or
locale, including one for the root locale. The project names should match the
names of the resource content files, except that a double underscore should be
inserted prior to including the language code.
The general naming format is as follows:
[AppName]__[Language Code].jdp
... or if including a country code:
[AppName]__[Language Code]_[Country Code].jdp
For example, the resource content file for the English locale is named
"LocalizationExApp_en.rrc". The project will be named
"LocalizationExApp__en.jdp".
Once all resource projects have been added, the workspace should appear as
shown below:
2) Confirm the Output Filename
Confirm each "Output File Name" associated with each project added in Step #
1.
Right-click on the project name and select "Properties". On the project
properties window, click on the "Build" tab. The first field listed is the
Output File Name and should be the same name as the project without the file
extension.
Example:
The Output File Name for the LocalizationExApp__en_GB project will appear as
follows:
3) Create an initialization file
The BlackBerry JDE provides a built-in initialization mechanism. As such, you
will be required to create a initialization class with an empty main() for each
resource project that is created.
The code that appears within this initialization class is:
package LocalizationExample; import net.rim.device.api.i18n.*; public class init { public static void main (String[] args) { } }
Note: The package name should be changed to reflect the
correct name of your application's package.
4) Add files to
appropriate resource projects
The last step is adding the appropriate resource files and initialization
files into the project, then making sure that the project is properly set up for
deployment.
- Add the initialization file created in Step # 3 into all resource projects
- Add the resource header file (.rrh) to the application and all resource
projects - Under each resource project, right-click on the resource header file name
(.rrh) and select "Properties" - Check "Dependency Only. Do not build." as shown in the screen capture below:
- Add each resource content file (.rrc) into its appropriate resource project
Right-click on each resource project name and select "Properties"
On the resource project name property window, select the "Application" tab
Change the "Project Type:" to be a "Library"
Click "OK"
Now the application has been localized using a more efficient method. When
finished, the workspace is divided into six projects, as shown below:
Since each project that was created above will compile into its own cod file,
you can edit the jad file for the application project to include references to
all cod files. This will make the installation or wireless download process much
simpler.
Application deployment can occur wirelessly or by using the BlackBerry
Desktop Manager.
For further information on deployment, please refer to the Enterprise and WAP Deployment guides found under
BlackBerry Java Development Environment.