| name | mobile-i18n |
|---|---|
| description | Add internationalization (i18n) to a React Native/Expo or Flutter app. Covers i18next, react-intl, flutter_localizations, locale detection, RTL layout, pluralization, date/number formatting, translation file structure, and CI string extraction. Use when the user wants to support multiple languages or localize the app for different markets. |
| standards-version | 1.6.3 |
Use this skill when the user:
- Wants to support multiple languages in their app
- Asks about i18n, localization, or translations
- Needs RTL (right-to-left) layout support for Arabic, Hebrew, or similar
- Mentions "internationalization", "localization", "translate", "multi-language", "locale", "RTL", or "pluralization"
- Wants to format dates, numbers, or currencies for different regions
- Framework: Expo (React Native) or Flutter
- i18n library: i18next (recommended for RN), react-intl, or flutter_localizations
- Default locale: e.g.
en - Target locales: list of additional languages to support
-
Choose an i18n approach. Options for each framework:
Library Framework Pluralization ICU Extraction tools i18next + react-i18next React Native Yes Plugin i18next-parser react-intl (FormatJS) React Native Yes (ICU) Native formatjs CLI flutter_localizations + intl Flutter Yes (ICU) Native gen-l10n i18next is the most popular for React Native with good TypeScript support. flutter_localizations is the official Flutter solution.
-
Set up i18next (Expo). Install dependencies:
npx expo install i18next react-i18next expo-localization
Create the i18n directory structure:
i18n/ en.json es.json index.tsInitialize in
i18n/index.ts:import i18n from "i18next"; import { initReactI18next } from "react-i18next"; import { getLocales } from "expo-localization"; import en from "./en.json"; import es from "./es.json"; const deviceLocale = getLocales()[0]?.languageCode ?? "en"; i18n.use(initReactI18next).init({ resources: { en: { translation: en }, es: { translation: es }, }, lng: deviceLocale, fallbackLng: "en", interpolation: { escapeValue: false }, }); export default i18n;
-
Structure translation files. Use nested keys for organization:
{ "common": { "ok": "OK", "cancel": "Cancel", "save": "Save" }, "auth": { "signIn": "Sign In", "signUp": "Sign Up", "forgotPassword": "Forgot Password?" }, "errors": { "network": "Check your internet connection", "generic": "Something went wrong" } } -
Use translations in components.
import { useTranslation } from "react-i18next"; function LoginScreen() { const { t } = useTranslation(); return ( <View> <Text>{t("auth.signIn")}</Text> <Button title={t("common.ok")} onPress={handleSubmit} /> </View> ); }
-
Handle pluralization. i18next supports count-based plurals:
{ "items": { "count_one": "{{count}} item", "count_other": "{{count}} items" } }t("items.count", { count: 5 }) // "5 items" t("items.count", { count: 1 }) // "1 item"
-
Format dates and numbers. Use the Intl API:
const formatDate = (date: Date, locale: string) => new Intl.DateTimeFormat(locale, { year: "numeric", month: "long", day: "numeric", }).format(date); const formatCurrency = (amount: number, locale: string, currency: string) => new Intl.NumberFormat(locale, { style: "currency", currency, }).format(amount);
-
Support RTL layout. React Native handles RTL automatically when the locale is RTL. Force it for testing:
import { I18nManager } from "react-native"; I18nManager.forceRTL(true);
Use
start/endinstead ofleft/rightin styles:const styles = StyleSheet.create({ container: { paddingStart: 16, marginEnd: 8, }, });
-
Set up Flutter localization. Create
l10n.yamlin the project root:arb-dir: lib/l10n template-arb-file: app_en.arb output-localization-file: app_localizations.dart
Create
lib/l10n/app_en.arb:{ "@@locale": "en", "ok": "OK", "@ok": { "description": "OK button label" }, "itemCount": "{count, plural, =1{1 item} other{{count} items}}", "@itemCount": { "placeholders": { "count": { "type": "int" } } } }Run code generation:
flutter gen-l10n
Use in widgets:
Text(AppLocalizations.of(context)!.ok) Text(AppLocalizations.of(context)!.itemCount(5))
-
Add a language switcher. Let users override the device language:
import i18n from "../i18n"; import AsyncStorage from "@react-native-async-storage/async-storage"; async function changeLanguage(locale: string) { await i18n.changeLanguage(locale); await AsyncStorage.setItem("user-locale", locale); }
- i18next: React Native
- expo-localization
- FormatJS (react-intl)
- Flutter: Internationalization
- ICU Message Syntax
User: "I want my app in English and Spanish with proper pluralization."
Agent:
- Runs
mobile_setupI18nwith default_locale=en, additional_locales=["es"] - Installs i18next, react-i18next, expo-localization with
mobile_installDependency - Creates
i18n/en.jsonandi18n/es.jsonwith matching key structure - Creates
i18n/index.tswith device locale detection and fallback - Imports
./i18nin the app entry point - Refactors one screen to use
const { t } = useTranslation() - Adds pluralization rules for count-based strings
- Suggests i18next-parser for extracting untranslated strings in CI
| Step | MCP Tool | Description |
|---|---|---|
| Initialize i18n | mobile_setupI18n |
Create i18n config, locale files, and init module |
| Install packages | mobile_installDependency |
Install i18next, react-i18next, expo-localization |
| Generate screen | mobile_generateScreen |
Create a language settings screen |
| Verify build | mobile_checkBuildHealth |
Ensure the app builds with i18n setup |
- Hardcoded strings - Every user-facing string must go through
t()orAppLocalizations. Use themobile-i18n-stringsrule to catch hardcoded text. - Missing pluralization - English has 2 plural forms but other languages (Arabic, Polish) have 6. Use ICU plural syntax, not string concatenation.
- RTL layout breaks - Using
left/rightinstead ofstart/endin styles breaks RTL layouts. Test withI18nManager.forceRTL(true). - String concatenation for sentences -
"Hello " + namebreaks in languages where word order differs. Use interpolation:t("greeting", { name }). - Date/number locale mismatch - Format dates and currencies with the user's locale, not the translation locale. A Spanish-speaking user in the US expects USD formatting.
- Missing translations at runtime - i18next shows the key path when a translation is missing. Set up CI checks to catch missing keys before release.
- Mobile Forms Validation - localized validation error messages
- Mobile App Store Prep - localized store listings and metadata
- Mobile Analytics - track locale distribution for prioritizing translations