[i18next] Enable i18n on Gatsby/React project

May 19, 2021

programming
article cover

i18n libraries

When we are to support i18n with React, we can narrow it down to the following three.

React-intl

12.5k stars on Github
A part of Format.js. The open-source library developed by Yahoo.

React-i18next

5.6k stars on Github
i18n framework for React.

LinguiJS

3.1k stars on Github
i18n framework. it seems most lightweight within these three.

After considering flexibility and simplicity, React-i18next seems to be well-balanced and very good, so I’ll use that one this time.

For a Gatsby project

If you are to use Gatsby, there is already a wonderful plugin as always.

gatsby-plugin-react-i18next

Seamless integration with react-i18next

This plugin helps to create a static file on SSR for each language, and also sets the proper URL for each language.
For example, https://sample.com/en/profile for an English page and https://sample.com/ja/profile for a Japanese page.

But this time, for me, this plugin seems too much because it is enough by enabling users to switch languages on the client side without changing URL. So I’m gonna use React-i18next to support i18n with Gatsby.

Make a sample component

I assume we already have a project.
Then let’s make a small component to test i18n.

// index.js

import React from 'react'

export const Index = () => {
  return (
    <div>
      <h1>Have a nice day!</h1>
      <ul>
        <li><button>EN</button></li>
        <li><button>DE</button></li>
        <li><button>JA</button></li>
      </ul>
    </div>
  )
} 

screenshot

Translation files

Prepare translation files arbitrary place.

*
└── locales/
     ├── en/
     │    └── translation.json
     │
     ├── de/
     │    └── translation.json
     │
     └── ja/
          └── translation.json

We can write translations using json format. Make sure the same sentence has the same key.
Nested json object is supported.

en/translation.json

{
  "topPage": {
    "greeting": "Have a nice day"
  }
}

de/translation.json

{
  "topPage": {
    "greeting": "Schönen tag noch"
  }
}

ja/translation.json

{
  "topPage": {
    "greeting": "よい1日を"
  }
}

React-i18next

first, Install.

npm install --save react-i18next i18next

or 

yarn add react-i18next i18next

Make i18n.js file on arbitrary place then init i18n.

import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'

i18n
  .use(initReactI18next)
  .init({
  resources: { // Import translations you made from certain path.
    en: {
      translation: require('../../static/locales/en/translation.json')
    },
    de: {
      translation: require('../../static/locales/de/translation.json')
    },
    ja: {
      translation: require('../../static/locales/ja/translation.json')
    }
  },
  debug: process.env.NODE_ENV === "development",
  fallbackLng: 'en', // Set default language
  interpolation: {
    escapeValue: false,
  },
})

export default i18n

Then pass i18n to whole app.
We use I18nextProvider to wrap the app.

import React from 'react'
import ReactDOM from 'react-dom'
import { I18nextProvider } from 'react-i18next'
import i18n from './src/providers/i18n'

const App =() => {
  return (
    <I18nextProvider i18n={i18n}>
      <Index />
    </I18nextProvider>
  )
}

ReactDOM.render(<App />, document.getElementById('root'))

For Gatsby, gatsby-browser.js and gatsby-ssr.js are the place to do this.

// gatsby-wrapper.js

import React from 'react'
import { I18nextProvider } from 'react-i18next'
import i18n from './src/providers/i18n'

export const rootWrapper = ({ element }) => (
  <>
    <I18nextProvider i18n={i18n}>
      {element}
    </I18nextProvider>
  </>
)
// gatsby-ssr.js

import { rootWrapper } from './gatsby-wrapper'

export const wrapRootElement = rootWrapper
// gatsby-browser.js

import { rootWrapper } from './gatsby-wrapper'

export const wrapRootElement = rootWrapper

Switch languages

Translating with i18next is quite simple, we just need to enclose the key string with t() function which comes from special hooks.
If you want to change the language, just use i18n.changeLanguage() passing the language as an argument.

// index.js

import React from 'react'
import { useTranslation } from 'react-i18next'

export const Index = () => {
  const { t, i18n } = useTranslation()
  
  const changeLang = (lang) => {
    i18n.changeLanguage(lang)
  }

  return (
    <Div>
      <h1>{t('topPage.greeting')}</h1>
      <ul>
        <li><button onClick={() => changeLang('en')}>EN</button></li>
        <li><button onClick={() => changeLang('de')}>DE</button></li>
        <li><button onClick={() => changeLang('ja')}>JA</button></li>
      </ul>
    </Div>
  )
} 

screen shot

Detect user language

i18next has another useful feature that detects the user’s language settings and automatically changes the language. To enable this, just install two dedicated packages and load them at init.

npm install --save i18next-http-backend i18next-browser-languagedetector

or 

yarn add i18next-http-backend i18next-browser-languagedetector
// i18n.js

import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import Backend from 'i18next-http-backend' // new
import LanguageDetector from 'i18next-browser-languagedetector' //new 

i18n
  .use(Backend)  // new
  .use(LanguageDetector) // new
  .use(initReactI18next)
  .init({
  resources: {
    en: {
      translation: require('../../static/locales/en/translation.json')
    },
    de: {
      translation: require('../../static/locales/de/translation.json')
    },
    ja: {
      translation: require('../../static/locales/ja/translation.json')
    }
  },
  debug: process.env.NODE_ENV === "development",
  fallbackLng: 'en',
  interpolation: {
    escapeValue: false,
  },
})

export default i18n

Done!
We can support i18n quite easily by using React-i18next.


Profile picture

Photographer / Web engineer working in Berlin.
A Japanese living with a Slovak wife and two German cats.