Internationalization

📘

We're using react-intl version 2.4.0 or compatible.

The Frontastic API hub inherently knows about internationalization. The locale of the consumer browsing the page is available, the configuration of the project (locales configured for the project and the default locale) as well as translatable data.

We support multi-language and multi-region setups which can be done in different ways depending on your requirements.

There are generally 3 things that can be translated or adapted to the target regions by other means:

  • Frontastic page structure and data
  • Data from external APIs (products, content, …)
  • User interface strings (buttons, captions, …)

Not only can each Frontastic project be multi-lingual and multi-region, but you can also have a dedicated Frontastic project by region or language.

We can identify languages and regions by their locale. For example, en_GB would stand for English - Great Britain. This is especially important for slight variations in languages, like the difference between English in Great Britain and the USA, but also for countries using multiple languages, like Switzerland (de_CH for German, fr_CH for French and it_CH for Italian).

Frontastic page structure and data

The simplest way to set up internationalization in Frontastic is just a single language per project. If you'd like to do this, just tell us to create multiple projects, with 1 language each.

The other way to do this is to use a single project with multiple languages. This way, the structure and pages are shared across all languages. You can still overwrite pages for a certain language or locale, for example, to show different start pages to customers from Germany. You would also need to define a default language.

Or, both mechanisms can be combined:

  • English website (en_GB)
  • Swiss website (de_CH, fr_CH, it_CH) with a similar page folder tree as the English website, but different pages

If a single project defines multiple locales all contents of fields configured as translatable can be translated in the Frontastic studio.

For more information on how a project can be set up, see this article.

Data from external APIs

The API hub always determines the locale of the user based on the locale configured for the current project and the accept header sent by the user's browser. For all subsequent requests, this locale will be used, except if the user chooses a different locale manually. This mechanism can be changed and extended. So, we always know which locale the current user uses.

The user's current locale is passed to all API requests (ProductAPI, ContentAPI, …) and those APIs can modify queries based on that and should only return data in the current user's locale.

Some APIs need even more information than language and region in the locale, which is supported by Frontastic. The system is also currency aware, which is especially important for commerce APIs, so you could use [email protected] to use Euro instead of Pounds when shopping even in Great Britain.

If there's some additional logic wanted like a fallback from de_AT and de_DE to just de (because you don't want to maintain dedicated product descriptions in Austrian German and German) this logic has to be implemented either in the API itself or inside the mapping in the API abstraction in Frontastic.

User interface strings

(Also known as internationalization of Frontastic components)

Making Frontastic component data translatable

There are 3 field types in Frontastic components that can be translatable, they are:

  • 'string'
  • 'text'
  • 'markdown'

❗️

These field types are translatable by default so if you don't want these to be translatable you need to explicitly switch the setting to false.

For example:

{
    ...
    "schema": [
        {
            "name": "Copy",
            "fields": [
            ...
                {
                    "label": "Title",
                    "field": "title",
                    "type": "string",
                    "translatable": false
                },
                {
                    "label": "CTA",
                    "field": "callToAction",
                    "type": "string",
                }
            ]
        }
    ]
}

This will give the user of the Frontastic studio the possibility to configure a setting callToAction for each of the locales available in the project but not the title.

Defining translatable data

Once you receive translatable data in your Frontastic component you'll see that the data format changes. Instead of retrieving a single instance of the value type, you receive a HashMap that uses locales as an index and has a value type assigned to each of these keys. For example:

{
    data: {
        // ...
        CTA: {
            'en_GB': true,
            'de_DE': false,
            'de_CH': true,
        }
    }
}

You'll then need to determine the correct one for your situation (see later section on advanced internationalization handling). But for textual values API hub provides a shortcut.

Translating textual data

If you defined a text field that's translatable as below in the JSON tab, you can simply render the description in the user's language using the JSX.

{
    ...
    "schema": [
        {
            "name": "Configuration",
            "fields": [
            ...
                {
                    "label": "Description",
                    "field": "description",
                    "type": "text"
                }
            ]
        }
    ]
}
import Translatable from 'frontastic-catwalk/src/js/component/translatable'

class TranslationExampleTastic extends Component {
    render () {
        return (<Translatable value={this.props.data.description} />
      }
}

export default TranslationExampleTastic

The Translatable helper component will retrieve the users preferred locale and the locales configured for the project. It'll work out the best possible match from the given value and render it. It will also render a ´with the CSS class untranslated` around it if an exact match can't be found. Feel free to style this in your development or staging environment so that you can easily spot missing translations.

Automatic translation

If you only have simple string fields in your Frontastic components that are translatable, you can try our withTranslatedTasticData higher-order component.

This will pass all string Frontastic component fields that are translatable as an already translated string. This means you don't need to use the <Translatable /> helper component anymore. Our <TranslationExampleTastic /> helper component from before would look like this:

import withTranslatedTasticData from "frontastic-catwalk/src/js/component/withTranslatedTasticData";

class TranslationExampleTastic extends Component {
    render() {
        return this.props.data.description;
    }
}

export default withTranslatedTasticData(TranslationExampleTastic);

Working manually with internationalized data

If you need to work with more advanced data structures, numbers or you have advanced requirements than our Translatable helper component provided, you can handle internationalization by yourself. To do this, you need to retrieve the involved locale from the Redux Store using a connector function:

export default connect(
    (globalState, props) => {
        return {
            currentLocale: globalState.app.context.locale,
            defaultLocale: globalState.app.context.project.defaultLanguage
        }
    }
)(AdvancedTranslationExample)

The currentLocale prop will contain the locale preferred by the current user and defaultLocale can be used as a fallback for missing translations.

Frontastic component configuration versus code

So, should you put translations into a Frontastic component configuration or in the code itself? Well, that depends on whether you want these values to be editable in the Frontastic studio or not.

Where you have a field type of string available in your Frontastic component configuration, that's translatable by default. This means translations can be changed within the Frontastic studio.

If you have content that's more static and it won't often need to be translated, for example, a button that says "OK", then this should go into the code.

We use React-Intl for this and it's where you can use the FormattedMessage component. Our DependencyInjector gives you the ability to overwrite our default App.IntlProvider, see here.