Mobile Application Multilingual Support (i18n)
Adding one language and adding support for N languages — different architectural tasks. When languages two — can manage with straightforward approach. When five or more — without properly built i18n infrastructure, each new language adds pain: missed strings, broken plurals, date formatting for wrong locale, and inability to give translation to team without code access.
i18n Architecture: From Simple to Proper
iOS
Standard — Localizable.strings + .stringsdict for plurals + XLIFF for translator exchange. String(localized:) API (Swift 5.5+) — preferred way, because supports interpolation, plural rules and translator comment in one call:
let message = String(localized: "notifications.count \(count)",
comment: "Number of unread notifications in tab bar")
For large projects with 1000+ strings and multiple teams — String Catalog (.xcstrings, Xcode 15+). Replaces scattered .strings files single JSON with built-in diff and untranslated string warnings directly in IDE.
Android
strings.xml in res/values/ (base) + res/values-{lang}/ for each language. plurals in same file. For parametric strings — named arguments:
<string name="welcome_user">Welcome, %1$s!</string>
%1$s instead of %s — mandatory with multiple arguments, because word order differs in languages.
React Native
react-i18next — de facto standard. Namespaces for module split (auth, profile, feed), lazy load namespaces to not load all translations at once. Plural forms through i18next-icu plugin with ICU Message Format — correctly handles Russian, Ukrainian, Polish plural rules without custom resolvers.
Flutter
flutter_localizations + intl package. ARB-files (Application Resource Bundle) — Flutter i18n standard. flutter gen-l10n generates type-safe Dart classes from ARB-files, excludes string key typos:
Text(AppLocalizations.of(context)!.notificationCount(count))
Instead string keys — methods with parameters. Plural forms described directly in ARB through ICU syntax.
Dynamic Language Switching
System locale — not always what user needs. Some apps provide language choice in settings. On iOS officially supported through CFBundleLocalizations in Info.plist + storing user choice. On Android — through AppCompatDelegate.setApplicationLocales() (AndroidX API, Android 13+) or manual Resources.updateConfiguration() replacement on old versions.
Runtime language change important: all formatters (dates, numbers, currencies) recreate with new locale. Cached DateFormatter with hardcoded locale remain on old language.
Plural Forms: Summary Table
| Language | Number of Forms | Complexity |
|---|---|---|
| English | 2 (one/other) | Low |
| Ukrainian | 3 (one/few/many) | Medium |
| Russian | 3 (one/few/many) | Medium |
| Polish | 4 (one/few/many/other) | High |
| Arabic | 6 forms | Very high |
| Chinese | 1 form | None |
ICU Message Format + i18next-icu or Flutter intl handle all variants automatically. Manual branching in code by form count — antipattern, breaks with each new language.
CI/CD and Translation Quality
Integrate checks into pipeline:
-
String lint:
twine(iOS),android-strings-lint— find missed translations, unused keys, unescaped special characters -
Screenshot tests:
fastlane snapshot(iOS) orscreengrab(Android) — auto make screenshots on all languages, UI regression visible immediately - Translation Management System: Phrase, Lokalise, Crowdin — sync strings between code and translator team through API, without manual XLIFF export/import
Timeframe: from three to ten working days depending on platform, number of languages and ready infrastructure presence.







