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.
Manage your translation strings in Translation Editor
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.
How to start auto-translation for many languages at once
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>