How to Internationalize a React App
Tags: react, javascript, css, tutorial
Published: 2021-09-02 07:59:17

---
title: How to Internationalize a React App
published: true
date: 2021-09-02 07:12:51 UTC
tags: React,Javascript,CSS,tutorial
canonical_url: https://blog.shahednasser.com/how-to-internationalize-a-react-app/
cover_image: https://blog.shahednasser.com/static/575f09aa1b1fb35308181d2a3f70cf12/ferenc-almasi-c8h0n7fSTqs-unsplash-2.jpg
---

_This article was originally published on [my personal blog](https://blog.shahednasser.com/how-to-internationalize-a-react-app/)_

Internationalization, or i18n, is supporting different languages in your website or app. It allows you to gain users from different parts of the world, which leads to growing your website's traffic.

In this tutorial, we'll learn how to internationalize a React website including translating content and changing the layout's direction based on the language chosen.

You can find the full code for this tutorial in [this GitHub repository](https://github.com/shahednasser/react-i18n-tutorial).

## Setup Website

First, we'll set up the React website with [Create React App (CRA)](https://create-react-app.dev).

Run the following command:

```
npx create-react-app react-i18n-tutorial
```

Once that is done, change the directory to the project:

```
cd react-i18n-tutorial
```

You can then start the server:

```
npm start
```

## Install Dependencies

The easiest way to internationalize a React app is to use the library [i18next](https://www.i18next.com). i18next is an internationalization framework written in Javascript that can be used with many languages and frameworks, but most importantly with [React](https://react.i18next.com).

Run the following command to install i18next:

```
npm install react-i18next i18next --save
```

In addition, we need to install [i18next-http-backend](https://github.com/i18next/i18next-http-backend) which allows us to fetch translations from a directory, and [i18next-browser-languagedetector](https://github.com/i18next/i18next-browser-languageDetector) which allows us to detect the user's language:

```
npm i i18next-http-backend i18next-browser-languagedetector
```

Last, we'll install [React Bootstrap](https://react-bootstrap.github.io) for simple styling:

```
npm install react-bootstrap@next bootstrap@5.1.0
```

## Create the Main Page

We'll create the main page of the website before working on the internationalization.

### Navigation Bar

We first need the Navigation component. Create `src/components/Navigation.js` with the following content:

```
import { Container, Nav, Navbar, NavDropdown } from "react-bootstrap";

function Navigation () {
    
  return (
    <Navbar bg="light" expand="lg">
      <Container>
        <Navbar.Brand href="#">React i18n</Navbar.Brand>
        <Navbar.Toggle aria-controls="basic-navbar-nav" />
        <Navbar.Collapse id="basic-navbar-nav">
          <Nav className="me-auto">
            <NavDropdown title="Language" id="basic-nav-dropdown">
              <NavDropdown.Item href="#">English</NavDropdown.Item>
              <NavDropdown.Item href="#">العربية</NavDropdown.Item>
            </NavDropdown>
          </Nav>
        </Navbar.Collapse>
      </Container>
    </Navbar>
  );
}

export default Navigation;
```

### Heading

Then, we'll create `src/components/Greeting.js` with the following content:

```
function Greeting () {

  return (
    <h1>Hello</h1>
  );
}

export default Greeting;
```

### Text

Next, we'll create `src/components/Text.js` with the following content:

```
function Text () {

  return (
    <p>Thank you for visiting our website.</p>
  )
}

export default Text;
```

Finally, we need to show these components on the website. Change the content of `src/App.js`:

```
import React from 'react';
import { Container } from 'react-bootstrap';
import 'bootstrap/dist/css/bootstrap.min.css';
import Greeting from './components/Greeting';
import Loading from './components/Loading';
import Navigation from './components/Navigation';
import Text from './components/Text';

function App() {

  return (
    <>
      <Navigation />
      <Container>
      	<Greeting />
      	<Text />
      </Container>
    </>
  );
}

export default App;

```

Run the server now, if it isn't running already. You'll see a simple website with a navigation bar and some text.

![How to Internationalize a React App](https://backend.shahednasser.com/content/images/2021/08/Screen-Shot-2021-08-31-at-1.22.22-PM.png)

### Configuring i18next

The first step of internationalizing React with i18next is to configure and initialize it.

Create `src/i18n.js` with the following content:

```
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import Backend from 'i18next-http-backend';
import I18nextBrowserLanguageDetector from "i18next-browser-languagedetector";

i18n
  .use(Backend)
  .use(I18nextBrowserLanguageDetector)
  .use(initReactI18next) // passes i18n down to react-i18next
  .init({
    fallbackLng: 'en',
    debug: true,

    interpolation: {
      escapeValue: false // react already safes from xss
    }
  });

  export default i18n;
```

We're first importing `i18n` from `i18next`. Then, we're adding `i18next-http-backend` and `i18next-browser-languagedetector` as plugins to `i18n`. We're also adding `initReactI18next` as a plugin to ensure that `i18next` works with React.

Next, we're initializing `i18n` by passing it an object of options. There are [many options](https://www.i18next.com/overview/configuration-options) you can pass to the initializer, but we're passing 3 only.

`fallbackLng` acts as the default language in i18n if no language is detected. Language is detected either from the user's preferred language, or a language they previously chose when using the website.

`debug` enables debug messages in the console. This should not be used in production.

As for `escapeValue` in `interpolation`, we're setting it to false since React already escapes all strings and is safe from [Cross-Site Scripting (XSS)](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwi3o9eav9vyAhXHX8AKHe31CgAQFnoECAMQAQ&url=https%3A%2F%2Fowasp.org%2Fwww-community%2Fattacks%2Fxss%2F&usg=AOvVaw38Aj4XcszVjxrjA0YToyVk).

## Adding the Translation Files

By default, `i18next-http-backend` looks for translation files in `public/locales/{language}/translation.json`, where `{language}` would be the code of the language chosen. For example, en for English.

In this tutorial, we'll have 2 languages on our website, English and Arabic. So, we'll create the directory `locales` and inside we'll create 2 directories `en` and `ar`.

Then, create the file `translation.json` inside `en`:

```
{
  "greeting": "Hello",
  "text": "Thank you for visiting our website.",
  "language": "Language"
}
```

This will create 3 translation keys. When these keys are used, the string value that the key corresponds to will be outputted based on the chosen language. So, each language file should have the same keys but with the values translated to that language.

Next, we'll create the file `translation.json` inside `ar`:

```
{
  "greeting": "مرحبا",
  "text": "شكرا لزيارة موقعنا",
  "language": " اللغة"
}
```

## Using the i18n Instance

The next step is importing the file with the settings we just created in `App.js`:

```
import i18n from './i18n';
```

Next, to make sure that the components are rendered once i18next and the translation files have been loaded, we need to surround our components with [Suspense from React](https://reactjs.org/docs/concurrent-mode-suspense.html#what-is-suspense-exactly):

```
<Suspense fallback={<Loading />}>
	<Navigation />
	<Container>
		<Greeting />
        <Text />
    </Container>
</Suspense>
```

As you can see, we're passing a new component `Loading` as a fallback while i18next loads with the translation files. So, we need to create `src/components/Loading.js` with the following content:

```
import { Spinner } from "react-bootstrap";

function Loading () {
  return (
    <Spinner animation="border" role="status">
      <span className="visually-hidden">Loading...</span>
    </Spinner>
  )
}

export default Loading;
```

Now, we're able to translate strings in the `App` components and its sub-components.

## Translating Strings with useTranslation

There are different ways you can translate strings in i18next, and one of them is using `useTranslation` hook. With this hook, you'll get the translation function which you can use to translate strings.

We'll start by translating the `Greeting` component. Add the following at the beginning of the component:

```
function Greeting () {
  const { t } = useTranslation();
    ...
}
```

Then, inside the returned JSX, instead of just placing the text "Hello", we'll replace it with the translation function `t` that we received from `useTranslation`:

```
return (
    <h1>{t('greeting')}</h1>
  );
```

Note how we're passing the translation function a key that we added in the `translation.json` files for each of the languages. i18next will fetch the value based on the current language.

We'll do the same thing for the `Text` component:

```
import { useTranslation } from "react-i18next";

function Text () {
  const { t } = useTranslation();

  return (
    <p>{t('text')}</p>
  )
}

export default Text;
```

Finally, we'll translate the text "Language" inside the `Navigation` component:

```
<NavDropdown title={t('language')} id="basic-nav-dropdown">
```

If you open the website now, you'll see that nothing has changed. The text is still in English.

Although technically nothing has changed, considering we are using the translation function passing it the keys instead of the actual strings and it's outputting the correct strings, that means that i18next is loading the translations and is displaying the correct language.

If we try to change the language using the dropdown in the navigation bar, nothing will happen. We need to change the language based on the language clicked.

## Changing the Language of the Website

The user should be able to change the language of a website. To manage and change the current language of the website, we need to create a context that's accessible by all the parts of the app.

Creating a context eliminates the need to pass a state through different components and levels.

Create the file `src/LocaleContext.js` with the following content:

```
import React from "react";

const defaultValue = {
  locale: 'en',
  setLocale: () => {} 
}

export default React.createContext(defaultValue);
```

Then, create the state `locale` inside `src/App.js`:

```
function App() {
  const [locale, setLocale] = useState(i18n.language);
```

As you can see, we're passing `i18n.language` as an initial value. The `language` property represents the current language chosen.

However, as it takes time for i18n to load with the translations, the initial value will be `undefined`. So, we need to listen to the `languageChanged` event that `i18n` triggers when the language is first loaded and when it changes:

```
i18n.on('languageChanged', (lng) => setLocale(i18n.language));
```

Finally, we need to surround the returned JSX with the provider of the context:

```
<LocaleContext.Provider value={{locale, setLocale}}>
      <Suspense fallback={<Loading />}>
        <Navigation />
          <Container>
            <Greeting />
            <Text />
          </Container>
	</Suspense>
</LocaleContext.Provider>
```

Now, we can access the locale and its setter from any of the subcomponents.

To change the language, we need to have a listener function for the click events on the dropdown links.

In `src/components/Navigation.js` get the locale state from the context at the beginning of the function:

```
const { locale } = useContext(LocaleContext);
```

Then, add a listener component that will change the language in `i18n`:

```
  function changeLocale (l) {
    if (locale !== l) {
      i18n.changeLanguage(l);
    }
  }
```

Finally, we'll bind the listener to the click event for both of the dropdown links:

```
<NavDropdown.Item href="#" onClick={() => changeLocale('en')}>English</NavDropdown.Item>
              <NavDropdown.Item href="#" onClick={() => changeLocale('ar')}>العربية</NavDropdown.Item>
```

If you go on the website and try to change the language, you'll see that the language changes successfully based on what you choose. Also, if you try changing the language then refreshing the page, you'll see that the chosen language will persist.

![How to Internationalize a React App](https://backend.shahednasser.com/content/images/2021/08/Screen-Shot-2021-08-31-at-1.22.32-PM.png)

## Changing the Location of the Translation Files

As mentioned earlier, the default location of the translation files is in `public/locales/{language}/translation.json`. However, this can be changed.

To change the default location, change this line in `src/i18n.js`:

```
.use(Backend)
```

To the following:

```
.use(new Backend(null, {
    loadPath: '/translations/{{lng}}/{{ns}}.json'
  }))
```

Where the `loadPath` is relative to `public`. So, if you use the above path it means the translation files should be in a directory called `translations`.

`{{lng}}` refers to the language, for example, `en`. `{{ns}}` refers to the namespace, which by default is `translation`.

You can also provide a function as a value of `loadPath` which takes the language as the first parameter and the namespace as the second parameter.

## Changing Document Direction

The next essential part of internationalization and localization is supporting different directions based on the languages you support.

If you have Right-to-Left (RTL) languages, you should be able to change the direction of the document when the RTL language is chosen.

If you use our website as an example, you'll see that although the text is translated when the Arabic language is chosen, the direction is still Left-to-Right (LTR).

This is not related to i18next as this is done through CSS. In this tutorial, we'll see how we can use [RTL](https://getbootstrap.com/docs/5.1/getting-started/rtl/) in Bootstrap 5 to support RTL languages.

The first thing we need to do is adding the `dir` and `lang` attributes to the `<html>` tag of the document. To do that, we need to install [React Helmet](https://www.npmjs.com/package/react-helmet):

```
npm i react-helmet
```

Then, inside `Suspense` in the returned JSX of the `App` component add the following:

```
<Helmet htmlAttributes={{
          lang: locale,
          dir: locale === 'en' ? 'ltr' : 'rtl'
        }} />
```

This will change the `lang` and `dir` attributes of `<html>` based on the value of the locale.

The next thing we need to do is surround the Bootstrap components with `ThemeProvider` which is a component from `react-bootstrap`:

```
<ThemeProvider dir={locale === 'en' ? 'ltr' : 'rtl'}>
	<Navigation />
	<Container>
		<Greeting />
		<Text />
	</Container>
</ThemeProvider>
```

As you can see we're passing it the `dir` prop with the direction based on the locale. This is necessary as `react-bootstrap` will load the necessary stylesheet based on whether the current direction is `rtl` or `ltr`.

Finally, we need to change the class name of `Nav` in the `Navigation` component:

```
<Nav className={locale === 'en' ? 'ms-auto' : 'me-auto'}>
```

This is only necessary since there seems to be a problem in the support for `ms-auto` when switching to RTL.

If you try opening the website now and changing the language to Arabic, you'll see that the direction of the document is changed as well.

![How to Internationalize a React App](https://backend.shahednasser.com/content/images/2021/08/Screen-Shot-2021-08-31-at-6.50.37-PM.png)

## Conclusion

i18next facilitates internationalizing your React app, as well as other frameworks and languages. By internationalizing your app or website, you are inviting more users from around the world to use it.

The main parts of internationalization are translating the content, supporting the direction of the chosen language in your website's stylesheets, and remembering the user's choice. Using i18next you're able to easily translate the content as well as remembering the user's choice.