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:
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 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 Locale
s
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 Locale
s 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 String
s that serve as keys. A ResourceBundle
of a given name may be defined
for many different Locale
s. 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 Locale
s; 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 ResourceBundle
s
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 Locale
s 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 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).
Get Learning Java, 4th Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.