Library lựa chọn:
- react-i18next: mạnh nhất, ecosystem React lớn. Hỗ trợ namespace, plural, interpolation, lazy load translation file.
- i18n-js (cũ, FormatJS-style): nhẹ hơn, ít feature, vẫn dùng nhiều legacy.
- expo-localization: detect locale từ device, dùng kèm react-i18next.
Setup react-i18next:
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
import * as Localization from 'expo-localization'
import en from './locales/en.json'
import vi from './locales/vi.json'
i18n.use(initReactI18next).init({
resources: { en: { translation: en }, vi: { translation: vi } },
lng: Localization.locale.split('-')[0],
fallbackLng: 'en',
interpolation: { escapeValue: false },
})Component:
import { useTranslation } from 'react-i18next'
function Welcome() {
const { t, i18n } = useTranslation()
return (
<>
<Text>{t('welcome.title', { name: 'Linh' })}</Text>
<Pressable onPress={() => i18n.changeLanguage('vi')}>
<Text>VI</Text>
</Pressable>
</>
)
}RTL (Arabic, Hebrew):
import { I18nManager } from 'react-native'
import * as Updates from 'expo-updates'
async function setRTL(rtl: boolean) {
if (I18nManager.isRTL !== rtl) {
I18nManager.allowRTL(rtl)
I18nManager.forceRTL(rtl)
await Updates.reloadAsync() // RN cần reload để apply RTL
}
}Khi I18nManager.isRTL === true:
- Mọi flexbox tự đảo (row → row-reverse).
- paddingLeft/paddingRight đổi thành paddingStart/paddingEnd để symmetric.
- Icon directional (back arrow) phải tự flip với transform: [{ scaleX: -1 }].
- Test cẩn thận text alignment, image asset (vd checkmark cũng có thể cần mirror).
Pitfall:
- Số/ngày format: dùng Intl.NumberFormat / Intl.DateTimeFormat thay self-format.
- App restart sau đổi RTL — báo user trước.
- iOS Right-to-Left language: nếu user device ngôn ngữ RTL nhưng app chỉ support LTR, set I18nManager.forceRTL(false) để override.