
i18n in React Native with Expo
Project on Github
To make this article comprehensive, I have created a repository with a real application where you can analyze the code and see the complete implementation of the examples mentioned. Visit the repository on GitHub: app-internationalization.



First, install the Libraries
You need to install the necessary libraries for react-i18next, i18next, and expo-localization.
npx expo install expo-localization react-i18next i18next
In this example, I use AsyncStorage, so you’ll need to install it as well. However, if you use another solution to persist the data, feel free to replace it accordingly.
npx expo install @react-native-async-storage/async-storage
Now, create the configuration file in your src directory. Create a file named ./i18n/index.ts
with the content below:
import i18n from "i18next";
import { initReactI18next } from "react-i18next";
import * as Localization from "expo-localization";
import AsyncStorage from "@react-native-async-storage/async-storage";
import translationEn from "./locales/en-US/translation.json";
import translationPt from "./locales/pt-BR/translation.json";
import translationZh from "./locales/zh-CN/translation.json";
const resources = {
"pt-BR": { translation: translationPt },
"en-US": { translation: translationEn },
"zh-CN": { translation: translationZh },
};
const initI18n = async () => {
let savedLanguage = await AsyncStorage.getItem("language");
if (!savedLanguage) {
savedLanguage = Localization.locale;
}
i18n.use(initReactI18next).init({
compatibilityJSON: "v3",
resources,
lng: savedLanguage,
fallbackLng: "pt-BR",
interpolation: {
escapeValue: false,
},
});
};
initI18n();
export default i18n;
In this example, I am using AsyncStorage to persist the internationalization data in case the user manually changes the language. Additionally, the configuration with expo-localization is used to get the device’s current language.
Import the i18n File in your root App
“I use it in _layout.tsx
, but if your root file is index.ts
or another file, you need to import it in that root file instead.”
Example import in the file _layout.tsx
of the root App:
import { useEffect } from 'react';
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native';
import { useFonts } from 'expo-font';
import { Stack } from 'expo-router';
import * as SplashScreen from 'expo-splash-screen';
import 'react-native-reanimated';
import '@/i18n'; // This line imports the i18n configuration
import { useColorScheme } from '@/hooks/useColorScheme';
export default function RootLayout() {
const colorScheme = useColorScheme();
const [loaded] = useFonts({
SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'),
});
useEffect(() => {
if (loaded) {
SplashScreen.hideAsync();
}
}, [loaded]);
if (!loaded) {
return null;
}
return (
<ThemeProvider value={colorScheme === 'dark' ? DarkTheme : DefaultTheme}>
<Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="+not-found" />
</Stack>
</ThemeProvider>
);
}
Now you need to create your translation files and use them in your components.
Create files for translated locales
In the i18n folder, create a folder named locales. Inside the locales folder, create subfolders for each locale, such as en-US, pt-BR, or zh-CN. Inside each subfolder, create a JSON file named translation.json
with your translation entries. Below are examples of these JSON files.
name file: ./i18n/locales/en-US/translation.json
{
"language": "Language",
"home": {
"title": "Home",
"welcome": "Welcome",
"subtitle": "Example i18n App!",
"description": "This is an example React Native application demonstrating how to implement internationalization (i18n) using react-i18next. The app allows users to switch between different languages for a more localized experience.",
"exploringLanguages": "Exploring Languages",
"exploringLanguagesDescription": "Click on country flags to explore the app's content in different languages.",
"learnMore": "Want to Learn More?",
"repositoryLinkText": "Project repository on GitHub",
"articlesLinkText": "More articles"
},
"features": {
"title": "Features",
"collapsibles": {
"i18n": {
"title": "Internationalization with i18next",
"description": "Uses react-i18next for language management, allowing the app to be localized for different languages."
},
"persistent": {
"title": "Persistent Language Selection",
"description": "Uses AsyncStorage to persistently store the user's preferred language, providing a consistent experience across app restarts."
},
"fallback": {
"title": "Language Fallback",
"description": "Defaults to the device's language if no language preference is saved."
},
"switching": {
"title": "Easy Language Switching",
"description": "Users can switch languages by tapping on country flags."
}
}
}
}
name file: ./i18n/locales/pt-BR/translation.json
{
"language": "Idioma",
"home": {
"title": "Início",
"welcome": "Bem-vindo",
"subtitle": "App de Exemplo com i18n!",
"description": "Este é um exemplo de aplicativo React Native que demonstra como implementar internacionalização (i18n) usando react-i18next. O aplicativo permite aos usuários alternar entre diferentes idiomas para uma experiência mais localizada.",
"exploringLanguages": "Explorando Idiomas",
"exploringLanguagesDescription": "Clique nas bandeiras dos países para explorar o conteúdo do aplicativo em diferentes idiomas.",
"learnMore": "Quer Saber Mais?",
"repositoryLinkText": "O repositório do projeto no GitHub",
"articlesLinkText": "Mais artigos"
},
"features": {
"title": "Funcionalidades",
"collapsibles": {
"i18n": {
"title": "Internacionalização com i18next",
"description": "Utiliza react-i18next para gerenciamento de idiomas, permitindo que o aplicativo seja localizado para diferentes idiomas."
},
"persistent": {
"title": "Seleção de Idioma Persistente",
"description": "Utiliza AsyncStorage para armazenar persistentemente o idioma preferido do usuário, proporcionando uma experiência consistente ao reiniciar o aplicativo."
},
"fallback": {
"title": "Fallback de Idioma",
"description": "Padrão para o idioma do dispositivo se nenhuma preferência de idioma for salva."
},
"switching": {
"title": "Troca Fácil de Idioma",
"description": "Os usuários podem trocar de idioma tocando nas bandeiras dos países."
}
}
}
}
name file: ./i18n/locales/zh-CN/translation.json
{
"language": "语言",
"home": {
"title": "开始",
"welcome": "欢迎",
"subtitle": "i18n示例应用!",
"description": "这是一个使用react-i18next实现国际化(i18n)的React Native示例应用。该应用允许用户在不同语言之间切换,以提供更本地化的体验。",
"exploringLanguages": "探索语言",
"exploringLanguagesDescription": "点击国家旗帜以在不同语言下探索应用内容。",
"learnMore": "想了解更多?",
"repositoryLinkText": "GitHub上的项目仓库",
"articlesLinkText": "更多文章"
},
"features": {
"title": "功能",
"collapsibles": {
"i18n": {
"title": "使用i18next进行国际化",
"description": "使用react-i18next进行语言管理,使应用程序能够在不同语言环境下本地化。"
},
"persistent": {
"title": "持久化语言选择",
"description": "使用AsyncStorage持久化存储用户的首选语言,提供应用重启后的一致体验。"
},
"fallback": {
"title": "语言回退
",
"description": "如果未保存语言首选项,则默认使用设备的语言。"
},
"switching": {
"title": "简便的语言切换",
"description": "用户可以通过点击国家旗帜来切换语言。"
}
}
}
}
Excellent! Now you have translation files for English, Portuguese, and Chinese.
Use your translations in components
Now, you need to use the translations in your components and create a list of flags for changing the locale using the useTranslation hook.
// import hook
import { useTranslation } from "react-i18next";
// inside a component
const { t } = useTranslation();
const text = t('example.text');
An example of basic usage in a real component:
import React, { useEffect } from "react";
import { StyleSheet, View, ScrollView, TouchableOpacity } from "react-native";
import AsyncStorage from "@react-native-async-storage/async-storage";
import { ThemedText } from "@/components/ThemedText";
import { useTranslation } from "react-i18next";
import Brasil from "./flags/Brasil";
import USA from "./flags/USA";
import China from "./flags/China";
const flags = [
{ component: Brasil, lang: "pt-BR", name: "Brasil" },
{ component: USA, lang: "en-US", name: "USA" },
{ component: China, lang: "zh-CN", name: "China" },
];
export function Language() {
const { i18n, t } = useTranslation();
const currentLanguage = i18n.language;
useEffect(() => {
const loadLanguage = async () => {
const savedLanguage = await AsyncStorage.getItem("language");
if (savedLanguage) {
i18n.changeLanguage(savedLanguage);
}
};
loadLanguage();
}, [i18n]);
const changeLanguage = async (lang: string) => {
await AsyncStorage.setItem("language", lang);
i18n.changeLanguage(lang);
};
return (
<View style={styles.container}>
<ThemedText style={styles.text}>{t('language')}</ThemedText>
<ScrollView
horizontal
showsHorizontalScrollIndicator={false}
contentContainerStyle={styles.flagsContainer}
>
{flags.map(({ component: Flag, lang, name }) => (
<TouchableOpacity
key={name}
onPress={() => changeLanguage(lang)}
style={[
styles.flag,
currentLanguage === lang && styles.activeFlag,
currentLanguage !== lang && styles.inactiveFlag,
]}
>
<Flag width={45} height={45} />
</TouchableOpacity>
))}
</ScrollView>
</View>
);
}
const styles = StyleSheet.create({
container: {
justifyContent: "center",
},
flagsContainer: {
flexDirection: "row",
paddingVertical: 10,
},
flag: {
paddingHorizontal: 10,
},
activeFlag: {
transform: [{ scale: 1.2 }],
},
inactiveFlag: {
opacity: 0.5,
},
text: {
fontSize: 22,
lineHeight: 32,
marginTop: -6,
},
});
Finished! You now have a React Native app with internationalization support for multiple languages, accessible to people around the world. Happy coding and enjoy your #hacktoberfest!
References
If you need references, check out the links below for more examples:
- The source of the app implementation on GitHub
- My profile on LinkedIn: Lucas Ferreira Lima
- My profile on GitHub: @lucasferreiralimax
- The official site: react-i18next
- React Native: reactnative.dev
- Expo: expo.dev
Need Help?
Comment or get in touch with me. I’d be happy to help, and it was nice to meet you.