Note: This article explains internationalization in the context of workbench applications. For RAP applications that don't use the workbench, only parts of this article are applicable. Plain RWT applications can also use traditional ResourceBundles and get the locale from the UISession as explained below.
Internationalization in RAP follows the same approach as RCP. However, due to the server-side, multi-user nature of RAP, a few adaptations are necessary. In the following, you will get a step-by-step guide to internationalize the simple Hello World application created in the getting-started chapter. For a more general introduction to internationalization in RCP, see [1].
In RAP we have to deal with different languages for different user sessions.
Indeed, the language can also change between requests within the same session.
Therefore, we cannot store language related information statically in Message
classes as this is done in RCP.
Instead, we must use a different instance of the Message
class for every language.
In the Eclipse IDE, it's quite simple to externalize strings using the Externalize
Strings
wizard provided by JDT.
Unfortunately, this wizard generates code that is not suitable for multi-user applications
because it stores translations in static fields.
You can probably benefit from the assistance of the Externalize Strings
wizard anyway, but only partly.
We will now explain how to do it from scratch.
Let's start with the preparations.
You probably know the resource bundle accessor classes (usually called
Messages
).
We also use such a class, but instead of accessing a resource bundle,
we use the RAP NLS facility to access nationalized strings.
We create a class Messages
in the package
org.eclipse.rap.helloworld
with the following initial
content:
public class Messages { private static final String BUNDLE_NAME = "org.eclipse.rap.helloworld.messages"; //$NON-NLS-1$ private Messages() { // prevent instantiation } public static Messages get() { Class clazz = Messages.class; return ( Messages )RWT.NLS.getISO8859_1Encoded( BUNDLE_NAME, clazz ); } }
The constant BUNDLE_NAME
contains the name (without extension) of
a properties file, that contains the mapping from keys to real strings.
Note that in contrast to RCP, the class does not extend
org.eclipse.osgi.util.NLS
.
Instances, which can be acquired through the factory method
get()
, contain fields that hold the translated strings.
In the next step, we create an empty properties file
messages.properties
in the same package.
This properties file follows the conventions of standard
ResourceBundle
properties files.
For each externalized string, there has to be a key entry in the properties
file.
Now we are prepared to externalize strings.
Let's start with the class HelloWorldView
from the hello world
example.
The class contains one string we'd like to externalize in order to make it
translatable:
public void createPartControl( Composite parent ) { Label label = new Label ( parent, SWT.NONE ); label.setText( "Hello RAP World" ); label.setSize( 80, 20 ); }
We change the string into the following code:
public void createPartControl( Composite parent ) { Label label = new Label ( parent, SWT.NONE ); label.setText( Messages.get().HelloWorldView_Message ); label.setSize( 80, 20 ); }
The key HelloWorldView_Message
can be freely chosen, however,
the RCP convention is to prefix it with the name of the class that uses it.
Now we have to add that key to the Messages
class:
public class Messages { ... public String HelloWorldView_Message; ... }
and add a definition to the messages.properties
file:
HelloWorldView_Message = Hello RAP World
Note that in contrast to RCP, you must use fields instead of constants in the
Messages
class, as they are not shared over all user session and
thus cannot be accessed in a static way in RAP.
Also the plug-in manifest file (plugin.xml
) may contain
translatable strings.
Like in RCP, those stings are replaced by unique keys, prefixed with a
%
sign.
The keys are then resolved in a plugin.properties
file that
resides in the root directory of the plug-in.
For example, the internationalized version of the HelloWorld plug-in manifest
file contains placeholders for the names of the view and the perspective.
... <extension point="org.eclipse.ui.views"> <view id="org.eclipse.rap.helloworld.helloWorldView" class="org.eclipse.rap.helloworld.HelloWorldView" name="%helloWorldView_name"> </view> </extension> <extension point="org.eclipse.ui.perspectives"> <perspective id="org.eclipse.rap.helloworld.perspective" class="org.eclipse.rap.helloworld.Perspective" icon="icons/icon.gif" name="%perspective_name"> </perspective> </extension>
And here's the plugin.properties
:
helloWorldView_name = Hello World View perspective_name = Hello World Perspective
To make this work, the OSGi manifest file (MANIFEST.MF
) must contain the line:
Bundle-Localization: plugin
In the end the Equinox extension registry must be made aware that it has 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 are 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 last step of the internationalization is to actually translate.
The translated strings are contained in localization properties files.
These files may also reside in a fragment of its own, together with other
localized resources.
Localization properties files have a suffix that determines the language,
optionally also the country, and a variant (refer to the
java.util.Locale
JavaDoc for these concepts), all preceded
by an underscore character.
For example, to create a translation to Swiss German, create a copy of the
messages.properties
file and name it
messages_de_CH.properties
.
Then you can start to translate the contained stings.
Be aware that the translated properties files will very likely contain
accented characters that are not included in the Latin-1 encoding
(UTF-8), which is expected by the RAP NLS support (as well as by the
Java ResourceBundle mechanism).
Those files can be converted using the native2ascii
conversion utility, included with the Java SDK.
Alternatively, RAP also allows for UTF-8 encoded properties files to ease the
translation into non-latin languages.
In this case, you have to change the call to
RWT.NLS.getISO8859_1Encoded
into
RWT.NLS.getUTF8Encoded
in the Messages
class.
A RAP client provides the user's preferred locale(s) in the ClientInfo service.
This information is based on the list of locales passed in the
Accept-Language
HTTP header, which usually reflects the user's language preferences
in the Web browser.
Alternative RAP clients may provide a different ClientInfo implementation.
Applications can obtain the preferred locale from the UISession using
UISession.getLocale().
The UISession locale is based on the preferred locale provided by the client, but can be
changed programmatically using UISession.setLocale().
If the client does not provide a locale, the system default locale is taken as a fallback.
The default locale can be set by adding the system property user.language
to the launch configuration.
If no matching properties file can be found, the default one
(messages.properties
) takes precedence.