Internationalization#

Internationalization (i18n) is the process of creating user interfaces which are suitable for different languages and cultural contexts. This chapter describes the most common use cases for internationalization when developing your Volto add-ons or contributing to the Volto core itself.

Process and file structure overview#

Volto uses the library react-intl to provide translations for any potential language. Anything in the official documentation of react-intl also applies to Volto.

The workflow for creating new translatable text strings is as follows:

  1. Create translatable i18n strings in your code.

  2. Extract all i18n strings from your code with a script and create artifacts, like .po and .pot files.

  3. Use your favorite editor to translate all i18n strings by editing the .po files.

  4. Re-run the script, which then moves the translations from the .po files into .json files for Volto to use.

  5. Edit packages/volto/src/constants/Languages.cjs, adding your new language's locale code, as defined in Locale and language tag conventions.

  6. Run the unit tests as described in Run Jest tests on Volto core, following the prompt to update the snapshot when the test fails.

This way of organizing translations relies on gettext, a proven and established system with great tool support. .json files in react-intl are equivalent to .mo files in gettext.

All translation files are located under the directory packages/volto/locales.

The file packages/volto/locales/volto.pot holds all extracted i18n strings, and acts as the source template for all the .po files. A translation for each language is stored in two files, both with their language code in the file name or path. Using English as an example, the .po file is stored at packages/volto/locales/en/LC_MESSAGES/volto.po, and the .json file is stored at packages/volto/locales/en.json. The abridged file structure for English would appear as follows.

locales/
├── en
│   └── LC_MESSAGES
│       └── volto.po
├── en.json
└── volto.pot

Create i18n strings#

In this section, you can learn how to translate HTML elements and attributes.

Translate text in HTML elements#

react-intl can identify translatable texts with the FormattedMessage components. As the name of this component suggests, it's also possible to format your messages as your prefer.

The following code snippet is an example of how you can write a text with the content Hello World, which can be identified via hello_world.

import { FormattedMessage } from 'react-intl';

function HelloWorld(props) {
  return (
    <div>
      <FormattedMessage
        id="hello_world"
        defaultMessage="Hello World"
      />
    </div>
  );
}

The identifier hello_world is then commonly used across all the translations. There are more features available, such as using placeholders. See the documentation for all features in the FormattedMessage component.

Translate attributes#

As FormatMessage is only suitable for creating text within HTML elements, it cannot be used for translating individual attributes. But with the method formatMessage, there is another way to translate primitive strings.

This approach can be best explained with an example. Assume you have a component called TeaserImage which contains an image that has, for accessibility reasons, the alt attribute. To translate the alt attribute, you have to do the following steps:

  1. Import the following required methods.

    import { defineMessages, injectIntl, intlShape } from 'react-intl';
    
  2. Define a message (or more) via defineMessages:

    const messages = defineMessages({
      teaserAltText: {
        id: 'teaser_alt_text',
        defaultMessage: 'Teaser Alt Text',
      },
    });
    
  3. Because your component class or function needs the method formatMessage, there is a special property intl that you need to inject in one of the two following ways.

    // When using a pure function:
    export default injectIntl(TeaserImage);
    
    // When using a component:
    @injectIntl
    class TeaserImage extends Component {
      // ...
    }
    

    Because you now have another prop available, you need to define it in the propTypes:

    TeaserImage.propTypes = {
      intl: intlShape.isRequired,
      // ...
    };
    
  4. As the final last step, you can use the method as follows:

    <img src="..." alt={intl.formatMessage(messages.teaserAltText)}>
    

Extract i18n strings#

Volto provides an i18n extraction script to get all translatable strings from your application. You can invoke this script with the following command.

yarn i18n

This will generate the following output:

Extracting messages from source files...
Synchronizing messages to pot file...
Synchronizing messages to po files...
Generating the json files...
done!

As the output suggests, it will first extract all messages from the source files into .json files. Then it will synchronize the extracted messages with the .pot main template and with all the .po files found in the project. This script will combine the messages located in Volto itself and the current project, and combine them into the .json files.

Override i18n messages#

If you want to override an existing translation, you should declare the original message again somewhere else in your project. For example in src/config.js:

import { defineMessages } from 'react-intl';

defineMessages({
  back: {
    id: 'Back',
    defaultMessage: 'Back',
  },
});

Then run yarn i18n. You will find the translation ready to override in your locales directory, such as locales/de/LC_MESSAGES/volto.po.

#: src/config
msgid "Back"
msgstr "My overridden translation"

After you set the override, then run yarn i18n again to create the de.json translation files. Restart Volto to see the changes applied.

Note

Shadowed components do not override translations. 99% of the time you do not want them to do that. Thus the customizations folder is excluded from the i18n build.

Contribute translations for an unsupported language#

The Volto project welcomes all speakers from the world to include any language, which is not supported yet.

If your language's .po file is not available in Volto, open an issue in GitHub. Either you can create the .po file or we can do it for you. After that, you can start contributing your translations.