This is the legacy documentation of Project-level Custom Applications, which is in maintenance mode. Visit the new documentation for Org-level Custom Applications.



Merchant Center applications use react-intl, a library built on top of the ECMAScript Internationalization API.

In the React components, you can use things like <FormattedMessage> or intl.formatMessage to render the correct translation message based on the active locale.

We usually define a messages.js file co-located to the React component using those messages. The file defines messages as following:

import { defineMessages } from 'react-intl';
export default defineMessages({
title: {
id: 'StateMachines.ListView.title',
description: 'The page title of state machines list',
defaultMessage: 'State Machines',

This is the default message, which will be used in case there is no translation available for the given locale.

Extracting messages for translations

To generate translation files, you can use the official @formatjs/cli package to extract message from React Intl messages files. For example:

formatjs extract \
--out-file=./src/i18n/data/core.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 \
--out-file=./src/i18n/data/core.json \

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.

"StateMachines.ListView.title": "State Machines"

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

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

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

Using the messages in the application

The JSON files containing the translations need to be loaded within the application. The <ApplicationShell> 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.js
const loadMessages = lang => {
let loadAppI18nPromise;
switch (lang) {
case 'de':
loadAppI18nPromise = import(
'../../i18n/data/de.json' /* webpackChunkName: "app-i18n-de" */
case 'es':
loadAppI18nPromise = import(
'../../i18n/data/es.json' /* webpackChunkName: "app-i18n-es" */
loadAppI18nPromise = import(
'../../i18n/data/en.json' /* webpackChunkName: "app-i18n-en" */
return loadAppI18nPromise.then(
result => result.default,
error => {
`Something went wrong while loading the app messages for ${lang}`,
return {};
const Application = () => (
// other props