DEV Community

Cover image for Internationalization using React-i18next legacy version (v9)
Peeyush Man Singh
Peeyush Man Singh

Posted on

Internationalization using React-i18next legacy version (v9)

What is i18next?

i18next is a popular framework for internationalization. React-i18next is the version of i18next for React applications.

Why legacy version React-i18next?

To use the latest version of React-i18next, the requirements are:

  • react >= v16.8.0
  • react-dom >= v16.8.0
  • react-native >= v0.59.0
  • i18next >= v10.0.0(typescript users: >= v17.0.9)

Requirements for React-i18next v9

  • react >= v0.14.0
  • i18next >= v2.0.0

Installation

npm install react-i18next@legacy i18next --save npm install i18next-browser-languagedetector --save npm install i18next-xhr-backend --save npm install i18next --save 
Enter fullscreen mode Exit fullscreen mode

For TypeScript users, install the type definitions manually.

Warning: Type definitions for i18next-xhr-backend and i18next-browser-languagedetector are deprecated.

npm install @types/i18next-xhr-backend --save npm install @types/i18next-browser-languagedetector --save 
Enter fullscreen mode Exit fullscreen mode

Folder structure

i18n-project/ ├── src/ │ ├── components/ │ │ ├── component1/ │ │ │ ├── locales/ │ │ │ │ ├── default.de.json │ │ │ │ └── default.en.json │ │ │ └── Component1.tsx │ │ ├── component2/ │ │ │ ├── locales/ │ │ │ │ ├── default.de.json │ │ │ │ └── default.en.json │ │ │ └── Component2.tsx │ │ └── App.tsx │ ├── i18n/ │ │ ├── locales/ │ │ │ ├── default.de.json │ │ │ ├── default.en.json │ │ │ └── index.tsx │ │ └── index.tsx │ ├── types/ │ │ └── module.d.ts │ └── index.tsx │ ├── package.json └── tsconfig.json 
Enter fullscreen mode Exit fullscreen mode

Configure TypeScript to import JSON files

i18n-project/src/types/module.d.ts

declare module "*.json"{ const value: any; export default value; } 
Enter fullscreen mode Exit fullscreen mode

i18n-project/tsconfig.json

{ "compilerOptions":{ ... "resolveJsonModule": true } } 
Enter fullscreen mode Exit fullscreen mode

Initialize main translation files

All other translation files are later concatenated onto the main translation file as we will see later.

i18n-project/src/i18n/locales/default.de.json

{ "de": {} } 
Enter fullscreen mode Exit fullscreen mode

i18n-project/src/i18n/locales/default.en.json

{ "en": {} } 
Enter fullscreen mode Exit fullscreen mode

i18next Configuration file

i18n-project/src/i18n/index.tsx

import i18n from "i18next"; import * as detector from "i18next-browser-languagedetector"; import * as Backend from "i18next-xhr-backend"; import { de, en } from "./locales"; i18n.use(Backend) .use(detector) //Browser Language Detector .init({ interpolation: { escapeValue: false }, debug: true, resources: { de: { common: de.de }, en: { common: en.en } }, fallbackLng: "en", //Fallback Language: English ns: ["common"], defaultNS: "common", react: { wait: false, bindI18n: "languageChanged loaded", bindStore: "added removed", nsMode: "default" } }); export default i18n; 
Enter fullscreen mode Exit fullscreen mode

Setup provider in App

i18n-project/src/index.tsx

import * as React from "react"; import * as ReactDOM from "react-dom"; import { Provider } from "react-redux"; import { I18nextProvider } from "react-i18next"; import i18n from "./i18n"; ReactDOM.render( <I18nextProvider i18n={i18n}> <Provider store={store}> ... </Provider> </I18nextProvider>, document.getElementById("root") ); 
Enter fullscreen mode Exit fullscreen mode

Configure local translation files for each component

i18n-project/src/components/component1/locales/default.de.json

{ "de": { "header": "Willkommen in Comp1", "body": "Comp1 ist auf Deutsch.", "...": "..." } } 
Enter fullscreen mode Exit fullscreen mode

i18n-project/src/components/component1/locales/default.en.json

{ "en": { "header": "Welcome in Comp1", "body": "Comp1 is in English.", "...": "..." } } 
Enter fullscreen mode Exit fullscreen mode

i18n-project/src/components/component2/locales/default.de.json

