FormatJS

Last modified: November 21, 2022Author: Jakub Pomykała

FormatJS: changing translations in realtime

In this article, you will learn how to configure FormatJS (react-intl) i18n library with Translation Hosting. Translation hosting allows you to change translations without re-deploying application as all messages are loaded directly from the cloud. This article comes with a demo application.

Table of contents

Before you start

Before you start, you need to have a SimpleLocalize account. If you don't have one yet, you can sign up for free. Create a new project and add get your API Key and Project Token.

Configuration

Install dependencies

Add react-intl library to your React application project and @formatjs/cli as development package.

# Using NPM
npm install -S react-intl
npm install -D @formatjs/cli

# Using yarn
yarn add react-intl
yarn add -D @formatjs/cli

Adjust package.json scripts

Add extract script to your package.json file.

{
  "scripts": {
    "extract": "npm run extract -- 'src/**/*.ts*' --ignore='**/*.d.ts' --out-file lang/en.json --id-interpolation-pattern '[sha512:contenthash:base64:6]'",
  }
}

Create language context

First, you need to configure react-intl library to use SimpleLocalize as a translation provider. We will start from creating a LanguageContext that will be used to store current language and provide it to the application.

interface LanguageContextInterface {
  changeLanguage: (language: string) => void;
  language: string;
}

const LanguageContext = React.createContext<LanguageContextInterface>({
  language: '',
  changeLanguage: () => {
    console.warn('LanguageContext.changeLanguage not implemented');
  },
});

export default LanguageContext;

Fetch translations

Next step is to create a SimpleLocalize component to fetch translations. We will use useEffect hook to fetch translations when the language in LanguageProvider changes. Additionally, we will use IntlProvider from react-intl to provide translations to the application.

import React from "react";
import {IntlProvider, MessageFormatElement} from 'react-intl'
import LanguageContext from "./LanguageContext";

const PROJECT_TOKEN = "<YOUR_PROJECT_TOKEN>"; // SimpleLocalize project > Integrations > Project Credentials > Project Token
const BASE_URL = "https://cdn.simplelocalize.io";
const ENVIRONMENT = "_latest"; // or "_production"
const DEFAULT_LANGUAGE = "en"; // comes from SimpleLocalize language key

const SimpleLocalize = ({children}: { children: React.ReactNode }) => {
  const [messages, setMessages] = React.useState<Record<string, string> | Record<string, MessageFormatElement[]> | undefined>({});
  const [language, setLanguage] = React.useState<string>(DEFAULT_LANGUAGE);

  const fetchTranslationMessages = (language: string): void => {
    const messages = `${BASE_URL}/${PROJECT_TOKEN}/${ENVIRONMENT}/${language}`;
    fetch(messages)
      .then((data) => data.json())
      .then((messages) => setMessages(messages));
  };

  React.useEffect(() => fetchTranslationMessages(language), [language]);

  return (
    <LanguageContext.Provider
      value={{
        changeLanguage: (language: string) => setLanguage(language),
        language
      }}>
      <IntlProvider
        locale={language}
        messages={messages}>
        {children}
      </IntlProvider>
    </LanguageContext.Provider>
  )
}

export default SimpleLocalize;

Remember to provide your Project Token in PROJECT_TOKEN variable. You can find it in your SimpleLocalize project > Integrations > Project Credentials > Project Token.

Provide translations to application

Now, we need to wrap our application with SimpleLocalize component.

import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import SimpleLocalize from "./SimpleLocalize";

ReactDOM.render(
  <React.StrictMode>
    <SimpleLocalize>
      <App/>
    </SimpleLocalize>
  </React.StrictMode>,
  document.getElementById('root')
);

Using translation messages

React-intl allows you to get translations in two ways:

Using useIntl hook

useIntl hook returns intl object that has formatMessage method to get translations by given id (translation key).

import React from 'react';
import {useIntl} from 'react-intl';

const App = () => {
  const intl = useIntl();
  return (
    <div>
      <h1>{intl.formatMessage({
            id: 'hello',
            defaultMessage: "My default message",
            description: "My code description"
        })}</h1>
    </div>
  );
};

export default App;

Using FormattedMessage component

FormattedMessage component is a wrapper for formatMessage method from intl object, that means you can use it in the same way as formatMessage method but as a component.

<FormattedMessage
  id="YOUR_TRANSLATION_KEY"
  defaultMessage="My default message"
  description="My code description"
/>

Extract translations from code

Now, let's use formatjs/cli which we installed together with react-intl library, to extract translations from the code.

npm run extract

Upload translations

Now you can use SimpleLocalize CLI to upload lang/en.json file to the translation editor. Use your project API Key in --apiKey parameter. You can find your API Key in SimpleLocalize project > 'Integrations' > 'Project credentials' > API Key.

$ simplelocalize upload \
        --apiKey YOUR_API_KEY \
        --languageKey en
        --uploadFormat simplelocalize-json \
        --uploadPath ./lang/en.json

Please note that we provided a languageKey in the upload component that matches the language of defaultMessage texts in your code. Thanks to that SimpleLocalize will automatically assign default messages as translations.

Translate your React application

Now you can translate your React application JSONs using the translation editor and publish changes.

translate project

If you want to translate your app in multiple languages fast, you can add new languages in the Languages tab, and use auto-translation features to speed up the process.

auto-translate project

After you finish translating your app, you can go to the 'Hosting' tab and click 'Publish' button to publish your translations to the app.

Switching between languages

You can now use LanguageContext.Consumer to switch between languages.

import LanguageContext from "./LanguageContext";

<LanguageContext.Consumer>
  {context => (<div>
    <button onClick={() => context.changeLanguage("en")}>English</button>
    <button onClick={() => context.changeLanguage("es")}>Spanish</button>
  </div>)}
</LanguageContext.Consumer>

Additional resources