How to translate your React app with react-i18next

Setup your first React app
We're setting up a small React application to learn how localization works. Of course, you can skip this section if you want to use your own application for that.
If you haven't already installed the create-react-app scripts, install them using npm:
npm install -g create-react-app
With the following lines you create an empty react app and start it:
npx create-react-app react-i18next-translation-demo
cd react-i18next-translation-demo
npm start
The last line automatically opens the URL http://localhost:3000 and dislays the welcome message rendered by the created app.
As we want to use react-i18next to localize our application, add it to you project:
npm install --save i18next react-i18next
Add internationalization
The file src/index.js
renders the App
react element into your DOM:
ReactDOM.render(
<React.StrictMode>
<App/>
</React.StrictMode>,
document.getElementById('root')
);
To make the i18next
configuration available in all our components we have to wrap the App
component
with I18nextProvider
. It expects an i18next
instance which must be initialized before:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import {I18nextProvider} from "react-i18next";
import i18next from "i18next";
i18next.init({
interpolation: { escapeValue: false }, // React already does escaping
});
ReactDOM.render(
<React.StrictMode>
<I18nextProvider i18n={i18next}>
<App/>
</I18nextProvider>
</React.StrictMode>,
document.getElementById('root')
);
serviceWorker.unregister();
Simple translations
Let's now create a simple component containing a translatable text. Name the component HeaderComponent
.
In order to use translations you have to call useTranslation()
which returns the translation function t()
, and
the i18n object. Wrap all text you want to translate with the function - e.g. t('your.translation.id')
You can also pass a context (a.k.a. namespace) name to useTranslation()
to organize your messages.
Use useTranslation('main')
to specify a single context, or useTranslation(['main','common'])
if you want
to use translations from multiple contexts.
Apply these changes to your App.js
file:
import React, {Suspense} from 'react';
import './App.css';
import {useTranslation} from "react-i18next";
function HeaderComponent()
{
const {t, i18n} = useTranslation('common');
return <h1>{t('welcome.title')}</h1>
}
function App()
{
return (
<Suspense fallback="loading">
<div className="App">
<HeaderComponent/>
</div>
</Suspense>
);
}
export default App;
Some more work is required if you want to use High-Order-Components (HOC).
The t()
function is available through the props
. But you also have to
wrap your component with withTranslation()
to get access to the translation methods:
class HighOrderComponent extends Component {
render() {
const { t } = this.props;
return (
<h1>{t('welcome.title')}</h1>
)
}
}
const HighOrderComponentTranslated = withTranslation('common')(HighOrderComponent)
Adding parameters
The t()
function also accepts a 2nd parameter: A javascript object with parameters that i18next uses
to replace parts of the translation strings.
<h1>{t('welcome.title', {framework:'React'})}</h1>
Translation IDs vs Translation strings
The t()
function does not really care what you use as translation string.
You'll see many examples where developers use the translation messages. E.g. t('Welcome to React')
.
We do not recommend using this. Especially when you are using our translation editor called BabelEdit.
For short texts like 'Welcome to React'
it seems to be ok — but you'll also have longer texts
which become harder and harder to handle.
Use meaningful identifiers instead. You can even use .
to display them as a tree later.
E.g.
- main.title
- main.subtitle
- main.form.name-label
- main.form.name-placeholder
- common.forms.submit-button
- common.forms.cancel-button
This translates into a clean hierarchy:
- main
- title
- subtitle
- form
- name-label
- name-placeholder
- common
- forms
- submit-button
- cancel-button
- forms
This gives the translator a quite good idea where a text is used.
It also has another important advantage: Say you use the translation text Welcome to React
and you decide that
this text has to get an exclamation mark at the end for the English version Welcome to React!
. This breaks
all other translation files! The reference Welcome to React
is not found anymore! Using IDs helps you keep
the translation files consistent and well-structured!
Translate your application
The translations of our custom text messsages will be stored for each language in a separate directory. For each namespace we create a separate .json file. Let's create JSON files for English and German translations:
src/translations/en/common.json:
{
"welcome": {
"title": "Welcome to {{framework}}"
}
}
src/translations/de/common.json:
{
"welcome": {
"title": "Willkommen bei {{framework}}"
}
}
You now have to load these JSON files and add them to the options passed to
i18next.init()
.
We are adding the translations at compile time to keep things simple for this tutorial. Translations can also be loaded at runtime, of course.
Update src/index.js with the following code:
import common_de from "./translations/de/common.json";
import common_en from "./translations/en/common.json";
i18next.init({
interpolation: { escapeValue: false }, // React already does escaping
lng: 'en', // language to use
resources: {
en: {
common: common_en // 'common' is our custom namespace
},
de: {
common: common_de
},
},
});
Changing languages
To automatically select a language you might want to use one of the language detector plugins
listed on this page. For our demo app we just add two buttons and trigger the language change manually.
Add the following lines to the render()
function in App.js
:
function HeaderComponent()
{
const [t, i18n] = useTranslation('common');
return <div>
<h1>{t('welcome.title', {framework:'React'})}</h1>
<button onClick={() => i18n.changeLanguage('de')}>de</button>
<button onClick={() => i18n.changeLanguage('en')}>en</button>
</div>
}
Maintain translation files
Yes — editing and keeping track of JSON files is a pain. Especially if you have multiple languages to work with.
It's also quite painful to work with translators — most of them don't really like to work with JSON files some of them even don't accept JSON files at all.
This is why we've created a specialized editor to help you with this: BabelEdit.
- Work with multiple JSON files and languages at once
- Simple UI with good usability
- Easy exchange with translators: Export and import translations for Excel, Pages, Google Sheets
- Add comments and directions for your translators
- Approval process for translations
To get started with BabelEdit download it from here: Download BabelEdit
Click the i18next button to create a react-i18next project:

Drop the project's root directory onto the Configure languages dialog. BabelEdit should automatically find your translation files, and detect the languages. You can adjust the settings manually:

If the selected file locations are wrong go deeper into your project tree and directly drop the src/translations folder onto BabelEdit.
Finally, select your primary language:

After opening the project you should see a screen with your translations:
- The left tree containing the translation IDs
- The right part with the translations

BabelEdit supports many nice features among others:
- Machine translations and suggestions
- Spell-checking
- Similar phrases view
- Comments for translators
Press Save after editing your translations. BabelEdit asks you where to save the translation project file (.babel). This file stores the configuration and features like the approved flags and translator comments.
Translations are always saved to the .JSON files directly.