Contents | Prev | Next

Localized Resources

All locale-sensitive classes must be able to access resources customized for the locales they support. To aid in the process of localization, it helps to have these resources grouped together by locale and separated from the locale-neutral parts of the program.

The class ResourceBundle is an abstract base class representing containers of resources. Programmers create subclasses of ResourceBundle that contain resources for a particular locale. New resources can be added to a ResourceBundle or new ResourceBundles can be added to a system without affecting the code that uses them. Packaging resources as classes allows developers to take advantage of Java's class loading mechanism to find resources.

Here's how localized labels for some buttons could be retrieved from a ResourceBundle:

ResourceBundle myResources
= ResourceBundle.getResourceBundle("myPackage.MyResources",
myLocale, myClassLoader); button1 = new Button(myResources.getString("OkKey"));
button2 = new Button(myResources.getString("CancelKey"));
To find the right ResourceBundle subclass, the getResourceBundle() method turns the given ResourceBundle name and locale into a class name then tries to load that class using the specified ClassLoader. If the class can not be found, variations of the class name are built until one can be loaded. The series of names looks like this:

bundleName + "_" + language1 + "_" + country1 + "_" + variant1 
bundleName + "_" + language1 + "_" + country1
bundleName + "_" + language1
bundleName + "_" + language2 + "_" + country2 + "_" + variant2
bundleName + "_" + language2 + "_" + country2
bundleName + "_" + language2
bundleName
where language1, country1 and variant1 are taken from the given locale and language2, country2 and variant2 are taken from the default locale.If no ResourceBundle subclass can be found, a MissingResourceException is thrown.

The bundleName argument must be a fully qualified name such as myPackage.MyResources. By convention, JDK places its ResourceBundles for a package in a sub-package called "resources". For 1.1 beta, the ResourceBundle sub-classes must be public. This is so that the getResourceBundle method may load and instantiate the ResourceBundle objects. Since an application's resources are not properly part of its API, this restriction may be removed for the FCS release.

The last parameter to getResourceBundle is a ClassLoader object. This is used to load the correct ResourceBundle sub-class. For ResourceBundles that are found on the local system, this parameter may be null.

The strings "OkKey" and "CancelKey" are keys used to look up labels appropriate for the locale myLocale. Typically, a stand-alone application uses resources from the default locale rather than for an explicit locale. In this case the argument myLocale may be omitted from the call to load(). An applet, however, will typically use the locale from their Component. This requires that the applet pass an explicit locale argument into load().

Although strings are the most common type of resource, arbitrary objects can be accessed from a ResourceBundle as follows:

SalesTaxTable taxTable 
= (SalesTaxTable) myResources.getObject( "SalesTax" );
There are also several convenience methods, such as getMenu() and getMenuBar(), that, when used, exempt developers from having to do explicit casting.

Organizing Resource Bundles

A typical application might create the following classes:

AppResources_fr would contain the French language resources useful for all French language locales. AppResources_fr_BE would contain specializations of AppResources_fr for Belgium. AppResources would probably contain English resources and would act as the class of "last resort" if resources are requested for a locale that does not exist. With this setup, the program would not have to supply separate AppResources_en or AppResources_en_US classes.

A program need not restrict itself to using a single set of ResourceBundles. For example, a set of bundles could be used for exception messages, (ExceptionResources_fr, ExceptionResources_de,...), and another one for widgets, (WidgetResources_fr, WidgetResources_de,...). This flexibility allows components at any granularity to maintain their own resources.

Building ResourceBundle Subclasses

Subclasses of ResourceBundle only need to override the handleGetObject() method. The following very simple example shows how this works. Note that null is returned if a key is not found.

class MyResources extends ResourceBundle {
public Object handleGetObject(String key) {
if (key.equals("okKey")) {
return "Ok";
}
if (key.equals("cancelKey")) {
return "Cancel";
}
return null;
}
}
class MyResources_DE extends ResourceBundle {
public Object handleGetObject(String key) {
if (key.equals("okKey")) {
return "Gut";
}
if (key.equals("cancelKey")) {
return "Vernichten";
}
return null;
}
}
class MyResources_UK extends MyResources {
public Object handleGetObject(String key) {
// don't need okKey, because MyResources class handles it.
if (key.equals("cancelKey")) {
return "Dispose";
}
return null;
}
}
Writing a lot of code like the above example becomes very tedious. For this reason, the Internationalization APIs provide two convenience subclasses of ResourceBundle. The first is ListResourceBundle, which allows the keys and values to be specified in a two-dimensional array. Subclasses of ListResourceBundle provide the array of keys and values and override the getContents method. The following is a simple example:

class MyResource extends ListResourceBundle {
public Object[][] getContents() {
return contents;
}
static final Object[][] contents = {
// LOCALIZE THIS
{"s1", "3"}, // starting value in choice field
{"s2", "MyDisk"}, // starting value in string field
{"s3", "3 Mar 96"}, // starting value in date field
"s4", "The disk `%1' contained %0 on %2."}, // pattern
{"s5", "0"}, // first choice number
{"s6", "no files"}, // first choice value
{"s7", "1"}, // second choice number
{"s8", "one file"}, // second choice value
{"s9", "2"}, // third choice number
// END OF MATERIAL TO LOCALIZE
};
}
The second convenience class is PropertyResourceBundle. This class reads keys and values from a Properties file. The developer need only supply a properties file using the same naming convention as the ResourceBundle subclasses, but with a .properties extension rather than a .class extension.



Contents | Prev | Next
java-intl@java.sun.com
Copyright © 1996 Sun Microsystems, Inc. All rights reserved.