In this article, you will learn how to configure FormatJS (react-intl) library with hosted translations via Translation Hosting. Translation hosting allows you to change translations without re-deploying application as all messages are loaded directly from the cloud. However, altering the application code to use locally loaded files is also possible and straightforward.
FormatJS backed by Yahoo is a very good alternative for i18next, it's recommended for applications that want to keep the i18n as simple as possible.
This article comes with a demo application.
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 > Settings > Credentials > Project Token
const BASE_URL = "https://cdn.simplelocalize.io";
const ENVIRONMENT = "_latest"; // or "_production"
const DEFAULT_LANGUAGE = "en"; // default language
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;
SimpleLocalize can be also easily configured to use local files instead fetching translations from the cloud, but the way how you load files locally depends on your project setup.
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 translations
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"
/>
By default, FormattedMessage
component renders a span
element. You can change it by passing tagName
prop to the component.
<FormattedMessage
id="YOUR_TRANSLATION_KEY"
defaultMessage="My default message"
description="My code description"
tagName="h1"
/>
Providing values to translation messages
You can use values
property to pass variables to the translation message.
<FormattedMessage
id="YOUR_TRANSLATION_KEY"
defaultMessage="Hello {name}"
description="My code description"
values={{
name: 'John'
}}
/>
Learn more about message interpolation.
Using custom component in translation messages
FormattedMessage
component also allows you to use custom components
to pass React components to the translation message.
<FormattedMessage
id="YOUR_TRANSLATION_KEY"
defaultMessage="Hello <strong>{name}</strong>"
description="My code description"
values={{
name: 'John'
}}
components={{
strong: <strong/>
}}
/>
Extract translations
Now, let's use formatjs/cli which we installed together with react-intl library, to extract translations from the code.
npm run extract
This command will extract all translation keys from the code and save them to lang/en.json file, we set this file as an output file in the package.json
script with --out-file lang/en.json
argument.
Read more about FormatJS CLI integration.
Upload translations
Now it's time to upload translations to SimpleLocalize. We will use the exported lang/en.json
file and simplelocalize-json
format.
Create simplelocalize.yml
file with CLI configuration in your project root directory.
$ simplelocalize upload \
--apiKey YOUR_API_KEY \
--languageKey en \
--uploadFormat simplelocalize-json \
--uploadPath ./lang/en.json
Please note that we provided a languageKey
parameter that matches the language of defaultMessage
texts in your code.
Thanks to that SimpleLocalize will automatically assign default messages as translations.
Translate application
Now, you can head to the Translation Editor and start translating your application.
Add new languages in the Languages tab and use auto-translation features to speed up the process. You can choose from multiple machine translation providers like OpenAI, Google Translate, or DeepL.
Once auto-translation is finished you can review changes or just publish translations to the CDN by clicking the 'Save & Publish' button or 'Publish' button in the Hosting tab.
Switching languages
Let's test if our application can switch between languages. We will add two buttons to the application that will change the language to English or Spanish, and
use LanguageContext.Consumer
to change the current language.
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
- In-depth guide on FormatJS CLI
- Full code on GitHub
- Official documentation: FormatJS
- JavaScript number formatting