Internationalization

An application is internationalized, if it can correctly handle different encodings of character data. An application is localized, if it formats and interprets data (dates, times, timezones, currencies, messages and so on) according to rules specific to the user's locale (country and language).

Internationalization (I18N) is the process of designing an application so that it can be adapted to various languages and regions without engineering changes. Localization (L10N) is the use of locale-specific language and constructs at run time.

The Java Internationalization API

Java Internationalization shows how to write software that is multi-lingual, using Unicode, a standard system that supports hundreds of character sets.
The Java Internationalization API is a comprehensive set of APIs for creating multilingual applications. The JDK internationalization features, from its version 1.1, include:

Java Interfaces for Internationalization

Users of the Java internationalization interfaces should be familiar with the following interfaces included in the Java Developer's Kit (JDK):

Set the Locale

The concept of a Locale object, which identifies a specific cultural region, includes information about the country or region. If a class varies its behavior according to Locale, it is said to be locale-sensitive. For example, the NumberFormat class is locale-sensitive; the format of the number it returns depends on the Locale. Thus NumberFormat may return a number as 902 300 (France), or 902.300 (Germany), or 902,300 (United States). Locale objects are only identifiers.

Most operating systems allow to indicate their locale or to modify it. For instance Windows NT does this through the control panel, under the Regional Option  icon. In Java, you can get the Locale object that matches the user's control-panel setting using
    myLocale = Locale.getDefault();

You can also create Locale objects for specific places by indicating the language and country you want, such as
    myLocale = new Locale("fr", "CA");
for "Canadian French."

The next example creates Locale objects for the English language in the United States and Great Britain:

bLocale = new Locale("en", "US");
cLocale = new Locale("en", "GB");
The strings you pass to the Locale constructor are two-letter language and country codes, as defined by ISO standards (put here reference)

Isolate your Locale Data

The first step in making an international Java program is to isolate all elements of your Java code that will need to change in another country. This includes user-interface text -- label text, menu items, shortcut keys, messages, and the like.

The ResourceBundle class is an abstract class that provides an easy way to organize and retrieve locale-specific strings or other resources. It stores these resources in an external file, along with a key that you use to retrieve the information. You'll create a ResourceBundle for each locale your Java program supports.

The ResourceBundle class is an abstract class in the java.util package. You can provide your own subclass of ResourceBundle or use one of the subclass implementations, as in the case of PropertyResourceBundle or ListResourceBundle.

Resource bundles inherit from the ResourceBundle class and contain localized elements that are stored external to an application. Resource bundles share a base name. The base name TeT_Bundle, to display transactional messages such “Transaction Commited”, might be selected because of the resources it contains. Locale information further differentiates a resource bundle. For example, TeT_Bundle_it means that this resource bundle contains locale-specific transactional messages for Italian.

To select the appropriate ResourceBundle, invoke the ResourceBundle.getBundle method. The following example selects the TeT_Bundle ResourceBundle for the Locale that matches the French language, the country of Canada.

Locale currentLocale = new Locale("fr", "CA");
ResourceBundle introLabels =    ResourceBundle.getBundle("TeT_Bundle", currentLocale);
Java loads your resources based on the locale argument to the getBundle method. It searches for matching files with various suffixes, based on the language, country, and any variant or dialect to try to find the best match. Java tries to find a complete match first, and then works its way down to the base filename as a last resort.

You should always supply a base resource bundle with no suffixes, so that your program will still work if the user's locale does not match any of the resource bundles you supply. The default file can contain the U.S. English strings. Then you should provide properties files for each additional language you want to support.

Basically, a resource bundle is a container for key/value pairs. The key is used to identify a locale-specific resource in a bundle. If that key is found in a particular resource bundle, its value is returned.

The jdk API defines two kinds of ResourceBundle subclasses -- the PropertyResourceBundle and ListResourceBundle.
A PropertyResourceBundle is backed by a properties file. A properties file is a plain-text file that contains translatable text. Properties files are not part of the Java source code, and they can contain values for String objects only. A simple default properties file, named hpts_Bundle.properties,  for messages sent by HPTS could be.

# Sample properties file for demonstrating PropertyResourceBundle
# Text to inform on transaction outcomes in English (by default)
trans_committed= Transaction Committed
trans_rolledback= Transaction Rolled Back
# …
The equivalent properties file, hpts_Bundle_fr_FR.properties, for French would be:
# Sample properties file for demonstrating PropertyResourceBundle
# Text to inform on transaction outcomes in French
trans_committed = La Transaction a été Validée
trans_rolledback = La Transaction a été Abandonnée
# …

