How to Internationalize a React Application Using i18next and GraphCMS

In this post, we will take a deep dive into how to internationalize a React Application using i18next and GraphCMS.

joel-olawanle-headshot-graphcms
Joel Olawanle
internationalization-of-react-application-graphcms-og

Internationalization (or i18n) is a feature that is used to overcome the language barrier by providing support for several languages to users who use a certain software application or website. Only 25.9 percent of internet users were English speakers as of March 2020, emphasizing the need for multilingual websites and applications and emphasizing that you may be missing out on a substantial percentage of potential users.

In this post, we will look at what internationalization is and how to implement it in our React apps with i18next, as well as how this can be done easily with GraphCMS.

What is Internationalization and why is it ImportantAnchor

Internationalization (or i18n) is the process of ensuring that your website's platforms, processes, and architecture can accommodate and support many languages in order to allow you to build localized sites/apps. It is also the process of supporting different time zones, date formats, currencies, text directions and more.

In simple terms, it is the process of ensuring that your website is set up to handle several languages and that your overall website design is capable of accommodating these numerous languages. This helps you to get people from all over the world, which increases the traffic to your website/app.

Note: There are several libraries for internationalization in JavaScript, such as i18next, globalize, node-polyglot, and so forth. More information on these JavaScript internationalization libraries may be found here.

Intro to i18next and why this libraryAnchor

I18next is a well-known JavaScript internationalization framework. It was built in late 2011 and integrates with a variety of front-end technologies, including React.js. I18next is adaptable and can accommodate the demands of developers by utilizing external integrations such as luxon for date formatting in i18next.

i18next is also incredibly scalable since it lets us split translations into many files and load them as needed. When compared to other ordinary 18n frameworks, i18next provides a plethora of capabilities and possibilities. Based on how long i18next has been open-source, there is no true i18n situation that i18next cannot address. More information on why i18next is unique and how it works can be found here.

PrerequisitesAnchor

To follow along with this guide and code, you should have:

  • Basic understanding of HTML, CSS, and JavaScript

  • At least a little experience or knowledge of React.

  • Node and npm or yarn installed on your machine

  • Basic knowledge of how the terminal works

Getting StartedAnchor

First, we'll set up our React application using Create React App (CRA), which is a quick way to start building a new single-page application in React. We will do this by running the following command in our terminal:

npx create-react-app react-i18n-tutorial

Once that is successful, the next step would be to change the directory to the project and then start our server:

cd react-i18n-tutorial
npm start

Install DependenciesAnchor

Once we've successfully developed our React application, the following step is to install the dependencies that will allow us to internationalize it. We would install three dependencies: i18next (the actual library), react-i18next (the React/React Native internationalization framework), and i18next-browser-languagedetector.

The i18next-browser-languagedetector is an i18next plugin that detects the language in the browser-based on the user's preferences. Session storage, cookies, local storage, directories, and HTML elements are also supported across several locales. More information can be found here. Let’s install all the dependencies at once by running the following command in our terminal:

npm install i18next react-i18next i18next-browser-languagedetector

When we check our package.json file at this point, we will see that all of the dependencies have been successfully installed. This object may be used to identify the versions of each package included in this article:

"dependencies": {
"i18next": "^21.6.14",
"i18next-browser-languagedetector": "^6.1.3",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-i18next": "^11.16.1",
"react-scripts": "5.0.0",
},

Note: I removed some other dependencies from the Object while pasting it here.

Translating Plain TextAnchor

Let’s learn how to translate normal/plain text from one language to another language easily.

Configure i18nextAnchor

For us to do this, we need to first configure i18next and the other dependencies we installed, we would do this by creating a dedicated file i18n.js in the src folder and pasting the following configuration code:

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
i18n
.use(initReactI18next) // passes i18n down to react-i18next
.init({
resources: {
en: {
translation: {
Welcome: {
text: 'Welcome to this React Internationalization App',
},
},
},
fr: {
translation: {
Welcome: {
text: "Bienvenue sur cette application d'internationalisation React",
},
},
},
},
lng: 'en', // if you're using a language detector, do not define the lng option
fallbackLng: 'en',
interpolation: {
escapeValue: false, // react already safes from xss => https://www.i18next.com/translation-function/interpolation#unescape
},
});

