Internationalization

The Java VM lets us write code that executes in the same way on any Java platform. But in a global marketplace, that is only half the battle. A big question remains: will the app content and data be understandable to end users worldwide? Must users know English to use your app? The answer is that Java provides thorough support for localizing the text of your app for most modern languages and dialects. In this section, we'll talk about the concepts of internationalization (often abbreviated "I18N") and the classes that support them.

The java.util.Locale Class

Internationalization coding revolves around the Locale class. The class itself is very simple; it encapsulates a country code, a language code, and a rarely used variant code. Commonly used languages and countries are defined as constants in the Locale class. (Maybe it's ironic that these names are all in English.) You can retrieve the codes or readable names, as follows:

 Locale l = Locale.ITALIAN;
 System.out.println(l.getCountry( )); // IT
 System.out.println(l.getDisplayCountry( )); // Italy
 System.out.println(l.getLanguage( )); // it
 System.out.println(l.getDisplayLanguage( )); // Italian


The country codes comply with ISO 3166. You will find a complete list of country codes at http://www.chemie.fu-berlin.de/diverse/doc/ISO_3166.html. The language codes comply with ISO 639. A complete list of language codes is online at http://www.ics.uci.edu/pub/ietf/http/related/iso639.txt. There is no official set of variant codes; they are designated as vendor-specific or platform-specific. You can get an array of all supported Locales with the static getAvailableLocales( ) method (which you might use to let your users choose). Or you can retrieve the default Locale for the location where your code is running with the static Locale.getDefault( ) method and let the system decide for you. Many classes throughout the Java API use a Locale to decide how to represent text. We ran into one earlier when talking about sorting text with the Collator class. We'll see more later in this chapter used to format numbers and currency strings, and again in the next chapter with the DateFormat class, which uses Locales to determine how to format and parse dates and times. Without getting into the details yet, here is a quick example:

 System.out.printf( Locale.ITALIAN, "%f\n", 3.14 ); // "3,14"


The statement above uses the Italian Locale to indicate that the decimal number 3.14 should be formatted as it would in Italian, using a comma instead of a decimal point. We'll talk about formatting text in detail later in this chapter.

Resource Bundles

Before we move on to the details of formatting messages and values, we might take a step back and ask a bigger question: what about the messages themselves? How can we write and manage apps that are truly multilingual in their user interfaces and in all the messages they display to the user? We can discover our locale, but how do we manage all of the app text in our code? The ResourceBundle class offers a clean, flexible solution for factoring out the text and resources of your app into language-specific classes or text files. A ResourceBundle is a collection of objects your app can access by name. It acts much like the Hashtable or Map collections we'll discuss in , looking up objects based on Strings that serve as keys. A ResourceBundle of a given name may be defined for many different Locales. To get a particular ResourceBundle, call the factory method ResourceBundle.getBundle( ), which accepts the name of the ResourceBundle and a Locale. The following example gets the ResourceBundle named "Message" for two Locales; from each bundle, it retrieves the message whose key is "HelloMessage" and prints the message:

 import java.util.*;
 public class Hello {
 public static void main(String[] args) {
 ResourceBundle bun;
 bun = ResourceBundle.getBundle("Message", Locale.ITALY);
 System.out.println(bun.getString("HelloMessage"));
 bun = ResourceBundle.getBundle("Message", Locale.US);
 System.out.println(bun.getString("HelloMessage"));
 }
 }


The getBundle( ) method throws the runtime exception MissingResourceException if an appropriate ResourceBundle cannot be located. You can provide ResourceBundles in two ways: either as compiled Java classes (hard-coded Java) or as simple property files. Resource bundles implemented as classes are either subclasses of ListResourceBundle or direct implementations of ResourceBundle. Resource bundles backed by a property file are represented at runtime by a PropertyResourceBundle object. ResourceBundle.getBundle( ) returns either a matching class or an instance of PropertyResourceBundle corresponding to a matching property file. The algorithm used by getBundle( ) is based on appending the country and language codes of the requested Locale to the name of the resource. Specifically, it searches for resources in this order:

 name_language_country_variant
 name_language_country
 name_language
 name
 name_default-language_default-country_default-variant
 name_default-language_default-country
 name_default-language


In this example, when we try to get the ResourceBundle named Message, specific to Locale.ITALY, it searches for the following names (no variant codes are in the Locales we are using):

 Message_it_IT
 Message_it
 Message
 Message_en_US
 Message_en


Let's define the Message_it_IT ResourceBundle as a hardcoded class, a subclass of ListResourceBundle:

 import java.util.*;
 public class Message_it_IT extends ListResourceBundle {
 public Object[][] getContents( ) {
 return contents;
 }
 static final Object[][] contents = {
 {"HelloMessage", "Buon giorno, world!"},
 {"OtherMessage", "Ciao."},
 };
 }


ListResourceBundle makes it easy to define a ResourceBundle class; all we have to do is override the getContents( ) method. This method simply returns a two-dimensional array containing the names and values of its resources. In this example, contents[1][0] is the second key (OtherMessage), and contents [1][1] is the corresponding message (Ciao.). Let's define a ResourceBundle for Locale.US. This time, we'll take the easy way and make a property file. Save the following data in a file called Message_en_US.properties:

 HelloMessage=Hello, world!
 OtherMessage=Bye.


So what happens if somebody runs your program in Locale.FRANCE, and no ResourceBundle is defined for that Locale? To avoid a runtime MissingResourceExcep-tion, it's a good idea to define a default ResourceBundle. In our example, you can change the name of the property file to Message.properties. That way, if a language- or country-specific ResourceBundle cannot be found, your app can still run.

Java ScreenShot
Comments