Previous Section
Table of Contents
Next Section


Chapter 10: Internationalizing Struts Applications

The Internet has created a global, electronic shopping mall. Whereas businesses once catered only to customers in their same geographic region, today business is transacted with customers worldwide. A product of this globalization is the need for software that can adapt to the conventions and languages used by customers in different countries. To achieve this, applications must provide content in multiple languages. This is called internationalization.

From the start, Java was designed with internationalization in mind. For example, it offers support for Unicode character sets and provides built-in classes that manage locale-specific content. Struts builds upon Java's support, making development of internationalized Struts applications straightforward. This chapter presents an overview of Java's built-in internationalization support and then explains how Struts' internationalization builds upon it.

Understanding Java's Internationalization Support

Java's built-in internationalization support is centered on three main classes. The following table lists each class and its description.

Class

Description

java.util.Locale

Encapsulates the language, country, and variant for a specific locale.

java.util.ResourceBundle

Encapsulates locale-specific resources.

java.text.MessageFormat

Provides methods for creating locale-specific formatted messages.

The following three sections provide a brief introduction to each class.

The java.util.Locale Class

At the core of Java's internationalization API is the Locale class. The Locale class encapsulates the language, country, and variant for a specific locale. That is, the Locale class is used to uniquely represent a specific locale. All other locale-oriented classes use instances of this class to tailor their behavior and output to a specific locale.

When you create a Locale object you specify the locale's language; language and country; or language, country, and variant. Thus, Locale lets you represent as specific a locale as needed. The constructors for the Locale class are shown here:

Locale(String language)
Locale(String language, String country)
Locale(String language, String country, String variant)

The following snippet illustrates each of these uses in order:

Locale locale1 = new Locale("en");
Locale locale2 = new Locale("en", "US");
Locale locale3 = new Locale("en", "US", "WIN");

The first example creates an English language-specific locale; the second example creates a United States English language-specific locale; and the third example creates a United States English language Windows-specific locale. The more attributes you specify, the more specific the locale is. For example, if you create a Spanish language Locale object, as shown next, it will serve all users whose language is Spanish, independent of what country they are from:

Locale spanishLocale = new Locale("es");

Alternatively, to create a Spanish language locale specific to Mexico, you can use this declaration:

Locale mexicoSpanishLocale = new Locale("es", "MX");

Using combinations of language, country, and variant together narrows the scope of Locale objects.

The Locale object's language argument must be specified using a valid two-letter ISO-639 language code (e.g., 'en' for English or 'es' for Spanish). The country argument must be specified using a valid uppercase two-letter ISO-3166 country code (e.g., 'US' for United States or 'CA' for Canada'). The variant argument is for a vendor- or browser-specific code (e.g., 'WIN' for Windows or 'MAC' for Macintosh). For a listing of each of the ISO language and country codes, visit their respective specification Web sites listed here:

http://www.unicode.org/unicode/onlinedat/languages.html

http://www.unicode.org/unicode/onlinedat/countries.html

Here are a couple important notes about Locale objects that you should know. First, Locale objects are immutable; thus, once you have created an instance, you cannot change any of its attributes (i.e., language, country, or variant). Because of this, if you need to change a locale for some reason, you must create a new locale instance. Second, the Locale class provides several static locale constants as a convenience. For example, Locale.US and Locale.FRANCE are country code constants and Locale.ENGLISH and Locale.FRENCH are language code constants. Following is an example use of the constants:

Locale us = Locale.US;
Locale french = Locale.FRENCH;

The java.util.ResourceBundle Class

The ResourceBundle class encapsulates locale-specific resources for an application. Essentially, the ResourceBundle class is a central repository that applications use to hold resources. Applications simply provide the name of a resource and a locale, and the bundle returns the application resource for the specified locale. The resource names are the same across all locales, enabling an application to know only the name of a resource and how it should be handled. For example, assume that a label is displayed beside a field on an HTML form. Using a resource bundle, an application can obtain the text for the label by requesting the label from the resource bundle by name. When requesting the label, the application passes a locale that specifies what version of the label's text to obtain, such as English, Spanish, and so on. The resource bundle returns to the application the proper version of the text, which it can then display beside the field. The application does not need to have separate logic for each translation. It just needs to know how to get the text and display it. This is the premise and power of internationalization.

