This is the early access documentation preview for Custom Views. This documentation might not be in sync with our official documentation.

Translations

The Merchant Center is translated in several languages like en, de, es, etc. The default language is en.

The Merchant Center locale can be changed in the user Profile page.

When you develop a Custom View you can choose to optionally translate it in one or more languages.
Translations are usually included as JSON files that are dynamically imported into the Custom View based on the user's locale.

To facilitate managing source messages and translations we provide some built-in functionalities in Custom Views and some tooling around that.

Defining source messages

As a first step, we recommend defining source messages in the messages.js file next to your React component.

messages.jsJavaScript
import { defineMessages } from 'react-intl';
export default defineMessages({
title: {
id: 'Channels.title',
defaultMessage: 'Channels list',
},
});

Source messages are defined as named objects by react-intl, a library with React abstractions on top of the ECMAScript Internationalization API.

We recommend getting familiar with the ICU Message syntax to take advantage of the different syntax features.

The source messages can then be used in your React components.
Below you will find some examples of consuming the Intl messages.

Consuming messages as React components

The react-intl library provides several React components to render the message, like <FormattedMessage>.

channels.jsJavaScript
import { FormattedMessage } from 'react-intl';
import messages from './messages';
const Channels = () => {
// ...
return (
<Spacings.Stack scale="l">
<Spacings.Stack scale="s">
<Text.Headline as="h2">
<FormattedMessage message={messages.title}>
</Text.Headline>
</Spacings.Stack>
{/* ... */}
</Spacings.Stack>
)
};

Most of the UI Kit components support formatting messages via the prop intlMessage. You can pass your raw message and the component takes care of rendering the message.

channels.jsJavaScript
import { FormattedMessage } from 'react-intl';
import messages from './messages';
const Channels = () => {
// ...
return (
<Spacings.Stack scale="l">
<Spacings.Stack scale="s">
<Text.Headline as="h2" intlMessage={messages.title} />
</Spacings.Stack>
{/* ... */}
</Spacings.Stack>
)
};

Consuming messages as React Hooks

The react-intl library also provides an imperative API approach via useIntl, which exposes an intl object with several formatting methods.

Using the intl object is usually necessary for props that only accept strings, for example aria-label, title, etc.

import { useIntl } from 'react-intl';
const Button = () => {
const intl = useIntl();
return (
<FlatButton
label={intl.formatMessage(messages.save)}
// ...
/>
)
}

Extracting messages for translations

To generate translation files, you can use the official @formatjs/cli package to extract source messages into a core.json source file. For example:

formatjs extract \
--out-file=./src/i18n/data/core.json \
'src/**/messages.js'

The core.json is the so-called source file, which should be used as the reference file for the translations in the other locales.

As a convention, we store the translation files in an i18n folder:

└── src
└── i18n
└── data
├── core.json
├── en.json
└── de.json

Based on your translation tool, you may need to transform the extracted messages to the appropriate format. For that you can write a formatter file and pass it as the --format option to the script.

formatjs extract \
--format=./intl-transformer.js
--out-file=./src/i18n/data/core.json \
'src/**/messages.js'

At commercetools we use Transifex as our translation tool. Therefore, in our applications we generate a core.json file with the key being the message id and the value being the default Intl message.

{
"Channels.title": "Channels list"
}

Importing translations

The JSON files containing the translations need to be loaded within the application. The <CustomViewShell> expects a applicationMessages prop that is used to load the messages in the react-intl provider.
The applicationMessages prop can either be a JSON object or a function returning a Promise with the loaded messages.

To keep the bundle size low, the application should only load the messages for a specific locale and not all of them. This can be achieved by using the Code-Splitting feature.

Given the translation messages are located in the i18n/data folder, you can define a function to dynamically load the messages:

entry-point.jsJavaScript
const loadMessages = lang => {
let loadAppI18nPromise;
switch (lang) {
case 'de':
loadAppI18nPromise = import(
'../../i18n/data/de.json' /* webpackChunkName: "app-i18n-de" */
);
break;
case 'es':
loadAppI18nPromise = import(
'../../i18n/data/es.json' /* webpackChunkName: "app-i18n-es" */
);
break;
default:
loadAppI18nPromise = import(
'../../i18n/data/en.json' /* webpackChunkName: "app-i18n-en" */
);
}
return loadAppI18nPromise.then(
result => result.default,
error => {
console.warn(
`Something went wrong while loading the app messages for ${lang}`,
error
);
return {};
}
);
};
const EntryPoint = () => (
<CustomViewShell
applicationMessages={loadMessages}
// ...
/>
);