- Published on
How to Localize an Expo App in Arabic with i18n-js & NativeWind (2025 Guide)
- Authors
- Name
- Ahmed Farid
- @
TIP
Localise early—international-ready architecture prevents refactors when you add more languages later.
This guide walks you through transforming a plain Expo app into a fully-localised, Arabic-friendly experience. We’ll cover:
- Installing and configuring i18n-js.
- Managing translation JSON files with pluralisation.
- Detecting locale & forcing RTL with I18nManager.
- Styling Arabic text with NativeWind utilities.
- Formatting dates, numbers, and currencies.
- Dynamic language switching at runtime.
- Automated testing and accessibility.
Table of Contents
- Table of Contents
- 1. Prerequisites & Terminology
- 2. Project Setup & Dependencies
- 3. Organise Translation Files
- 4. Configure i18n-js & Locale Detection
- 5. Force RTL for Arabic & Persist Choice
- 6. Tailwind RTL Utilities with NativeWind
- 7. Load Arabic Fonts
- 8. Format Dates & Numbers
- 9. Dynamic Language Switcher
- 10. E2E & Unit Testing
- 11. Accessibility & UX Tips
- 12. Performance Considerations
- 13. Further Reading & Resources
- 14. Conclusion
1. Prerequisites & Terminology
- Expo SDK 50 (managed workflow).
- Node.js 18+.
- Basic Tailwind / NativeWind knowledge.
Term | Meaning |
---|---|
RTL | Right-to-left scripts (Arabic, Hebrew…) |
i18n-js | Lightweight internationalisation library for JS |
I18nManager | React Native API to control layout direction |
2. Project Setup & Dependencies
npx create-expo-app ArabicLocaleApp
cd ArabicLocaleApp
yarn add i18n-js expo-localization nativewind tailwindcss-rtl
npx nativewind init
3. Organise Translation Files
Create src/locales/en.json
& src/locales/ar.json
:
// en.json
{
"welcome": "Welcome, %{name}!",
"items": {
"one": "%{count} item",
"other": "%{count} items",
},
}
// ar.json
{
"welcome": "مرحبا، %{name}!",
"items": {
"one": "عنصر واحد",
"other": "%{count} عناصر",
},
}
4. Configure i18n-js & Locale Detection
// src/i18n/config.ts
import * as Localization from 'expo-localization'
import I18n from 'i18n-js'
import en from './locales/en.json'
import ar from './locales/ar.json'
I18n.fallbacks = true
I18n.translations = { en, ar }
I18n.locale = Localization.locale // e.g. ar-EG
export default I18n
Use in components:
import I18n from '@/i18n/config'
;<Text>{I18n.t('welcome', { name: 'Ali' })}</Text>
5. Force RTL for Arabic & Persist Choice
// src/i18n/rtl.ts
import { I18nManager } from 'react-native'
import * as Updates from 'expo-updates'
export async function applyDirection(locale: string) {
const isRTL = locale.startsWith('ar')
if (I18nManager.isRTL !== isRTL) {
I18nManager.allowRTL(true)
I18nManager.forceRTL(isRTL)
await Updates.reloadAsync() // full reload required
}
}
Call applyDirection(I18n.locale)
once at app start or when language changes.
6. Tailwind RTL Utilities with NativeWind
Update tailwind.config.js
:
module.exports = {
content: ['./App.{js,jsx,ts,tsx}', './src/**/*.{js,jsx,ts,tsx}'],
plugins: [require('tailwindcss-rtl')],
theme: {
extend: {
fontFamily: {
arabic: ['Tajawal_400Regular', 'System'],
},
},
},
}
Use logical classes:
<View className="space-s-4 flex-row-reverse">
<Text className="font-arabic text-right">{I18n.t('welcome', { name })}</Text>
</View>
space-s-4
adds spacing on the start side, automatically flipping between LTR/RTL.
7. Load Arabic Fonts
yarn add @expo-google-fonts/tajawal expo-font
import { useFonts, Tajawal_400Regular } from '@expo-google-fonts/tajawal'
8. Format Dates & Numbers
import dayjs from 'dayjs'
import 'dayjs/locale/ar'
const formatted = dayjs().locale(I18n.locale).format('DD MMMM YYYY')
const price = new Intl.NumberFormat('ar-EG', { style: 'currency', currency: 'SAR' }).format(1234.5)
9. Dynamic Language Switcher
const changeLang = async (lang: 'en' | 'ar') => {
I18n.locale = lang
await applyDirection(lang)
setLang(lang) // state for re-render
}
Persist choice via SecureStore or AsyncStorage.
10. E2E & Unit Testing
- Jest: mock
expo-localization
to each locale. - Detox: run with
I18nManager.forceRTL(true)
to screenshot RTL. - Snapshot tests ensure no hard-coded English.
11. Accessibility & UX Tips
✅ Mirror navigation icons (back arrow) when RTL.
✅ Keep numbers Arabic-Indic if audience expects (ar-EG
vs ar-SA
).
✅ Avoid mixed alignment—whole paragraph should be text-right
.
12. Performance Considerations
- Lazy-load translation JSON per locale to reduce bundle size.
- Memoise expensive
Intl
formatting.
13. Further Reading & Resources
- i18n-js docs.
- Expo Localisation API.
- Tailwind RTL plugin.
- W3C bidi guidelines.
14. Conclusion
You now have a robust Arabic localisation setup: translations, RTL layouts, fonts, formatting, and runtime switching—all without ejecting from Expo. 🌍📱