In this file, we first imported i18n from i18next and also initReactI18next from react-i18next, a plugin to ensure that i18next works with React. The next step was to initialize i18n by passing it an object of options. There are many options you can pass to the initializer, but we're passing 4 for now:

  • resources: This contains the different translations that are available. We would eventually relocate our translations to a separate file/folder and eliminate this option. Note: These translations contain both the key and the value; we utilize the key to obtain the value afterward. For example, in the above, text is the key, and Welcome to this React Internationalization App is the value of en (English) while Bienvenue sur cette application d'internationalisation React is the value for fr (French).

  • lng: This is used to provide the language we want to show or retrieve from the resources object; later on, we will utilize a language detector, which eliminates the need to specify the lng option.

  • fallbackLng: If no language is recognized, it serves as the default language in i18n. Language is determined by either the user's chosen language or a language previously selected by the user when accessing the website.

  • interpolation: Interpolation is one of the most commonly utilized features in I18N. It enables you to incorporate dynamic values into your translations. This would later be used to manage date formatting.

Using the t() Function in i18nextAnchor

At this point, we have fully configured i18next on our React Application. The next step is to manage translations in our application. The first step is to include the i18n.js file we prepared earlier in our index.js file so it can be utilized throughout our application.

import './i18n';

The next step is to translate. To do so, we utilize the t() method, which is derived from useTranslation and imported from react-i18next. Let's take care of this in our App.js file:

import { useTranslation } from 'react-i18next';
function App() {
const { t } = useTranslation();
return (
<div>
<h2>{t('Welcome.text')}</h2>
</div>
);
}
export default App;

Note: We make use of the key to get the value.

At this point when we load (via npm start) our app, we would see an output like this, and when we go to the i18n.js file to change the lng option to fr, the French text would be displayed.

internalization from en to fr

Interpolation and PluralizationAnchor

Interpolation is one of the most commonly utilized features in I18N. It enables the incorporation of dynamic values into your translations and may be used in conjunction with Pluralization. In English, most plural nouns are produced by adding the “s” suffix, thus "There is only one Article on this" becomes "There are 5 Articles on this" for its plural. This pluralization is handled effectively by I18next. We would also like a circumstance in which these numbers are entered dynamically, for example, "There are {{ count }} Articles on this," which is an interpolation.

Suppose we update the resources option to include key-value pairs we could use to illustrate interpolation:

resources: {
en: {
translation: {
Welcome: {
"text": "Welcome to this React Internationalization App",
},
interpolation_pluralization: {
"text_one": "There is only one Article on this",
"text_other": "There are {{count}} Articles on this"
}
}
},
fr: {
translation: {
Welcome: {
"text": "Bienvenue sur cette application d'internationalisation React",
},
interpolation_pluralization: {
"text_one": "Il n'y a qu'un seul article à ce sujet",
"text_other": "Il y a {{count}} articles sur ce sujet"
}
}
},
},

Here, text_one and text_other are the new entries (see the _other suffix as that of plural and _one as the singular). You can check out more examples and explanations here. I18next uses a {{placeholder}} for dynamic values in the translations. We can now update our App.js file to make use of the text key to get the value:

<div>
<h2>{t('Welcome.text')}</h2>
<p>{t('interpolation_pluralization.text', { count: 5 })}</p>
</div>

At this point when we load our app, we would see an output like this, and this would also apply when we change the language:

Handling interpolation and pluralization

Date/time TranslationsAnchor

Now, let's look at how we may use multiple date formats to manage date and time with the aid of i18next and Luxon. This is when the interpolation option comes into play. This would assist us in defining the format. The first step is to install luxon by typing the following command into our terminal:

npm install luxon

Once that is done, we can add the date key and value for each language format to the resource option:

resources: {
en: {
translation: {
...
date: 'Today is {{date, DATE_HUGE}}'
},
},
fr: {
translation: {
...
date: 'Aujourd'hui, nous sommes le {{date, DATE_HUGE}}'
},
},
},

Note: DATE_HUGE is coming from luxon to help format the date like this - “Friday, October 14, 1983”. You can check out more options here.

Once this is fixed, we need to implement date formatting using luxon. The first thing will be to import luxon and then add the formatting logic to the interpolation option:

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import { DateTime } from 'luxon';
i18n
.use(initReactI18next) // passes i18n down to react-i18next
.init({
...
lng: 'en', // if you're using a language detector, do not define the lng option
fallbackLng: 'en',
interpolation: {
format: (value, format, lng) => {
// legacy usage
if (value instanceof Date) {
return DateTime.fromJSDate(value)
.setLocale(lng)
.toLocaleString(DateTime[format]);
}
return value;
},
},
});

In the above, we imported the newly installed library(luxon) and then added the logic to help format the date to the interpolation option. Finally, we can now add this to the App.js file, so it appears:

<div>
<h2>{t('Welcome.text')}</h2>
<p>{t('interpolation_pluralization.text', { count: 5 })}</p>
<p>{t('date', { date: new Date() })}</p>
</div>

At this point when we load our app, we would see an output like this:

working with dates

Creating/Adding Translation FilesAnchor

So far, we have been retrieving translations from our i18n.js file, which works great but is not suited for translators to work with. Let's have a look at how we can split these translations from the code, store them in dedicated JSON files, and load them asynchronously (on demand). To do so, we'll need to use the i18next-http-backend module, which we can get by typing the command below in our terminal:

npm install i18next-http-backend

Once this is successful, the first step would be to import this package into the i18n.js file and then configure it:

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import { DateTime } from 'luxon';
import Backend from 'i18next-http-backend';
i18n
.use(Backend)
.use(initReactI18next) // passes i18n down to react-i18next
.init({
...
});

The next step would be to move the translations to the public folder with the following structure:

file structure

Each translation.json file contains translations for a single language, e.g: this is for en/translation.json

{
"Welcome": {
"text": "Welcome to this React Internationalization App",
"description": "This app aims to help bridge the gap and help you easily internationalize your web page easily"
},
"interpolation_pluralization": {
"text_one": "There is only one Article on this",
"text_other": "There are {{count}} Articles on this"
},
"date": "Today is {{date, DATE_HUGE}}"
}

Now, the translations are loaded asynchronously, ensure you wrap, the App.js code with the Suspense component to wait for the translations to be loaded so as to prevent some errors.

import { Suspense } from 'react';
import { useTranslation } from 'react-i18next';
function App() {
const { t } = useTranslation();
return <Suspense fallback="...is loading">...</Suspense>;
}
export default App;

Now when you load your app, it still looks the same, but your translations are now separated. If you want to support a new language, you just create a new folder and a new translation JSON file. This gives you the possibility to send the translations to some translators.

Note: You can also have multiple translation files thanks to the namespaces feature of i18next.

Switching Between LanguagesAnchor

We will implement the language-switching feature in this part. These would simply be buttons on the web page that would allow us to choose between languages without changing the lng option in our i18n.js file.

We installed the LanguageDetector requirement previously in this project to allow us easily manage language change in our app; at this point, we will implement it. The first step would be to include it in our i18n.js file and configure it as follows:

import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import { DateTime } from 'luxon';
import Backend from 'i18next-http-backend';
import LanguageDetector from 'i18next-browser-languagedetector';
i18n
.use(Backend)
.use(LanguageDetector)
.use(initReactI18next) // passes i18n down to react-i18next
.init({
...
});

At this point, we may remove the lng option because we no longer require it. Instead, we will change the language using App.js or any other component you set up. The first step would be to generate an array with the language code and nativename(optional) so that we can loop over it to obtain our buttons instead of hardcoding them one by one.

