MOBILE internationalization is a fancy phrase that means making your application run in different langauges. Localization is the process of creating phrases, images, and other resources for a specific language. Internationalization (abbreviated i18n) is what you do to your application so that it can be localized easily.
The Mobile Internationalization API (MIA) is defined by JSR 238, and it includes four elements:
A ResourceManager
provides the plumbing your application needs to retrieve resource strings, images, and other objects.
The MIA specification also defines a resource file format that describes how localized resources are stored.
Formatter
knows how to represent numbers, currency, dates, and times in a specific language or region.
StringComparator
understands how to sort strings according to the rules for a particular language.
MIA is relatively compact and resides in the javax.microedition.global
package.
Resources are based on locales, which are combinations of a language and a country or region. A locale, in essence, is a language, but it can be made more specific by adding a country as well. This makes sense because the same language is used differently in different parts of the world. Spanish in Mexico, for example, is different from Spanish in Spain.
Languages are represented using two-letter codes as defined by ISO 639. A list is here:
http://ftp.ics.uci.edu/pub/ietf/http/related/iso639.txt
Countries are represented using two-letter codes defined by ISO 3166. The list of country codes is here:
http://userpage.chemie.fu-berlin.de/diverse/doc/ISO_3166.html
A locale code is simply a language code, a hyphen, and a country code. For example, en-US
represents English in the United States of America.
The system property microedition-locale
contains the default locale code for a device. It is allowed to be null
, in which case you must fall back on appropriate default behavior.
In the absence of MIA, you might create a simple user interface like this:
This little code snippet contains three hardcoded strings. Let’s assume that “ApplicationName” will be shown regardless of the language of the rest of the user interface. The other two strings should be retrieved from a resource file so that they can easily be localized.
A resource file is represented by ResourceManager
. ResourceManager
contains strings and byte arrays, each of which has an identification (ID) number. You can retrieve a resource from ResourceManager
by passing the ID to get()
or getString()
. A globalized version of the previous example looks like this:
It’s good practice to use static final
constants for resource IDs instead of embedding the numbers directly in your code. Here is an even more correct version:
To load a resource file, pass a base name to one of ResourceManager
’s static getManager()
factory methods. ResourceManager
attempts to use the default locale (from the microedition.locale
system property) if you don’t specify a locale. If you do, you can pass either a single locale or an array of locales.
Resource files are stored in a binary format in the MIDlet suite JAR file. They are located under the global
directory. The location and format of resource files is defined in the JSR 238 specification. You should not have to know the details, because developer tools that support MIA include some kind of resource editor.
In general, resource files have the same base name and are located in directories that are named for locales.
In the Sun Java Wireless Toolkit, navigate to the resource editor (Figure 17.1) by choosing File > Utilities from the KToolbar menu, then selecting i18n Resources Manager and pressing Launch. Select your project from the Projects combo box and you are ready to edit resources. See the documentation for more details.
Figure 17.1. The Sun Java Wireless Toolkit’s resource editor
In NetBeans, choose Tools > Java Platform Manager from the menu. Select the Sun Java Wireless Toolkit from the list of platforms. Click on the Tools & Extensions tab, then press Open Utilities. Select i18n Resources Manager and press Launch.
The example at the end of this chapter shows a resource file with the base name “Babble,” which is defined at the top level (right under global
) and for the en-US
and de-DE
locales. The contents of the de-DE
resource file are shown in the bottom part of the window. You can see the IDs and the corresponding resource. All of them are strings except for 3, which is an image byte array.
Typically, your application will attempt to load resources for the device’s locale with code like this:
It is possible that microedition.locale
is null
. ResourceManager
will throw an exception if the requested locale is null
, so this example tests for the condition and assigns an empty string if needed. The empty string will at least match the default resource file for the given base name.
ResourceManager
tries hard to honor requests for resources. This commitment to customer service begins at the call to getManager()
. ResourceManager
attempts to find a resource file that exactly matches the locale you request (or the default locale if you did not specify one).
If an exact match cannot be found, ResourceManager
will try to find a resource file that matches just the language part of the locale. If it can’t find that, it will see if there is a default resource file, one with no associated locale, located at the root of the global
directory.
Supplying a default resource file is therefore a very good idea, because it allows your application to function even when a resource file matching the requested locale does not exist.
Once you have an appropriate ResourceManager
, another inheritance scheme takes effect. Let’s say, for example, that you’ve obtained a ResourceManager
for en-US
and you request string resource 2. If the resource does not exist, ResourceManager
will look for it in an en
resource file (if it is available) or the default resource file.
To support both British and American English, then, you could create an en
resource file that contains most of the phrases and resources for your application. Then you could also create an en-US
resource file with all the Americanisms and an en-GB
resource file with the specifically British resources.
The example at the end of this chapter demonstrates this technique. The default resource file contains phrases in English. The en-US
resource file contains an image of the American flag but does not define any of the phrases. When the application attempts to retrieve the phrases from the en-US ResourceManager
, they are returned from the default resource file.
A separate class in MIA, Formatter
, knows how to format numbers, currency, dates, times, and percentages in different locales.
To get a Formatter
, just pass a locale to its constructor. If an appropriate Formatter
does not exist, an UnsupportedLocaleException
is thrown. You can also use the Formatter
constructor with no arguments, which attempts to use microedition.locale
. Finally, if you pass null
for the locale, you’ll get a Formatter
that works in a locale-neutral way.
The actual formatting is straightforward. Use the formatCurrency()
methods for money, the formatNumber()
methods for numbers, and formatPercentage()
for percentages.
Using floating-point variables for currency is asking for trouble, but MIA embraces it. A safer approach is to use an int
or long
to store pennies (or the equivalent). Consult the following sources for more information.
Representing money
http://www.javapractices.com/Topic13.cjp
Beware of floating-point numbers
http://www.javapractices.com/Topic213.cjp
Working with money in Java
http://www.javaranch.com/newsletter/July2003/MoneyInJava.html
Currency
http://mindprod.com/jgloss/currency.html
Floating Point
http://mindprod.com/jgloss/floatingpoint.html
The formatDate()
method can create a variety of date and time styles. Pass in a java.util.Calendar
and one of the style constants from Formatter
, either DATE_LONG
, DATE_SHORT
, TIME_LONG
, TIME_SHORT
, DATETIME_LONG
, or DATETIME_ SHORT
.
The trickiest method in Formatter
is formatMessage()
, which is useful for inserting parameters into a fixed message. For example, you might want to localize the message “You now have n points.” The key is to put the parameters in curly braces. In this example, you could create a string resource like this:
The parameters should be numbered starting from 0 and counting up. You can use as many as 100 parameters in a message. When you want to show the message, pass the message and a string array of parameters to Formatter
’s formatMessage()
method.
It looks something like this:
Different languages have different alphabets and their own rules about how strings should be sorted (alphabetized). MIA’s StringComparator
encapsulates these rules for a locale.
To use StringComparator
, pass a locale to its constructor. To compare two strings, pass them to the compare()
method. The method returns a negative number if the strings are already in ascending alphabetical order, a positive number if they are in descending order, and zero if the two strings are identical.
You can use StringComparator
to sort a list of strings in a manner that is appropriate for a particular locale.
The Sun Java Wireless Toolkit includes a demonstration of this technique. In the i18nDemo
project, take a look at the String Comparator example. It shows a list of cities. Choose the Sort - default command to sort the cities using default rules. Now choose Sort - slovak and notice how the sorting changes.
BabbleMIDlet
includes examples of most of the techniques you’ve been reading about. It will be useful to you if you are writing an internationalized application. It shows how to retrieve a ResourceManager
, how to obtain a Formatter
, and how to use both.
Run it with the emulator’s locale as en-US
, and the result will look like Figure 17.2.
Figure 17.2. BabbleMIDlet
in en-US
The flag image comes from the en-US
resource file, while the text phrases are inherited from the default resource file. The Formatter
knows about American dollars and successfully formats the points value by inserting a comma.
To really appreciate the talents of this application, you need to change the default locale (the microedition.locale
system property) for the emulator. In the Sun Java Wireless Toolkit, choose Edit > Preferences… from the KToolbar menu and click on the i18n category.
In NetBeans, choose Tools > Java Platform Manager from the menu. Select the Sun Java Wireless Toolkit from the list of platforms. Click on the Tools & Extensions tab, then press Open Preferences. Click on the i18n category.
Fill in de-DE
for the locale and run BabbleMIDlet
again (see Figure 17.3).
Figure 17.3. BabbleMIDlet
in de-DE
As you can see, the text phrases and the flag image come from the de-DE
resource file. However, the toolkit’s emulator does not include an appropriate formatter. Default behavior, produced from new Formatter(null)
, occurs instead. The date, time, currency, and number formatting are all plain vanilla, locale-neutral.
For another perspective, try cs-CZ
, shown in Figure 17.4.
Figure 17.4. BabbleMIDlet
in cs-CZ
This time, no appropriate resource file is available, so all the text prompts come from the default resource file, and the image is not defined at all. Notice how the code in buildForm()
catches the ResourceException
and will not attempt to add the flag image when it is not available.
However, an appropriate Formatter
is available on the toolkit’s emulator. The date, time, currency, and number formatting are correct for cs-CZ
, even though the phrases have all been pulled from the default resource file.
The Mobile Internationalization API provides the plumbing you need to make applications that can run smoothly in different languages. It defines a scheme for resource files, represented by ResourceManager
, which contain phrases and other data. A resource file belongs to a locale, which is a combination of a language and a country. Resource information can be inherited from less specific resource files, including the default. A separate class, Formatter
, can format dates, currency, and numbers for a locale. Finally, StringComparator
encapsulates string sorting functionality for a locale.
18.117.74.231