Использование локализаций и кодов локали в Kotlin Multiplatform 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.
Передавайте код локали точно в том виде, в котором он настроен в дашборде — строчные буквы, подтеги разделены дефисами. Не передавайте платформенный идентификатор локали напрямую: на Android Locale.getDefault().toLanguageTag() возвращает pt-BR, на iOS NSLocale.currentLocale.localeIdentifier возвращает pt_BR. В обоих случаях используется локализация по умолчанию. Преобразуйте значение в приложении перед передачей.
Онбординги
Онбординги локализуются на стороне сервера, и серверные правила допускают другие форматы. Когда вы передаёте locale в getOnboarding:
- Строка локали приводится к нижнему регистру, а все символы подчёркивания (
_) заменяются дефисами (-) - Adapty ищет локализацию с полностью совпадающим кодом локали
- Если совпадение не найдено, Adapty берёт подстроку до первого дефиса (
ptдляpt-br) и ищет соответствующую локализацию - Если совпадение снова не найдено, Adapty возвращает контент для локали онбординга по умолчанию
Таким образом, pt_BR, pt-BR и pt-br — все они указывают на одну и ту же локализацию онбординга.
Реализация локализаций
В SDK v4 не нужно передавать код локали при получении флоу — getFlow возвращает флоу со всеми его локализациями, а Adapty применяет нужную при создании представления флоу.
-
Флоу, созданные в конструкторе: SDK не читает локаль устройства, поэтому определите её в своём приложении и передайте как параметр
localeвcreateFlowView. Параметр необязателен — если его не указать, флоу отобразится наenили на локали по умолчанию, если у флоу нет локализацииen.import com.adapty.kmp.AdaptyUI AdaptyUI.createFlowView(flow = flow, locale = "es") .onSuccess { view -> view.present() } .onError { error -> // handle the error }
createNativeFlowView и составной элемент AdaptyUIFlowPlatformView принимают одинаковый необязательный параметр locale. view.locale возвращает локализацию, с которой было создано представление. Для параметра locale и view.locale требуется Kotlin Multiplatform SDK версии 4.0.1-beta.1 или выше.
- Кастомные пейволы (Remote Config):
getFlowвозвращает все настроенные локализации вflow.remoteConfigs. Каждая запись является объектомAdaptyRemoteConfigс кодомlocaleиdataMap. Выберите запись, соответствующую пользователю, реализовав собственный фолбэк:
Adapty.getFlow("YOUR_PLACEMENT_ID")
.onSuccess { flow ->
val config = flow.remoteConfigs.firstOrNull { it.locale == "en" }
?: flow.remoteConfigs.firstOrNull()
// read your values from config?.dataMap
}
.onError { error ->
// handle the error
}Adapty хранит коды 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. Add the Adapty locale code to your Compose Multiplatform resources
/*
composeResources/values/strings.xml (default — English)
*/
<string name="adapty_paywalls_locale">en</string>
/*
composeResources/values-es/strings.xml (Spanish)
*/
<string name="adapty_paywalls_locale">es</string>
/*
composeResources/values-pt-rBR/strings.xml (Portuguese — Brazil)
*/
<string name="adapty_paywalls_locale">pt-br</string>
// 2. Extract and use the locale code
suspend fun fetchPaywall() {
val locale = getString(Res.string.adapty_paywalls_locale)
Adapty.getPaywall(
placementId = "YOUR_PLACEMENT_ID",
locale = locale
).onSuccess { paywall ->
// запрошенный пейвол
}.onError { error ->
// обработка ошибки
}
}Таким образом вы полностью контролируете, какая локализация будет получена для каждого пользователя вашего приложения.
Если вы не используете ресурсы Compose Multiplatform, та же идея применима к любой другой библиотеке локализации (например, moko-resources) — сохраните код локали Adapty как строку в ресурсном бандле каждой локали и считайте его перед вызовом SDK.
Реализация локализаций: альтернативный подход
Можно получить похожий (но не идентичный) результат, не указывая явно коды локалей для каждой локализации. Для этого нужно извлекать код локали напрямую с устройства — но это потребует объявлений expect/actual, поскольку в commonMain нет общего API для работы с локалями:
// commonMain
expect fun currentLocaleTag(): String
// androidMain
actual fun currentLocaleTag(): String = Locale.getDefault().toLanguageTag()
// iosMain
actual fun currentLocaleTag(): String = NSLocale.currentLocale.localeIdentifier
// commonMain — pass the locale code to Adapty
suspend fun fetchPaywall() {
Adapty.getPaywall(
placementId = "YOUR_PLACEMENT_ID",
locale = currentLocaleTag()
).onSuccess { paywall ->
// the requested paywall
}.onError { error ->
// handle the error
}
}Обратите внимание, что мы не рекомендуем этот подход по ряду причин:
- На iOS предпочитаемый язык пользователя и региональная локаль устройства — не одно и то же.
NSLocale.currentLocale.localeIdentifierвозвращает региональную локаль, которая может отличаться от языка, на котором пользователь читает ваше приложение. iOS-приложения, использующие локализованные строковые файлы, опираются на логику разрешения Apple, которая объединяет оба параметра — это работает автоматически при использовании рекомендованного подхода выше. - Сложно предсказать, что именно вернёт устройство и совпадёт ли это с локализацией в Adapty. Локаль устройства может содержать расширения или региональные коды, которые вы не настроили в Adapty — в этом случае SDK откатывается до совпадения по первому подтегу или, в крайнем случае, до
en. Should you decide to use this approach anyway — make sure you’ve covered all the relevant use cases.