As the Internet's rapid growth opens up unprecedented opportunities for businesses beyond national borders, the multilingual and multiplatform releases of software products begin to play a key role in the success of any global business. However, while initial Web development was mainly based on the English language, people who speak languages other than English understandably prefer to see software interfaces in their native languages and sensitive to their cultural and national conventions. Consequently, a growing number of globalization demands will inevitably fall on e-business applications.
When it comes to globalization support on pervasive devices, developers face the challenge of how to utilize the limited support of run times and resources to meet the various globalization needs. Here, we address these challenges on the Java™ 2 Platform, Micro Edition (J2ME) platform.
Generally speaking, to develop a globalized application you need to take several steps, which we group into two categories: internationalization and localization. Internationalization means that the application design and development should be flexible enough to satisfy the requirement of multiple language and cultural differences. To make this possible, you should separate textual data from codes, and separate codes relating to cultural convention from the basic application that deals with business logic. This basic, globalized application is called a single executable. It can run in all language versions and can invoke the corresponding textual data and culture-sensitive codes that generate a user interface (UI) to meet the current user locale's requirements.
Localization refers to the generation of code that supports globalization features by translating textual data and setting culture-related configurations, such as name and address formats. You store the translation and configuration files in different directories or folders based on different locales, so that the globalized application program can invoke them at any time. You can greatly enhance the efficiency of localization if you provide all the textual data in a uniform format, such as the OASIS XML Localisation Interchange File Format (XLIFF).
Internationalization: Language support
One of globalization's key features is the ability to handle multiple languages. Language support usually relates closely with UI design. Here's how to add multilingual support for your UI.
In an application supporting globalization, you often have a function named Select language, which displays a different language on the user interface according to a user's selection. You can implement the support for a multilingual feature with the help of Java.util.ResourceBundle. Through a Messages class implemented using a ResourceBundle, you can easily get a multilingual text from the corresponding properties file. We will give the detailed information of the Messages class in Listing 1. There are three methods in the Messages class: setBundle(), getString(), and getLocale(). By using setBundle(String propertyFileName), the class associates ResourceBundle with a new properties file and a corresponding locale. When using getString(String key), the class returns the text related with the corresponding locale.
Listing 1. Source code of Messages class
public class Messages {
/**
* Property file name
*/
private static String propertyFileName;
/**
* ResourceBundle associated with the property file name
*/
private static ResourceBundle resourceBundle;
/**
* Locale related with the selected language
*/
private static Locale locale;
static {
propertyFileName = UIConstant.EN_PROPERTY_NAME;
resourceBundle = ResourceBundle.getBundle(propertyFileName);
locale = Locale.US;
}
/**
* The default constructor, preventing others to generate
* instance of this class.
*/
private Messages() {
}
/**
* Set a new property file associated with the newName.
* @param newName Name of the new property file
*/
public static void setBundle(String newName) {
propertyFileName = newName;
resourceBundle = ResourceBundle.getBundle(propertyFileName);
if (UIConstant.EN_PROPERTY_NAME.equals(newName)) {
locale = Locale.US;
} else if (UIConstant.ZH_PROPERTY_NAME.equals(newName)) {
locale = Locale.SIMPLIFIED_CHINESE;
} else if (UIConstant.FR_PROPERTY_NAME.equals(newName)) {
locale = Locale.FRANCE;
}
}
/**
*
* @param key Key in the property file for fetching value.
* @return String value associated with the key value
*/
public static String getString(String key) {
String result = "";
try {
result = resourceBundle.getString(key);
} catch (MissingResourceException e) {
e.printStackTrace();
}
return result;
}
/**
*
* @return the Locale associated with the property file.
*/
public static Locale getLocale() {
return locale;
}
}
|
Listing 2 sets the text on the label that displays string "user ID."
Listing 2. Set text on label
String userIDStr = Messages.getString("label_userID"); //$NON-NLS-1$
userIDLabel.setText(userIDStr)
|
For detailed information about how to use the Messages class in your UI class, we show the UI in Figure 1 and the class skeleton in Listing 3. For details, download the source code at the end of this article.
Figure 1. A user interface with language selection

