DEV Community

Cover image for Creating a Multi-Language App in React Native
Vikrant Negi
Vikrant Negi

Posted on

Creating a Multi-Language App in React Native

Language has always been an essential part of any business. As the business grows it becomes important for the business to expand in different countries and regions. To achieve success in these local regions it is important to provide localization.

When it comes to the mobile app, the conditions are no different. As the app users expand to different countries, it becomes important to provide the users with the ability to use the app in their native languages.

In this article, we are going to build a React Native app that supports multi-language using react-native-localize package.

Prerequisites

This tutorial requires basic knowledge of React Native. To set up your development machine, follow the official guide here.

To make sure we are on the same page, following are the versions used in this tutorial -

  • Node v10.15.0
  • npm 6.4.1
  • yarn 1.16.0
  • react-native 0.59.9
  • react-native-localize 1.1.3
  • i18n-js 3.3.0

Getting Started

We'll be building a React Native app which will support English, French and Arabic language.

If you want to have a look at the source code right away, here is the Github link.

To create a new project using react-native-cli, type the following in the terminal:

$ react-native init multiLanguage
$ cd multiLanguage

Add required libraries

Install react-native-localize by typing the following:

$ yarn add react-native-localize

and then link it using:

$ react-native link react-native-localize

If you face any error during installation, check the installation instructions here.

The react-native-localize lib gives you access to a lot of localization related device constants, but does not come with an i18n lib.

We'll use I18n.js to provide a I18n translations on the JavaScript.

$ yarn add i18n-js

Since i18n-js does not seem to provides any cache / memoization we'll use lodash.memoize for the same.

$ yarn add lodash.memoize

Add Translations

Create a translations directory inside src and then create, three JSON files as below for each language.

  1. en.json for English
  2. fr.json for French
  3. ar.json for Arabic

Inside these files will be JSON object with keys and values.

Key will the be same for each language and will be used in the app to display text.

The value will be the actual translation that we want to show to the user and will be different for each language.

For English:

{
  "hello": "Hello World!"
}

For French:

{
  "hello": "Salut le Monde!"
}

For Arabic:

{
  "hello": "أهلاً بالعالم"
}

Similarly, you can add more key-value pairs for every text that will be used in the app.

Add Main Code

Open App.js file and the following imports:

import React from "react";
import * as RNLocalize from "react-native-localize";
import i18n from "i18n-js";
import memoize from "lodash.memoize"; // Use for caching/memoize for better performance

import {
  I18nManager,
  SafeAreaView,
  ScrollView,
  StyleSheet,
  Text,
  View
} from "react-native";

After that, we'll add some helper functions and constants that we'll use later.

const translationGetters = {
  // lazy requires (metro bundler does not support symlinks)
  ar: () => require("./src/translations/ar.json"),
  en: () => require("./src/translations/en.json"),
  fr: () => require("./src/translations/fr.json")
};

const translate = memoize(
  (key, config) => i18n.t(key, config),
  (key, config) => (config ? key + JSON.stringify(config) : key)
);

const setI18nConfig = () => {
  // fallback if no available language fits
  const fallback = { languageTag: "en", isRTL: false };

  const { languageTag, isRTL } =
    RNLocalize.findBestAvailableLanguage(Object.keys(translationGetters)) ||
    fallback;

  // clear translation cache
  translate.cache.clear();
  // update layout direction
  I18nManager.forceRTL(isRTL);
  // set i18n-js config
  i18n.translations = { [languageTag]: translationGetters[languageTag]() };
  i18n.locale = languageTag;
};

Now, we'll create our App class component.

export default class App extends React.Component {
  constructor(props) {
    super(props);
    setI18nConfig(); // set initial config
  }

  componentDidMount() {
    RNLocalize.addEventListener("change", this.handleLocalizationChange);
  }

  componentWillUnmount() {
    RNLocalize.removeEventListener("change", this.handleLocalizationChange);
  }

  handleLocalizationChange = () => {
    setI18nConfig();
    this.forceUpdate();
  };

  render() {
    return (
      <SafeAreaView style={styles.safeArea}>
        <Text style={styles.value}>{translate("hello")}</Text>
      </SafeAreaView>
    );
  }
}

const styles = StyleSheet.create({
  safeArea: {
    backgroundColor: "white",
    flex: 1,
    alignItems: "center",
    justifyContent: "center"
  },
  value: {
    fontSize: 18
  }
});

