Packaging Locale-Sensitive Data

   

The Locale class allows you to easily handle Locale -sensitive methods . However, most programs ( especially applets and GUI-based applications) require the use of Strings, data, and other resources that also need to be localized. For instance, most GUI programs have OK and Cancel buttons. This is fine for the United States, but other locales require different labels for these buttons . In Germany, for instance, you might use Gut and Abbrechen instead. Traditionally, information such as this was included in the source code of an application, which can lead to many problems when trying to simultaneously support many localized versions of one program. To solve this problem, Java provides a way to encapsulate this data into objects called ResourceBundles that are loaded upon demand.

Resource Bundles

A ResourceBundle conceptually represents a set of Locale -specific information. In reality, the specific data for the resource bundle can be stored in a class or in a text file. The ResourceBundle base class provides functionality for locating and instantiating the correct ResourceBundle for a given locale. ResourceBundles are loaded according to the name of the ResourceBundle being referenced and the bundles must follow a strict naming convention to be loaded properly. For example, if you have a class called LabelBundle that extends ResourceBundle and contains the names of all GUI labels you use in an application. The class called LabelBundle provides default information; LabelBundle_fr provides French labels; LabelBundle_ge_GE provides German labels; and LabelBundle_en_US_MAC provides Macintosh-specific American English labels. You request a ResourceBundle using the following static method:

 ResourceBundle static getResourceBundle(String baseName, Locale locale); 

You can leave off the Locale parameter and the getResourceBundle method will use the default Locale instance. The getResourceBundle method searches for a class that matches baseName, plus certain attributes of the specified Locale. There is a specific search pattern that is used to find the closest match to the Bundle you request:

 bundleName + "_" + localeLanguage + "_" + localeCountry + "_" + localeVariant bundleName + "_" + localeLanguage + "_" + localeCountry bundleName + "_" + localeLanguage bundleName + "_" + defaultLanguage + "_" + defaultCountry + "_" + defaultVariant bundleName + "_" + defaultLanguage + "_" + defaultCountry bundleName + "_" + defaultLanguage bundleName 

In this example, if you request the baseName LabelBundle with a fr_FR_WIN (French language, France, Windows platform) Locale, the getResourceBundle() method performs the following steps:

  1. Searches for the class LabelBundle_fr_FR_WIN, which fails because you have defined no such class.

  2. Searches for the class LabelBundle_fr_FR, which also fails because you did not define a France-only Bundle.

  3. Searches for class LabelBundle_fr. This succeeds and returns the class with this name. However, if this search had failed (if you had not supplied a French-language Bundle ), the search would have continued , using the language, country, and variant codes supplied in the default Locale.

Now that you understand the naming convention used with ResourceBundles, take a look at how they are created. The simplest form of ResourceBundles extends the ResourceBundle class directly, and then overrides one method:

 Object handleGetObject(String key) 

This method returns an object that corresponds to the specified key. These keys are internal representations of the content stored in the ResourceBundle and should be the same for all localized versions of the same data. An extremely simple version of your LabelBundle might be defined as follows :

 class LabelBundle extends ResourceBundle {   public Object handleGetObject(String key)   {    if( key.equals("OK") )     return "OK";     else if( key.equals("Cancel") )     return "Cancel";   // Other labels could be handled here   return null; // If the key has no matches, always return null  } } 

Other versions of the same bundle might return values translated into different languages. You can see, however, that this method of handling key-value pairs is inefficient if you have more than a few keys. Luckily, Java provides two subclasses of ResourceBundle that can make life easier:

  • ListResourceBundle

  • PropertyResourceBundle

ListResourceBundles

ListResourceBundles use an array of two-element arrays to store the key-value pairs used earlier. All you have to do is override the default getContents method , like this:

 class LabelBundle extends ListResourceBundle {   static final Object[][] labels = {   { "OK", "OK"} ,   { "Cancel", "Cancel"} ,   ("AnotherKey", "Another Value"}   //More key-value pairs can go here   } ;   public Object[][] getContents()   {     return labels;   } } 

You could also provide your own similar functionality using a hash table, but that's only worthwhile if you want the contents to change dynamically over time.

PropertyResourceBundle

PropertyResourceBundles are created as needed from predefined "property" files stored on disk. These are usually used for system wide settings, or when large amounts of data need to be stored in a key-value pair. PropertyResourceBundles are built from files with the same name as the corresponding class file, but with the .properties extension instead. To implement the LabelBundle_de_DE class, you might provide a file called LabelBundle_de_DE. properties with the following content:

 OK=Gut Cancel=Abbrechen 

Contents are always specified in the form key=value and are assumed to be Strings (although they can be cast into other appropriate objects). This functionality is based on the java.util.Properties class. See Chapter 10, "Data Structures and Java Utilities," for more information on the java.util.Properties class. There is no need to define a Java class when you are using PropertyResourceBundle's. All you need to do is create the properties file (which should be just a plain text file), and make sure the properties file is located somewhere in your system CLASSPATH. This makes it kind of nice because the values can be changed without having to recompile code.

Note

Although the examples given here all deal with String objects, ResourceBundles can store objects of any type, including Dates, Applets, GUI elements, or even other ResourceBundles.


Accessing ResourceBundles as previously mentioned, you load ResourceBundles by name using the static method getResourceBundle. Assuming this succeeds (it throws an exception otherwise ), you can then query individual values within the bundle using the getObject method. Of course, this also usually requires an explicit cast to the kind of object you want, so you need to know this information ahead of time. As a matter of convenience, ResourceBundle also provides the following methods that return already-cast objects:

  • String[] getStringArray(String key)

  • Enumeration getKeys()

  • Object getObject(String key)

  • getString(String key)

   


Special Edition Using Java 2 Standard Edition
Special Edition Using Java 2, Standard Edition (Special Edition Using...)
ISBN: 0789724685
EAN: 2147483647
Year: 1999
Pages: 353

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net