Creating a localized experience for visitors from other countries using React Redux
Tags: react, redux, tutorial, javascript
Published: 2019-10-15 21:02:34

##Getting Started
It is assumed you are already familiar with both React and Redux and looking to add internalization to your application. If you are not, there is a number of boilerplate options out there that can help you get started. 
Feel free to check out [our implementation](https://github.com/Jam3/nyg-jam3) of it that we use at [Jam3](https://www.jam3.com/).

###Prerequisites
You are highly advised to read the [i18next](https://www.i18next.com) internationalization framework documentation to get an understanding of the main [concepts](https://www.i18next.com/#complete-solution) and benefits of using it.

List of required extra dependencies:
- [i18next](https://www.npmjs.com/package/i18next) (37kB / 10.5kB)
- [react-i18next v.9](https://www.npmjs.com/package/react-i18next/v/9.0.10) (12.4kB / 4.6kB)
- [i18next-browser-languagedetector](https://www.npmjs.com/package/i18next-browser-languagedetector) (6kB / 2kB)
- [i18next-redux-languagedetector](https://www.npmjs.com/package/i18next-redux-languagedetector) (2.2kB / 790B)
- [i18next-chained-backend](https://www.npmjs.com/package/i18next-chained-backend) (2.2kB / 933B)
- [i18next-fetch-backend](https://www.npmjs.com/package/i18next-fetch-backend) (4.3kB / 1.7kB)

Take a sneak peek at these libraries before we proceed.

👉 _Consider the overall additional cost of roughly 20kB (minified and gzipped) added to the production build_.

###Installation
Run this command in your terminal to install the above modules in one batch:
`$ npm i --save i18next react-i18next@9.0.10 i18next-fetch-backend i18next-browser-languagedetector i18next-redux-languagedetector i18next-chained-backend`

##Configuration
The example we’ll be referring to is bootstrapped with [Create React App](https://github.com/facebook/create-react-app) with added Redux on top.
Here’s what our application structure will look like:

![App structure](https://thepracticaldev.s3.amazonaws.com/i/hx7k81886fgzpzb6gqwx.png)

See the [CodeSandbox example](https://codesandbox.io/s/github/Jam3/react-redux-i18n) or check this [GitHub repo](https://github.com/Jam3/react-redux-i18n).

###Step 1: Creating translation files
We are going to use English and Russian translations as an example.
Let’s create two JSON files with identical structure and keep them in their respective folders:

{% gist https://gist.github.com/namniak/984d06b6d1e0f84234abb68022f87bb7 %} <center>`/public/locales/en-US/common.json`</center>
&nbsp;
{% gist https://gist.github.com/namniak/7dc672356e122feccdb21bb72d269adf %} <center>`/public/locales/ru/common.json`</center>

These files will serve as our translation resources that are automatically loaded based on the detected browser language.

###Step 2: Creating the i18n config file
Make sure to check the complete list of available [i18next config options](https://www.i18next.com/overview/configuration-options).
This is our main localization config file:

{% gist https://gist.github.com/namniak/c07f70e00f6e1c6dd8456bfcc0028152 %} <center>`/src/i18n/index.js`</center>

- First off, we need to add the `i18next-chained-backend` plugin which allows chaining multiple backends. There are several [backend types](https://www.i18next.com/overview/plugins-and-utils#backends) available for different purposes. We are using `fetch` to load our translation resources.
- Then we are adding `Browser Language Detector` (connected with Redux store through `Redux Language Detector`) for automatic user language detection in the browser. Read more about [the approach](https://github.com/i18next/i18next-browser-languageDetector#introduction).
- Next up, we use `reactI18nextModule` to pass `i18n` instance down to `react-i18next`.
- Finally, we initialize `i18next` with basic config options.

###Step 3: Adding i18next reducer to the store
`Redux Language Detector` provides `i18nextReducer` so you don’t need to implement your own reducers or actions for it — simply include it in your store:

{% gist https://gist.github.com/namniak/d31e1074b45e62b1821f800b339a2108 %} <center>`/src/redux/index.js`</center>

👉 _For your convenience, use Redux dev tools in dev environment and make sure you import `composeWithDevTools` from `redux-devtools-extension/developmentOnly`_.

###Step 4: Creating the main app file
There’s nothing specifically related to the internalization in this file.
We simply set the routes for our pages in a standard way.

{% gist https://gist.github.com/namniak/d2c276a15499d560f50d8f5320b85796 %} <center>`/src/app/index.js`</center>

###Step 5: Initializing the app and adding [I18nextProvider](https://react.i18next.com/legacy-v9/i18nextprovider)
The provider is responsible for passing the `i18next` instance down to [withNamespaces](https://react.i18next.com/legacy-v9/withnamespaces) HOC or [NamespacesConsumer](https://react.i18next.com/legacy-v9/namespacesconsumer) render prop.

{% gist https://gist.github.com/namniak/021a1d2d6fdc11592b1b0b3e54c7f5ff %} <center>`/src/index.js`</center>

We initialized our store and `i18n` config file with the same options to keep both in sync.

###Step 6: Using translation keys
We‘ll use [withNamespaces](https://react.i18next.com/legacy-v9/withnamespaces) HOC that passes the [_t_ function](https://www.i18next.com/overview/api#t) as a prop down to the component. We need to specify the namespace(s), and the copy is now accessible via object properties using _`t`_ function: `t(‘homePage.title’)`.
Note, it is required to prepend the namespace when accessing the copy from multiple namespaces within one component e.g. `t('shared:banner.title')`.

{% gist https://gist.github.com/namniak/09f06b69baee12c6e96186bfa9fafae0 %} <center>`/src/pages/Home.js`</center>

Alternatively, we could use [NamespacesConsumer](https://react.i18next.com/legacy-v9/namespacesconsumer) component which would also give us access to the _`t`_ function. We’ll cover it in the next step.

👉 _You can test language detection by changing your default browser language. When using Chrome, go to `chrome://settings/languages` and move the languages up and down in the list_.

###Step 7 (Bonus part): Creating language switcher
Ok, we’ve implemented language auto-detection and dynamic translation resources loading. Now it’s time to take it up a notch and create a component that allows users switching the language through user interface.
Make sure to include this component in your app.

{% gist https://gist.github.com/namniak/7a76142f6a9360244f228d158823474e %} <center>`/src/components/LanguageSwitcher.js`</center>

`NamespacesConsumer` render prop provides access to the `i18n` instance. Its `changeLanguage` method can be used to change language globally. This will force the app to re-render and update the site with the translated content.

🎉That’s a wrap!

##Code examples
- [CodeSandbox](https://codesandbox.io/s/github/Jam3/react-redux-i18n)
- [GitHub example](https://github.com/Jam3/react-redux-i18n)

##Related documentation
- [i18next](https://react.i18next.com/)
- [React i18next (v.9)](https://react.i18next.com/legacy-v9/)
- [i18next Browser Language Detector](https://www.npmjs.com/package/i18next-browser-languagedetector)