How to Internationalize a RAP application
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].
Why does RAP internationalization differ from RCP?
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.
Move translatable strings into *.properties files
In RCP, it's quite simple to externalize strings using the Externalize
Strings
wizard provided by JDT.
Unfortunately, this wizard is not yet prepared to externalize strings the RAP
way.
You can probably benefit from the assistance of the Externalize Strings
wizard anyway, but only partly.
Until now, you are on your own here.
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.
Translate plug-in manifest
Extension definitions in the plug-in manifest file can also contain strings
that are subject to internationalization.
In order to get this working a few prerequisites are necessary:
- check out the plug-in
org.eclipse.rap.equinox.registry
from the RAP CVS
- also include the plug-in into your workspace.
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
Again, RAP needs a special treatment to support the internationalization of
extensions because of it's server-side, multi-user nature.
The problem is that the Equinox extension registry does the translation on
startup and caches the results.
In RAP, different sessions may require translations into different languages.
In order to allow for this, we had to exploit the compatibility mechanism of
the Equinox extension registry.
As a side effect, the internationalization of Eclipse 3.2 plug-ins won't work.
To launch it from Eclipse, you need to include the plug-in
org.eclipse.equinox.registry
as a source plug-in into your
workspace.
Make sure that this plug-in is also included in the launch configuration
(from the workspace, not from the target platform).
We hope that we can remove these inconveniences once Equinox supports
server-side applications with user sessions.
Create translations of your *.properties files
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
(ISO-8859-1), 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.
How does RAP select the language for a user session?
Web browsers allow users to set their preferred languages and they pass those
preferences in an Accept-Language
HTTP header with every request.
RAP tries to select one of the requested languages in the preferred order and
if this is not possible, it uses the default locale as 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.
References
1. How to Internationalize your Eclipse Plug-In (Article on Eclipse.org)