• No results found

WindowTitle=Search Results OKLabel=OK

CancelLabel=Cancel

ResultMessage1=The search found

ResultMessage2= files containing “

ResultMessage3=” on disk “

ResultMessage4=”.

Java provides two concrete subclasses of ResourceBundle. The simpler of these is PropertyResourceBundle. PropertyResourceBundle provides an interface to access resource data in a properties file. A properties file is simply a text file containing a series of key-value pairs. The keys are separated from the values by = signs, and the key-value pairs are separated by carriage returns.

For all types of resource bundles, you give the bundle a name (it’s up to you whether you want to keep all of your program’s resources in a single resource bundle or spread across several). Then you assign each individual resource a programmatic ID (such as “WindowTitle” or “CancelLabel” in the example above). This ID is how the program will access the bundle (this is another case of a hard- coded string that is for internal use and must not be translated). The ID is the key and the actual resource data is the value.

ListResourceBundle

File MyResources.java

public class MyResources extends ListResourceBundle { public Object[][] getContents() {

return contents; }

static final Object[][] contents = { { "WindowTitle", "Search Results" }, { "OKLabel", "OK" },

{ "CancelLabel", "Cancel" },

{ "ResultMessage1", "The search found " }, { "ResultMessage2", " files containing \"" }, { "ResultMessage3", "\" on disk \"" },

{ "ResultMessage4", "\"." } };

}

The other built-in subclass of ResourceBundle is ListResourceBundle.

ListResourceBundles contain the resource data as static class members. This means each list resource bundle is a new class. In essence, the resource-data file the translators mess with is the source code file itself. That means there’s some extra cruft here that the translators don’t need to worry about, but the file format is still pretty simple (you only mess with the contents of “contents”), and it can

accommodate any character encoding and can contain any type of resource data. Again, the key-value-pair structure of a ListResourceBundle is evident here.

User-visible text

ResourceBundle resources =

ResourceBundle.getBundle("MyResources"); Dialog dialog = new Dialog(

rootWindow, resources.getString("WindowTitle"), true);

dialog.add("Center", new Label(

resources.getString("ResultMessage1") + hits

+ resources.getString("ResultMessage2") + searchString + resources.getString("ResultMessage3") + searchRoot + resources.getString("ResultMessage4")));

Container container = new Panel(); dialog.add("South", container); container.setLayout(new FlowLayout(FlowLayout.RIGHT)); container.add( new Button(resources.getString("OKLabel"))); container.add( new Button(resources.getString("CancelLabel"))); dialog.pack(); dialog.show();

Whichever type of resource bundle you’re using, your code accesses it the same way. Our original code snippet putting up the dialog box would look like this whether the resource data is in a PropertyResourceBundle, a ListResourceBundle, or some program-defined type of resource bundle. The red parts are the parts that changed from the original version of this snippet. Instead of having hard-coded strings, we have calls to fetch a particular value from the resource bundle. We also have to add a line at the beginning of the function to fetch the resource bundle itself. I ran out of room to show it here, but the ResourceBundle APIs can throw

exceptions (what if the resource bundle isn’t there, or a particular resource isn’t in it?), so this code snippet would normally be enclosed in a try-catch construct. This code is obviously somewhat longer and harder to read, but it completely separates the code from the resource data.

Translated ListResourceBundle

File MyResources_fr.java

public class MyResources_fr

extends ListResourceBundle { public Object[][] getContents() {

return contents; }

static final Object[][] contents = {

{ "WindowTitle", "Résultant de la recherche" }, { "CancelLabel", "Annuler" },

{ "ResultMessage1", "La recherche a trouvé " }, { "ResultMessage2",

" fichiers ayant le mot \"" },

{ "ResultMessage3", "\" sur le disque \"" }, { "ResultMessage4", "\"." }

}; }

So what happens now when you want to translate the text? Instead of going through the program source code with a fine-toothed comb looking for strings that need translating, all of those strings are collected here in one place. The translator simply copies the untranslated file and translates all the strings into his language. Notice that we’ve given the resource bundle a new name. This allows a program to carry around resources for several different languages or countries with the same executable, and allows the program to dynamically select the right resources for whatever language a particular user is using at the moment. For each variant of the same resource bundle, the new one has the original one’s name tagged with a locale ID (a Locale is a Java object that identifies a particular combination of language and country [and sometimes other distinguishing characteristics]).

Notice that the definition of “OKLabel” is missing here. That’s because “OK” is still “OK” when translated into French. Resource bundles are arranged in a hierarchy going from least specific to most specific. Any bundle can omit a resource and the resource-loading mechanism will automatically fall back on the more general bundle for that resource’s value. In order for this “inheritance” mechanism to work right (which is especially important when you don’t have a resource for some locale), there are certain rules you have to follow about which bundles you provide and which resources go into which ones.

The Java Internationalization

Related documents