Resource bundles most often contain locale-specific text (e.g., error messages, field and button labels, and so on) for applications, but can also be used to manage any locale-specific resources. The ResourceBundle class provides the core interface for working with resource bundles, but is not intended to be used directly because it is an abstract class. Java provides two subclasses of ResourceBundle: java.util.ListResourceBundle and java.util.PropertyResourceBundle. The ListResourceBundle class is an abstract class that provides a mechanism for using lists to store resources; because it's abstract you must provide a concrete subclass implementation to make use of it. The PropertyResourceBundle class is a concrete subclass that provides a mechanism for using properties files to store resources. This class is the most commonly used for working with resource bundles in Java and is the default mechanism used by the ResourceBundle class's static getBundle( ) methods.

Because the PropertyResourceBundle class is the default (and the most commonly used) implementation, it's important to know how it works. The PropertyResourceBundle class provides an interface to access resources stored in properties files. Internally, it uses the java.util.Properties class. The PropertyResourceBundle class requires that resource bundle properties files be named using a special scheme, which takes the following format:

bundlename_language_country_variant.properties

For example, if you had a bundle named ApplicationResources for the English language in the United States for the Windows platform, the properties file would be ApplicationResources_en_US_WIN.properties. Of course, not all locale components are required, so the name for a simple English file would be ApplicationResources_en.properties. Resource bundles also support the concept of a default resource bundle, which in this case is simply ApplicationResources.properties.

The default resource bundle is used when there is not a bundle for a locale or if a locale-specific bundle does not have an entry for a certain resource. For example, if you have a French bundle and it does not contain an entry for a requested resource, the ResourceBundle classes will attempt to find an entry for that resource in the default bundle. Typically, applications use the default bundle to store the English version of resources and then create language-specific bundles for other languages. However, if a language-specific bundle does not have an entry for a resource, the English version will be used from the default bundle.

The java.text.MessageFormat Class

The MessageFormat class provides methods for creating locale-specific, formatted messages. As you saw in the previous section, resource bundles can be used to retrieve locale-specific static messages from properties files. Although that is quite useful, there will be times when a message needs to be constructed dynamically, at run time. The MessageFormat class provides the means for creating locale-specific dynamic messages. To understand what dynamic messages are, it helps to first review properties file-based static messages. An example snippet from a properties file is shown next:

error.firstName.required=First Name is required.
error.lastName.required=Last Name is required.

This snippet shows two properties whose values are very similar. The main difference between them is simply the subject of what is required. Using this model, a property would have to be created for every required field, with each property being almost identical to the next. Thus, significant duplication would exist.

With the MessageFormat class, you can create a dynamic message to solve the problem of duplication. Instead of creating a separate static property for each required field, you create one dynamic property that can be used to generate specific messages. Following is an example of a dynamic message:

error.required={0} is required.

This property specifies a placeholder with {0}. The MessageFormat class takes a dynamic message and a list of substitution data and replaces the dynamic message's placeholders with the substitution data. To illustrate how this works, consider the following example code:

ResourceBundle bundle = ResourceBundle.getBundle("ApplicationResources");
String requiredMessage = bundle.getString("error.required");

String[] substituteData1 = {"First Name"};
String firstNameMessage =
  MessageFormat.format(requiredMessage, substituteData1);

String[] substituteData2 = {"Last Name"};
String lastNameMessage =
  MessageFormat.format(requiredMessage, substituteData2);

In this example, the dynamic message is used twice, with different substitution data to create unique messages. This is a powerful and often used technique in internationalized applications. Of course, the MessageFormat class accepts a locale in its constructor so that dynamic messages can be tailored to specific locales.



Previous Section
Table of Contents
Next Section