Home Tutorials Training Consulting Products Books Company Donate Contact us









NOW Hiring

Quick links

Share

Eclipse internationalization and localization. This article describes how to externalize your strings in Eclipse RCP and Eclipse plug-ins and how to support different languages. Includes how to translate your Eclipse 4.x workbench model.

1. Internationalization and localization

1.1. Translation of a Java applications

The process of preparing an application for being translated into several languages is called internationalization. This term is typically abbreviated to i18n.

The process of translating the application is called localization and is abbreviated to l10n.

1.2. Property files

Java applications are typically translated via property files. Such a file contains key/values. The key can be used in your application and is substituted at runtime with the corresponding value. The following listing demonstrates an example content of such a file.

#Properties file
part.overview=Overview
part.detail=Details

Based on the language of the user, the Java runtime searches for the corresponding resource bundle using language identifiers. If a certain language identifier is not provided, the Java runtime will fall back to the next general resource bundle.

For example:

  • messages.properties: default language file, if nothing else is available

  • messages_de.properties: used for German

  • messages_en.properties: default for English

  • messages_en_US.properties: US English file

  • messages_en_UK.properties: British English file

The Eclipse platform provides functionality to support translations based on properties files but also allows the usage of alternative approaches.

1.3. Encoding of property files in Java

Resource bundles in Java are always LATIN-1 (ISO 8859-1) encoded. This is defined by the Java specification.

1.4. Relevant files for translation in Eclipse applications

The following table lists the relevant elements for translating an Eclipse RCP application.

Table 1. Translation relevant entities for Eclipse plug-ins
Entity Description

application model (Application.e4xmi and model fragment files)

Describes the application model in Eclipse RCP.

plugin.xml

Primarily important for Eclipse 3.x based plug-ins.

Source Code

The source code contains text, e.g., labels which must be translated.

1.5. Where to store the translations?

It is not uncommon that a plug-in also contains its translations.

If a translation should be used by several plug-ins it is good practice to have one central plug-in which contains the translations. This plug-in contains at least the main language and potentially more.

It is also possible to provide translation files via fragment projects. Fragment projects extend their host plug-in. This approach allows that the text files can be maintained in separate plug-ins, which is sometimes easier to handle for the translation team. This approach also allows configuring the included languages via the product configuration file.

1.6. Setting the language in the launch configuration

By default Eclipse uses the language configured in the operating system of the user.

For testing you can set the language manually. In your Eclipse launch configuration on the Arguments tab you can specify the runtime parameter -nl to select the language, e.g. -nl en.

1.7. Translation service

Eclipse uses a translation service via the TranslationService interface. The default implementation of this class is the BundleTranslationProvider which uses property files as input for the translations. But you can provide your own OSGi service which uses a different source, e.g., a database to get the translations.

This service is stored in the application context. You can replace it there, e.g., via a life cycle hook or via a model-addon which modifies the application context once the application startup is completed.

By storing it in the IEclipseContext context hierarchy you can have different translation services for different local contexts, e.g., for different windows you can use different translation services.

2. OSGi resource bundles

For the translation of the plugin.xml and application model files the Eclipse runtime uses by default OSGi resource bundles. These are property files in a specified location. OSGi expects at least one resource bundle in this location.

OSGI-INF/l10n/bundle is the default location and file prefix which the Eclipse translation service is using. Via the Bundle-Localization attribute in the manifest file, you can specify an alternative location for the bundle resources. The usage of this attribute is demonstrated in the following listing.

Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: I18n
Bundle-SymbolicName: de.vogella.rcp.i18n; singleton:=true
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: de.vogella.rcp.i18n.Activator
Require-Bundle: org.eclipse.ui,
 org.eclipse.core.runtime
Bundle-ActivationPolicy: lazy
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
Bundle-Localization: OSGI-INF/anotherlocation/bundle

Alternative languages are defined via additional property files. The filename of these files can include a language and optional a country variant as described in Property files . For example bundle_en.properties or bundle_en_UK.properties.

3. Translating the application model

The translation key can be specified in your application model via the %key reference. This usage is demonstrated via the following screenshot of the model data for a part.

How to use external strings in the workbench model

Currently the application model tooling includes rudimentary support for extracting Strings. Press the right mouse button on an empty space in the application model editor and select Externalize Strings.

This action extracts all Strings from the application model. If you want to replace only a set of relevant Strings, you have to do this manually. If you do it manually you can use the PDE wizard as described in Translating plugin.xml to create the bundle.property file and the OSGi reference.

4. Translating plugin.xml

