How to Easily Localize React-Native Apps in 2023

Featured 2, React-Native
Supporting multiple locales in your mobile app is a crucial feature if you want to scale your app globally for your users to have personalized experiences.
In this article, we will develop a simple react-native application that has multi-lingual support using the react-i18next framework, which is a powerful internationalization framework for React/React Native based on i18next.
If you don’t want to setup the project yourself, you can find the full one in our GitHub repository and clone it—easy as pie.


This article assumes that you have:
  1. Knowledge in React.
  2. Node.js and npm are installed on your computer.

Why react-18Next?

Extensibility: Compared to other i18n frameworks, i18next is extensible and can be used in any Javascript environment (and a few non-Javascript – .net, elm, iOS, Android), with any UI framework, and with any i18n format.

Project structure

The final project should look like the picture below:

Installing Expo

To build the react-native app with Expo, we first need to install expo-cli. It’s the primary interface between a developer and other Expo tools used for different tasks throughout the project development cycle.


To use the expocli you will need these requirements.

Include the following lines:

  npm install --global expo-cli && npx create-expo-app l10n-app
This will create an Expo project called ‘l10n-app’

Initialize react-i18next

In your project root directory, install the react-i18next package.
npm install react-i18next i18next --save
 Next, install Expo-localization to get access to the locale data of the native device.
 npx expo install expo-localization
 Create an i18n.js file in your project’s root directory and write the code below.

					import i18n from 'i18next';
import * as Localization from 'expo-localization';
import {initReactI18next} from "react-i18next";

// creating a language detection plugin using expo

