Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Orion/Internationalization"

Line 1: Line 1:
{{warning|The information on this page applies only to Orion 7.0 and earlier releases. It must be updated for Orion 8.0.}}
+
{{warning|The information on this page applies only to Orion 7.0 and earlier releases. The document is in the process of being updated for Orion 8.0.}}
  
 
This page is a hub for resources relating to preparing Orion for use in other languages and locales (also known by the abbreviation [http://en.wikipedia.org/wiki/I18n i18n]). This work involves translating strings into other languages (often called NLS for Natural Language Support), handling bi-directional text (BIDI), and preparing content such as dates and times in a format appropriate for a given locale.  
 
This page is a hub for resources relating to preparing Orion for use in other languages and locales (also known by the abbreviation [http://en.wikipedia.org/wiki/I18n i18n]). This work involves translating strings into other languages (often called NLS for Natural Language Support), handling bi-directional text (BIDI), and preparing content such as dates and times in a format appropriate for a given locale.  

Revision as of 16:22, 11 March 2015

Warning2.png
The information on this page applies only to Orion 7.0 and earlier releases. The document is in the process of being updated for Orion 8.0.


This page is a hub for resources relating to preparing Orion for use in other languages and locales (also known by the abbreviation i18n). This work involves translating strings into other languages (often called NLS for Natural Language Support), handling bi-directional text (BIDI), and preparing content such as dates and times in a format appropriate for a given locale.

Overview

The Orion i18n framework enforces a separation between defining translate-able messages, providing translations, and the job of mapping definitions to translations.

Defining messages
Message modules (or message bundles) are JavaScript objects mapping keys to values. The messages are defined by the keys. The values are localized strings in some language. The Orion client source ships with message modules for the default English localization of Orion.
Providing translation
Translations are provided by plugins. A translation is essentially a message module whose values are localized strings in some language. Plugins can provide translation for any message modules: those used for the Orion UI, or for other i18n-aware plugins, or some combination of the two. A language pack is a set of plugins that collectively provide a comprehensive translation of the product. Because they are plugins, language packs can be added and removed dynamically.
Performing translation
The framework coordinates translations with the help of the plugin registry. When a particular translation of some message module is needed, a plugin that provides the translation service is activated and its getMessageBundle() invoked to obtain the translated module.

Client - installing new language pack

The Orion language pack

Orion language pack is a set of Orion client plugins that contain messages translated in the desired language.

This is typical contents of the top folder of the Orion language pack:

Resource Description
lib/ contains libraries needed for the language pack to run
org.eclipse.orion.client.core/ contains translated messages
org.eclipse.orion.client.editor/ contains translated messages
org.eclipse.orion.client.git/ contains translated messages
org.eclipse.orion.client.users/ contains translated messages
nlsEditorPlugin.html a plugin that needs to be installed to translate Orion Editor
nlsGitPlugin.html a plugin that needs to be installed to translate Orion Git Client
nlsOrionPlugin.html a plugin that needs to be installed to translate the basic Orion functionality
nlsUsersPlugin.html a plugin that needs to be installed to translate user-related content in Orion

How to install the language pack

  1. Obtain Orion language packs from the Eclipse Babel downloads page.
  2. Go to the Settings page
    Settings.png
  3. Choose Plugins section
    OrionPlugins.png
  4. Click Install button on the top of the section
    InstallPlugin.png
  5. Paste URL to one of the plugins and click Submit
    InstallingPlugin.png
  6. Repeat for all the plugins in the language pack (there should be at least 5).
  7. Reload page
  8. The new Plugins should be installed:
    border=1

Browsing Orion in a different language

Under the hood, Orion uses RequireJS's i18n plugin to implement bundle loading. This means that your browser's navigator language (in other words, the navigator.language or navigator.userLanguage property) determines the language Orion is displayed in.

The steps required to change the navigator language vary depending on browser and platform. Moreover, most browsers treat the navigator language as an independent setting from the Accept-Language header sent to the server, meaning that Orion may display in a different language from the one you're accustomed to seeing during everyday browsing (as most websites will rely on Accept-Language).

To verify the navigator language, evaluate this expression in your browser's JavaScript console:

    navigator.language || navigator.userLanguage

Below is an overview of how to change the navigator language.

Firefox (Windows)
  1. Go to Tools > Options > Content > Languages, then click Choose...
  2. Put your preferred language at the top of the list.

Firefox keeps things simple by having the ordering of languages in the list determine both the Accept-Language header and the navigator.language property (the first value in the list is used as the navigator language).

Google Chrome (Windows)
  1. Go to Settings > Show Advanced Settings > Languages then click Language and Input Settings...
  2. Add your preferred language to the list, then click Display Google Chrome in this language and restart Chrome.
Internet Explorer 10 (Windows)
  1. Go to Control Panel > Regional and Language > Formats and select your preferred language from the Format menu.
  2. Click Apply and restart Internet Explorer.

If you previously accessed Orion in your browser, you must perform localStorage.clear() from a JS console window on an Orion page before the new language will take effect. (This flushes the translation cache for the old language.)

Note that the Orion deployment you are using must have a language pack installed that provides translation for your preferred language. Orion ships with an English language translation, but additional translations may be installed by end users, or by the Orion server administrator on a server-wide basis.

See also

Client - adding new messages file

We are using i18n plugin of RequireJS, but to make our messages consumed by Babel we need to follow some extra rules. If you want to create a new messages file you need to add an "nls" directory with a given file structure:

nls/
   messages.js
   root/
      messages.js

Instead of "messages.js" we may use a different file name, but both *.js files need to have the same name.

The nls/messages.js file does not contain any strings for translation and will not be sent for translation. This file should have format as follows:

OrionMasterGlobalization.png

Orion Editor does not have dependency nlsutil, it should use 'orion/textview/util' import instead:

OrionMasterGlobalizationEditor.png

The nls/root/messages.js file is the root messages file, it contains externalized strings and is sent for translation. For this reason it is very important that the format is kept as follows:

OrionGlobalization.png

Client - using messages

To use the messages defined in the external file we should import them using i18n! prefix.

OrionGlobalizedFileHeader.png

Please take into account dependencies between out bundles. Editor cannot use messages from our Client, but Git client can.

In result of this import messages variable is an object containing all messages. The key of messages stay the same, but the message value is translated into a appropriate locale, if translation is available. If translation file exists, but it doesn't contain some of the messages the missing messages will be taken directly from the root translation. There are two formats to get the message under key "Navigator":

messages.Navigator
messages["Navigator"]

For messages containing parameters we can use dojo.substitute, but we also have more convenient helper method that is available in 'orion/util' for client bundles and in 'orion/textview/util' for editor. This is the use of the function:

mUtil.formatMessage(message, param0, param1, param2...)

The message parameter needs to be already globalized. Example use:

mUtil.formatMessage(messages.lineColumn, lineIndex + 1, offsetInLine + 1)

Orion Editor

Strings in Orion editor have already been externalized. The two files containing externalized strings are:

  • web/orion/editor/nls/root/messages.js - for strings in editor
  • web/orion/textview/nls/root/messages.js - for strings in text view

We also have a helper method for formatting string like "Line ${0} : Col ${1}", it can be found in orion/textview/util#formatMessage.

The editor also needs to make sure stuff like BIDI, IME, DBSC and Unicode Surrogates are all working properly. We paid some attention to these when we were first designing the editor, but there is lots of work to make the Orion editor BIDI aware to the same level of the Eclipse Desktop editor.

Plugins

Translating a plugin

In the Orion i18n framework, page UI code is responsible for loading message modules and looking up translated strings. Plugins cannot perform this work themselves. Therefore, plugins cannot load any i18n! modules using Orion's i18n loader.

However, plugins can indirectly refer to messages. Here's an example of a plugin-contributed service that refers to the "Navigator" message key from the orion/nls/messages messages module:

  1. // Primary navigation links
  2. provider.registerServiceProvider("orion.page.link", serviceImpl, {
  3.     id: "orion.navigator",
  4.     nls: "orion/nls/messages", // message module
  5.     nameKey: "Navigator",      // key
  6.     uriTemplate: "{OrionHome}/navigate/table.html#"
  7. });

At display time, the Orion UI loads and caches the "orion/nls/messages" module, and looks up the key "Navigator" in that module to obtain the translated string.

When externalizing a service definition from a plugin, the usual pattern is to add an nls property giving the path to the messages module, and indicate to-be-translated properties by appending Key to the property name. For example, to externalize string a service that normally has name and tooltip properties, we add an nls property to the service definition, and replace the name and tooltip properties with nameKey and tooltipKey. The nls property should gives the messages module path (don't include "i18n!", "root" or ".js") and the values of nameKey and tooltipKey properties are the keys of the translated messages in that module.

Here's a partial list of services that can currently be localized in this manner:

  • orion.page.link
  • orion.page.link.related
  • orion.edit.command
  • orion.edit.editor
  • orion.edit.outliner
  • orion.navigate.command
  • orion.navigate.openWith
  • orion.core.setting

Plugin authors may opt to use their own localization methods instead of Orion's, in which case any technology can be used. In this case they should not include an nls property in their service definitions, but simply provide already-translated strings as the value of name and tooltip.


Providing translation from a plugin

The orion.i18n.message service allows a plugin to contribute a translated message bundle. This service is of interest to plugin developers that want to provide translations of their plugin messages, as well as developers intending to write a language pack for Orion.

Service properties

name
String The name of the message bundle that this service provides. The name has the general form {prefix}/nls/{suffix} for a master bundle, and {prefix}/nls/{locale}/{suffix} for a particular locale, where:
  • {prefix} is any prefix (by convention, a path).
  • {locale} is a locale identifier (de, en-us, fr, pt-br, etc.)
  • {suffix} is any suffix (by convention, a filename.)

For example, a master bundle might be named my/stuff/nls/catalog, and a French translation of that bundle would be my/stuff/nls/fr/catalog. Here are some more examples:

Bundle name Language
orion/editor/nls/messages master
foo/nls/de/strings German
orion/navigate/nls/fr/messages French
myplugin/widgets/nls/pt-br/messages Portuguese (Brazilian)

Service methods

getMessageBundle()
Returns the message bundle.

Example: providing a master bundle and zh translation

This example plugin code registers a master bundle example/nls/messages, and a Chinese (Simplified) translation example/nls/zh/messages. In this case, the master bundle happens to contain English values, but that is not required.

  1. // Register the "master" message bundle
  2. pluginProvider.registerService("orion.i18n.message", {
  3.     getMessageBundle: function() {
  4.         return {
  5.             root: { // Note 'root' property here
  6.                 "GetPlugins": "Get plugins",
  7.                 "GetPluginsTooltip": "Find more plugins",
  8.             }
  9.         };
  10.     }
  11. }, {
  12.     name: "example/nls/messages"
  13. });
  14.  
  15. // Register the Chinese (Simplified) translation
  16. pluginProvider.registerService("orion.i18n.message", {
  17.     getMessageBundle: function() {
  18.         return {
  19.             "GetPlugins": "获取插件",
  20.             "GetPluginsTooltip": "查找更多插件",
  21.         };
  22.     }
  23. }, {
  24.     name: "example/nls/zh/messages"
  25. });

As always, the framework invokes getMessageBundle() in an asynchronous manner: the implementer can return a Promise or other "then-able" object to defer computation. This enables the actual message strings to be downloaded lazily on demand using any async technique (XMLHttpRequest, AMD require(), etc.)


Example 2: referencing translated strings

Assume that the services shown in the previous example have been registered. The code below shows how an Orion editor command extension could be registered, referencing strings from the message bundle to provide its name and tooltip. The command does not have to be registered in the same plugin as the message bundles.

  1.     provider.registerService("orion.edit.command", {
  2.         execute: function() {
  3.             // Command implementation goes here
  4.         }
  5.     }, {
  6.         id: "example.linkaccount",
  7.         nls: "example/nls/messages", // Refer to the master bundle
  8.         nameKey: "GetPlugins",
  9.         tooltipKey: "GetPluginsTooltip",
  10.     });

Note that when referring to the strings, we've used the master bundle name example/nls/messages, as we do not want to specify which particular locale is ultimately loaded. The i18n loader will make that decision for us.

String Xtrnalizr

You don't have to externalize strings manually, we have a tool that helps you do this.

Install localization plugin

  1. Choose "Get Plugins" from Orion toolbar
    GetPlugins.png
  2. Find "String Externalizer" on the plugins list and click Install
    StringExternalizerInstall.png
  3. Confirm installing the plugin by clicking "Submit"
    SubmitInstallingStringExternalizer.png
  4. Installing plugin gives you a warning about not externalized strings and provides access to String Xtrnalizr

Usage of String Xtrnalizr

String Xtrnalizr can help you externalize strings from chosen JavaScript files from a folder to one messages file at a time.

Please beware of bug 428373 (Strings Xtrnlizr silently fails to generate messages file when run on single file).

  1. To externalize strings first choose the folder you wish to modify and from the "More" menu ("View Related" in Orion 5.0) choose "String Xtrnalizr"
    OpenXtrnalizr.png
    Try to choose folders with enclosed functionality without any unnecessary content, for instance processing the whole Orion client repository can take long, but choosing only org.eclipse.orion.server.git should be just fine.
  2. You should be redirected to the String Xtrnalizr site. Wait a moment until the folder you chose is parsed, Xtrnalizr is searching for not externalized strings, this may take a while.
  3. The left menu contains configuration that needs to be changed each time you are externalizing strings to a new file. The configuration will be saved and the next time you externalize strings from the same folder the last configuration will be filled for you.
    ExternalizeStringsConfiguration.png
    1. Messages directory - the main directory of the messages file. If you choose 'orion/nls' your messages will be externalized to 'orion/nls/root/messages.js' file
    2. Messages file name - the name of the file to externalize strings. By default strings will be externalized to 'messages.js', but you may as well change the name, using for instance 'gitmessages.js'
    3. Messages module - the requireJS module name that should be added to your file's define section. Xtrnalizr does not try to guess the module name, so please, set the module name manually. If you choose to externalize your strings to 'orion/nls/root/messages.js' please set the header to 'i18n!orion/nls/messages'
    4. Mark not exported as NON-NLS - if you externalized all nls strings from chosen files and any strings left are non-nls check this property. All non externalized strings will be marked with NON-NLS end of line comments and you won't be seeing them in the Xtrnalizr report the next time you run it.
  4. Choose strings to be externalized
    StringXtrnalizr.png
    String Xtrnalizr shows you the list of non externalized strings grouped by files. Selecting each file will show you a preview of the changes that will be made after the string externalization is completed.
    1. Expand the file nodes to see the non externalized strings found
    2. Select the string node to see it in the preview
    3. Uncheck the file to omit it in string externalization. Files that are not checked will not be modified.
    4. Check/uncheck the changes to decide if they should be externalized. Depending of how you checked 'Mark not exported as NON-NLS' property the unchecked changes will be marked as NON-NLS or left unchanged.
    5. If you check the file node, but none of the changes will be checked, the only changes added to the file are NON-NLS marks.
  5. When you are done click "Apply Changes" button to generate the messages file.
  6. Important! - if you were creating a new messages file you still have to finish it up. Go to the directory you chose in "Messages directory", it should contain a "root" folder. You need to create a master messages file. If you were externalizing files from Orion then you just need to copy any messages.js file that already exists and change the module name in "define" function to match the one you chose in "Messages module" property. If you are externalizing files that use dojo 1.7 messages bundle see this dojo tutorial [1] to learn creating dojo 1.7 i18n files. See also [Editor no longer requires dijit/dojo * Close to getting navigator dijit/dojo free]
  7. Recovering from accidental localization - I you find a string was externalized to message["key"] by mistake you can look up the original message value under "key": "value" in the nls/root/messages.js file. Just replace message["key"] with "value" in the source file and remove the key value entry from messages.js. It's best to start the NLS work with a unmodified git workspace. That way all changes made by Strings Xtrnlizr can easily be reviewed and reverted.

Server

Most server strings are not currently externalized, but could easily be done using current Equinox API. The easy case is single language servers, where the natural language is provided when the server starts and it never changes. More complex would be multi-language servers where each request can have a different language and server uses the HTTP Accept-Language header to determine response language for that request. Equinox has not come up with a well performing way to do this (see bug 226340), and any prerequisite dependencies such as org.eclipse.* libraries won't support it.


Related Links

Back to the top