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

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.
You can find the full project in our GitHub repository and clone it.
Prerequisites
This article assumes that you have:
- Knowledge in React.
- 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
It should roughly 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 expo–cli you will need these requirements.
Include the following lines:
npm install --global expo-cli && npx create-expo-app l10n-app
npx create-expo-app l10n-app
They 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
// http://i18next.com/docs/ownplugin/#languagedetector
const languageDetector = {
type: 'languageDetector',
async: true, // async detection
detect: (callback) => {
// We will get back a string like "en-UK".
callback(Localization.locale);
},
init: () => {
},
cacheUserLanguage: () => {
},
};
i18n
.use(initReactI18next) // passes i18n down to react-i18next
.use(languageDetector)
.init({
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.
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 (
);
}
Navigation
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 write this code in your
Navigation.js
file in the navigation directory.
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 (
);
}
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.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 (
{t('home_title_1')}
{t('home_title_2')}
{t('home_body')}
);
}
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
Add this code to your
Languages.js
file in the screens directory. 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} = i18n.services.resourceStore.data
return (
(
i18n.changeLanguage(item.code)} style={styles.listItem}>
{item.locale}
)}/>
);
}
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.
Result
At this point, the app is able to support multiple locales and switch between them.


Interpolation
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.
{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.
Pluralization
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',

Adding more locales
For an app already in production, it is better to load translations through a backend service, i.e., Lokalize. In this way, when translations are published, they readily become available for use in the app without having to go back to the code.
Tagged Code Highlight