Similar to the usage of keys in the application model, you can use the key prefixed with % in the plugin.xml file. The Eclipse tooling supports extracting existing String values from the plugin.xml file. Select your plugin.xml file, right-click on it and select Plug-in Tools ▸ Externalize Strings.

Eclipse PDE Wizard for externizing plugin.xml

Via PDE Tools ▸ Internationalize you can also directly create one or several fragments for the result of the internationalization.

5. Source code translation with NLS support

5.1. NLS compared to the Eclipse translation service

This part of the description is included for completeness. It describes the NLS approach for translation. This approach still works but the Eclipse translation mechanism provides more flexibility, e.g., dynamic switching of languages, less memory consumption and a translation service which can be exchanged. This superior approach is described in Translation with POJOs.

5.2. Translating your custom code

It is possible to translate the Java source with two different approaches, based on Strings and based on constants. The approach based on constants is more reliable and should be preferred. To enable this support you need to configure the org.eclipse.core.runtime plug-in as dependency in your related plug-in.

To translate Strings in the source code, select the file you want to translate and select Source ▸ Externalize Strings.

Externalizing strings in source files dialog

Please note that the Use Eclipse’s string externalization mechanism option is only visible if you have the org.eclipse.core.runtime plug-in configured as a dependency in your plug-in.

Externizing strings in source files dialog

In this wizard you can select which Strings should be translated, which should be skipped and which should be marked as not translatable.

If you select that a String should not be translated, Eclipse marks the occurrence with a $NON-NLS comment in the source code.

As the result a Messages class is generated which serves as an access point for the properties file.

package test;

import org.eclipse.osgi.util.NLS;

public class Messages extends NLS {
        private static final String BUNDLE_NAME
                = "test.messages"; //$NON-NLS-1$
        public static String View_0;
        public static String View_1;
        static {
                // initialize resource bundle
                NLS.initializeMessages(BUNDLE_NAME, Messages.class);
        }

        private Messages() {
        }
}

In this example the Messages class uses a constant called BUNDLE_NAME to point to the message*.properties file in the test package. * is a placeholder for your locale, e.g., _de,_en, etc.

View_0=test.view
View_1=Hello

In your code you access the translations via the Message class.

label.setText(Messages.View_1);

You can also use placeholders in the messages and evaluate them with the NLS.bind() method.

MyMessage = {0} says {1}
// NLS bind will call the toString method on obj
NLS.bind(Message.MyMessage, obj, obj);

If translations should be used over several plug-ins to ensure consistency, it is good practice to create separate plug-ins or fragments for the translations. All plug-ins which want to use the translations define a dependency to the corresponding plug-in.

Additional languages are typically contributed via Eclipse fragment projects to this message plug-in.

6. Translating SWT and JFace code

The SWT and JFace plug-ins include their resource translation files. You can change these default texts and their translations because resource bundles have an override support built in.

If you supply a different file of the SWT or JFace translation bundles in a fragment or plug-in (such as SWTMessage_de.properties), then the most local bundle matching the current local is used.

To change the text supplied by SWT and JFace do the following:

  • Create two fragments projects, which will hold the translations. One with org.eclipse.jface and one with org.eclipse.swt as Host-Plug-in.

  • Create the org.eclipse.jface and org.eclipse.swt.internal packages in the corresponding fragments.

  • Copy the org/eclipse/jface/message.properties file from the org.eclipse.jface plug-in and the org/eclipse/swt/internal/SWTMessage.properties file from the SWT plug-in to the corresponding packages in your project. See the appendix for the links to their location in the Eclipse Git repository.

  • Provide the files with your desired language extensions, e.g. message_de.properties.

  • Edit the files to contain your text properties.

The original message.properties in JFace or SWTMessage.properties in SWT cannot be overridden by a fragment. Therefore, you can only provide additional *.properties files for a certain locale like message_de.properties.

You can translate selected properties of the original message.properties file.

Package structure of the JFace fragment

7. Exporting Plug-ins and Products

The build.properties file in a plug-in defines which of the files are included in the exported product.

You must include your property files in the build.properties file, otherwise your translations will be missing in the exported product.

8. Common problems with i18n

* If the translations are not available in the exported product, ensure that the property files are included in the export. You do this via the build.properties file of the plug-in which provides the property files.

  • If the translations are not displayed in the application, select Clear Configuration in your launch configuration. OSGi caches text information sometimes.

9. Eclipse 4 internationalization

9.1. Translation with POJOs

To define your translations you simply define a Java object with several public fields of type String.

