# 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

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>

## 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 (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.

## 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)