Реализация мультиязычности (i18n) мобильного приложения
Добавить один язык и добавить поддержку N языков — разные архитектурные задачи. Когда языков два — можно сойти с рук с прямолинейным подходом. Когда их пять и больше — без правильно выстроенной i18n инфраструктуры каждый новый язык добавляет боль: пропущенные строки, сломанные plurals, форматирование дат под неправильную локаль, и невозможность отдать перевод команде без доступа к коду.
Архитектура i18n: от простого к правильному
iOS
Стандарт — Localizable.strings + .stringsdict для plurals + XLIFF для обмена с переводчиками. String(localized:) API (Swift 5.5+) — предпочтительный способ, потому что поддерживает interpolation, plural rules и comment для переводчика в одном вызове:
let message = String(localized: "notifications.count \(count)",
comment: "Number of unread notifications in tab bar")
Для больших проектов с 1000+ строками и несколькими командами — String Catalog (.xcstrings, Xcode 15+). Он заменяет разрозненные .strings файлы единым JSON-файлом с встроенным diff и предупреждениями о непереведённых строках прямо в IDE.
Android
strings.xml в res/values/ (базовый) + res/values-{lang}/ для каждого языка. plurals в том же файле. Для параметрических строк — именованные аргументы:
<string name="welcome_user">Добро пожаловать, %1$s!</string>
%1$s вместо %s — обязательно при множественных аргументах, потому что порядок слов в разных языках разный.
React Native
react-i18next — де-факто стандарт. Namespace-ы для разбивки по модулям (auth, profile, feed), lazy loading namespace-ов чтобы не грузить все переводы сразу. Plural forms через i18next-icu плагин с ICU Message Format — он корректно обрабатывает русские, украинские, польские plural rules без написания кастомных resolver-ов.
Flutter
flutter_localizations + intl пакет. ARB-файлы (Application Resource Bundle) — стандарт для Flutter i18n. flutter gen-l10n генерирует type-safe Dart-классы из ARB-файлов, что исключает опечатки в ключах строк:
Text(AppLocalizations.of(context)!.notificationCount(count))
Вместо строковых ключей — методы с параметрами. Plural forms описываются прямо в ARB через ICU syntax.
Динамическая смена языка
Системная локаль — не всегда то, что нужно пользователю. Часть приложений предоставляет выбор языка внутри настроек. На iOS это официально поддерживается через CFBundleLocalizations в Info.plist + хранение выбора пользователя. На Android — через AppCompatDelegate.setApplicationLocales() (AndroidX API, Android 13+) или ручную замену Resources.updateConfiguration() на старых версиях.
При смене языка в рантайме важно: все форматтеры (дат, чисел, валют) пересоздать с новой locale. Закэшированные DateFormatter с hardcoded locale останутся на старом языке.
Plural forms: сводная таблица
| Язык | Количество форм | Сложность |
|---|---|---|
| Английский | 2 (one/other) | Низкая |
| Украинский | 3 (one/few/many) | Средняя |
| Русский | 3 (one/few/many) | Средняя |
| Польский | 4 (one/few/many/other) | Высокая |
| Арабский | 6 форм | Очень высокая |
| Китайский | 1 форма | Нет |
ICU Message Format + i18next-icu или Flutter intl обрабатывают все эти варианты автоматически. Ручное ветвление в коде по количеству — антипаттерн, который ломается при добавлении каждого нового языка.
CI/CD и качество переводов
Интегрируем проверки в пайплайн:
-
Lint для строк:
twine(iOS),android-strings-lint— находят пропущенные переводы, неиспользуемые ключи, неэкранированные спецсимволы -
Screenshot тесты:
fastlane snapshot(iOS) илиscreengrab(Android) — автоматически делают скриншоты на всех языках, регрессию UI заметно сразу - Translation Management System: Phrase, Lokalise, Crowdin — синхронизируют строки между кодом и командой переводчиков через API, без ручного экспорта/импорта XLIFF
Сроки: от трёх до десяти рабочих дней в зависимости от платформы, количества языков и наличия готовой инфраструктуры.







