DEV Community

Oksana Ivanchenko
Oksana Ivanchenko

Posted on • Updated on

How to translate your React.js app with i18next

Today, I will show you how to translate the text and date in your React.js application.

First of all, you must install some dependencies:

yarn add i18next i18next-xhr-backend i18next-browser-languagedetector react-i18next
Enter fullscreen mode Exit fullscreen mode

Now we need to create a file called i18n.js in our src folder:

import i18n from 'i18next';
import Backend from 'i18next-xhr-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import { initReactI18next } from 'react-i18next';

const fallbackLng = ['en']; 
const availableLanguages = ['en', 'ru'];

i18n
  .use(Backend) // load translation using xhr -> see /public/locales. We will add locales in the next step

  .use(LanguageDetector) // detect user language

  .use(initReactI18next) // pass the i18n instance to react-i18next.

  .init({
    fallbackLng, // if user computer language is not on the list of available languages, than we will be using the fallback language specified earlier
    debug: true,
    whitelist: availableLanguages,

    interpolation: {
      escapeValue: false
    },
  });

export default i18n;
Enter fullscreen mode Exit fullscreen mode

Then we will import i18n in index.js in src/index.js:

...
import './i18n';

ReactDOM.render(<App />, document.getElementById('root'));
...
Enter fullscreen mode Exit fullscreen mode

The next step is to create our locales: public/locales/en/translation.json and public/locales/ru/translation.json. This translation will be loaded automatically thanks to i18next-xhr-backend.

Also, in our App.js we need to add Suspense in oder to display a loading indicator.

import React, { Suspense } from 'react';
...
function App() {
  return (
    <div className="App">
      <Suspense fallback={(<div>Loading</div>)}>
      <WeatherForecast /> 
      </Suspense>
    </div>
  );
}
...
Enter fullscreen mode Exit fullscreen mode

Now we go to the component that we want to translate. If you have a Class Component we will be using the Higher Order Component withTranslation:

import React, { Component } from 'react';
import { withTranslation } from 'react-i18next';

class News extends Component {

   render(){
     const { t } = this.props;

     return (
       <h1>{t('news.title')}</h1>
     )
   };
};

export default withTranslation()(News);

Enter fullscreen mode Exit fullscreen mode

If you have a Functional Component you should use the react hook useTranslation:

import React from 'react';
import { useTranslation } from 'react-i18next';

const WeatherForecast = () => {
  const { t } = useTranslation();
  return (
    <h1>{t('weather.title')}</h1>
  )
};

export default WeatherForecast;
Enter fullscreen mode Exit fullscreen mode

Now we will modify our locales. In public/locales/en/translation.json:

{
  "news": {
    "title": "Our news"
  },
  "weather": {
    "title": "Weather forecast"
  }
}
Enter fullscreen mode Exit fullscreen mode

In public/locales/ru/translation.json:

{
  "news": {
    "title": "Наши новости"
  },
  "weather": {
    "title": "Прогноз погоды"
  }
}
Enter fullscreen mode Exit fullscreen mode

If you want to translate a phrase with a variable in it, you need do this:

  <p>{t('weather.temprature', {number: temprature})}</p>
Enter fullscreen mode Exit fullscreen mode

And in your .json locale file you need to write:

  "temprature": "Today in Lyon is cloudy: {{number}}°C."
Enter fullscreen mode Exit fullscreen mode

We will the same do for our Russian or any other version.

Now I will show you how to translate a date. To do this I will be using moment.js.

yarn add moment
Enter fullscreen mode Exit fullscreen mode

In my WeatherForecast.js I add:

<p>{t('weather.date', {date: Date.now()})}</p>
Enter fullscreen mode Exit fullscreen mode

Now in public/locales/en/translation.json:

"weather": {
        "date": "Today's date: {{date, currentDate}}"
    },
Enter fullscreen mode Exit fullscreen mode

Here in {{}} date is value, variable that we asigned in our component and currentDate is the format that we will use next.

