Использование локализаций и кодов локали во Flutter SDK
Почему это важно
Коды локалей используются, когда Adapty подбирает локализацию для флоу или онбординга, а также когда вы читаете Remote Config для кастомного пейвола.
Коды локалей — сложная тема, и они могут отличаться в зависимости от платформы, поэтому Adapty опирается на единый внутренний стандарт для всех поддерживаемых платформ. Понимание этого стандарта поможет вам предсказать, какую локализацию получит пользователь.
Стандарт кодов локали в Adapty
Для кодов локали Adapty использует слегка модифицированный стандарт BCP 47: каждый код состоит из подтегов в нижнем регистре, разделённых дефисами. Примеры: en (английский), pt-br (португальский (Бразилия)), zh (упрощённый китайский), zh-hant (традиционный китайский).
Сопоставление кодов локали
В SDK v4 флоу и онбординги сопоставляют коды локали по-разному: флоу локализуются SDK на устройстве, онбординги — на сервере Adapty.
Флоу и пейволы Paywall Builder
Пейвол, созданный в Paywall Builder, в SDK v4 доставляется как флоу, поэтому правило ниже распространяется на оба случая.
Совпадение точное. SDK сравнивает переданный вами код с кодами локализации флоу посимвольно: не меняет регистр, не заменяет подчёркивания (_) на дефисы (-) и не откатывается к языковому тегу. Для флоу с локализацией pt-br подходит только pt-br: pt-BR, pt_BR и pt-PT — не совпадают.
Когда код не совпадает ни с одной локализацией, флоу автоматически отображается на локале по умолчанию — SDK не возвращает ошибку и не выводит предупреждение.
Когда код совпадает, Adapty объединяет найденную локализацию с локализацией по умолчанию: строки и ресурсы, которые не определены в найденной локализации, берутся из локализации по умолчанию.
Отсутствие кода локали — это не то же самое, что запрос локализации флоу по умолчанию: SDK подставляет фиксированный en. Флоу, у которого в качестве локали по умолчанию задан de, всё равно будет отображаться на en, если у него есть локализация en, и переключится на de только в том случае, если локализации en нет.
Передавайте код локали ровно в том виде, в каком он задан в дашборде — строчные буквы, подтеги разделены дефисами. Не передавайте системный идентификатор локали как есть: Platform.localeName возвращает pt_BR, а PlatformDispatcher.instance.locale.toLanguageTag() — pt-BR, и оба варианта откатываются к локализации по умолчанию. Перед передачей преобразуйте значение в своём приложении.
Онбординги
Онбординги локализуются на сервере, и серверные правила допускают другие форматы. Когда вы передаёте locale в getOnboarding:
- Строка локали приводится к нижнему регистру, а все символы подчёркивания (
_) заменяются дефисами (-) - Adapty ищет локализацию с точным совпадением кода локали
- Если совпадение не найдено, Adapty берёт подстроку до первого дефиса (
ptдляpt-br) и ищет соответствующую локализацию - Если совпадение снова не найдено, Adapty возвращает контент для локали онбординга по умолчанию
Таким образом, pt_BR, pt-BR и pt-br — все они указывают на одну и ту же локализацию онбординга.
Реализация локализаций
В SDK v4 не нужно передавать код локали при получении флоу — getFlow возвращает флоу со всеми его локализациями, а Adapty применяет нужную при построении представления флоу. Аргумент locale в getFlow и getFlowForDefaultAudience не влияет на флоу; он устарел и выводит предупреждение в логах.
- Флоу, созданные в билдере: SDK не считывает локаль устройства, поэтому определите её в приложении и передайте как аргумент
localeвcreateFlowViewилиAdaptyUIFlowPlatformView. Аргумент необязательный — если его не указать, флоу отобразится наenили на своей локали по умолчанию, если локализацииenв флоу нет.
AdaptyUIFlowView.locale сообщает локализацию, с которой было построено представление. Для этого требуется Flutter SDK 4.0.3 с нативными релизами iOS 4.0.2 и Android 4.0.1; со старыми нативными SDK возвращает null.
- Пользовательские (Remote Config) пейволы:
getFlowвозвращает все настроенные локализации вflow.remoteConfigs. Каждая запись содержит кодlocaleи содержимое конфига (строкаdataили разобранныйdictionary). Выберите запись, соответствующую пользователю, реализовав собственный фолбэк:
final flow = await Adapty().getFlow(placementId: 'YOUR_PLACEMENT_ID');
final config = flow.remoteConfigs.firstWhereOrNull((c) => c.locale == 'en') ??
flow.remoteConfig; // the first remote config, if present
// read your values from config?.dictionaryAdapty хранит коды locale в формате, описанном в разделе Стандарт кодов локалей в Adapty. SDK не сопоставляет Remote Config с локалью автоматически — то, какую запись применять, решает само приложение.
Почему это важно
Есть несколько сценариев, в которых коды локалей играют ключевую роль — например, когда нужно получить правильный пейвол для текущей локализации вашего приложения.
Поскольку коды локалей бывают сложными и могут различаться в зависимости от платформы, мы используем внутренний стандарт для всех поддерживаемых платформ. Тем не менее именно из-за этой сложности важно чётко понимать, что именно вы отправляете на наш сервер для получения нужной локализации и что происходит дальше — чтобы вы всегда получали именно то, что ожидаете.
Стандарт кодов локалей в Adapty
Для кодов локалей Adapty использует немного изменённый стандарт BCP 47: каждый код состоит из подтегов в нижнем регистре, разделённых дефисами. Примеры: en (английский), pt-br (португальский (Бразилия)), zh (упрощённый китайский), zh-hant (традиционный китайский).
Сопоставление кода локали
Когда Adapty получает вызов от клиентского SDK с кодом локали и начинает поиск соответствующей локализации пейвола, происходит следующее:
- Входящая строка локали приводится к нижнему регистру, а все символы подчёркивания (
_) заменяются дефисами (-) - Затем выполняется поиск локализации с полностью совпадающим кодом локали
- Если совпадение не найдено, берётся подстрока до первого дефиса (
ptдляpt-br) и выполняется поиск соответствующей локализации - Если совпадение снова не найдено, возвращается контент для локали пейвола по умолчанию
Таким образом, устройство iOS, отправившее 'pt_BR', устройство Android, отправившее pt-BR, и другое устройство, отправившее pt-br, получат одинаковый результат.
Реализация локализаций: рекомендуемый подход
Если вы задаётесь вопросом о локализациях, скорее всего, вы уже работаете с файлами локализованных строк в своём проекте. В таком случае мы рекомендуем добавить в каждый файл пару ключ-значение с нужным кодом локали Adapty для соответствующей локализации. Затем при вызове SDK извлекайте значение по этому ключу, как показано ниже:
// 1. Modify your app_en.arb, app_es.arb, app_pt_br.arb files
/*
app_en.arb
*/
"adapty_paywalls_locale": "en",
/*
app_es.arb
*/
"adapty_paywalls_locale": "es",
/*
app_pt_br.arb
*/
"adapty_paywalls_locale": "pt-br",
// 2. Extract and use the locale code
final locale = AppLocalizations.of(context)!.adapty_paywalls_locale;
// pass locale code to AdaptyUI.getViewConfiguration or Adapty.getPaywall methodТаким образом вы полностью контролируете, какая локализация будет загружена для каждого пользователя вашего приложения.
Реализация локализаций: альтернативный способ
Похожего (но не идентичного) результата можно добиться без явного указания кодов локали для каждой локализации. Это означает извлечение кода локали из других объектов, предоставляемых вашей платформой, например:
final locale = Localizations.localeOf(context).languageCode;
// pass locale code to AdaptyUI.getViewConfiguration or Adapty.getPaywall methodОбратите внимание, что мы не рекомендуем этот подход по нескольким причинам:
- На iOS предпочтительные языки и текущая локаль — не одно и то же. Чтобы локализация подбиралась корректно, придётся либо опираться на логику Apple (которая работает из коробки при рекомендованном подходе с локализованными строковыми файлами), либо реализовывать её самостоятельно.
- Сложно предсказать, что именно получит сервер Adapty. Например, на iOS устройство может вернуть локаль вида
ar_OM@numbers='latn', которая будет отправлена на сервер. В ответ вы получите не локализациюar-om, которую ожидали, аar— что, скорее всего, не то, что нужно. Should you decide to use this approach anyway — make sure you’ve covered all the relevant use cases.