Internationalization and Localization Let's Translate Our ...

kaputmaltwormSoftware and s/w Development

Aug 15, 2012 (4 years and 10 months ago)

504 views

205
■ ■ ■
C H A P T E R 1 0
Internationalization and
Localization
Let’s Translate Our Code!
T
his chapter shows how to internationalize an application, adapting it to several languages.
You will learn the specifics on internationalizing source code and manifest files, as well as
adapting help topics and other resources, such as graphics.
Professional, flexible applications must be designed to adapt as easily as possible to their
specific target countries and languages. For this, the Java API, the NetBeans Platform, and the
NetBeans IDE support internationalization at minimum effort.
INTERNATIONALIZATION VS. LOCALIZATION
Internationalization is the process of designing/implementing an application so that it can easily be localized.
This is the first step, and is done by the software developer. For example, if you put something like
NbBundle.getMessage(...) in your code, you internationalize your application.
Localization is the process of creating/providing of resources (text, icons, etc.) for a specific country/
language. So, if you provide, for example, a Bundle_es_ES.properties file, you localize your application.
An application must be internationalized before it can be localized.
String Literals in Source Code
String literals in source code are outsourced in properties files. The language-dependent
literals can be separated and changed into other languages. This is possible even after the
release of an application. The constants are saved as key/value pairs in a properties file:
CTL_MyTopComponent = My Window
HINT_MyTopComponent = This is My Window
Any such resource file is handled by the Java class ResourceBundle. A ResourceBundle is
responsible for resources of a particular locale setting that specifies both country and language.
For easy handling of properties files and access to a ResourceBundle instance, the NetBeans
206 CHAPTER 10

I NTERNATI ONALI ZATI ON AND LOCALI ZATI ON
Platform provides the class NbBundle. The resource file must be named Bundle.properties, and
typically such a file is created for each package. The easiest way to create ResourceBundle
objects is by the following call:
ResourceBundle bundle = NbBundle.getBundle(MyTopComponent.class);
The class NbBundle creates a ResourceBundle object for the Bundle.properties file,
provided in the package of the class MyTopComponent. The required string literal is easily read
with the ResourceBundle method getString():
String msg = bundle.getString("CTL_MyTopComponent");
If only a few literals are required inside your class, use the method getMessage() to read a
literal directly without creating a ResourceBundle instance:
String msg = NbBundle.getMessage(MyTopComponent.class, "CTL_MyTopComponent");
Also, it is possible to add a placeholder to your string literals. This is most often required for
data or file names/paths. A pair of braces is used as a placeholder, which includes the number
of the parameter:
Result = {0} MP3-Files found for {1}
Pass these parameters to the getMessage() method, which replaces the placeholder with
the parameter. Alternatively, you can pass up to three parameters or an array with an unlimited
number of parameters:
String label = NbBundle.getMessage(MyTopComponent.class,
"Result",
new Integer(results.size()),
search.getText());
For each properties file, only the literals of a single language are saved. To add another
language to your application, save the literals—with the same keys—in a properties file named
Bundle_<language>_<country>.properties in the same folder. The class NbBundle returns the
ResourceBundle using the method getBundle(). This equals the locale setting, which Locale.
getDefault() returns. The Bundle.properties file that does not contain language and country
identification is the default package. This package is used if there is no bundle for the locale
setting available. A specific bundle can also be requested by passing a Locale object to the
getBundle() method. To know in which order the bundles are searched, the method NbBundle.
getLocalizingSuffixes() lists all suffixes in the order used.
The method Locale.getDefault() returns the locale setting of the virtual machine by
default. To run the whole application with a specific locale setting, set the command-line
parameter locale. This parameter passes a language and country identification to the applica-
tion. You can find more information on this in Chapter 11.
The NetBeans IDE provides a wizard for the internationalization of string literals for your
source files. Let the wizard scan your files for strings, which can be moved to a properties file
(see Figure 10-1). You can thereby edit the key, the value, and the code to be pasted, rather than
the literal. You can find the wizard by going to Tools ➤ Internationalization ➤ International-
ization Wizard.
CHAPTER 10

