Back

Multilingual sites in React

Multilingual sites in React

Our chances of converting visitors into clients considerably rise if our website is translated into the user’s native language. This article will explain implementing the multilingual feature in a website using React. Hopefully, by the end of this article, we will be able to translate any website into any language we want. The multilingual functionality makes navigating sites in different languages easier for our readers.

Getting Started

At first, we create a React app using the following:

npx create-react-app app-name

This command lets us create a basic react app.

After that, we change the working directory using the following command -

cd app-name

Then we start the React application using -

npm start

Next, we follow mentioned steps below -

  1. Create an i18n file.
  2. Create a LanguageSelector file enabling us to select the website’s language.
  3. Create a locales folder where we store the translations in different languages used by our site.
  4. Map contents with languages.

Following are the npm packages used in translation -

  • i18next
  • react-i18next

Command to install the packages -

npm install react-i18next i18next

Let’s dive deeper now.

Following is the project structure -

--

1. Creating i18n file

src > i18n > index.js

import i18n from "i18next";
import { initReactI18next } from "react-i18next";

import translationsInEng from '../locales/en/translation.json';
import translationsInGerman from '../locales/de/translation.json';
import translationsInItalian from '../locales/it/translation.json';

// the translations
const resources = {
  en: {
    translation: translationsInEng
  },
  de: {
    translation: translationsInGerman
  },
  it: {
    translation: translationsInItalian
  },
};

i18n
  .use(initReactI18next) // passes i18n down to react-i18next
  .init({
    resources, // resources are important to load translations for the languages.
    lng: "it", // It acts as default language. When the site loads, content is shown in this language.  
    debug: true,
    fallbackLng: "de", // use de if selected language is not available
    interpolation: {
      escapeValue: false
    },
    ns: "translation", // namespaces help to divide huge translations into multiple small files.
    defaultNS: "translation"
  });

export default i18n;

In this file, we described

a. resources (for translating in different languages): It contains the files helping map the translations with the selected languages. If we interchange translationsInEng and translationsInGerman, then the content available in the translation.json file of the en and de folders will be shown in German and English, respectively. The translations in the en and de folders get mapped in German and English.

b. lng: The default language is responsible for showing the translations when the site loads. If we change it to de, the website will be translated to de on loading the site.

c. debug: It is boolean and gives detailed information on the console if assigned true. It also helps in analyzing the issues(if any occurred). Below is the screenshot attached for more information - --

d. fallbackLng: This language is used when the selected language(lng) is unavailable. Acts as default language in the absence of lng.

e. ns (Namespaces): It allows us to break huge translations into multiple small files instead of writing in a single large file.

2. Create a LanguageSelector file enabling us to select the language of the website

The language can be changed by using i18n imported from the i18n.js file.

src > components > LanguageSelector.js

import React, {useState} from "react";
import i18n from '../i18n';

const LanguageSelector = () => {
    
    const [selectedLanguage, setSelectedLanguage] = useState(i18n.language); // i18n.language contains the language assigned to lng in i18n.js file.

    const chooseLanguage = (e) => {
        e.preventDefault();
        i18n.changeLanguage(e.target.value);   // i18n.changeLanguage() is used to change the language assigned to lng in i18n.js file.
        setSelectedLanguage(e.target.value);
    }

    return (
        <select defaultValue={selectedLanguage} onChange={chooseLanguage}>  
            <option value="de">German</option>
            <option value="en">English</option>
            <option value="it">Italian</option>
        </select>
    );
};

export default LanguageSelector;

3. Create a locales folder to store the translations in our site’s different languages.

  1. de

src > locales > de > translation.json

{
    "React" : "Das ist React",
    "Home": "Daheim",
    "Contact": "Kontaktiere uns",
    "About": "Über uns",
    "username": {
        "label": "Nutzername",
        "placeholder": "Platzhalter..."
    },
    "password": {
        "label": "Kennwort",
        "placeholder": "passwort platzhalter..."
    },
    "location": "Geben Sie den Standort ein",
    "Address": "Gib die Adresse ein"
}
  1. en

src > locales > en > translation.json

{
    "React" : "This is React",
    "Home": "Home",
    "Contact": "Contact Us",
    "About": "About Us",
    "username": {
        "label": "username",
        "placeholder": "placeholder..."
    },
    "password": {
        "label": "password",
        "placeholder": "password placeholder..."
    },
    "location": "Enter the location",
    "Address": "Enter the address"
}
  1. it

src > locales > it > translation.json

{
    "React" : "Questo è React",
    "Home": "Casa",
    "Contact": "Contattaci",
    "About": "Riguardo a noi",
    "username": {
        "label": "nome utente",
        "placeholder": "segnaposto..."
    },
    "password": {
        "label": "parola d'ordine",
        "placeholder": "segnaposto password..."
    },
    "location": "Inserisci la posizione",
    "Address": "Inserisci l'indirizzo"
}

4. Map contents with the languages.

Following is the App.js -

src > App.js

import './App.css';
import LanguageSelector from './components/LanguageSelector';
import Content from './components/Content';
import Sidebar from './components/Sidebar';