In our i18n.js file, we must import moment.js and configure the translation for our date:

import moment from 'moment';

...

availableLanguages.forEach((element) => {
  if (element !== 'en') {
     import(`moment/locale/${element}`);
   }
}); // we are importing only the locales that we need.

.init({
    ...
    interpolation: {
      escapeValue: false, 
       format: (value, format, lng) => {
        if (format === 'currentDate') return 
           moment(value).locale(lng).format('LL');
          return value;
       },//if the format is 'currentDate', then take its __value__ transfom it to a moment object, translate it to the current language and show it in Month DD, YYYY format.    
    },
  });
Enter fullscreen mode Exit fullscreen mode

It should work well. Hope this article was helpful.

Top comments (15)

Collapse
 
tonixtuft profile image
Anton Bagdatyev (Tonix-Tuft)

Thank you for your article Oksana!

How do you manage the fact that translations files may be cached by the browser and therefore if you publish an updated translation file, let's say public/locales/ru/translation.json, the browser may still continue to use an outdated cached version of public/locales/ru/translation.json.

Did you find a solution for that as well?

Collapse
 
superturbis profile image
Super Turbo • Edited

Hi Oksana! Great solution, thank you! I'd like to ask you about an issue I have whenever I run it...

It outputs a red screen, with white fonts and the following error:

“A React component suspended while rendering, but no fallback UI was specified. Add a component higher in the tree to provide a loading indicator or placeholder to display”

I don't understand where does it come from, since the <Suspense/> component has already been filled with the fallback:

const Loader = () => {
  return <div>loading...</div>
};
export default props => {
    return (
        <Suspense fallback={<Loader />}>
        ...
        </Suspense>
    )
}

Thank you in advance Oksana!

Collapse
 
fkirc profile image
Felix K

Thank you for the summary. I found that those i18n-libraries are not enough to keep translation-files in sync, which is why I wrote "attranslate" as an additional tool for file-synchronization: github.com/fkirc/attranslate you can give it a try if you are seeking a free solution

Collapse
 
gtobar profile image
Guillermo Tobar

Thanks for sharing the post, its content is very valuable.

Collapse
 
kilofafeure profile image
kilofafeure

Beginner web developer? I've read a lot of solutions for i18next and you have been the only one that has made my app be translated correctly, all very clear. You are agoddess!!! Thanks a lot!!!

Collapse
 
ksushiva profile image
Oksana Ivanchenko

Wow! Thanks for your comment! I’m very happy that you found this article helpful

Collapse
 
burzumumbra profile image
Ronald Flores Sequeira

Cool, now I have something to play with on the weekend.

Collapse
 
etainlove profile image
RemeberIamLove

Thank you Oksana! It works like a charm!

Collapse
 
ahmetcetin profile image
Ahmet Cetin

The simplest just to the point explanation I've seen so far about i18next. Thx a lot for sharing this. i18next should put this article in their website as Getting Started.

Collapse
 
adrai profile image
Adriano Raiano

i18next-xhr-backend is deprecated, i18next-http-backend can be used now.

i.e. dev.to/adrai/how-to-properly-inter...

Collapse
 
jericfarias profile image
Eric Farias

It works fine with props? I mean suppose I have a header component that receive a prop title to render some things like this.

Collapse
 
duongynguyen profile image
Nguyen Duong

I read and integrated follow i18next documents but my project failed. Since I read your post and follow it, I very surprise. I got it. You are my idol. Thank you so much!

Collapse
 
trejocode profile image
Sergio A. Trejo

Thank you, for me es better i18n than react-intl, works fine and it's super easy to config

Collapse
 
kanags91 profile image
Kanagaraj Palanisamy

Wonderful. Able to set up our entire app in just a week..!! Thanks a lot :)

Collapse
 
hd4ng profile image
Huy Dang

Thanks Oksana, nice article. Could you explain why locales should be placed in the public folder? Thanks