public class Messages {
        public String labelSummary;
        public String labelDescription;
        public String labelDone;
        public String labelDueDate;
        public String labelWithPlaceholder = {0} says {1}
}

The fields of such a Java object are initialized when an instance is injected via the @Translation annotation. The org.eclipse.e4.core.services plug-in contains the annotations which are used for translations.

@Inject
@Translation
Messages messages;

// more code
myLabel.setText(messages.label_message);

To find the property files for the key/value pairs, if searches for property files based on the rules described in the next section.

9.2. Search process for translation files

The default translation service searches for property files with the translations in the following order.

  • Check the @Message annotation in the Messages class for the correct location. This annotation allows defining the location of the file, see Using the optional @Message annotation

    1. If not present, go to the next step.

  • Check if a property file with a corresponding name is available relatively to the Message class. If not present, go to the next step.

  • Check if a property file is configured via the OSGi-ResourceBundle header in the MANIFEST.MF file. resource bundle. If this entry is not present check the OSGI-INF/l10n folder otherwise use the entered location. Such a property file is called OSGi resource bundle (see OSGi resource bundles).

9.3. Using the optional @Message annotation

The optional @Message annotation can be used to define the location of the resource bundle. It allows also to define how and if translations should be cached by the Eclipse platform. See the Javadoc of the @Message annotation for more information on this topic.

9.4. Using the optional @PostConstruct in message POJOs

It is possible to initialize fields via the @PostConstruct method in a message object as demonstrated in the following snippet.

public class Messages {
        public String labelWithPlaceholder1 = {0} says {1}
        public String labelWithPlaceholder2 = {0} says {1}

        // initialize the first message

        @PostConstruct
        public void format() {
                labelWithPlaceholder2 =
                                MessageFormat.
                                        format(labelWithPlaceholder2, "Test", "this is fun.");
        }
}
@PostConstruct does not support parameter injection in the context of a message object.

9.5. Dynamic language switch

The currently active locale, e.g., "de" or "en", is represented by an object of type Locale. The Eclipse runtime supports a dynamic switch of this locale at runtime.

The type of the TranslationService.LOCALE key was changed from String to Locale in Eclipse 4.4.2.

To support a dynamic locale switch the application must be able to react on changes in the messages. This requires that the widgets are declared as public fields and that a method exists which updates them. This is called the Eclipse Translation Pattern.

public class TranslationExamplePart {
        private Label myLabel;

        @PostConstruct
        public void postConstruct(Composite parent, @Translation Messages messages) {
                // create the UI
                myLabel = new Label(parent, SWT.NONE);
                // as @PostConstruct is executed after field injection, we need to
                // call translate here manually to fill the UI initially
                translate(messages);
        }

        // the method that will perform the dynamic locale changes
        @Inject
        public void translate(@Translation Messages messages) {
                if (myLabel != null && !myLabel.isDisposed())
                        myLabel.setText(messages.labelSummary);
        }
}

To change the active locale at runtime use the ILocaleChangeService service. This is demonstrated by the following class which could be used as part of a tool control.

public class SwitchLanguageToolControl.java {

        Button button;

        @Inject
        ILocaleChangeService lcs;

        @PostConstruct
        public createPartControls(Composite parent) {

                final Text input = new Text(parent, SWT.BORDER);


                input.addKeyListener(new KeyAdapter() {
                        @Override
                        public void keyPressed(KeyEvent event) {
                                if (event.keyCode == SWT.CR
                                                || event.keyCode == SWT.KEYPAD_CR) {
                                        lcs.changeApplicationLocale(input.getText());
                                }
                        }
                });

                button = new Button(parent, SWT.PUSH);
                button.addSelectionListener(new SelectionAdapter() {
                        @Override
                        public void widgetSelected(SelectionEvent e) {
                                lcs.changeApplicationLocale(input.getText());
                        };
                });
        }

        @Inject
        public void translate(@Translation Messages messages) {
                // button localization via Eclipse Translation Pattern
                button.setText(messages.button_change_locale);
        }
}

To get notified you can use dependency injection.

@Inject
@Optional
private void getNotified(
    @Named(TranslationService.LOCALE) Locale s) {
    System.out.println("Injected via context: " + s);
}

@Inject
@Optional
private void getNotified(
    @UIEventTopic(ILocaleChangeService.LOCALE_CHANGE) Locale s) {
    System.out.println("Injected via event broker: " + s);
}

9.6. Using the BaseMessageRegistry to track language changes

Instead of implementing a method to receive the Message object on locale change in a part, like @Inject public void translate(@Translation Messages messages), a BaseMessageRegistry can be implemented.

