loading...
Cover image for Configuring i18n for react

Configuring i18n for react

devabhijeet profile image Abhijeet Yadav ・3 min read

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

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

import LocalStorageBackend from 'i18next-localstorage-backend';

Provides option to store the translated contents once downloaded.

import XHRBackEnd from 'i18next-xhr-backend';

Loads translations from the locales directory.

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.

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.

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

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.

.use(Backend) 

Chains multiple backend provided in the utils, namely XHR and LocalStorage.

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

use(initReactI18next)

provides react bindings.

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.

defaultNS: 'common',

When binding translation key to view, the keys will be searched inside the common namespace unless explicitly mentioned otherwise.

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'

load: 'languageOnly',

If not set to languageOnly. The lng inside the loadPath will default to en-EN or es-Es.

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

Discussion

pic
Editor guide