function App() {
  return (
    <div className="App">
      <LanguageSelector />
      <Content />
      <Sidebar />
    </div>
  );
}

export default App;

In Sidebar.js, we import useTranslation() hook to implement the translation of the words. We get the t function and i18n instance from useTranslation().

The t function is used to translate our contents while i18n changes the language.

But, we will only use t here.

The following code will translate words like Home, Contact, and About.

src > components > Sidebar.js

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

const Sidebar = () => {
    const { t } = useTranslation();
  return (
    <div style={{marginTop: "10px"}}>
        <button>{t("Home")}</button>
        <button>{t("Contact")}</button>
        <button>{t("About")}</button>
    </div>
  )
}

export default Sidebar

Content.js shows how to translate the content.

src > components > Content.js

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

const Content = () => {
    const { t } = useTranslation();

    return (
        <div>
            <p>{t("React")}</p>
            <hr/>
            <p>{t("username.label")}</p>
            <p>{t("username.placeholder")}</p>
            <hr/>
            <p>{t("password.label")}</p>
            <p>{t("password.placeholder")}</p>
            <hr/>
            <p>{t("location")}</p>
            <p>{t("Address")}</p>
        </div>
    )
}

export default Content;

Translation of contents takes time. As a result, we wrap the <App/> component within Suspense with fallback.

Loading... will be shown on the screen until the translation is done.

src > index.js

import React, { Suspense } from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <Suspense fallback={<div>Loading....</div>}>
      <App />
    </Suspense>
  </React.StrictMode>
);

Session Replay for Developers

Uncover frustrations, understand bugs and fix slowdowns like never before with OpenReplay — an open-source session replay tool for developers. Self-host it in minutes, and have complete control over your customer data. Check our GitHub repo and join the thousands of developers in our community.

Common Mistake

Till this point, everything seems OK, but it’s not.

Once you select a particular language from the dropdown, the contents of the website change accordingly, but on refreshing the webpage, the selected language changes back to that language which is mentioned in lng in the i18n.js file because we assigned i18n.language to selectedLanguage (check 6th line in LanguageSelector.js).

Fun Activity

Change the lng (in i18n.js file) to en and now change the language from the dropdown on the webpage; you’ll see contents change as per the selected language but do refresh the page. This time you’ll notice the content is in English as the selectedLanguage; this time is en.

The problem, as of now, is refreshing the webpage changes the selected language of the website. So, what to do now?

Solution

The solution is to use the localStorage object. This object stores the data in the browser’s key:value pair with no expiration date.

  • Let’s see what changes we must introduce in the already available code.
  1. In chooseLanguage() in LanguageSelector.js, we set the key as lang and its value equal to the language selected by the user.

src > components > LanguageSelector.js

import React, {useState} from "react";
import i18n from '../i18n';

const LanguageSelector = () => {
    const [selectedLanguage, setSelectedLanguage] = useState(i18n.language); // i18n.language contains the language assigned to lng in i18n.js file.

    const chooseLanguage = (e) => {
        e.preventDefault();
        i18n.changeLanguage(e.target.value);   // i18n.changeLanguage() is used to change the language assigned to lng in i18n.js file.
        setSelectedLanguage(e.target.value);
        localStorage.setItem("lang", e.target.value);
    }

    return (
        <select defaultValue={selectedLanguage} onChange={chooseLanguage}>  
            <option value="de">German</option>
            <option value="en">English</option>
            <option value="it">Italian</option>
        </select>
    );
};

export default LanguageSelector;
  1. In i18n.js, lng gets the value available in the lang key (in our case, it’s it) because of lng: localStorage.getItem("lang").

src > i18n > index.js

import i18n from "i18next";
import { initReactI18next } from "react-i18next";

import translationsInEng from '../locales/en/translation.json';
import translationsInGerman from '../locales/de/translation.json';
import translationsInItalian from '../locales/it/translation.json';

// the translations
const resources = {
  en: {
    translation: translationsInEng
  },
  de: {
    translation: translationsInGerman
  },
  it: {
    translation: translationsInItalian
  },
};

i18n
  .use(initReactI18next) // passes i18n down to react-i18next
  .init({
    resources, // resources are important to load translations for the languages.
    lng: localStorage.getItem("lang"), // It acts as default language. When the site loads, content is shown in this language.  
    debug: true,
    fallbackLng: "de", // use de if selected language is not available
    interpolation: {
      escapeValue: false
    },
    ns: "translation", // namespaces help to divide huge translations into multiple small files.
    defaultNS: "translation"
  });

export default i18n;

Check the Local Storage section under the Application tab in the developer console. The selected language is it. Now if you refresh the browser, the selected language will still be it.

--

Output

We are focusing on translating, so there is no styling in it. The final output looks like this -

Selected Language is it(Italian)

--

Selected Language is en(English)

--

Selected Language is de(German)

--

Conclusion

With this, we come to the end of this article. I believe you have learned how to implement the multilingual feature in a website.

References

https://react.i18next.com/

Gain Debugging Superpowers

Unleash the power of session replay to reproduce bugs and track user frustrations. Get complete visibility into your frontend with OpenReplay, the most advanced open-source session replay tool for developers.

OpenReplay