7. Internationalization

7.1. Making Text Translatable

Volto uses react-intl to translate strings. We start by importing the component:

import { FormattedMessage } from 'react-intl';

Then we can use the FormattedMessage component to translate a string:

<FormattedMessage
  id="Some string"
  defaultMessage="Some string"
/>

7.2. Extracting i18n Strings

Volto provides an i18n extraction script to get all translatable strings from your application. Run the following:

$ 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 description suggests it will first extract all messages from the source files into a .json file. Then it will synchronize the extracted messages with the .pot master file and with all the .po files found in the project.

After that it will generate the final .json files which are used in the application. If you want to translate the strings edit the .po files and run the script again. This script will combine the messages located in Volto itself and the current project, and combine them into the .json files.

7.3. Excercise

Translate the Tags: label which was added in the previous chapter to Dutch and German.

Solution

/**
 * Tags component.
 * @module components/theme/Tags/Tags
 */

import React from 'react';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import { Container } from 'semantic-ui-react';
import { FormattedMessage } from 'react-intl';

/**
 * Tags component class.
 * @function Tags
 * @param {array} tags Array of tags.
 * @returns {string} Markup of the component.
 */
const Tags = ({ tags }) =>
  tags && tags.length > 0 ? (
    <Container>
      <FormattedMessage id="Tags" defaultMessage="Tags" />:
      {tags.map(tag => (
        <Link className="ui label" to={`/search?Subject=${tag}`} key={tag}>
          {tag}
        </Link>
      ))}
    </Container>
  ) : (
    <span />
  );

/**
 * Property types.
 * @property {Object} propTypes Property types.
 * @static
 */
Tags.propTypes = {
  tags: PropTypes.arrayOf(PropTypes.string),
};

/**
 * Default properties.
 * @property {Object} defaultProps Default properties.
 * @static
 */
Tags.defaultProps = {
  tags: null,
};

export default Tags;

7.4. Translate Attributes

When you want to translate attributes you can’t use the FormattedMessage component since it will generate markup. In order to translate a normal string or attribute you can use the formatMessage method of react-intl. First you need to import all the required methods:

import { defineMessages, injectIntl, intlShape } from 'react-intl';

Then you can use the defineMessages method to define your messages:

const messages = defineMessages({
  site: {
    id: 'Site',
    defaultMessage: 'Site',
  },
});

In order to to use the formatMessage method you have to inject the intl object into your class or function. For pure functions you can use:

export default injectIntl(Logo);

And for classes you can use:

@injectIntl
class Logo extends Component {

}

You can use the provided proptype as follows:

intl: intlShape.isRequired,

Now we can use the method like this:

<Link to="/" title={intl.formatMessage(messages.site)}>

7.5. Exercise

Add a title to the tag links with the message Search for tag {tag}.

Solution

/**
 * Tags component.
 * @module components/theme/Tags/Tags
 */

import React from 'react';
import { Link } from 'react-router-dom';
import PropTypes from 'prop-types';
import { Container } from 'semantic-ui-react';
import {
  defineMessages,
  injectIntl,
  intlShape,
  FormattedMessage,
} from 'react-intl';

const messages = defineMessages({
  searchTag: {
    id: 'Search for tag {tag}',
    defaultMessage: 'Search for tag {tag}',
  },
});

/**
 * Tags component class.
 * @function Tags
 * @param {array} tags Array of tags.
 * @returns {string} Markup of the component.
 */
const Tags = ({ tags, intl }) =>
  tags && tags.length > 0 ? (
    <Container>
      <FormattedMessage id="Tags" defaultMessage="Tags" />:
      {tags.map(tag => (
        <Link
          className="ui label"
          to={`/search?Subject=${tag}`}
          key={tag}
          title={intl.formatMessage(messages.searchTag, { tag })}
        >
          {tag}
        </Link>
      ))}
    </Container>
  ) : (
    <span />
  );

/**
 * Property types.
 * @property {Object} propTypes Property types.
 * @static
 */
Tags.propTypes = {
  tags: PropTypes.arrayOf(PropTypes.string),
  intl: intlShape.isRequired,
};

/**
 * Default properties.
 * @property {Object} defaultProps Default properties.
 * @static
 */
Tags.defaultProps = {
  tags: null,
};

export default injectIntl(Tags);