React i18next

Jan 29, 2020

React i18next

React i18next is a library that allows to set up internationalization on your website. This library is based on i18next.


The first thing we will do is add the library to our project:

npm install react-i18next --save

Then we need to configure the way we are going to use it. For that, I will create a file helpers/i18n.js:

import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; i18n .use(initReactI18next) .init({ debug: true, // useful to see events that occur during development lng: 'en', fallbackLng: 'en', resources: { en: { translations: { // default namespace 'home.hello': 'Hello! Welcome to my app!' }, }, }, }); export default i18n;

And I will import it into index.js of my application:

import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; import i18n from './helpers/i18n'; // initialisation of i18next ReactDOM.render( <App />, document.getElementById("root") );


Simple translations

Here is the App.js file with a simple example:

import React from 'react'; import { useTranslation } from 'react-i18next'; const App = () => { const { t } = useTranslation(); return ( {t('home.hello')} ); }; export default App;

Here we use the useTranslation hook to access the translation service, but you can also use the HOC withTranslation:

import React from 'react'; import { withTranslation } from 'react-i18next'; const App = ({ t }) => { return ( {t('home.hello')} ); }; export default withTranslation()(App);

If you want to pass variables, this can also be done very easily. Let's say our translation key looks like this:

translations: { 'home.hello': 'Hello, {{ name }}! Welcome to my app!' },

To pass the name variable in our translation key we can do:

{t('home.hello'), { name: 'Astronaut' }}


If you need to put HTML in the translation, or if your translation key contains some, you can use the Trans component:

import React from 'react'; import { Trans } from 'react-i18next'; const App = () => { return ( <Trans values={{ name: 'Astronaut' }}><h1>home.hello</h1></Trans> ); }; export default App;


Of course, we also need to handle the case where the translation varies according to a number or quantity.

translations: { 'message': 'You have one message', 'message_plural': 'You have several messages', },

In this case, we will specify an additional argument, count, like this:

{t('home.hello'), { count: 5 }} <Trans count={5}><h1>home.hello</h1></Trans>

Detecting user's preferred language

Since we are working on a multi-language site, we want the user's preferred language to be automatically detected. In general, it is the browser's language. For this, we will add a new dependency:

npm install i18next-browser-languagedetector --save

And we will change our configuration as follows:

import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; // <= new i18n .use(LanguageDetector) // <= new .use(initReactI18next) // ... ; export default i18n;

Changing the language

Now that our application is able to detect the user's browser language, the user may want to change it. For this, let's add a button:

import React from 'react'; import { useTranslation } from 'react-i18next'; import Button from '@material-ui/core/Button'; const LanguageSwitcher = () => { const { i18n } = useTranslation(); const changeLanguage = async (lng) => { await i18n.changeLanguage(lng); // i18n.changeLanguage returns a Promise }; return ( <div> <Button onClick={() => changeLanguage('en')}>English</Button> <Button onClick={() => changeLanguage('fr')}>Français</Button> </div> ); } export default LanguageSwitcher;

Translation files

Obviously, we will want to put the translations into dedicated files, rather than keeping them in the configuration directly. Translation files are simple JSON files. We can imagine the following structure in our project:

public/ locales/ en/ common.json translations.json other.json fr/ common.json translations.json other.json


i18next works with namespaces, and you can have multiple namespaces per language. As a reminder, the default namespace is translations. In our example, common, translations and other are namespaces.

In this case, each time we want to access the keys that are in a particular namespace, we will do:

const { t } = useTranslation(['ns1', 'ns2', 'ns3']); t('key'); // loaded from namespace 'ns1' t('ns2:key'); // loaded from namespace 'ns2'

It is also possible to define a custom default namespace in the configuration:

import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; i18n .use(LanguageDetector) .use(initReactI18next) .init({ defaultNS: 'common', // <= new // ... }); ; export default i18n;

Loading local files

Now that the translations are in separate JSON files, we need to indicate in the i18next configuration how to retrieve them. For that, we will use i18next-xhr-backend:

npm install i18next-xhr-backend --save

Let's update the configuration file:

import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; import Backend from 'i18next-xhr-backend'; // <= new i18n .use(LanguageDetector) .use(Backend) // <= new .use(initReactI18next) .init({ backend: { // <= new loadPath: '/locales/{{lng}}/{{ns}}.json' } // ... }); ; export default i18n;

Note that the path specified in loadPath is in the public folder at the root of your project.

Loading remote files

We have presented in a previous article that we use service and save translation files on a server in the cloud. Hence, we need to recover the files hosted on a remote server:

import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; import LanguageDetector from 'i18next-browser-languagedetector'; import Backend from 'i18next-xhr-backend'; i18n .use(LanguageDetector) .use(Backend) .use(initReactI18next) .init({ debug: true, lng: 'en', fallbackLng: 'en', defaultNS: 'common', backend: { loadPath: `${process.env.TRANSLATIONS_ENDPOINT_URI}/{{ns}}.{{lng}}.json`, // we simply indicate the full URL to retrieve files } }); ; export default i18n;


React i18next can be configured to work with SSR. Here is the page that explains how to put this in place:

Nevertheless, we had a problem activating the SSR - the loading of the translations from the remote server was not done on the server side. This happens because i18next-xhr-backend uses fetch to recover the files, and fetch is not available on the server side.

As a result, we needed to write a custom backend based on the documentation here: We used the cross-fetch library that works on both the client and server sides.

Related articles

Debugging with Git

Today I'd like to talk about a tool that will allow us to rapidly isolate an incorrect commit, that caused a bug in our application: git bisect

Nov 8, 2017•4mn•Romain Pierlot

Discover Eleven Labs

To learn more, visit our website

Let's go


Eleven Labs - Paris

102, rue du Faubourg Saint Honoré

75008 Paris

Eleven Labs - Nantes

42, rue la Tour d'Auvergne

44200 Nantes

Eleven Labs - Montréal

1155, Metcalfe St Suite 1500

Montréal, QC H3B 2V6, Canada