I NTERNATI ONALI ZATI ON AND LOCALI ZATI ON 207
Figure 10-1. Move string literals automatically to a bundle and paste the needed source code with
the Internationalization wizard.
String Literals in the Manifest File
Among the string literals of the source files, you can also internationalize the textual informa-
tion of the manifest file. There are two options for doing so. The first option appends a
language identifier to the manifest attributes for using the same attribute several times:
Manifest-Version: 1.0
OpenIDE-Module: com.galileo.netbeans.module
OpenIDE-Module-Name: My Module
OpenIDE-Module-Name_de: Mein Modul
The second option (preferred by the author) is to move the attributes you intend to inter-
nationalize to a properties file. The attribute names are used as keys and are provided in the
according bundle for each language. For the attributes to be read off the bundle, notify the
manifest file by using the OpenIDE-Module-Localizing-Bundle attribute, as shown in Listings
10-1 through 10-3 (see also Chapter 3).
Listing 10-1. Manifest.mf
Manifest-Version: 1.0
OpenIDE-Module: com.galileo.netbeans.module
OpenIDE-Module-Localizing-Bundle: com/galileo/netbeans/module/Bundle.properties
208 CHAPTER 10

I NTERNATI ONALI ZATI ON AND LOCALI ZATI ON
Listing 10-2. Bundle.properties
OpenIDE-Module-Name = My Module
Listing 10-3. Bundle_de.properties
OpenIDE-Module-Name = Mein Modul
Internationalization of Help Pages
Generally, the help pages, including the helpset configuration files, are internationalized like
properties files (see the “String Literals in Source Code” section) by appending country and/or
language identifiers. Since a helpset typically consists of a large number of files, this results in a
confusing structure. Therefore, it is possible to store the files intended for internationalization
in a subfolder (see Figure 10-2). Language and country identification is no longer necessary, as
this is already represented by the subfolder.
Figure 10-2. Helpsets for specific languages are stored in separate folders.
Only the helpset file module-hs.xml remains in the default folder, and has no identifier. In
this file, you delegate to the corresponding folders (see Listing 10-4). The helpset file without
an identifier is always used when the active locale setting does not match with the existing files.
Normally, the default package contains the English version.
CHAPTER 10

I NTERNATI ONALI ZATI ON AND LOCALI ZATI ON 209
Listing 10-4. Helpset file that refers to a language-specific package
<maps>
<homeID>com.galileo.netbeans.module.about</homeID>
<mapref location="default/module-map.xml"/>
</maps>
<view mergetype="javax.help.AppendMerge">
<name>TOC</name>
<label>Contents</label>
<type>javax.help.TOCView</type>
<data>default/module-toc.xml</data>
</view>
<view mergetype="javax.help.AppendMerge">
<name>Index</name>
<label>Index</label>
<type>javax.help.IndexView</type>
<data>default/module-idx.xml</data>
</view>
Internationalizing Other Resources
Consider previously mentioned areas of internationalization of applications. Among those of
greatest importance, there are additional possibilities for internationalizing other application
components. The NetBeans Platform provides such possibilities.
Graphics
Not only can text be adapted as language and country specific, but also graphics such as icons.
For this purpose, the ImageUtilities class provides a version of the loadImage() method that is
usually used for loading graphics. Set a Boolean parameter for whether an available language/
country-specific version of the graphic should be loaded against the current locale setting. The
method NbBundle.getLocalizingSuffixes() lists possible identifications used as the search
order.
Image img = ImageUtilities.loadImage("resources/icon.gif", true);
If the active locale setting is, for example, de_DE on this call, first icon_de_DE.gif and
icon_de.gif are searched.
Any File
The NetBeans Platform defines a special protocol for loading other internationalized resources.
This is the nbresloc protocol, an extension of the nbres protocol that loads resources of all avail-
able modules. You can easily create a URL object for a resource addressed with this protocol:
URL u =new URL("nbresloc:/com/galileo/netbeans/module/icon.png");
ImageIcon icon = new ImageIcon(u);
210 CHAPTER 10

