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
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
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
Configure TypeScript to import JSON files
i18n-project/src/types/module.d.ts
declare module "*.json"{
const value: any;
export default value;
}
i18n-project/tsconfig.json
{
"compilerOptions":{
...
"resolveJsonModule": true
}
}
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": {}
}
i18n-project/src/i18n/locales/default.en.json
{
"en": {}
}
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;
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")
);
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.",
"...": "..."
}
}
i18n-project/src/components/component1/locales/default.en.json
{
"en": {
"header": "Welcome in Comp1",
"body": "Comp1 is in English.",
"...": "..."
}
}
i18n-project/src/components/component2/locales/default.de.json
{
"de": {
"header": "Willkommen in Comp2",
"link": "Comp2 ist auf <1>Deutsch</1>.",
"...": "..."
}
}
i18n-project/src/components/component2/locales/default.en.json
{
"en": {
"header": "Welcome in Comp2",
"link": "Comp2 is in <1>English</1>.",
"...": "..."
}
}
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>
);
}
}
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>
);
}
}
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 };
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();
});
}
}
}
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";
}
}
}
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:
- Official documentation for React-i18next legacy version: https://react.i18next.com/legacy-v9/step-by-step-guide
- Language codes can be found in: https://www.w3.org/International/O-charset-lang.html
- https://medium.com/rd-shipit/internationalizing-a-front-end-application-88f1baae3d82
Github: https://github.com/pssingh21
#first_post
😉
Top comments (2)
fyi: i18next-xhr-backend is deprecated... you should use i18next-http-backend instead...
i.e. dev.to/adrai/how-to-properly-inter...
nice article. dry and effective.