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 application content and data be understandable to end users worldwide? Must users know English to use your application? The answer is that Java provides thorough support for localizing the text of your application 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.
Internationalization programming 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:
Localel=Locale.ITALIAN;System.out.println(l.getCountry());// ITSystem.out.println(l.getDisplayCountry());// ItalySystem.out.println(l.getLanguage());// itSystem.out.println(l.getDisplayLanguage());// Italian
The country codes comply with ISO 3166. You will find a
complete list of country codes at the RIPE Network Coordination Centre.
The language codes comply with ISO 639. A complete list of language
codes is online at the US
government website. 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 preceding statement 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 more about formatting text later in this
chapter.
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 applications
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 application text in our code? The ResourceBundle class
offers a clean, flexible solution for factoring out the text and
resources of your application into language-specific classes or text
files.
A ResourceBundle is a
collection of objects that your application can access by name. It acts
much like the Hashtable or Map collections we’ll discuss in Chapter 11, 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:
importjava.util.*;publicclassHello{publicstaticvoidmain(String[]args){ResourceBundlebun;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_variantname_language_countryname_languagenamename_default-language_default-country_default-variantname_default-language_default-countryname_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_ITMessage_itMessageMessage_en_USMessage_en
Let’s define the Message_it_IT
ResourceBundle as a hardcoded class, a subclass of ListResourceBundle:
importjava.util.*;publicclassMessage_it_ITextendsListResourceBundle{publicObject[][]getContents(){returncontents;}staticfinalObject[][]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 MissingResourceException, 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 application can still run (by falling back to this
English representation).