Contributing to Nuxeo

HOWTO: Translate the Nuxeo Platform

Updated: December 19, 2024

Nuxeo supported modules handle English translations for each of their bundles. Other translations are managed on Crowdin and synchronized automatically with the Nuxeo source code.

Hyland University
Watch the related courses on Hyland University:
Expert Session on Translations.

Languages that are sufficiently translated are available in the released default distribution. The conditions for a language to be available by default are that it is translated at 95% and approved at 75% minimum. Default languages in Nuxeo Platform 10.1 are:

On JSF UI:

  • Albanian
  • Basque
  • Chinese
  • Dutch
  • English
  • French
  • German
  • Italian
  • Japanese
  • Spanish
  • Swedish

On Web UI:

  • Dutch
  • English
  • French
  • German
  • Italian
  • Japanese
  • Spanish
  • Swedish

Other languages are available in the Nuxeo Platform Additional Languages addon from the Nuxeo Marketplace.

Here are some instructions to help you contributing or changing translations, in English or other languages.

Instructions for Translators

Translating Labels

Labels translation are managed on Crowdin. To translate Nuxeo labels or change a translation, you need to join the Nuxeo JSF UI or Nuxeo Web UI project on Crowdin.

If you are not familiar with Crowdin, take a look at their quick start guide . Basically here are the steps to contribute:

  1. Join the Nuxeo translation group of your choice at crowdin.net/profile/nuxeo. Pick a language you want to translate and start by clicking Translate.
  2. In the Crowdin translation view you will find all the phrases to translate to the left. (To view only the ones that still need translation, use the Untranslated filter.)
  3. Click on a phrase you want to translate. You see the original phrase in the top, and a box to fill out your translation beneath.
  4. Enter the translation and by clicking Save, and optionally, if you're a proofreader, you can approve the translation.
  5. Contact one or several of the Crowdin project managers to be credited for your contribution.

By default, new users have the translator status. It means you can propose new translations, but you can't validate them. To gain the proofreader status and have the validation permission, please contact one or several of the Crowdin project managers.

Now here's a couple of tips:

  • Some phrases have placeholders in them (like {0}). These are placeholders for values that are added later. E.g. a name: “Hello, {0}”, could be “Hello, Peter” on the website. They must remain in the translation as well, although placement may vary.

  • If the context of a label is unclear, please request the context from the Nuxeo team. These comments are also visible for anyone else that translates or approves translations.

Before every release, the translations are exported from Crowdin to the nuxeo-platform-lang-ext and nuxeo-platform-lang-incomplete addons by Nuxeo.

Adding a New Language

Please contact one or several managers of the project if you'd like to contribute translations to a language that's not available on Crowdin yet.

Nuxeo developers will perform the additional steps for this language to be added to the Nuxeo source code (see NXP-19840 as a reference, for instance).

Editing English Labels

  1. For now, English translations are managed only on GitHub.

    • For Web UI labels, edit the Nuxeo Web UI or Nuxeo UI Elements JSON files.

    • For JSF UI labels, looking at the reference messages.properties file can help you understand in which GitHub repository or module the original translation is. For instance, look for the following sample lines:

      ## DO NOT EDIT FOLLOWING LINE
      # Translations from ./nuxeo/nuxeo-features/nuxeo-platform-lang/src/main/resources/web/nuxeo.war/WEB-INF/classes/messages_en_US.properties
      
      [...]
      
      ## DO NOT EDIT FOLLOWING LINE
      # Translations from ./nuxeo/addons/nuxeo-agenda/src/main/resources/OSGI-INF/l10n/messages_en_US.properties
      
      [...]
      

      If the module is under the addons directory, it will be in a specific GitHub repository. Otherwise, it will be in the main Nuxeo repository.

  2. Use any standard Java i18n tool to edit the files.

  3. Make a pull-request on GitHub.

If you'd like to include quotes in a translation that is holding placeholders, this quote should be doubled. Note that Crowdin will handle that double-quoting at export on its own, so you can disregard this warning if you are using the Crowdin UI.

Testing Translations

To test a translation file provided by Crowdin against your Nuxeo server, you can benefit from messages labels hot reload by following these instructions:

  1. Download the messages.properties file in your language from Crowdin (after some changes to translations for instance). As an alternative, you can pick the current messages.properties file on your server at $SERVER/nxserver/nuxeo.war/WEB-INF/classes/messages_xx_XX.properties and edit it manually.
  2. Start your Nuxeo server in development mode:

    • Add the following to the $SERVER/bin/nuxeo.conf file.

      org.nuxeo.dev=true
      
    • Or in JSF UI go to ADMIN > Update Center > Nuxeo Studio and click on the dedicated button.

  3. Log in as Administrator.

  4. Replace the file at $SERVER/nxserver/nuxeo.war/WEB-INF/classes/messages_xx_XX.properties by the one you downloaded from Crowdin or edit it.
  5. Click on Dev mode: force flush next to the Logout button in the UI. Your translations are displayed in the UI.

    If you haven't changed the default language in your preferences, you can add ?language=xx_XX at the end of the page URL to see the page in your target language. For instance https://nightly.nuxeo.com/nuxeo/nxpath/default/default-domain@view_documents?language=pt_BR.

  6. If you downloaded translations from Crowdin, do not forget to upload back your changes!