const languageDetector = {
    type: 'languageDetector',
    async: true, // async detection
    detect: (callback) => {
        // We will get back a string like "en-UK".


    init: () => {

    cacheUserLanguage: () => {

    .use(initReactI18next) // passes i18n down to react-i18next
        fallbackLng: 'en-US',
        compatibilityJSON: 'v3',
        // the translations

        resources: {
            'en-US': {
                translation: {
                    home_title_1: "Meeting scheduling",
                    home_title_2: "made easy",
                    header_title_home: 'Home',
                    header_title_lang: 'Change Language',
                    home_button_text: 'Change Language',
                    home_body: "Never miss a meeting. Never be late for one too. Keep track of your meetings and receive smart reminders in appropriate times. Read your smart “Daily Agenda” every morning.",

            'zh-CN': {
                translation: {
                    home_title_1: "会议安排",
                    home_title_2: "变得容易",
                    header_title_home: '主页',
                    header_title_lang: '改变语言',
                    home_button_text: '改变语言',
                    home_body: "永远不要错过会议。也不要迟到。跟踪您的会议并在适当的时间收到智能提醒。每天早上阅读您的智能“每日议程”。",

            // have a initial namespace
            ns: ['translation'],
            supportedLngs: [  // Supported languages
                    code: 'en',
                    locale: 'English'
                }, {
                    code: 'zh-CN',
                    locale: 'Chinese (Simplified)'
            defaultNS: 'translation',
            interpolation: {
                escapeValue: false // not needed for react
export default i18n;
  l18n.js file


In production, it is better to put the translations in separate .json files and import them.
  Read the react18next documentation if you want to learn more about the above code.


We are going to add navigation to our app so that we can navigate to different screens.

					npm install @react-navigation/native
Run this in your project directory to add react-navigation dependencies.

					npx expo install react-native-screens react-native-safe-area-context
  Finally, we need to install @react-navigation/native-stack

					npm install @react-navigation/native-stack
After that create a navigation directory, then create a Navigation.js file in it. Finally include the code below in the file:
					import Home from "../screens/Home";
import {NavigationContainer} from '@react-navigation/native';
import {createNativeStackNavigator} from '@react-navigation/native-stack';
import Languages from "../screens/Languages";
import {useTranslation} from "react-i18next";

const Stack = createNativeStackNavigator();

export default function Navigation() {
    const {t} = useTranslation()

    return (

            <Stack.Navigator initialRouteName="Home">
                <Stack.Screen name="Home"
                              options={{title: t('header_title_home')}}
                <Stack.Screen name="Languages"
                              options={{title: t('header_title_lang')}}
 createNativeStackNavigator is a function that returns an object containing 2 properties: Screen and Navigator. Both of them are React components used to configure the navigator.
Wrap the Stack component inside the NavigationContainer to manage our navigation tree.
  • Component prop: accepts a component.
  • Options: accepts navigator options such as the title to render.
  • Name: corresponds to the name of the route we will use to navigate.


Since we also want to translate the header title, we need to pass the text to the t function.

Wrap your Navigation component with I18nextProvider to pass down the i18next instance to the children components.
					import {I18nextProvider} from "react-i18next";
import i18n from "./i18n";
import Navigation from "./navigation/Navigation";
import {SafeAreaProvider} from "react-native-safe-area-context";

export default function App() {
    return (
        <I18nextProvider i18n={i18n}>
To learn more about the above code, feel free to read the react18next documentation.

Home screen

Create a screens directory in your project’s root directory and create a Home.js file in the screens directory.
  We are going to use the useTranslation hook to get the t function in our functional component. The t function is used to translate content in your app.
  Pass all the translatable strings inside the t function as arguments.
					import {Button, StyleSheet, Text, View} from 'react-native';
import {useTranslation} from "react-i18next";

export default function Home({navigation}) {
    const {t} = useTranslation()

    return (

        <View style={styles.container}>
            <Text style={{...styles.text, color: '#fff'}}>
            <Text style={{...styles.text, color: '#f97316'}}>
            <Text style={{...styles.body, color: '#fff'}}>
            <Button color={'#f97316'} title={t('home_button_text')} onPress={() => navigation.navigate('Languages')}/>

const styles = StyleSheet.create({
    container: {
        flex: 1, backgroundColor: '#1f2937', alignItems: 'center',
        justifyContent: 'center', padding: '5%'

    }, text: {
        fontSize: 30, fontWeight: 'bold', padding: '4%'
    }, body: {
        fontSize: 14, fontWeight: 'normal', marginBottom: '20%'

Here is how it should look like:

Language screen

Now create a Languages.js file in the screens directory and add this code to it. We are going to use this screen to list all supported languages and switch between languages.
					import {FlatList, StyleSheet, Text, TouchableOpacity, View} from 'react-native';
import {useTranslation} from "react-i18next";

export default function Languages() {
    const {i18n} = useTranslation()
    // Supported Languages List
    const {supportedLngs} =

    return (

        <View style={styles.container}>
            <FlatList data={supportedLngs} renderItem={({item}) => (

                <TouchableOpacity onPress={() => i18n.changeLanguage(item.code)} style={styles.listItem}>

const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#fff',


    listItem: {
        padding: '4%',
        flex: 1,
        width: '100%',
        borderBottomWidth: 1,

  Remember, we passed a list of objects containing the supported languages. This is where we use it to list all the supported languages/locales. Pass the array in the data prop in the FlatList component and a component to the renderItem prop.

How to Easily Localize React-Native Apps in 2023
How to Easily Localize React-Native Apps in 2023


At this point, the app is able to support multiple locales and switch between them.

Translated Copy - How to localize react-native apps - 3
Translated UI
Full UI sequence- React-Native Localization


Interpolation allows the integration of dynamic values into your translations. To demonstrate this, add this key to the English translation.

					launch_date: '{{count}} day left to launch',
On the front end, add this line to the home screen page.

					<Text style={{...styles.body, color: '#fff',padding :'0%'}}>
    {t('launch_date', {count:1})}
Keys, by default, are strings surrounded by curly brackets, i.e., count. The dynamic value is 1 for the count key.

Updated Home Screen


Now that we have passed dynamic data inside our strings, we need to consider pluralization. Suppose our count was 2 or more. Do you see the problem now? How would we handle that? If we don’t take it into account, it will display 2 day left to launch.
Add the key below, which is the plural form of the launch_date key. Now if your count is greater than one, it’ll take the plural form.

					launch_date_plural: '{{count}} days left to launch',
Pluralization of the launch_date key.

Adding more locales

For an app already in production, it is better to load translations through a backend service (Starling, Lokalize, etc.). In this way, when translations are published, they readily become available for use in the app without having to go back to the code. Furthermore, considering the scale and depth of the necessary potential changes, we encourage you to partner with battle-tested professionals.