Use a Hint to retrieve a translation

As described in properties files, keys are exactly the same, only the associated strings differ according to the national text. To enable a particular reader understanding a particular language, say English, to recognize the corresponding English message for a message displayed with a different language such French or Japanese, it is useful to add within the displayed string a particular hint for this aim. For instance properties files described above, hpts_Bundle.properties and   hpts_Bundle_fr_FR.properties, could be modified as follow: [trans_1] and [trans_2] are displayed whatever the resource bundle or language chosen. In such way a reader, such a support team can determine quickly the corresponding message to its own language.

The ListResourceBundle class manages resources with a convenient list. Each ListResourceBundle is backed by a class file. You can store any locale-specific object in a ListResourceBundle. Resources in a ListResourceBundle are not limited to text strings, as are the resources in a PropertyResourceBundle. For example, images are one example of resources that can be localized, but can't be stored as text strings.  To add support for an additional Locale, you create another source file and compile it into a class file as described below where the first file, hpts_Bundle.java defines the default English messages and the second file, hpts_Bundle_fr_FR.java, its equivalent for French.
 

import java. util.*;
public class TeT_Bundle extends ListResourceBundle {
  public Object [][] getContents() {
    return contents;
  }

  static final Object [][] contents = {
    {" trans_committed", “Transaction Committed”},
    {" trans_rolledback", “Transaction Rolled Back”},
  };
}


 
import java. util.*;
public class TeT_Bundle extends ListResourceBundle {
  public Object [][] getContents() {
    return contents;
  }

  static final Object [][] contents = {
    {" trans_committed", “Transaction Committed”},
    {" trans_rolledback", “Transaction Rolled Back”},
  };
}

Note that the ResourceBundle class is flexible. If you first put your locale-specific String objects in a PropertyResourceBundle and then later decided to use ListResourceBundle instead, there is no impact on the code.

Example
The following example illustrates how to use the internationalization API allowing separating the text with a language specified by the user, from the source code.
 
import com.hp.mw.orbportability.*;
import com.hp.mw.ts.jts.extensions.*;
import com.hp.mw.ts.jts.common.jtsPropertyManager;
import com.hp.mw.ts.jts.common.Environment;
import com.hp.mw.ts.jts.OTSManager;
import com.hp.mwlabs.ts.jts.ORBManager;

import org.omg.CosTransactions.*;
import org.omg.CosTransactions.Unavailable;
import org.omg.CosTransactions.WrongTransaction;
import org.omg.CORBA.SystemException;
import org.omg.CORBA.UserException;
import org.omg.CORBA.INVALID_TRANSACTION;
import org.omg.CORBA.TRANSACTION_REQUIRED;
import org.omg.CORBA.TRANSACTION_ROLLEDBACK;

public class TransactionTest1
{
    public static void main (String[] args)
    {
      ORB myORB = null;
      RootOA myOA = null;
      String language;
      String country;
      if (args.length != 2) {
          language = new String("en");
          country = new String("US");
      } else {
          language = new String(args[0]);
          country = new String(args[1]);
      }
      Locale currentLocale;
      ResourceBundle messages;
      currentLocale = new Locale(language, country);
      trans_message = ResourceBundle.getBundle("hpts_Bundle", currentLocale);

      try
      {
        myORB = ORB.getInstance("test");
        myOA = OA.getRootOA(myORB); 
        myORB.initORB(args, null);
        myOA.initOA(); 
        ORBManager.setORB(myORB);
        ORBManager.setPOA(myOA);
       }
      catch (Exception e)
      {
        System.err.println("Initialisation failed: "+e);
      }

      try
      {
        Current current = OTSManager.get_current();
        current.begin();
        current.commit(true);
        System.out.println(tran_message.getString("trans_committed"));
      }
      catch (INVALID_TRANSACTION  e1)
      {
        System.err.println("Invalid Transaction "+e1);
      }
      catch (TRANSACTION_ROLLEDBACK  e1)
      {
        System.out.println(tran_message.getString("trans_rolledback"));
      }

      myOA.destroy();
      myORB.shutdown();
    } 
}

In the following example the language code is fr (French) and the country code is FR (France), so the program displays the messages in French:

% java TransDemoClient fr FR
La Transaction a été validée
Rather to specify explicitly the language to be used to display messages, a property variable can be