Configuring i18n for react
Tags: i18n, react, localization, translation
Published: 2020-06-30 15:23:29

This post is second in series of react i18n integration. 

To summarise the previous post, we installed bunch of dependency related to i18next library. Then we created locale folder inside the public directory.

Now let's see how those steps come together to make a config file.

Start by creating a file i18n.js inside src/utils/ directory.
paste the following code...

``` sh
import LocalStorageBackend from 'i18next-localstorage-backend';
import XHRBackEnd from 'i18next-xhr-backend';

export const isDevelopmentEnv = process.env.NODE_ENV === 'development';

export const backEnds = isDevelopmentEnv ? [XHRBackEnd] : [LocalStorageBackend, XHRBackEnd];

export const loadPath = () => '/locales/{{lng}}/{{ns}}.json';

export const backEndOptions = isDevelopmentEnv
  ? [{ loadPath }] : [{ prefix: 'app_name_', versions: { en: 'v1', es: 'v1' } }, { loadPath }];
```
Let's break this line by line

```sh
import LocalStorageBackend from 'i18next-localstorage-backend';
```
Provides option to store the translated contents once downloaded.

```sh
import XHRBackEnd from 'i18next-xhr-backend';
```
Loads translations from the locales directory.

```sh
export const loadPath = () => '/locales/{{lng}}/{{ns}}.json';
```
loadPath when executed translates to **'/locales/en/home.json'** OR **'/locales/es/home.json'**. ns stands for **namespace** which is defaulted to fileName.

```sh
export const backEndOptions = isDevelopmentEnv
  ? [{ loadPath }] : [{ prefix: 'app_name_', versions: { en: 'v1', es: 'v1' } }, { loadPath }];
```
> What does **prefix** and the **versions** option inside the **backEndOptions** stand for?.

Let's say your app is released to production, when the user downloads and opens the web page, the translations are saved in localStorage. What if we make a hotfix after the release. The hotfix contains additional translations. How do we let the app know that the content stored in localStorage is a stale one. We do that by bumping the version number. When version don't match new request is made to translate recent contents.

```sh
export const isDevelopmentEnv = process.env.NODE_ENV === 'development';
```
> What is **isDevelopmentEnv** boolean flag.

You see, when we imported **i18next-localstorage-backend** on the first line we did so with the intention of using **localstorage** to store all the translations once it was downloaded. However, this may not be the ideal behaviour we would expect when developing since we may keep changing the translation files.

Sigh!

Let's now create our config file named i18n.js inside the src/ directory. Remember the one we created above was utils for our config file.

Add the following code...

```sh
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import Backend from 'i18next-chained-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
import { backEnds, backEndOptions, isDevelopmentEnv } from 'utils/i18n';

if (isDevelopmentEnv) {
  window.i18n = i18n; //useful for debugging
}

i18n
  .use(Backend) 
  .use(LanguageDetector)
  .use(initReactI18next)
  .init({
    ns: ['common'],
    defaultNS: 'common',
    fallbackLng: 'en',
    debug: true,
    load: 'languageOnly',
    returnObjects: true,
    joinArrays: true,
    cleanCode: true,
    interpolation: {
      escapeValue: false,
    },
    react: {
      wait: true,
      useSuspense: false,
    },
    backend: {
      backends: backEnds,
      backendOptions: backEndOptions,
    },
  });

export default i18n;
```

Going through each and every config option is beyond the scope of this blog post. Let's quickly glance over the one's that are important.

```sh
.use(Backend) 
```
Chains multiple backend provided in the utils, namely **XHR** and **LocalStorage**.

```sh
.use(LanguageDetector)
```
Detects Language in given order...
> ['querystring', 'cookie', 'localStorage', 'sessionStorage', 'navigator', 'htmlTag', 'path', 'subdomain']. Querystring used is **?lng**. When set the contents are translated.

```sh
use(initReactI18next)
```
provides react bindings.

```sh
ns: ['common'],
```
ns stands for namespace. When the app is bootstrapped, files mentioned in the ns list are downloaded. Above example downloads **common.json** file.

```sh
defaultNS: 'common',
```
> When binding translation key to view, the keys will be searched inside the **common** namespace unless explicitly mentioned otherwise.

```sh
fallbackLng: 'en',
```
> If language is not mentioned it will fallback to **en**. Hence the **loadPath** defined in utils file will be equal to **'/locales/en/common.json'**

```sh
load: 'languageOnly',
```
> If not set to **languageOnly**. The **lng** inside the loadPath will default to **en-EN** or **es-Es**.

```sh
react: {
 wait: true,
 useSuspense: false,
}
```
> since the process of loading translation file is asynchronous, How should we let our React app know that the file are being fetched? before it shows flash of **key,value** bindings instead of actual translations. We do that by setting above flags.

I guess, that's it for the config file. In the next post we will look into the providers that will consume the above config and spit out API's for changing language, loading name space based on routes. 


Find the github repo containing all codes mentioned in this series [here](https://github.com/devAbhijeet/react-i18n-app)

{% codesandbox blue-brook-xgg5v %}