In our constructor method, we called setI18nConfig() which will set the initial configuration.

Then in componentDidMount() we'll add an event listener which will listen for any changes and call handleLocalizationChange() if any changes occur.

The handleLocalizationChange() method does two things one is that it fires setI18nConfig() and forceUpdate(). This is necessary for Android devices as the component needs to be re-render for the changes to be visible.

We will remove the listener in componentWillUnmount() lifecycle method.

Finally, in render() we'll print out hello by using translate() and passing our key as a parameter into it. It will then automatically figure out the language and the text that needs to be shown for that language.

Running the App

Now its time to see our if the translations are working.

Run your app in simulator or emulator by typing:

$ react-native run-ios
$ react-native run-android

It should be something like this:

Hello World English

Now, change your device language setting to French and open your app again.

Hello World French

Similarly, you can change your language setting to Arabic and see hello in Arabic.

So far so great.

But what happens if I choose some random language whose translation is not added in the app? What language will it fall back to?

Well as it turns out the goal of findBestAvailableLanguage is to return the best available translation. So It will look at your language preference setting to figure out the fallback language.

If you go to the Language & Region setting in your iOS simulator you can see the preference order of the languages.

Language Preference Setting

If the language chosen is not in the preferred language findBestAvailableLanguage returns undefined (so the retained value will be your fallback) because none of the user's preferred languages are available in your translations.

Bonus

The react-native-localize has API that can provides access to lot localization related device constants. Be sure to check out the full APIs available in the docs.

Conclusion

Adding multi-language support is that easy. You can now use react-native-localize to easily provide multiple language support in your app which can help in increasing user's app usage.

Find the source code in the Github repo here.

Originally published on Medium

If you like this article, go ahead and show some love and share.

Oldest comments (9)

Collapse
 
anytram profile image
Martyna

Hello, I have problem with rewrite your code using Typescript, can you help me with types?

Collapse
 
vikrantnegi profile image
Vikrant Negi

Sorry, I'm not very good at the typescript.

Collapse
 
omatrot profile image
Olivier MATROT • Edited

Here is what I have in a typescript file for the helpers:

import memoize from "lodash.memoize";
import * as RNLocalize from "react-native-localize";
import i18n from "i18n-js";
import { I18nManager } from "react-native";

const translationGetters = {
  // lazy requires (metro bundler does not support symlinks)
  en: () => require("../../src/translations/en.json"),
  fr: () => require("../../src/translations/fr.json")
};

const translate = memoize(
  (key, config?) => {
    return i18n.t(key, config);
  },
  (key, config) => (config ? key + JSON.stringify(config) : key)
);

const setI18nConfig = () => {
  // fallback if no available language fits
  const fallback = { languageTag: "en", isRTL: false };

  const { languageTag, isRTL } =
    RNLocalize.findBestAvailableLanguage(Object.keys(translationGetters)) ||
    fallback;

  // clear translation cache
  translate.cache.clear!();
  // update layout direction
  I18nManager.forceRTL(isRTL);
  // set i18n-js config
  i18n.translations = {
    [languageTag]: (translationGetters as any)[languageTag]()
  };
  i18n.locale = languageTag;

};

export { setI18nConfig, translate };
Collapse
 
luizfbrisighello profile image
Luiz Felipe Shimizu Brisighello • Edited

Hi Vikrant Negi! How do I go about doing this with multiple screens? Can I have all of these code in a translate.js file and pass the translate variable as a prop?

Collapse
 
vikrantnegi profile image
Vikrant Negi

You can put all your translations in the translate.js file and use the keys in your multiple files

Collapse
 
luizfbrisighello profile image
Luiz Felipe Shimizu Brisighello

I figured it out! The question now is how do I use useMemo hook instead of lodash.memoize?

Collapse
 
sankar2389 profile image
sankar

Is it possible to change the language from app settings menu itself? So that I can change the language for this app alone

Collapse
 
nihp profile image
nihp

Did you got any solution?

Collapse
 
mansi09876 profile image
Mansi09876

This is a great article about creating a multi-language app in React Native. It is interesting to see how the React Native app development company has been able to utilize this technology to create a great app for their clients. The article is definitely worth reading for anyone interested in learning about the process of creating a multi-language app.