Internationalization

To internationalize a Java program, you first have to identify the culturally dependent data in your application. Culturally-dependent data is any data that varies from one culture or country to another. Text displayed to the user in the form of titles, labels, button names, numbers, and error messages, for example, are the most obvious and pervasive example of culturally dependent data, but other things like number formats, sounds, times, and dates should be considered too.

//Access to error text
public void someMethod(){
  try {
    //do something
  } catch (java.util.NoSuchElementException {
    System.out.println(“Print some error text”);
  }
}

Methods can be coded to declare the exception in their throws clause, but this way you cannot access the error message text thrown when the method tries to access unavailable data in the set. In this case, the system-provided text for this error message is sent to the command line regardless of the locale in use for the application.

It is always better to use try and catch blocks wherever possible if there is any chance the application will be internationalized so you can access and localize the error message text.

 //No access to error text    
  public void someMethod() 
     throws java.util.NoSuchElementException{ 
      //do something    
  }

Create Keyword and Value Pair Files

Because all text visible to the user will be moved out of the application and translated, your application needs a way to access the translated text during execution. This is done with properties files that specify a list of keyword and value pairs for each language to be used. The application code loads the properties file for a given language and references the keywords instead of using hard-coded text.

For example, you could map the keyword purchase to Kaufen in the German file, Achetez in the French file, and Purchase in the United States English file.

In your application, you load the properties file for the language you want to use and reference the keyword purchase in your code. During execution when the purchase keyword is encountered, Achetez,  Kaufen, or Purchase is loaded depending on the language file in use.
Keyword and value pairs are stored in properties files because they contain information about a program’s properties or characteristics. Property files are plain-text format, and you need one file for each language you intend to use.

In this example, there are three properties files, one each for the English, French, and German translations. Because this example currently uses hard-coded English text, the easiest way to begin the internationalization process is to use the hard-coded text to set up the key and value pairs for the English properties file.

The properties files follow a naming convention so the application can locate and load the correct file at run time. The naming convention uses language and country codes, which you should make part of the file name. Both the language and country are included because the same language can vary between countries. For example, United States English and Australian English are a little different, and Swiss German and Austrian German both differ from each other and from the German spoken in Germany.

These are the names of the properties files for the German (de_DE), French (fr_FR), and American English (en_US) translations where de, fr, and en indicate the German (Deutsche), French, and English languages; and DE, FR, and US indicate Germany (Deutschland), France, and the United States:

  • MessagesBundle_de_DE.properties
  • MessagesBundle_en_US.properties
  • MessagesBundle_fr_FR.properties

English Translations

Following is an example English language properties file. Keywords are to the left of the equals (=) sign, and text values are on the right.

apples = Apples:
peaches = Peaches:
pears = Pears:
items = Total Items:
cost=Total Cost:
card=Credit Card:
customer=Customer ID:
title=Fruit 1.25 Each
1col=Select Items
2col=Specify Quantity
reset=Reset
view=View
purchase = Purchase
invalid = Invalid Value
send = Cannot send data to server
nodata = No data available
noID = No customer IDs available
noserver = Cannot access data in server

You can hand this file off to your French and German translators and ask them to provide the French and German equivalents for the text to the right of the equals (=) sign. Keep a copy because you will need the keywords to internationalize your application text.

German Translations

apples=Äpfel:
peaches=Birnen:
pears=Pfirsiche:
items=Anzahl Früchte:
cost=Gesamtkosten:
card=Kreditkarte:
customer=Kundenidentifizierung:
title=Früchte 1,25 jede
1col=Auswahl treffen
2col=Menge angeben
reset=Zurücksetzen
view=Sehen Sie an
purchase=Kaufen
invalid=Ungültiger Wert
send=Datenübertragung zum Server nicht möglich
nodata=Keine Daten verfügbar
noID=Keine Kundenidentifizierungen verfügbar
noserver=Kein Zugang zu den Daten beim Server

French Translations

apples=Pommes:
peaches=Pêches:
pears=Poires:
items=Partial total:
cost=Prix total:
card=Carte de Crédit
customer=Numêro de client:
title=Fruit 1,25 pièce
1col=Choisissez les éléments
2col= Indiquez la quantité
reset=Réinitialisez
view=Visualisez
purchase=Achetez
invalid=Valeur incorrecte
send=Les données n’ont pu être envoyées au serveur
nolookup=Accès impossible à l’objet du serveur distant
nodata=Aucune donnée disponible
noID=dentifiant du client indisponible
noserver=Accès aux données du serveur impossible

Access A Translation File

You can hard code which translation file to use or pass in command-line parameters, depending on your application requirements. In either case, you need the following internationalization variables:

//Internationalization variables
    static Locale currentLocale;
    static ResourceBundle messages;
    static String language, country;
    NumberFormat numFormat;
  • currentLocale is initialized from the language and country information within your program or passed in at the command line.
  • messages is initialized from the currentLocale.

The messages object provides access to the translated text for the language in use. It takes the following two parameters: MessagesBundle, which is the prefix of the family of translation files this application uses, and Locale, which tells the ResourceBundle which translation to use.

For example, if the application is invoked with de DE command line parameters, this code creates a ResourceBundle variable to access the MessagesBundle_de_DE.properties file.

. . .
language = new String(“de”);
country = new String (“DE”);

//Create locale and resource bundle
currentLocale = new Locale(language, country);
messages = ResourceBundle.getBundle
           (“MessagesBundle”, currentLocale);
. . .

Applicable error text is accessed by calling the getString method on the ResourceBundle, and passing it the keyword that maps to the applicable error text.

try {
  //try to get data
} catch (javax.ws.rs.NotFoundException e) {           
  System.out.println(messages.getString(“nodata”));
}

User interface text is accessed similarly.

//Set stage title
 setTitle(messages.getString(“title”));

//Create column labels
 col1 = new Label(messages.getString(“1col”));
 col2 = new Label(messages.getString(“2col”));
 ...
 //Create buttons and make action listeners
 purchase = new Button(messages.getString(“purchase”));
 reset = new Button(messages.getString(“reset”));

Internationalize Numbers

Use a NumberFormat object to translate numbers to the correct format for the language in use. A NumberFormat object is created from the currentLocale. The information in the currentLocale tells the NumberFormat object what number format to use.

Once you have a NumberFormat object, all you do is pass in the value you want translated, and you receive a String that contains the number in the correct format. The value can be passed in as any data type used for numbers such as int, Integer, double, or Double. No code to convert an Integer to an int and back again is needed.

 //Create number formatter
    numFormat = NumberFormat.getNumberInstance(
                currentLocale);
    //Display running total
    text = numFormat.format(order.itotal);
    this.items.setText(text);
    //Calculate and display running cost
    order.icost = (order.itotal * 1.25);
    text2 = numFormat.format(order.icost);
    this.cost.setText(text2);
    try {
        send.sendOrder(order);
    } catch (java.rmi.RemoteException e) {
        System.out.println(messages.getString(“send”));
    } catch (java.io.IOException e) {
        System.out.println("nodata");
    }
Advertisements
Internationalization

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s