How to Localize a React Native App
React Native does not include a built-in localization system. You need a third-party library. The most common setup is react-native-localize for detecting the device locale combined with i18next or react-intl for managing translations. This guide covers the full setup from scratch.
Choose your i18n library
i18next
npm install i18next react-i18next
npm install react-native-localize
npm install @os-team/i18next-react-native-language-detectorThe most popular choice for React Native. Huge ecosystem with plugins for language detection, backend loading, and formatting. Active community, good documentation, and works with both Expo and bare React Native. Supports namespaces, plurals, and interpolation out of the box.
react-intl (FormatJS)
npm install react-intl
npm install react-native-localizeBuilt on the ICU message format standard. Strong support for plurals, dates, numbers, and relative time. Heavier than i18next. Best if you already use FormatJS on the web and want format consistency across platforms.
Custom JSON loader
// No library needed
import en from './locales/en.json';
import fr from './locales/fr.json';
const t = (key) => translations[locale][key];Works for small apps with fewer than 100 strings and no plural rules. No dependencies, no build step. You lose interpolation, plurals, and namespace splitting. Not recommended for production apps that will grow.
Setting up i18next with React Native
i18next is the most common choice. Here is the full setup, step by step.
i18next setup from scratch
Install packages
Run npm install i18next react-i18next react-native-localize. If you use TypeScript, also install @types/react-i18next. For Expo projects, expo install react-native-localize.
Create translation JSON files
Create a locales directory with one JSON file per language: locales/en.json, locales/fr.json, locales/de.json. Each file is a flat or nested object with translation keys and values.
Configure i18next
Create an i18n.ts config file that initializes i18next with your resources, default language, and fallback behavior. Import this file at the top of your App entry point.
Create the useTranslation hook
i18next provides the useTranslation hook via react-i18next. It returns a t function you call with a translation key. The hook re-renders your component when the language changes.
Wrap your app with I18nextProvider
In your App.tsx, import the i18n instance and wrap your root component with I18nextProvider. This makes the translation context available to all child components.
{
"dependencies": {
"i18next": "^24.2.0",
"react-i18next": "^15.4.0",
"react-native-localize": "^3.3.0"
}
}{
"common": {
"save": "Save",
"cancel": "Cancel",
"delete": "Delete"
},
"home": {
"title": "Welcome",
"subtitle": "Start building something great",
"itemCount_one": "{{count}} item",
"itemCount_other": "{{count}} items"
},
"settings": {
"title": "Settings",
"language": "Language",
"notifications": "Notifications"
}
}{
"common": {
"save": "Enregistrer",
"cancel": "Annuler",
"delete": "Supprimer"
},
"home": {
"title": "Bienvenue",
"subtitle": "Commencez a construire quelque chose de genial",
"itemCount_one": "{{count}} element",
"itemCount_other": "{{count}} elements"
},
"settings": {
"title": "Parametres",
"language": "Langue",
"notifications": "Notifications"
}
}import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import * as RNLocalize from 'react-native-localize';
import en from '../locales/en.json';
import fr from '../locales/fr.json';
const deviceLanguage = RNLocalize.getLocales()[0]?.languageCode ?? 'en';
i18n
.use(initReactI18next)
.init({
resources: {
en: { translation: en },
fr: { translation: fr },
},
lng: deviceLanguage,
fallbackLng: 'en',
interpolation: {
escapeValue: false, // React Native already handles escaping
},
defaultNS: 'translation',
});
export default i18n;import React from 'react';
import { View, Text, StyleSheet } from 'react-native';
import { useTranslation } from 'react-i18next';
export function HomeScreen() {
const { t } = useTranslation();
const itemCount = 3;
return (
<View style={styles.container}>
<Text style={styles.title}>{t('home.title')}</Text>
<Text style={styles.subtitle}>{t('home.subtitle')}</Text>
<Text>{t('home.itemCount', { count: itemCount })}</Text>
</View>
);
}
const styles = StyleSheet.create({
container: { flex: 1, padding: 16 },
title: { fontSize: 28, fontWeight: 'bold' },
subtitle: { fontSize: 16, color: '#666', marginTop: 4 },
});Use namespaces
Use namespaces to split translations by screen or feature. This keeps files manageable as your app grows.
Detecting device locale
react-native-localize reads the device's current language and region settings. Use it to set the initial language in i18next and to respond to language changes while the app is running.
import * as RNLocalize from 'react-native-localize';
import { useEffect } from 'react';
import i18n from './i18n';
export function useDeviceLocaleSync() {
useEffect(() => {
const onChange = () => {
const bestLang = RNLocalize.findBestLanguageTag(
Object.keys(i18n.options.resources ?? {})
);
if (bestLang?.languageTag) {
i18n.changeLanguage(bestLang.languageTag);
}
};
// Listen for language changes (user changes device language while app is open)
RNLocalize.addEventListener('change', onChange);
return () => {
RNLocalize.removeEventListener('change', onChange);
};
}, []);
}RTL support in React Native
React Native supports RTL layouts through the I18nManager API. When a user switches to Arabic or Hebrew, you need to force RTL and restart the app for layout changes to take effect.
import { I18nManager } from 'react-native';
import * as RNLocalize from 'react-native-localize';
import RNRestart from 'react-native-restart'; // optional, for hot restart
const RTL_LANGUAGES = ['ar', 'he', 'fa', 'ur'];
export function configureRTL() {
const locale = RNLocalize.getLocales()[0];
const shouldBeRTL = RTL_LANGUAGES.includes(locale?.languageCode ?? '');
if (I18nManager.isRTL !== shouldBeRTL) {
I18nManager.forceRTL(shouldBeRTL);
I18nManager.allowRTL(shouldBeRTL);
// Restart required for layout direction change to take effect
// RNRestart.restart(); // Uncomment if using react-native-restart
}
}Call configureRTL() early in your app's entry point, before any components render. Use flexDirection: 'row' with I18nManager.isRTL checks for any custom layout logic that needs to respect text direction.
LocaleKit + React Native
LocaleKit supports JSON localization files. Run localekit translate to fill in missing keys across all your language JSON files.
React Native localization FAQ
How do I handle plurals in React Native?
i18next handles plurals with suffixed keys. Define key_one, key_other (and key_zero, key_few, key_many for languages that need them). Pass { count: n } when calling t(). i18next picks the correct form based on the count and the current language's plural rules.
How do I test locale changes during development?
On iOS Simulator, go to Settings > General > Language & Region. On Android Emulator, Settings > System > Language. For faster iteration, add a debug language picker screen that calls i18n.changeLanguage() directly without restarting.
How do I lazy-load translations?
Use i18next-http-backend or i18next-resources-to-backend to load translation files on demand. This is useful for apps with 20+ languages where bundling all translations increases app size. Load the active language at startup and fetch others when the user switches.
Does this work with Expo?
Yes. expo install react-native-localize adds the native module. Everything else (i18next, JSON files, useTranslation) works the same. Expo Go has limited locale support, so test on a dev build or standalone build for full locale detection.
We localized our React Native app into 8 languages in a single sprint using i18next and LocaleKit for the translation heavy lifting. The hardest part was not the code setup - it was agreeing on how to structure the JSON files. Start with namespaces by screen and you will save yourself a refactor later.
Stop managing translation files manually
LocaleKit detects, translates, and syncs all your localization files — iOS, Android, Flutter, and more. Everything runs locally on your machine.
Privacy-first. No cloud required.