Files at $SERVER/nxserver/nuxeo.war are re-computed from Nuxeo bundles at each server startup: make sure that you have a copy of your file before restarting your server, to avoid losing your work. If you'd like these translations to be kept after a restart, follow instructions below to make translations part of a Nuxeo bundle.

Identifying a Specific Label to Translate

If you want to translate or update a specific label you saw in the UI but you can identify it in Crowdin between several messages, follow the steps below:

  1. Activate the Dev mode on your Nuxeo server:

    • Add the following to the $SERVER/bin/nuxeo.conf file.

       org.nuxeo.dev=true
      
    • Or in JSF UI go to ADMIN > Update Center > Nuxeo Studio and click on the dedicated button.

  2. Log in as Administrator.

  3. Edit the file $SERVER/nxserver/nuxeo.war/WEB-INF/classes/messages_xx_XX.properties and replace the labels you want to identify and translate by their id in order to get something like action.view.rights.management=action.view.rights.management.
  4. Click on Dev mode: force flush next to the Logout button in the UI.

    If you haven't changed the default language in your preferences, you can add ?language=xx_XX at the end of the page URL to see the page in your target language. For instance https://nightly.nuxeo.com/nuxeo/nxpath/default/default-domain@view_documents?language=pt_BR.

    The label you want to translate is replaced by its id.

Contributions

Contributions on Crowdin side will be synchronized daily to the Nuxeo master source code. Translations that are not approved will also be taken into account.

If you are not using Crowdin, please contribute your translation work back to Nuxeo so that other users can benefit from it.

Instructions for Developers

In documentation below, the xx_XX notation will be used to reference the four letter code name for your language.

Updating or Creating Localized Messages for a Custom Nuxeo Bundle

  1. Place your custom messages.properties file in your bundle at src/main/resources/OSGI-INF/l10n/messages_xx_XX.properties.
  2. Create of edit the file at src/main/resources/OSGI-INF/deployment-fragment.xml to contribute the translations to the main aggregated translation files used by the application:

    <?xml version="1.0"?>
    <fragment version="1">
    
      <require>org.nuxeo.ecm.platform.lang</require>
      <require>org.nuxeo.ecm.platform.lang.ext</require>
    
      <install>
        <delete path="${bundle.fileName}.tmp" />
        <mkdir path="${bundle.fileName}.tmp" />
        <unzip from="${bundle.fileName}" to="${bundle.fileName}.tmp" />
    
        <append from="${bundle.fileName}.tmp/OSGI-INF/l10n/messages_xx_XX.properties"
          to="nuxeo.war/WEB-INF/classes/messages.properties" addNewLine="true" />
        <append from="${bundle.fileName}.tmp/OSGI-INF/l10n/messages_xx_XX.properties"
          to="nuxeo.war/WEB-INF/classes/messages_xx.properties" addNewLine="true" />
        <append from="${bundle.fileName}.tmp/OSGI-INF/l10n/messages_en_US.properties"
          to="nuxeo.war/WEB-INF/classes/messages_xx_XX.properties" addNewLine="true" />
    
        <delete path="${bundle.fileName}.tmp" />
      </install>
    
    </fragment>
    
    

    In the above example, translations from the file at messages_xx_XX.properties are also contributed to the main messages.properties file (used as a fallback when translation is not defined for a given language). Translations are also contributed to the messages_xx.properties file, as well as the original main messages_xx_XX.properties file. You can adapt this behavior depending on your use case.

Note:

The require tag ensures that you are contributing to existing files, already created thanks to the default nuxeo-platform-lang and nuxeo-platform-lang-ext modules. If you are not contributing to an existing file, you should create it:

<copy from="${bundle.fileName}.tmp/OSGI-INF/l10n/messages_xx_XX.properties"
  to="nuxeo.war/WEB-INF/classes/messages_xx_XX.properties"/>

You can also use the require tag to control ordering of contributions to the final nuxeo.war/WEB-INF/classes/messages_xx_XX.properties file, so that you can override values for existing translations: if a duplicate key is present in the final file, the last value will be taken into account.