{ "de": { "header": "Willkommen in Comp2", "link": "Comp2 ist auf <1>Deutsch</1>.", "...": "..." } } 
Enter fullscreen mode Exit fullscreen mode

i18n-project/src/components/component2/locales/default.en.json

{ "en": { "header": "Welcome in Comp2", "link": "Comp2 is in <1>English</1>.", "...": "..." } } 
Enter fullscreen mode Exit fullscreen mode

Notice we used <1>...</1> tags to feature Trans component. To learn more about the numbering, checkout this fantastic resource:
https://github.com/arkross/arkross.github.io/wiki/Using-react-i18next-Trans-Component

Change constants from each component to load from i18n

i18n-project/src/components/component1/Component1.tsx

import * as React from "react"; import i18n from "./../../i18n"; export class Component1 extends React.PureComponent<..., ...> { render() { return( <h1>{i18n.t("Component1.header")}</h1> <p>{i18n.t("Component1.body")}</p> ); } } 
Enter fullscreen mode Exit fullscreen mode

i18n-project/src/components/component2/Component2.tsx

import * as React from "react"; import i18n from "./../../i18n"; import { Trans } from "react-i18next"; export class Component2 extends React.PureComponent<..., ...> { render() { return( <h1>{i18n.t("Component2.header")}</h1> <Trans i18nKey="Component2.link" i18n={i18n}> Comp2 is in <a href="..." >English</a>. </Trans> ); } } 
Enter fullscreen mode Exit fullscreen mode

Notice we used <a>...</a> tags in place of <1>...</1>.

Combine all translation files onto main translation file

i18n-project/src/i18n/locales/index.tsx

let de: any = require("./default.de.json"); let en: any = require("./default.en.json"); import * as Component1De from "./../../components/component1/locales/default.de.json"; import * as Component1En from "./../../components/component1/locales/default.en.json"; import * as Component2De from "./../../components/component2/locales/default.de.json"; import * as Component2En from "./../../components/component2/locales/default.en.json"; ... de["de"].Component1 = Component1["de"]; en["en"].Component1 = Component1["en"]; de["de"].Component2 = Component2["de"]; en["en"].Component2 = Component2["en"]; ... export { de, en }; 
Enter fullscreen mode Exit fullscreen mode

Language Change feature (optional)

i18next-project/src/components/component-of-your-choice

import * as React from "react"; import i18n from "./../i18n"; interface ChangeLngState{ language: string; } export class ChangeLng extends React.Component<..., ChangeLngState> { state = { language: i18n.language }; render(){ return( <div> <Input type="select" name="language" id="language" onChange={this.languageChanged.bind(this)} defaultValue={this.getDefaultValue()} > <option>Deutsch<option> <option>English<option> </Input> <Button onClick={this.onApply.bind(this)}> Apply </Button> </div> ); } languageChanged(event: any) { if (event.target.value === "Deutsch") { this.setState({ language: "de" }); } else { this.setState({ language: "en" }); } } getDefaultValue() { if (i18n.language === "de") { return "Deutsch"; } else { return "English"; } } onApply() { if (i18n.language != this.state.language) { i18n.changeLanguage(this.state.language, () => { location.reload(); }); } } } 
Enter fullscreen mode Exit fullscreen mode

Bonus: Change title according to language

i18next-project/src/components/App.tsx

import * as React from "react"; import i18n from "./../i18n"; ... export class App extends React.Component<...> { componentDidMount(): void { if (i18n.language == "de") { document.title = "Titel auf Deutsch"; } else { document.title = "Title in English"; } } } 
Enter fullscreen mode Exit fullscreen mode

Advantages of using this approach

  • Translations can be split into multiple files for multiple languages.
  • Translation files are split for every component inside the component folder.
  • Automatic browser default language detection.
  • Change language with ease.

Drawbacks of the approach

  • React version must be greater than 16.8.0 to use latest version of i18next.
  • Splitted translations should be combined in a main translation file.
  • Translation keys must match exactly for translation to work.
  • All translation keys must exist on fallback language.

References:

Github: https://github.com/pssingh21
#first_post😉

Top comments (2)

Collapse
 
ayushbhusal profile image
Ayush-B

nice article. dry and effective.

Collapse
 
adrai profile image
Adriano Raiano

fyi: i18next-xhr-backend is deprecated... you should use i18next-http-backend instead...

i.e. dev.to/adrai/how-to-properly-inter...