Internationalization (i18n) is the process of adapting applications to the user's language and region, including things like number formats, date and time, etc. The Java class library has support for internationalization, most notably by locales and resource bundles. RAP applications can use these features just like other applications, but since they are accessed by multiple users simultaneously, they must be designed to work with user-specific locales instead of a single, system-wide locale.
In RAP, every UISession has its own locale that can be obtained using the method
getLocale.
By default, this locale is based on the user's preferred locale provided by the client.
If the client does not provide a locale, the system-wide default locale is taken as a fallback.
The default locale can be set by adding the system property user.language
to the launch configuration.
The UISession locale can also be changed programmatically using
setLocale,
for example when the user selects a preferred locale in the application's UI.
Note: The static methods RWT.getLocale() and RWT.setLocale() are shortcuts for RWT.getUISession().getLocale() and RWT.getUISession().getLocale(...), respectively.
In addition to this, a client provides the user's preferred locales in the
ClientInfo
service.
This information is based on the list of locales passed in the
HTTP header Accept-Language
, which usually reflects the user's language preferences
in the Web browser.
Alternative RAP clients may provide a different ClientInfo implementation.
Localization is the process of translating an internationalizable application
for a certain language and region.
The locale-specific strings are usually kept in a Java properties files, accessed
by a ResourceBundle.
Translated versions of these property files must have a suffix that indicates
their language, optionally also the country and a variant (for the details, please refer to
Locale and
ResourceBundle.
For example, to create a German translation for a resource property file named
MyResources.properties
, you would create a copy of the file and name it
MyResources_de.properties
.
Then you would translate the strings in this file and put it into the same package as the
original file.
The translated files and other localized resources can go into a separate .jar file or,
if your application uses OSGi, in a fragment bundle.
Be aware that the property files are expected to be in ISO-8859-1 (Latin 1) encoding.
If the translated properties files contain accented characters that are not included
in the Latin-1 encoding, they have to be represented by native2ascii
utility, that is shipped with the Java SDK.
Note: The ResourceBundle fallback mechanism can sometimes lead to confusion.
You could expect that the system falls back to the default properties file if no translated
properties file can be found that matches the user's locale. That's not the case.
If a translation is not found for the locale in question, the ResourceBundle lookup will
first try the default locale, i.e. Locale.getDefault().
For example, if your VM is set to German and there is a MyResources_de.properties
file, the translation will fallback to this file instead of the default
MyResources.properties
.
If you want to have English as your fallback, you have to set your VM to English.
In case of session timeout, a network error, and other kinds of exception, the web client will display a message box. In an internationalized application these messages should be translated as well. The default texts reside in the properties file org/eclipse/rap/rwt/internal/RWTMessages.properties in the bundle org.eclise.rap.rwt. To localize the application, include the translated versions (RWTMessages_xx.properties) in the deployed application, e.g. in an OSGi fragment bundle. At the moment, we don't provide any translated versions, so you'll have to create them on your own.
The texts for JFace dialog buttons are traditionally set directly to one of the constants
in IDialogConstants
.
This method is not suitable for multiple users because the strings contained in these constants
are translated only once.
For multi-user applications, IDialogLabelKeys
also provides keys that can
be resolved to localized strings dynamically using JFaceResources.getString( key )
.
In a RAP application, label texts should be obtained as in this example:
protected void createButtonsForButtonBar( Composite parent ) { createButton( parent, IDialogConstants.OK_ID, JFaceResources.getString( IDialogLabelKeys.OK_LABEL_KEY ), true ); createButton( parent, IDialogConstants.CANCEL_ID, JFaceResources.getString( IDialogLabelKeys.CANCEL_LABEL_KEY ), false ); }
RCP established its own internationalization mechanism that is explained in [1]. This approach is based on resource bundle accessor classes that keep translated strings in static fields, which is not suitable for a multi-user system. To solve this problem, the RAP project has developed a method to adjust existing RCP applications for use in multi-user environments with minimal modifications.
Here's an example snippet of RCP code that reads a localized string from a static field of an accessor class (commonly named “Messages”).
label.setText( Messages.ExampleView_Message );
For multiple users, we need different translations. Therefore we suggest to use instance fields instead of static fields and to create a different instance of the class for every language. This allows for re-using the existing code with only a minimal change, which is to insert a get() method that returns an instance of the resource bundle accessor class for the current UISession locale:
label.setText( Messages.get().ExampleView_Message );
To allow for this pattern, the static fields in the Messages
class must be changed
to instance fields.
Moreover, the get() method must be added to this class.
This method must return an instance of the class with all fields translated to the current
UISession locale.
The RAP framework provides a helper class RWT.NLS to do this.
Here's an example of a modified accessor class:
public class Messages { private static final String BUNDLE_NAME = "org.example.views.messages"; //$NON-NLS-1$ public String ExampleView_Title; public String ExampleView_Message; public static Messages get() { Class clazz = Messages.class; return ( Messages )RWT.NLS.getISO8859_1Encoded( BUNDLE_NAME, clazz ); } private Messages() { // prevent instantiation } }
Note that in contrast to RCP, the class does not extend
org.eclipse.osgi.util.NLS
.
The constant BUNDLE_NAME
contains the name (without extension) of
the properties file that contains the mapping from keys to localized strings.
In the example above, a properties file messages.properties
is expected in
the package org/example/views
.
This property file must contain the default strings for every key:
ExampleView_Title = Example ExampleView_Message = Hello World
Note: to simplify the translation to non-Latin languages, the RAP NLS utility also accepts UTF-8 encoded property files.
Plug-in manifest files (plugin.xml
) may also contain translatable strings,
e.g. view names or menu labels.
Therefore, a plugin.xml may contain placeholder strings that are prefixed with a
%
sign.
These placeholder keys are then resolved in a properties file, which is by convention named
plugin.properties
and resides in the root directory of the plug-in.
Here's an example of a view extension that contains a placeholder for the name attribute:
<extension point="org.eclipse.ui.views"> <view id="org.example.views.exampleView" class="org.example.views.ExampleView" name="%exampleView_name"> </view> </extension>
The bundle would include a file plugin.properties
that contains the translatable
text for this placeholder in a line like the following:
exampleView_name = Example
To make this work, the OSGi manifest (MANIFEST.MF
) must contain a
Bundle-Localization header that points to the name of the properties file
(without extension):
Bundle-Localization: plugin
In a multi-user environment, the Equinox extension registry must be set to multi-locale mode
to be able to serve strings in multiple locales simultaneously. This is done by setting the
system property eclipse.registry.MultiLanguage
to true
or by setting
the framework property registryMultiLanguage
.
If you're deploying your application as a WAR,
make sure to include the framework property in the web.xml
like
shown below:
<init-param> <param-name>commandline</param-name> <param-value>-registryMultiLanguage</param-value> </init-param>
The Java tools in the Eclipse IDE provide a tool to easily extract strings into message bundles, the Externalize Strings wizard (Source > Externalize String…). This wizard has an option to “use Eclipse's string externalization mechanism” (the mechanism that is described in [1]), which is enabled by default for Plug-in Projects. As explained above, this mechanism is not suitable for multi-user applications.