Listing 3. Skeleton of the UI class
public class LogonUI {
/**
* The only instance of LogonUI class
*/
private static final LogonUI instance;
// The definition of all UI components comes following
ᾯ
static {
instance = new LogonUI();
}
/**
* Private constructor, make sure the only instance
* of the LogonUI class.
* Create the components on LogonUI
*/
private LogonUI() {
//Create the shell object in this UI
createShell();
//Create the logo image
createImage();
//Create the userID and password-related components
createInputArea();
//Create the select language combo
createCombo();
//Create the login and exit button
createButton();
//Set the text on this UI
setUIText();
}
//The method definition createShell(), createImage(),
createInputArea(),createButton() come following
"//
/**
* private method, used to create the language combo
*
*/
private void createCombo() {
//create select language label and arrange it properly
lanLabel = new Label(shell, SWT.LEFT);
"/
//create the combo box and arrange it properly
lanCombo = new Combo(shell, SWT.CENTER | SWT.READ_ONLY);
" lanCombo.addSelectionListener(new SelectionListener() {
public void widgetSelected(SelectionEvent event) {
Combo cb = (Combo) event.getSource();
selectedLan = cb.getText();
if (UIConstant.ENGLISH.equals(selectedLan)) {
Messages.setBundle(UIConstant.EN_PROPERTY_NAME);
} else if (UIConstant.CHINESE.equals(selectedLan)) {
Messages.setBundle(UIConstant.ZH_PROPERTY_NAME);
} else {
Messages.setBundle(UIConstant.FR_PROPERTY_NAME);
}
}
public void widgetDefaultSelected(SelectionEvent event) {
}
});
data = new FormData();
data.top = new FormAttachment(pwdText, 10);
data.left = new FormAttachment(35);
data.right = new FormAttachment(95);
lanCombo.setLayoutData(data);
}
/**
* Used for setting the text displayed on the UI according
* to the selected language.
*/
private void setUIText() {
welcome = Messages.getString("TITLE_WELCOME"); //$NON-NLS-1$
shell.setText(welcome);
userIDStr = Messages.getString("TABLE_USERID"); //$NON-NLS-1$
userIDLabel.setText(userIDStr);
pwdStr = Messages.getString("TABLE_PASSWORD"); //$NON-NLS-1$
pwdLabel.setText(pwdStr);
lanStr = Messages.getString("TABLE_SELECTLANGUAGE"); //$NON-NLS-1$
lanLabel.setText(lanStr);
loginStr = Messages.getString("BUTTON_LOGIN"); //$NON-NLS-1$
loginBtn.setText(loginStr);
exitStr = Messages.getString("BUTTON_EXIT"); //$NON-NLS-1$
exitBtn.setText(exitStr);
}
// The method definition for open(), which is used to show the
LogonUI on the screen
"/
|
Note that we declare the LogonUI constructor as private. Here, we try to make the LogonUI class an implementation of the Singleton pattern, as only one instance of the LogonUI class exists in the application.
You use the private method setUIText() to set all the text on the LogonUI. The method extracts all the displayed text from the properties files. There is also another invocation of setUIText() in the SelectionListener. After every language change, the text displayed on the LogonUI changes accordingly. The ResourceBundle instance and the Locale related to Messages also change, so that all text displayed on other UIs changes accordingly.
We also extract setUIText as a single method instead of setting the text on each component in its creation method; this is out of reuse consideration. And just as after every language selection, you need to change the text accordingly. You achieve this by writing a single method to change the displayed text.
WebSphere® Studio Device Developer 5.7.1 ships with three libraries: JCL MIDP 2.0, JCL Foundation 1.0, and JCL Personal Profile 1.0, each with different levels of localization support. Table 1 compares each library's support for localization.
Table 1. Support for localization in three WebSphere Studio Device Developer 5.7.1 libraries
| JCL MIDP 2.0 | JCL Foundation 1.0 | JCL Personal Profile 1.0 |
|---|---|---|
| Java.util.TimeZone | Java.util.Locale | Java.util.Locale |
| Java.util.TimeZones | Java.util.ResouceBundle | Java.util.ResouceBundle |
| Java.util.TimeZone | Java.util.TimeZone | |
| Java.util.TimeZones | Java.util.TimeZones | |
| Java.text.DateFormat | Java.text.DateFormat | |
| Java.text.DateFormatSymbols | Java.text.DateFormatSymbols | |
| Java.text.NumberFormat | Java.text.NumberFormat | |
| Java.text.DecimalFormat | Java.text.DecimalFormat | |
| Java.text.DecimatformatSymbols | Java.text.DecimatformatSymbols |
Note that the support for localization in Mobile Information Device Profile (MIDP) 2.0 is quite limited, while Foundation 1.0 provides the same support for localization as in Personal Profile 1.0.
Based on JCL Foundation 1.0, we explain how to support the number format both in formatting and validation, date and time format according to a different time zone and language, and the currency format according to the different locale.
Almost every country in the world uses the decimal system (base 10). However, number formats vary considerably. Table 2 shows examples of different number formats according to different locales.
Table 2. Different number formats for different locales
| Locale | Positive Format | Negative Format |
|---|---|---|
| ar_EG | 12,345.67 | 12,345.67- |
| de_GE | 12.345,67 | -12.345,67 |
| en_US | 12,345.67 | -12,345.67 |
| zh_CN | 12.345,67 | -12.345,67 |
| fr_FR | 12 345,67 | -12 345,67 |
| fr_CH | 12'345.67 | -12'345.67 |
| de_CH | 12'345.67 | -12'345.67 |
Number formatting is rather trivial. Listing 4 shows how to format a number according to a different locale.
Listing 4. Format numbers according to locale
double width = 12345.67; NumberFormat nf = NumberFormat.getInstance(Locale.FRANCE); nf.setMinimumFractionDigits(3); nf.setMaximumFractionDigits(3); String result = nf.format(width); |
Note that when the locale is fr_FR and the double value is larger than 1,000, the formatted string using NumberFormat.format() will contain blank spaces; this is because the digit grouping symbol in locale fr_FR is " ". But this space has the ASCII value of 160, while the space we use generally has the ASCII value of 32. If you are dealing with the formatted string, you need to pay special attention to the blank space issue. For example, just using Listing 5 won't give you the result you want; you have to delete the spaces in the formatted output.
Listing 5. Delete blank spaces in a string
StringBuffer sb = new StringBuffer(result);
for (int i = 0; i < sb.length(); i++) {
if (' ' == sb.charAt(i)) {
sb.deleteCharAt(i);
}
}
|
You must modify the line (' ' == sb.charAt(i)) to if(160 == sb.charAt(i)) to delete the space in locale fr_FR.
Validating input data plays an important role in keeping software robust. Validation allows the application to make sure the user enters data correctly. Therefore, it keeps invalid data from entering the business logic. If any validation fails, the application redisplays the user interface or displays another one requesting the user to correctly input the data. Otherwise, the data processing begins.
If the application expects a number, you must ensure that your data follows the correct number format. Typically, the locale-sensitive Java API will help you do this job, saving you a lot of work. But it becomes a little tough in a globalized application. As Table 2 shows, the number format looks different for different locales. When you need to process a number from input, you need to first validate it. In locale Locale.US and Locale.SIMPLIFIED_CHINESE, the thousands-separator is "," (a comma) and the decimal point is "." (a period). When it comes to Locale.FRANCE, the thousands-separator becomes " " (a blank space) and the decimal point is "," (a comma). Though the basic Java API provides some classes, such as Java.text.NumberFormat and Java.text.DecimalFormat to do this job, these classes cannot guarantee that the input string stays in a valid number format. (Listing 6 shows the basic use of Java.text.NumberFormat to parse a string).
Listing 6. Parse function in NumberFormat class
Number number =NumberFormat.getNumberInstance(locale).parse(str);
if (number instanceof Long){
//Long value
}else{
//Double value
}
|
If using the US locale for an input string of "0.34.56", the parsed result will still be "0.34"; if the input is ",455,0.0", the output will be "4550"; and if the input is "234.23ab", the output will be a valid number "234.23". The parsing function stops working when it meets an illegal character. Because of this, you need to validate the number format first.
As an example, Listing 7 shows how to deal with the fr_FR locale. As fr_FR allows the blank space, we will provide two functions to validate the input.
First, we need to delete the spaces in the input string.
Listing 7. Check and delete spaces in fr_FR locale
/**
* If locale is fr_FR, check the format and delete spaces
* @param number The number getting from input
* @param locale The locale related with the selected language
* @return If the format is valid, return the modified string,
* @else return null
*/
public static String deleteSpaceIfFR(String number, Locale locale) {
if (Locale.FRANCE.equals(locale)) {
// " " should be before ","
//Note: ' 'here is the ASCII value 0X20
if (number.indexOf(',') > 0
&& number.indexOf(' ') > 0
&& number.lastIndexOf(' ') > number.indexOf(',')) {
return null;
}
// " " should be before ","
//Note: ' 'here is the ASCII value 0XA0
if (number.indexOf(',') > 0
&& number.indexOf(160) > 0
&& number.lastIndexOf(160) > number.indexOf(',')) {
return null;
}
//No continuous " " is allowed
if (number.indexOf(" ") > 0) {
System.out.println("No continuous is allowed");
return null;
}
//delete all spaces in the number string
StringBuffer sb = new StringBuffer(number);
for (int i = 0; i < sb.length(); i++) {
if (32 == sb.charAt(i) || (160 == sb.charAt(i))) {
sb.deleteCharAt(i);
}
}
return sb.toString();
} else {
return number;
}
} |
Next, do the detailed validation, as Listing 8 shows.
Listing 8. Do the detailed validation
/**
* To check whether a string is a valid number string
*
* @param number the number string
* @param locale the locale selected
* @return whether the number is in valid format
*/
public static boolean validateNumber(String number, Locale locale) {
//empty string
if (null == number || "".equals(number)) {
return false;
} else {
// ensure no invalid character is contained in the number string
for (int i = 0; i < number.length(); i++) {
char c = number.charAt(i);
boolean isallowed =
(c >= '0' && c <= '9') || (c == ',') || (c == '.');
if (!isallowed) {
return false;
}
}
}
//for fr_FR locale
if (Locale.FRANCE.equals(locale)) {
//should not contain "."
if (number.indexOf('.') >= 0) {
return false;
}
//should contain "," once
if (number.indexOf(',') > 0
&& (number.indexOf(',') != number.lastIndexOf(','))) {
return false;
}
//"," can not at the last position of number
if (number.indexOf(',') == (number.length() - 1)) {
return false;
}
//"0" cannot at the first position of number else it appends with
// ","
if (0 == number.indexOf('0') && 1 != number.indexOf(',')) {
return false;
}
}
return true;
} |
More often than not, we meet Date or Time in our applications. Date and Time play an important role in globalized apps too. If we develop an app for a global company, a user in New York City will see the date or time in that time zone, while a user located in Paris will see the date and time in Paris' time zone. Users can also select a different language to display the date or time information.
Take the time representation in a globalized app, for example. The user lives in New York City. If he chooses Simplified Chinese as the display language, he will see the local time displayed in Chinese customs. If he chooses English, time will be displayed according to American customs. It's rather simple to display date and time fields according to different locales and selected languages.
Let's suppose our user is Chinese and located in New York City. He wants to see the date displayed according to Chinese custom; Listing 10 shows you how.
Listing 10. Display date in New York City time zone and in Chinese language
1 Date date = new Date();
2 //Get the date formatter according to the selected language
3 DateFormat df = DateFormat.getDateInstance(
DateFormat.LONG,Locale.SIMPLIFIED_CHINESE);
4 TimeZone zone = TimeZone.getTimeZone("America/New_York");
5 df.setTimeZone(zone);
6 String dateValue = df.format(date);
|
Figure 2 displays the UI related to the Chinese language.
Figure 2. A user interface in Chinese