const lngs = [
{ code: 'en', nativeName: 'English' },
{ code: 'fr', nativeName: 'Francais' },
];
We can now loop through the button within our markup:
```jsx
<div>
{lngs.map((lng) => {
return (
<button
className="m-4 p-2 bg-blue-600 rounded"
key={lng.code}
type="submit"
onClick={() => i18n.changeLanguage(lng.code)}
>
{lng.nativeName}
</button>
);
})}
</div>

It’s important to note that when you run the code, it will throw an error because the onClick() method is trying to access the i18next library to change the Language whenever it is clicked. This mean we would need to import i18n from i18next:

import i18n from 'i18next';

Once this is done, and we load our app, the buttons will now appear and we can change the languages easily as seen below:

final output of simple app

So far, we have been able to learn and practice how to perform internationalization with i18next, in the next section, we would handle internationalization with GraphCMS and see how easier it is. The source code for this first part can be found here with better styling and more language.

Managing and Loading Translations from GraphCMSAnchor

GraphCMS is a cutting-edge content management platform that enables teams to provide content to any channel. If this is your first time exploring GraphCMS, create a free-forever developer account and begin your first project. You may also find some tips on how to integrate GraphCMS in React.

Let's look at how we can simply translate, publish, and manage material with several locales from a single piece of content using GraphCMS, which maintains and loads translations with GraphQL. The first step would be to establish a project, then construct fields for the project, being sure to check the box next to "Localize fields" while creating each field, as seen below.

localization in graphcms

Once all fields have been created, the next step before you start inputting content would be to add the particular locales (languages) you want your application to support by going to settings → Locales → and then selecting the language as seen below:

implementing localization in graphcms

At this point, we can start input data into these fields, but we first need to enable the particular languages we want on the right side so a field can be created for them

adding content to localized fields.png

Note: You can read more on how to handle localization in GraphCMS here

Loading Translations from GraphCMSAnchor

Once you are done inputting all your content, you can publish. Let’s now see how to load those translations via their languages. In GraphCMS querying is done with graphql-request and for us to do that in React, we first need to install the dependency by running the command below:

npm i graphql-request

Once it has successfully installed, the next step would be to import it into our App.js file, so we can now query the endpoint from GraphCMS:

import { request } from 'graphql-request';

Querying with GraphQLAnchor

Let’s now see various ways we can query our endpoint, we can set a default locale (via its code) and a fallback in case the particular language is not available by passing in the (``*locales*``: [en, de]) parameter:

{products (locales: [en, fr]) {
id
title
description
}
}

This will give us our output in English since we have our content in English, but suppose there is no content found for en, it will then fetch content in fr. Here is the output:

{
"data": {
"products": [
{
"id": "cl0xocls20r3k0bmp2hfhb0ey",
"title": "Here is a flower",
"description": "This is a very beautiful flower, many people will love"
}
]
}
}

We can also get data for a particular language by entering that locale, as seen below:

querying with graphql.png

Finally, the last step would be to load our translations into our react application using graphql-request:

const fetchProducts = async () => {
const { products } = await request(
'https://api-eu-west-2.graphcms.com/v2/cl0xn4yvn3iq501xm93s418jo/master',
`
{
products (locales: [fr]) {
id
title
description
}
}
`
);
};

ConclusionAnchor

In this guide, we learned how to use i18next and GraphCMS to internationalize and localize a React application. We also learned about the many libraries that may aid in the process and why we should utilize GraphCMS.


  • Joel Olawanle
  • Joel Olawanle is a Frontend Engineer and Technical writer based in Nigeria who is interested in making the web accessible to everyone by always looking for ways to give back to the tech community. He has a love for community building and open source.

Related articles

It's Easy To Get Started

GraphCMS plans are flexibly suited to accommodate your growth. Get started for free, or request a demo to discuss larger projects with more complex needs