I NTERNATI ONALI ZATI ON AND LOCALI ZATI ON
If the locale setting is de_DE and a file named icon_de_DE.png or icon_de.png exists, then
this icon is loaded instead of icon.png.
Folders and Files
The System Filesystem provides two special attributes to internationalize names and icons of
folders and files. This makes sense for, e.g., menus whose names are only in the layer file
declared and cannot be read from the NbBundle class. These attributes are SystemFileSystem.
localizingBundle and SystemFileSystem.icon. With the first, link to your resource bundle,
leaving the .properties extension out. In this bundle, a key is searched that matches the full
path of the folder or file that the SystemFileSystem.localizingBundle attribute contains. In the
following example (see Listings 10-5 through 10-7), it concerns Menu/MyMenu and Menu/MyMenu/
MySubMenu. With the SystemFileSystem.icon attribute, you may additionally set an icon for the
folder or the file. Use the nbresloc protocol to load the internationalized version.
Listing 10-5. Layer.xml
<folder name="Menu">
<folder name="MyMenu">
<attr name="SystemFileSystem.localizingBundle"
stringvalue="com.galileo.netbeans.module.Bundle"/>
<folder name="MySubMenu">
<attr name="SystemFileSystem.localizingBundle"
stringvalue="com.galileo.netbeans.module.Bundle"/>
<attr name="SystemFileSystem.icon"
urlvalue="nbresloc:/com/galileo/netbeans/module/icon.png"/>
</folder>
</folder>
</folder>
Listing 10-6. Bundle.properties
Menu/MyMenu=Extras
Menu/MyMenu/MySubMenu=My Tools
Listing 10-7. Bundle_de.properties
Menu/MyMenu=Extras
Menu/MyMenu/MySubMenu=Meine Tools
In addition to these two special attributes, NetBeans Platform 6.5 introduces a generic
approach to localizing attributes. Instead of stringvalue or urlvalue, you can use bundlevalue
to point to a string literal in a properties bundle. You can use bundlevalue for any attributes in
CHAPTER 10

I NTERNATI ONALI ZATI ON AND LOCALI ZATI ON 211
an XMLFileSystem like the System Filesystem. We already used this approach while registering
actions in Chapter 4, registering DataLoaders in Chapter 7, and adding Option panels in
Chapter 9.
Let’s now look at an action registration entry in the layer file. We want to place the action’s
name in a properties bundle instead of writing it directly into the layer file. To load the value of
the displayName attribute from a properties bundle, the entry is defined as shown in Listing 10-8.
Listing 10-8. Usage of bundlevalue attributes
<file name="com-galileo-netbeans-module-MyFirstAction.instance">
<attr name="displayName"
bundlevalue="com.galileo.netbeans.module.Bundle#CTL_MyFirstAction"/>
</file>
With this entry, we are able to put the name of the action in a properties file, as shown in
the Listing 10-9.
Listing 10-9. Values of the layer attributes in properties files
com/galileo/netbeans/module/Bundle.properties
CTL_MyFirstAction=My First Action
Administration and Preparation of Localized
Resources
Up until this point, localized resources have been stored in the same folder as the module,
whether Bundle.properties files or graphics are found. But how do you keep resources for each
language separated and later extend an already provided module with an additional transla-
tion? In this scenario, the NetBeans Platform shines. It easily provides an opportunity to
separate localized resources from the rest (basically the classes) of the module. It provides a
locale folder, stored under the module storage folder (see Figure 10-3). The resources for a
language in a JAR archive in this folder are extended with a language/country identifier. The
archive must have the same name as the module JAR archive. In this locale extension archive,
all language/country-specific resources are managed. They have the same package structure
as the module. The resources are separated and can be updated individually in this way. The
translation of the NetBeans Platform modules is done in the same manner.
212 CHAPTER 10

I NTERNATI ONALI ZATI ON AND LOCALI ZATI ON
Figure 10-3. Allocation of language-specific resources in a separate JAR archive in the locale folder
Note that the individual localized resources still need the language/country identifier. The
locale extension archive needs no manifest file, because the archive is exclusively identified by
the name of the localized packet and is added to the classpath of the module classloader (see
Chapter 2).
Here in this example, the resources for German are in the locale folder as a locale exten-
sion archive. The English resources that have no identifier are the default resources and are
provided in the JAR archive of the module. It is interesting that you can put the default resources
into a locale extension archive, because it has no identifier, has the same name as the module,
and is in the locale folder. Therefore, the resources you plan to localize are completely sepa-
rated from the module itself. Later, adding another language is done by a third party because it
is obvious which resources must be localized.
Summary
In this chapter, you learned how to prepare your application for different countries and
languages (internationalization) and how to provide different resources for different countries
and languages (localization). We started by looking at string literals in source code. You saw
that with the help of the NbBundle class, together with a NetBeans IDE wizard, it is very easy to
put your language-specific content into a separate properties file.
We also looked at the localization of help pages. Not only strings can be localized, since
there is also support for loading locale-specific graphics. You also learned how to localize layer
file values, and you learned how to distribute your localized content as a separate module.