Dev.to · 3 min read

React Native i18n in 2026: A Practical Expo Router Playbook

React Native i18n in 2026: A Practical Expo Router Playbook

If your React Native app only speaks English, you're leaving ~75% of smartphone users on the table. Here's the modern stack for shipping a multilingual Expo app without three days of glue code. The Stack react-i18next: translation management (6M+ weekly downloads, TS-first) expo-localization: device locale detection (or react-native-localize for bare RN) Intl: dates, numbers, currencies (built into Hermes) I18nManager: RTL layout flipping Setup in 4 Steps 1. Install npx expo install expo-localization npm i i18next react-i18next 2. Translation files locales/ en.json es.json ar.json { "welcome": "Welcome", "cart": { "itemCount_one": "{{count}} item", "itemCount_other": "{{count}} items" } } Use the ICU plural suffixes (_one, _other, _few, _many, _zero). Arabic has 6 plural forms, so get this right on day one. 3. Bootstrap // i18n.ts import i18n from 'i18next'; import { initReactI18next } from 'react-i18next'; import * as Localization from 'expo-localization'; import en from './locales/en.json'; import es from './locales/es.json'; import ar from './locales/ar.json'; const deviceLocale = Localization.getLocales()[0]?.languageCode ?? 'en'; i18n.use(initReactI18next).init({ compatibilityJSON: 'v4', resources: { en: { translation: en }, es: { translation: es }, ar: { translation: ar }, }, lng: deviceLocale, fallbackLng: 'en', interpolation: { escapeValue: false }, }); export default i18n; Two production-important bits: compatibilityJSON: 'v4' for modern plural rules, and escapeValue: false because RN already escapes JSX. 4. Wire into Expo Router // app/_layout.tsx import { Stack } from 'expo-router'; import './i18n'; export default function RootLayout() { return ; } Then in any component: import { useTranslation } from 'react-i18next'; const { t } = useTranslation(); {t('cart.itemCount', { count: 3 })} // "3 items" The Gotchas Nobody Warns You About Never concatenate around t(). Word order changes across languages: // ❌ Breaks in Japanese, German, Arabic {t('hello')} {user.name}! // ✅ Whole sentence is one key {t('greeting', { name: user.name })} RTL doesn't flip everything. Flexbox auto-flips, but you'll need to fix: left/right → use start/end Directional icons → wrap in transform: [{ scaleX: -1 }] when I18nManager.isRTL Hardcoded textAlign Hardcoded strings in third-party UI kits. Buttons in libraries like RN Elements ship English defaults. Override or pick i18n-aware libs. Push notifications aren't localized by the phone: your backend has to send the localized copy or use OS-level notification categories. Formatting Never string-concat dates or currencies. Use Intl: new Intl.DateTimeFormat(i18n.language, { dateStyle: 'medium' }).format(new Date()); new Intl.NumberFormat(i18n.language, { style: 'currency', currency: 'USD' }).format(29.99); Add @formatjs/intl-datetimeformat polyfills if you need older Android or less common locales. Testing Discipline Turn on saveMissing in dev. react-i18next logs every missing key. Fail CI on any. Pseudo-localize with en-XA. This surfaces hardcoded strings and 30–40% longer text for layout stress-tests. Snapshot per locale. French/German/Arabic snapshots catch layout regressions. Shortcut If you're starting fresh, RapidNative generates the whole i18n scaffold (provider, layout wiring, translation files, RTL flags) from a natural-language prompt. You get the same setup you'd write by hand, in about 30 seconds. Otherwise: ship it. It's 2–3 days of setup and it opens up 3 billion more users.

This is a summary aggregated from Dev.to. Read the complete article on the original site:

Read full article at Dev.to

More Programming & Dev News