Adding a New Language to a Nuxeo Application

  1. Add the new messages_xx_XX.properties file in src/main/resources/web/nuxeo.war/WEB-INF/classes/.
  2. Modify the deployment-fragment.xml file adding a new entry with your locale:
    <extension target="faces-config#APPLICATION_LOCALE">
     <locale-config>
       <supported-locale>xx_XX</supported-locale>
     </locale-config>
    </extension>
    

Understanding Locale and Lookup of Translations

Here's the resolving order when looking for a label in Brazilian, for instance.

messages_pt_BR.properties -> messages_pt_PT.properties -> messages_en.properties -> messages.properties

Brazilian is a 'dialect' of Portuguese, so there is first a fallback on Portuguese, then a fallback to the default language of the application ("en" for Nuxeo) then to messages.properties.

Most of the fallback is actually handled directly by Crowdin, the tool we use for translations. When downloading a file from Crowdin, like messages_pt_BR.properties for instance, the missing labels will be replaced by the ones from file messages_pt_PT.properties (if it exists), then by the reference English file used by Crowdin. This is why you'll see English translations by default in some non-English files.

What's with these two letter files like messages_pt.properties? Well those are actually an automatic copy of the four letter version. It's only here to have a two letter fallback when browsers language are set to a two letter format.

Unit-Testing Translations

Test cases are available to test for default translations provided in Nuxeo bundles. To check your translation file, you can just add the following test case to your bundle:

package org.nuxeo.ecm.collections.lang.test;
import org.nuxeo.ecm.platform.test.TranslationTestCase;
/**
 * Simple integrity tests on messages file(s).
 */
public class TestMessages extends TranslationTestCase {
}

The TranslationTestCase file is defined in the nuxeo-platform-test module, so you should add the following to your project pom.xml file:

<dependency>
  <groupId>org.nuxeo.ecm.platform</groupId>
  <artifactId>nuxeo-platform-test</artifactId>
  <scope>test</scope>
</dependency>

This test case will assume that the English translation file is at the "usual" location, you can override method getEnTranslationsPath if you placed your properties file elsewhere. It will check for simple things: syntax errors (errors in unicode characters can prevent the file from being loaded, for instance) and duplicate entries (that can happen easily when copy/pasting translations).

Additional test cases exist if you'd like to check for consistency between translations of two different languages for instance, you can check the TranslationEnFrTestCase logics or the TestExtMessages parametrized tests on the nuxeo-platform-lang-ext module.

Nuxeo Crowdin Translations Synchronization Process

Nuxeo - Crowdin Synchronization

Since a Nuxeo application is composed of several bundles, which can contribute their own translations to the main translation file, all translation files for supported modules are aggregated and pushed to Crowdin as a "reference" file.

Only the master branch is handled by Crowdin: changes to translation files in maintenance branches will have to be committed manually in GitHub.

  • The generated aggregate file at https://github.com/nuxeo/nuxeo-platform-lang-ext/blob/master/src/main/resources/crowdin/messages.properties is generated from all supported Nuxeo modules that hold a crowdin.ini file, referencing the relative path to their messages_en_US.properties file. Here is a sample crowdin.ini file content:

    [nuxeo]
    en_US=src/main/resources/OSGI-INF/l10n/messages_en_US.properties
    

    So to add or remove a module translation files to the main aggregated file, you should just add or remove the crowdin.ini file from the root of the module.

  • This file is generated daily and pushed to Crowdin as the new "reference" file: you should not make any manual changes to it, otherwise they will be lost at next automatic update. You should modify the original messages_en_US.properties file instead.

  • If the aggregated file holds duplicate keys, or syntax errors, it will not be pushed to Crowdin (unit tests are run on the lang-ext module before pushing).
  • Modifications to the reference file have the following result on Crowdin side:
    • Existing approved translations do not change if the reference value did not change.
    • Existing approved translations are unapproved if the reference value changed (but current value is kept). Crowdin helps detecting these translations for proofreading.
    • New translations are marked as untranslated (they will present the reference English value at export).
    • Removed translations are not presented on the Crowdin UI anymore.

Crowdin - Nuxeo Synchronization

  • Daily, non-English translations from Crowdin are pushed to the Nuxeo source code, inside the nuxeo-platform-lang-ext module.
  • All translations are exported, even unapproved.
  • Untranslated labels take the reference English value by default.
  • If exported files hold duplicate keys, or syntax errors, they will not be pushed to GitHub (unit test are run on the lang-ext module before pushing).

An export of translations files from Crowdin requires the project to be "built" first. This build is quite costly on Crowdin side, so it will not be performed more than once every 30 minutes via the API (used by automated synchronization). In this case, you should ask one of the Crowdin project managers to build the project directly on the Crowdin UI (or wait 30 minutes) before launching the synchronization again.