Реалізація динамічного перемикання мови в мобільному додатку

Перемикання мови без перезапуску додатку — задача, яка виглядає тривіально, поки не натрапиш на те, що половина рядків змінилася, а Fragment з ViewPager2 залишився старою мовою, тому що пережив зміну конфігурації через `setRetainInstance(true)`. Або що DateTimeFormatter закешував Locale при першому

Розробка та підтримка будь-яких видів мобільних додатків:

Інформаційні та розважальні мобільні програми
Новинки, ігри, довідники, онлайн-каталоги, погодні, фітнес та здоров'я, туристичні, освітні, соціальні мережі та месенджери, квіз, блоги та подкасти, форуми, агрегатори
Мобільні програми електронної комерції
Інтернет-магазини, B2B-додатки, маркетплейси, онлайн-обмінники, кешбек-сервіси, біржі, дропшиппінг-платформи, програми лояльності, доставка їжі та товарів, платіжні системи
Мобільні програми для управління бізнес-процесами
CRM-системи, ERP-системи, управління проектами, інструменти для команди продажів, облік фінансів, управління виробництвом, логістика та доставка, управління персоналом, системи моніторингу даних
Мобільні програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, платформи надання електронних послуг, платформи кешбеку, відеохостинги, тематичні портали, платформи онлайн-бронювання та запису, платформи онлайн-торгівлі

Це лише деякі з типів мобільних додатків, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Послуги, які ми пропонуємо
Показано 1 з 1Усі 1734 послуг
Реалізація динамічного перемикання мови в мобільному додатку
Середній
від 1 дня до 3 днів

Наші компетенції:

Часті запитання

Останні роботи

  • image_mobile-applications_feedme_467_0.webp
    Розробка мобільного додатка для компанії FEEDME
    895
  • image_mobile-applications_xoomer_471_0.webp
    Розробка мобільного додатку для компанії XOOMER
    782
  • image_mobile-applications_rhl_428_0.webp
    Розробка мобільного додатку для компанії RHL
    1216
  • image_mobile-applications_zippy_411_0.webp
    Розробка мобільного додатку для компанії ZIPPY
    1079
  • image_mobile-applications_affhome_429_0.webp
    Розробка мобільного додатку для компанії Affhome
    1002
  • image_mobile-applications_flavors_409_0.webp
    Розробка мобільного додатку для компанії FLAVORS
    597

Перемикання мови без перезапуску додатку — задача, яка виглядає тривіально, поки не натрапиш на те, що половина рядків змінилася, а Fragment з ViewPager2 залишився старою мовою, тому що пережив зміну конфігурації через setRetainInstance(true). Або що DateTimeFormatter закешував Locale при першому виклику і тепер форматує дати російською, хоча користувач вибрав англійську три екрани тому. Ми, як команда з досвідом понад 5 років у мобільній розробці, стикаємося з цими проблемами регулярно. Наші інженери сертифіковані Apple та Google, і ми гарантуємо, що ваш користувач побачить актуальну мову одразу після вибору, без перезапуску додатку. Замовте аудит поточної архітектури, щоб виявити приховані проблеми локалізації.

Чому динамічне перемикання мови — складна задача?

Android

Стандартний спосіб — AppCompatDelegate.setApplicationLocales(LocaleListCompat) з AndroidX (API 33+ нативно, AndroidX-бекпорт працює до API 21). До AndroidX доводилося вручну перестворювати Configuration і перезапускати Activity.

Проблема з setApplicationLocales: він зберігає вибір користувача в системних налаштуваннях додатку. Це добре для інтеграції з системними Language Settings, але ламає сценарій, коли locale керується з бекенду (мультитенантні додатки, де мова задається профілем). Тоді потрібен власний ContextWrapper з перевантаженням attachBaseContext, який обгортає Context з потрібною Locale до того, як Activity почне інфлейтити layout.

ViewModel не перестворюється при зміні locale через setApplicationLocales — вона переживає зміну конфігурації. Рядки всередині ViewModel (якщо вони туди потрапили — помилка архітектури) залишаться старою мовою. Рядки в LiveData<String> або StateFlow<String> потрібно або зберігати як @StringRes Int і форматувати у View, або перевипускати після зміни locale.

RecyclerView.Adapter із закешованими рядками потрібно явно смикнути через notifyDataSetChanged() або краще через DiffUtil, інакше вже відмальовані елементи не перемалюються.

iOS

Bundle.main.localizedString(forKey:value:table:) читає рядки із завантаженого бандла — тобто з бандла тієї локалі, що була активною при запуску. UserDefaults.standard.set(["ru"], forKey: "AppleLanguages") змінює мову тільки після наступного запуску. Це обмеження iOS до версії 13.

Для iOS 13+ правильний шлях — Bundle swizzling: створюємо кастомний Bundle, який перевизначає localizedString(forKey:) і читає з бандла потрібної локалі. Типова реалізація — через розширення Bundle зі зберіганням поточної languageBundle у статичній змінній:

private var bundleKey: UInt8 = 0 class LanguageBundle: Bundle { override func localizedString(forKey key: String, value: String?, table tableName: String?) -> String { guard let bundle = objc_getAssociatedObject(self, &bundleKey) as? Bundle else { return super.localizedString(forKey: key, value: value, table: tableName) } return bundle.localizedString(forKey: key, value: value, table: tableName) } } extension Bundle { static func setLanguage(_ language: String) { object_setClass(Bundle.main, LanguageBundle.self) let path = Bundle.main.path(forResource: language, ofType: "lproj") let bundle = path.flatMap { Bundle(path: $0) } ?? Bundle.main objc_setAssociatedObject(Bundle.main, &bundleKey, bundle, .OBJC_ASSOCIATION_RETAIN_NONATOMIC) NotificationCenter.default.post(name: .languageDidChange, object: nil) } } 

Підхід описаний у документації Apple по Bundle

SwiftUI спрощує: environment(\.locale, Locale(identifier: "ar")) на кореневому View — і всі дочірні в'ю перемалюються з новою locale. Рядки через LocalizedStringKey підхоплюються автоматично. Але UIViewController-based екрани в SwiftUI-обгортці через UIViewControllerRepresentable вимагають явного тригера — NotificationCenter або @Published флаг перебудови.

Flutter

MaterialApp(locale: _currentLocale) + setState — стандартний підхід. Пакет flutter_localizations + intl для форматування. Зміна locale через Provider або Riverpod (Notifier з Locale-стейтом) — UI перебудовується реактивно.

Кейс із практики: додаток із cached_network_image — при зміні мови кеш зображень з alt-текстами скидався (бо ключ кешу включав locale-залежний URL). Рішення — locale-agnostic ключі кешу.

Як уникнути помилок при зміні мови?

Типові помилки та їх запобігання:

Помилка Рішення
Кешування форматувальників дат/чисел Створювати новий екземпляр при кожній зміні мови
Рядки в ViewModel Використовувати @StringRes Int або локалізувати на рівні View
Неоновлення відмальованих UI-елементів Викликати notifyDataSetChanged() або використовувати реактивний підхід
Ігнорування системних налаштувань Відключити автосинхронізацію, якщо мова керується додатком
Приклад для Flutter з Riverpod
final localeProvider = StateNotifierProvider<LocaleNotifier, Locale>((ref) => LocaleNotifier()); class LocaleNotifier extends StateNotifier<Locale> { LocaleNotifier() : super(Locale('en')); void setLocale(Locale locale) { state = locale; } } 

Як ми реалізуємо перемикання мови?

Ми пропонуємо комплексне рішення, адаптоване під ваш стек. Процес включає:

  1. Аудит поточної архітектури — перевірка зберігання рядків, форматувальників, кешування
  2. Вибір механізму зберігання — SharedPreferences/UserDefaults для клієнтського вибору або серверний профіль для мультитенантних додатків
  3. Реалізація locale-провайдера з реактивним стейтом (Riverpod, Room + Flow, Combine)
  4. Оновлення всіх місць форматування дат, чисел, валют (NumberFormat, DateFormat — не кешувати з locale)
  5. Тестування на ключових екранах — deep link, push-notification landing pages, таби, діалоги
Платформа Підхід Час впровадження (типовий проєкт) Складність
iOS Bundle swizzling або SwiftUI environment 1-2 дні Середня
Android AppCompatDelegate.setApplicationLocales або ContextWrapper 1-2 дні Середня
Flutter MaterialApp locale + Provider 0.5-1 день Низька
React Native i18n library + native module (за потреби) 1-2 дні Середня

Що входить у роботу

  • Детальний звіт за результатами аудиту
  • Реалізація механізму перемикання мови
  • Налаштування рядкових ресурсів і форматувальників
  • Інтеграція з існуючими сервісами (бекенд, аналітика)
  • Тестування на всіх сценаріях (включаючи офлайн)
  • Надання доступу до репозиторію з кодом
  • Навчання команди (1 годинна сесія)
  • Гарантія на впровадження 30 днів

Типові помилки та як їх уникнути

  • Кешування форматувальників — завжди створюйте новий екземпляр при зміні мови
  • Рядки в ViewModel — використовуйте @StringRes або локалізуйте на рівні View
  • Неоновлення вже відмальованих елементів — викликайте notifyDataSetChanged() або використовуйте реактивний підхід
  • Ігнорування системних налаштувань — якщо додаток керує мовою, відключіть автосинхронізацію з системою

Терміни та вартість

Терміни варіюються від 1 до 5 робочих днів залежно від складності архітектури та кількості екранів. Вартість розраховується індивідуально після аналізу кодової бази. Зв'яжіться з нами, щоб отримати консультацію та оцінку вашого проєкту. Ми проаналізуємо поточну реалізацію та запропонуємо оптимальне рішення під ключ. Отримайте консультацію з локалізації вже сьогодні.