If the user is French and wants to see the date information displayed in French, just modify line 3 in Listing 10 to DateFormat df = DateFormat.getDateInstance(DateFormat.LONG, Locale.FRANCE);, as Figure 3 shows.
Figure 3. A user interface in French

If you want to get the DateTime format, just replace the code line with the following DateFormat df = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.SHORT,Locale.FRANCE);. Table 3 displays the DateTime format for a different language.
Table 3. DateTime format in the same time zone and different selected language
| en_US | zh_CN | fr_FR |
|---|---|---|
| Dec 19, 2005 2:04 AM | 2005-12-19 ??2:04 | 19 dè¡® 2005 02:04 |
Above, we have introduced the DateTime format related with the same time zone and different language. Next, we will display the DateTime format in the same language and different time zone, as Listing 11 shows.
Listing 11. Display DateTime in the same language and different time zone
Date date = new Date();
TimeZone zones[] =
{
TimeZone.getTimeZone("America/New_York"),
TimeZone.getTimeZone("Asia/Shanghai"),
TimeZone.getTimeZone("Europe/Paris")};
DateFormat df =
DateFormat.getDateTimeInstance(
DateFormat.MEDIUM,
DateFormat.SHORT,
Locale.US);
for (int i = 0; i < 3; i++) {
df.setTimeZone(zones[i]);
System.out.println("time:" + df.format(date));
} |
Table 4 shows the result of the above code.
Table 4. DateTime format in the same language and different time zone
| America/New_York | Asia/Shanghai | Europe/Paris |
|---|---|---|
| Dec 15, 2005 10:09 PM | Dec 16, 2005 11:09 AM | Dec 16, 2005 4:09 AM |
Currency format is usually composed of a locale, currency name, currency subunits, currency symbol, positive format, negative format, currency codes, and currency separators. Currency separators include the thousands separators, decimal separators, decimal position, field length, and padding character.
The Java.text.NumberFormat class also supports currency format. Listing 12 shows how to support currency formatting according to a different locale.
Listing 12. Currency formatting
Locale locale = Locale.FRANCE;
double price = 12345.078;
NumberFormat formatter = NumberFormat.getCurrencyInstance(locale);
System.out.println(formatter.format(price));
|
Table 5 shows the resulting output according to different locales.
Table 5. Different currency formats for different locales
| en_US | zh_CN | fr_FR |
|---|---|---|
| $12,345.08 | ?12,345.08 | 12 345,08 ÃÂ |
Listing 13 shows how to parse currency.
Listing 13. Currency parsing
try {
Number number =
NumberFormat.getCurrencyInstance(Locale.FRANCE).parse(
"12 345, 08 ÃÂ");
System.out.println(number);
} catch (ParseException e) {
e.printStackTrace();
} |
The output from Listing 13 will be 12345.08, the same as the price variable.
While pervasive devices are limited by memory and screen size, the J2ME API provides some support for globalization features, such as multiple language support, number format, date and time format, as well as currency format. By adding just these basic features, you'll give your embedded applications the competitive edge to succeed in the ever-expanding global market.
| Description | Name | Size | Download method |
|---|---|---|---|
| Code sample | wi-globalappssource.zip | 104KB | HTTP |
Information about download methods
Learn
-
Read the redbook e-business Globalization Solution Design Guide: Getting Started to get more details about a globalization solution.
-
Read the article "Considerations of globalization solutions in J2ME" (developerWorks, April 2004) to get an overall view of a globalization solutions in J2ME.
-
Read the article "A new strategy of language pack management for wireless apps" (developerWorks, February 2005) to learn how to use the Service Management Framework to develop globalized wireless apps.
-
Stay current with developerWorks technical events and Webcasts.
Get products and technologies
- Build your next development project with IBM trial software, available for download directly from developerWorks.
-
Download WebSphere Studio Device Developer, which provides an integrated development environment (IDE) for building, testing, and deploying J2ME applications that run on wireless devices.
-
To use J2ME, download it from the Sun Microsystems Web site.
Discuss
-
Participate in developerWorks blogs and get involved in the developerWorks community.