The BaseMessageRegistry<M> contains methods to register controls, which are supposed to be translated. So basically the BaseMessageRegistry<M> will be informed about new Message objects and applies the new values to its registered controls.

The BaseMessageRegistry<M> itself gets the Message object injected.

package com.example.e4.rcp.todo.i18n;

import javax.inject.Inject;

import org.eclipse.e4.core.di.annotations.Creatable;
import org.eclipse.e4.core.services.nls.BaseMessageRegistry;
import org.eclipse.e4.core.services.nls.Translation;

@Creatable
public class MessagesRegistry extends BaseMessageRegistry<Messages> {

        @Inject
        @Override
        public void updateMessages(@Translation Messages messages) {
                // Update the Messages for the BaseMessageRegistry by DI
                super.updateMessages(messages);
        }
}

Because the MessagesRegistry class is annoted with @Creatable, it is instanciated by the framework and can directly be injected into the postConstruct method.

package com.example.e4.rcp.todo.parts;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

import org.eclipse.e4.core.services.nls.MessageConsumer;
import org.eclipse.e4.core.services.nls.MessageFunction;
import org.eclipse.e4.core.services.nls.Translation;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;

import com.example.e4.rcp.todo.i18n.Messages;
import com.example.e4.rcp.todo.i18n.MessagesRegistry;

public class MessagesRegistryExamplePartWithoutJava8 {
        private Label myLabel;

        @PostConstruct
        public void postConstruct(Composite parent, MessagesRegistry messagesRegistry) {
                // create the UI
                myLabel = new Label(parent, SWT.NONE);
                // Let the MessagesRegistry apply the correct label
                messagesRegistry.register(new MessageConsumer() {

                        @Override
                        public void accept(String value) {
                                myLabel.setText(value);
                        }
                }, new MessageFunction<Messages>() {

                        @Override
                        public String apply(Messages m) {
                                return m.labelSummary;
                        }
                });
        }
}

By using Java 8 registering controls looks better and is even shorter.

package com.example.e4.rcp.todo.parts;

import javax.annotation.PostConstruct;
import javax.inject.Inject;

import org.eclipse.e4.core.services.nls.MessageConsumer;
import org.eclipse.e4.core.services.nls.MessageFunction;
import org.eclipse.e4.core.services.nls.Translation;
import org.eclipse.swt.SWT;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;

import com.example.e4.rcp.todo.i18n.Messages;
import com.example.e4.rcp.todo.i18n.MessagesRegistry;

public class MessagesRegistryExamplePartWithJava8 {

        @PostConstruct
        public void postConstruct(Composite parent, MessagesRegistry messagesRegistry) {
                // create the UI
                Label myLabel = new Label(parent, SWT.NONE);
                // Let the MessagesRegistry apply the correct label
                messagesRegistry.register(myLabel::setText, m -> m.labelSummary);
        }
}

10. Tutorial: Translate plugin.xml

Create a new Eclipse RCP application "de.vogella.rcp.i18n" based on the "RCP application with a view" template. Check the contributions to the "org.eclipse.ui.views" extension point in your plugin.xml file and validate that the name of the View is hard coded to "View".

Right-click on the plugin.xml file and select PDE Tools ▸ Externalize Strings. Accept the default and press ok.

This will create the file bundle.properties and replace the hard-coded string in plugin.xml with a "%key" placeholder, e.g. %View.

<extension
       point="org.eclipse.ui.views">
    <view
         name="%View"
         class="de.vogella.rcp.i18n.View"
         id="de.vogella.rcp.i18n.view">
    </view>
</extension>

Copy bundle.properties to "bundle_fr.properties" and "bundle_en.properties" Change the text in the property views for the view.

Start you application in the different languages and valid that the translation is correctly used.

View = Lars Test

11. About this website

12. Eclipse i18n resources

12.1. vogella GmbH training and consulting support

TRAINING SERVICE & SUPPORT

The vogella company provides comprehensive training and education services from experts in the areas of Eclipse RCP, Android, Git, Java, Gradle and Spring. We offer both public and inhouse training. Whichever course you decide to take, you are guaranteed to experience what many before you refer to as “The best IT class I have ever attended”.

The vogella company offers expert consulting services, development support and coaching. Our customers range from Fortune 100 corporations to individual developers.

Copyright © 2012-2016 vogella GmbH. Free use of the software examples is granted under the terms of the EPL License. This tutorial is published under the Creative Commons Attribution-NonCommercial-ShareAlike 3.0 Germany license.

See Licence.