Multilingual sites in React
Tags: webdev, react
Published: 2023-03-12 21:56:11

by [Suprabhat Kumar](https://blog.openreplay.com/authors/suprabhat-kumar)

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 - 

![--](https://blog.openreplay.com/images/multilingual-sites-in-react/images/HMcRi4y.png)



### 1. Creating i18n file
 src > i18n > index.js

```javascript
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 -
![--](https://blog.openreplay.com/images/multilingual-sites-in-react/images/IhizeF1.png)

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

```javascript
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

```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"
}
```

2. en

src > locales > en > translation.json

```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"
}
```

3. it

src > locales > it > translation.json

```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

```javascript
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

```javascript
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

```javascript
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

```javascript
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>
);
```



<h2>Session Replay for Developers</h2> 
<p><em>
Uncover frustrations, understand bugs and fix slowdowns like never before with <strong><a href="https://github.com/openreplay/openreplay" target="_blank">OpenReplay</a></strong> — an open-source session replay suite for developers. It can be <strong>self-hosted</strong> in minutes, giving you complete control over your customer data
</em></p>
<img alt="OpenReplay" width="768" height="400" src="https://blog.openreplay.com/assets/overview_Z17Qdyg.png" class="astro-UXNKDZ4E" loading="lazy" decoding="async">
<em>Happy debugging! <a href="https://openreplay.com" target="_blank">Try using OpenReplay today.</a></em>






## 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

```javascript
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;
```

2. 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


```javascript
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`.

![--](https://blog.openreplay.com/images/multilingual-sites-in-react/images/5VXOiym.png)



## Output

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

 Selected Language is `it(Italian)`

![--](https://blog.openreplay.com/images/multilingual-sites-in-react/images/IbajKe8.png)


 Selected Language is `en(English)`

![--](https://blog.openreplay.com/images/multilingual-sites-in-react/images/Jia3UZK.png)

Selected Language is `de(German)`

![--](https://blog.openreplay.com/images/multilingual-sites-in-react/images/lTj04Ye.png)



## 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/



[![newsletter](https://blog.openreplay.com/newsletter_Z1P0UIP.png)](https://newsletter.openreplay.com/)