Internationalization on React Native with react-i18next

Petrus Pierre
Petrus Pierre

Fullstack Developer | Working @ Translational Analytics & Statistics

Introduction

i18n stands for Internationalization, which is an important key to a successful mobile application, mainly if your app's default language isn't English. With this feature, you will be able to achieve new audiences and engage more people in your app.

If you are interested to know, before continue reading this article, more about why should you use i18n on your project, you can check this article on The Miners Blog written by Abner Alves, "Your App Across the Globe".

The principle of creating i18n in most applications is really simple, basically, you will have files with many strings, and depending on the user preference or system settings, the application will use the appropriate files with the correct language.

So, in this article, we will learn how to properly set up i18n on a React Native project using the react-i18next library. Let's do it!

Hand's on!

Setting up the translation configs

First, we need a React Native project, so if you don't have one, type the react-native init command on your Terminal.

npx react-native init MyI18nProject

After that, let's add the necessary dependencies, react-i18next and i18next:

react-i18next will be responsible for managing the translation files and switching the app language powered by the i18next library.

npm install --save react-i18next i18next

Now, we'll create the i18n files, to do that I recommend creating a folder called i18n on your project source directory, usually /src but for learning purposes in this post I'll put everything on the root /.

With this folder created, let's add some content to it. Let's start with the translation files.

We need to create a new directory in the i18n folder called locales with directories for each language we want*. On each language directory, we'll have an index.js and a JSON file for each namespace we want for our project.

*This structure is not mandatory but that's how I do in my personal projects and it's being good enough.

A namespace is basically to separate the translation into scopes, so when we call the hook to use the translations, we can specify a namespace to search for the wanted copy.

So, let's add support for two languages on our app: English and Portuguese, so the i18n/locales directory will look something like this:

Locales directory structure

If you're not up to doing all this file creation manually, just run these commands (if you're on Linux/Mac):

mkdir i18n && touch i18n/index.js
mkdir i18n/locales && touch i18n/locales/index.js
mkdir i18n/locales/pt && touch i18n/locales/pt/index.js
touch i18n/locales/pt/home.json
mkdir i18n/locales/en && touch i18n/locales/en/index.js
touch i18n/locales/en/home.json

In the home.json file we're defining our home namespace, so let's add some translations to it!

// en/home.json

{
  "changeLanguage": "Change the language!"
}
// pt/home.json

{
  "changeLanguage": "Mude o idioma!"
}

Inside each index.js file on languages directories we will just export the json file(s):

// locales/en/index.js
// locales/pt/index.js

export { default as home } from './home.json';

and let's export it again on the locales/index.js file

// locales/index.js

import * as en from './en';
import * as pt from './pt';

export { en, pt };

Now let's add to the i18n folder an index.js file with the needed configuration.

// i18n/index.js
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';

import * as resources from './locales';

i18n.use(initReactI18next).init({
  resources, // the translation files we've just created
  fallbackLng: 'en', // if the user tries to access some language that is not available in our app, let's use English as default
  compatibilityJSON: 'v3',
});

These are basic configurations so the library will work. You can of course use the configs you want, so feel free to check out more options in the official documentation

And we're finally done with all the setup for the i18n!

Rendering the translations

Let's use the useTranslation hook from the react-i18next library to get the translations we've just set up.

Important: to use the hook, you'll need to encapsulate your app in the I18nextProvider, just as in any other React Context.

It's not a hard task, just import it on top-level (just as an ordinary react hook) of your component file and get a t function from it. As a parameter, you can pass the namespaces you want to use in our component.

You will use it to get the strings, so your component will look like this by now:

// App.js
import React from 'react';
import {View, Text, StyleSheet, TouchableOpacity} from 'react-native';
import {I18nextProvider, useTranslation} from 'react-i18next';

import i18nConfig from './i18n';

const App = () => {
  const {t} = useTranslation('home');

  return (
    <I18nextProvider i18n={i18nConfig}>
      <View style={styles.container}>
        <TouchableOpacity onPress={() => {}}>
          <Text>{t('changeLanguage')}</Text>
        </TouchableOpacity>
      </View>
    </I18nextProvider>
  );
};

const styles = StyleSheet.create({
  container: {
    padding: 50,
  },
});

export default App;

So far, so good. If you run your app right now, you'll see the "Change the language!" copy on the button because your default language is English and we're not changing this by now. That's the next step!

Switching languages

Now we can add a simple function to switch languages in our app! it will use the i18n object from the useTranslation hook

const {t, i18n} = useTranslation('home');
const switchLanguage = () => {
    if (i18n.language === 'en') {
      i18n.changeLanguage('pt');
    } else {
      i18n.changeLanguage('en');
    }
};

and finally, just pass in the onClick prop in the Touchable component

<TouchableOpacity onPress={switchLanguage}>
     <Text>{t('changeLanguage')}</Text>
</TouchableOpacity>

and use this function in our component, so it will look like this:

// App.js
import React from 'react';
import {View, Text, StyleSheet, TouchableOpacity} from 'react-native';
import {I18nextProvider, useTranslation} from 'react-i18next';
import i18nConfig from './i18n';

const App = () => {
  const {t, i18n} = useTranslation('home');

  const switchLanguage = () => {
    if (i18n.language === 'en') {
      i18n.changeLanguage('pt');
    } else {
      i18n.changeLanguage('en');
    }
  };

  return (
    <I18nextProvider i18n={i18nConfig}>
      <View style={styles.container}>
        <TouchableOpacity onPress={switchLanguage}>
          <Text>{t('changeLanguage')}</Text>
        </TouchableOpacity>
      </View>
    </I18nextProvider>
  );
};

const styles = StyleSheet.create({
  container: {
    padding: 50,
  },
});

export default App;

Stay tuned!

Hope you liked it!

Here's the repository containing the source code of this project, available on GitHub

I'll post more content about i18n: how to persist it and also use a language detector for your app, so stay tuned and see you in the next post.