Using Java 6 to Access Translatable Text in XML Files | WebReference

Using Java 6 to Access Translatable Text in XML Files

By Rob Gravelle


[next]

Java's Properties files and ResourceBundleLists class were introduced as a means of efficiently managing translatable text in your Java applications. Moreover, they represent an extensible framework that can be adapted to suit your particular circumstances. Thus, Java code can easily be made to work with translatable text in various formats, such as XML.

The mechanism that allows you to work with your preferred file format(s) is the Java 6 ResourceBundle.Control object. As you shall see in this article, developers can use it in conjunction with the Properties class to access XML files with little effort.

The Properties Class

The Properties class represents a persistent set of properties. These properties can be saved to and loaded from a stream. The Properties class inherits from Hashtable, so each key and its corresponding value in the property list is a string. A property list can contain another property list as its "defaults"; this second property list is searched if the property key is not found in the original property list.

It possesses a number of methods to help manage your properties, two of which are especially relevant to this discussion. They are loadFromXML(InputStream) and storeToXML(OutputStream, String, String), which load and store properties in a simple XML format.

If you're not into reading XML Document Type Definitions (DTDs), here's some news that you'll love! Java accepts a specially formatted XML file by default. For each <ENTRY> tag in a Java XML properties document, there is a key attribute, with the contents of the entry being its value. By default Java uses UTF-8 character encoding, but a specific encoding may be specified if required. As seen in the following sample, a Java XML Properties document contains a special DOCTYPE declaration to identify the format:

Note that the system URI (https://java.sun.com/dtd/properties.dtd) in the DOCTYPE declaration is not accessed when exporting or importing properties; it merely serves as a string to uniquely identify the DTD. Many people make the mistake of substituting their own DTD. This does not work; using the loadFromXML() and storeToXML() methods to access XML that does not conform to the above format will result in an InvalidPropertiesFormatException.

The following XMLResourceBundle class contains a private Properties member variable to store properties retrieved from an XML document. The constructor accepts an InputStream, which can be read directly from the Properties.loadFromXML(InputStream) method. The required handleGetObject() method accesses the requested property using the Properties.getProperty(String key) method, which overrides the HashTable implementation:

Loading XML-based ResourceBundles

You will need some code to use your XMLResourceBundle class instead of the usual ResourceBundle. In the Isolating Locale-Specific Text in International Java Applications Using ResourceBundles article, I described how to write your own ResourceBundle by subclassing ResourceBundle and how to use the ResourceBundle.Control class to locate and instantiate your resource bundle. By doing so, you'll be able to create your custom ResourceBundle class to fetch the appropriate bundle using the getBundle() method. You'll use code similar to the following:

Of course, there is no Control.FORMAT_XML constant, but you can just as easily set the format type by overriding the ResourceBundle.Control class's getFormats() method. It returns a list of strings containing formats you can use to load resource bundles for the given base name. The ResourceBundle's getBundle() static method tries to load resource bundles with formats in the order specified by the list. The list returned by this method must have at least one string. The predefined formats are FORMAT_CLASS for class-based resource bundles and FORMAT_PROPERTIES for properties-based ones:

The getCandidateLocales() method, which returns a list of locale objects as candidate locales for the base name and locale, is the only method of the ResourceBundle.Control class that you must override. This is well suited to an anonymous class, as I created in the isolating locale-specific text article:


[next]