Skip to main content

skip to main content

developerWorks  >  Java technology | WebSphere  >

Globalize your embedded apps

Enhance pervasive devices with globalization features

developerWorks
Document options

Document options requiring JavaScript are not displayed

Sample code


Rate this page

Help us improve this content


Level: Introductory

Shu Fang Rui (shufangrui@gmail.com), Software Engineer
Hu Wei Hong (nobleoliver@hotmail.com), Software Engineer

31 Jan 2006

This article covers the two aspects of a globalization solution -- internationalization and localization -- on embedded devices. For internationalization, learn how to design your code structure to support multiple languages. For localization, learn how to customize the number, date, time, and currency format.

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
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.

Localization

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.0JCL Foundation 1.0JCL Personal Profile 1.0
Java.util.TimeZoneJava.util.LocaleJava.util.Locale
Java.util.TimeZonesJava.util.ResouceBundleJava.util.ResouceBundle
 Java.util.TimeZoneJava.util.TimeZone
 Java.util.TimeZonesJava.util.TimeZones
 Java.text.DateFormatJava.text.DateFormat
 Java.text.DateFormatSymbolsJava.text.DateFormatSymbols
 Java.text.NumberFormatJava.text.NumberFormat
 Java.text.DecimalFormatJava.text.DecimalFormat
 Java.text.DecimatformatSymbolsJava.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.



Back to top


Number formatting

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
LocalePositive FormatNegative Format
ar_EG12,345.6712,345.67-
de_GE12.345,67-12.345,67
en_US12,345.67-12,345.67
zh_CN12.345,67-12.345,67
fr_FR12 345,67-12 345,67
fr_CH12'345.67-12'345.67
de_CH12'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.

Number validating

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.

Validating the number format

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;
   }



Back to top


Date and time format

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
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
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_USzh_CNfr_FR
Dec 19, 2005 2:04 AM2005-12-19 ??2:0419 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_YorkAsia/ShanghaiEurope/Paris
Dec 15, 2005 10:09 PMDec 16, 2005 11:09 AMDec 16, 2005 4:09 AM



Back to top


Currency format

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_USzh_CNfr_FR
$12,345.08?12,345.0812 345,08 €

Currency parsing

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.



Back to top


In conclusion

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.




Back to top


Download

DescriptionNameSizeDownload method
Code samplewi-globalappssource.zip104KBHTTP
Information about download methods


Resources

Learn

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


About the authors

Shu Fang Rui is a graduate student from Shanghai Jiaotong University, China. She is interested in wireless technology and Web services. Besides traveling, she also likes several kinds of sports.


Hu Wei Hong is a graduate student from Zhejiang University, China. He is interested in both Java technology and dancing.




Rate this page


Please take a moment to complete this form to help us better serve you.



 


 


Not
useful
Extremely
useful
 


Share this....

digg Digg this story del.icio.us del.icio.us Slashdot Slashdot it!



Back to top