Использование локализаций и кодов локали в Capacitor SDK

Почему это важно

Коды локали используются, когда Adapty выбирает локализацию для флоу или онбординга, а также при чтении Remote Config для кастомного пейвола.

Коды локали устроены непросто и могут отличаться от платформы к платформе, поэтому Adapty использует единый внутренний стандарт для всех поддерживаемых платформ. Понимание этого стандарта помогает предсказать, какую локализацию получит пользователь.

Стандарт кодов локализации в Adapty

В кодах локализации Adapty использует слегка изменённый стандарт BCP 47: каждый код состоит из подтегов в нижнем регистре, разделённых дефисами. Например: en (английский), pt-br (португальский (Бразилия)), zh (упрощённый китайский), zh-hant (традиционный китайский).

Соответствие кодов локали

В SDK v4 флоу и онбординги сопоставляют коды локали по-разному: флоу локализуются SDK на устройстве, онбординги — сервером Adapty.

Флоу и пейволы

Пейвол, который Adapty рендерит, доставляется как флоу в SDK v4, поэтому приведённое ниже правило распространяется на оба случая.

Совпадение точное. SDK сравнивает переданный вами код с кодами локализации флоу посимвольно: не изменяет регистр, не заменяет подчёркивания (_) дефисами (-) и не откатывается к языковому subtag. Для флоу с локализацией pt-br подойдёт только pt-br: pt-BR, pt_BR и pt-PT — всё это не совпадёт.

Если код не совпадает ни с одной из локализаций, флоу молча отображается в локали по умолчанию — SDK не возвращает ошибку и не выводит предупреждение.

Если код совпадает, Adapty объединяет найденную локализацию с локалью по умолчанию: строки и ресурсы, которые не определены в найденной локализации, берутся из локали по умолчанию.

Пропуск кода локали не равнозначен запросу локализации флоу по умолчанию: SDK подставляет фиксированный код en. Флоу, у которого локаль по умолчанию — de, всё равно отобразится на en, если у него есть локализация en, и только при её отсутствии откатится к de.

Warning

Передавайте код локали точно так, как он настроен в дашборде: строчные подтеги, разделённые дефисами. Не передавайте системный идентификатор локали как есть: navigator.language возвращает pt-BR, и это приведёт к откату на локализацию по умолчанию. Преобразуйте значение в своём приложении перед передачей.

Онбординги

Онбординги локализуются на сервере, и серверная логика допускает различные форматы. Когда вы передаёте locale в getOnboarding:

  1. Строка локали приводится к нижнему регистру, все символы подчёркивания (_) заменяются дефисами (-)
  2. Adapty ищет локализацию с точно совпадающим кодом локали
  3. Если совпадение не найдено, Adapty берёт подстроку до первого дефиса (pt для pt-br) и ищет соответствующую локализацию
  4. Если совпадение снова не найдено, Adapty возвращает контент для локали онбординга по умолчанию

This way pt_BR, pt-BR, and pt-br all resolve to the same onboarding localization.

Реализация локализаций

В SDK v4 при получении флоу не нужно передавать код локали — getFlow возвращает флоу со всеми его локализациями, а Adapty применяет одну из них при построении представления флоу.

  • Флоу, собранные в билдере: SDK не считывает локаль устройства, поэтому определите её в своём приложении и передайте через опцию locale в createFlowView. Это необязательно — если не передать, флоу отобразится на en или в локали по умолчанию, если флоу не имеет локализации en.

    import { createFlowView } from '@adapty/capacitor';
    
    const view = await createFlowView(flow, { locale: 'es' });

view.locale сообщает локализацию, с которой было построено представление. Оба параметра — locale и view.locale — требуют Capacitor SDK 4.1 или выше; на более ранних версиях view.locale равен undefined. Обработчик onAppeared возвращает то же значение.

  • Кастомные пейволы (Remote Config): getFlow возвращает все настроенные локализации в flow.remoteConfigs. Каждая запись содержит код lang и объект data. Выберите запись, подходящую пользователю, реализовав собственный фолбэк:

const flow = await adapty.getFlow({ placementId: 'placement_id' });
const config = flow.remoteConfigs?.find((c) => c.lang === 'en') ?? flow.remoteConfigs?.[0];
// read your values from config?.data

Adapty хранит коды lang в формате, описанном в разделе Стандарт кодов локалей в Adapty. SDK не сопоставляет Remote Config с локалью автоматически, поэтому выбор нужной записи остаётся на усмотрение вашего приложения.

Почему это важно

Коды локалей используются в нескольких сценариях — например, когда нужно получить правильный пейвол для текущей локализации приложения.

Поскольку коды локалей сложны и могут отличаться от платформы к платформе, мы опираемся на внутренний стандарт для всех поддерживаемых платформ. Именно поэтому важно понимать, что именно вы отправляете на наш сервер для получения правильной локализации и что происходит дальше — чтобы вы всегда получали ожидаемый результат.

Стандарт кодов локалей в Adapty

Для кодов локалей Adapty использует слегка модифицированный стандарт BCP 47: каждый код состоит из подтегов в нижнем регистре, разделённых дефисами. Несколько примеров: en (английский), pt-br (португальский (Бразилия)), zh (китайский упрощённый), zh-hant (китайский традиционный).

Сопоставление кода локали

Когда Adapty получает вызов от клиентского SDK с кодом локали и начинает поиск соответствующей локализации пейвола, происходит следующее:

  1. Входящая строка локали приводится к нижнему регистру, а все символы подчёркивания (_) заменяются дефисами (-)
  2. Затем выполняется поиск локализации с полностью совпадающим кодом локали
  3. Если совпадение не найдено, берётся подстрока до первого дефиса (pt для pt-br) и выполняется поиск совпадающей локализации
  4. Если совпадение снова не найдено, возвращается контент для локали пейвола по умолчанию

Таким образом, устройства iOS, отправившее 'pt_BR', Android-устройство, отправившее pt-BR, и другое устройство, отправившее pt-br, получат один и тот же результат.

Если вы думаете о локализациях, скорее всего, вы уже работаете с локализованными строковыми файлами в вашем проекте. В таком случае мы рекомендуем добавить пару ключ-значение с нужным кодом локали Adapty в каждый из ваших файлов для соответствующих локализаций. Затем извлекайте значение для этого ключа при вызове SDK:

// 1. Modify your localization files (e.g., using react-i18next)

/*
en.json
*/
{
  "adapty_paywalls_locale": "en"
}

/*
es.json
*/
{
  "adapty_paywalls_locale": "es"
}

/*
pt-BR.json
*/
{
  "adapty_paywalls_locale": "pt-br"
}

// 2. Extract and use the locale code

const MyComponent = () => {
  const { t } = useTranslation();
  
  const fetchPaywall = async () => {
    const locale = t('adapty_paywalls_locale');
    // pass locale code to adapty.getPaywall or adapty.getPaywallForDefaultAudience method
    const paywall = await adapty.getPaywallForDefaultAudience('placement_id', locale);
  };
};

Таким образом вы можете быть уверены, что полностью контролируете, какая локализация будет получена для каждого пользователя вашего приложения.

Альтернативный способ реализации локализаций

Похожего (но не идентичного) результата можно добиться, не задавая явно коды локалей для каждой локализации. Для этого достаточно извлечь код локали из других объектов, предоставляемых платформой, например:


const getLocaleCode = () => {
  if (Capacitor.getPlatform() === 'ios') {
    return navigator.language || 'en';
  } else {
    return navigator.language || 'en';
  }
};

const fetchPaywall = async () => {
  const locale = getLocaleCode();
  // pass locale code to adapty.getPaywall or adapty.getPaywallForDefaultAudience method
  const paywall = await adapty.getPaywallForDefaultAudience('placement_id', locale);
};

Обратите внимание, что мы не рекомендуем этот подход по нескольким причинам:

  1. На iOS предпочитаемые языки и текущая локаль — это не одно и то же. Чтобы локализация выбиралась корректно, придётся либо положиться на логику Apple, которая работает из коробки при рекомендуемом подходе с локализованными файлами строк, либо воспроизвести её самостоятельно.
  2. Сложно предсказать, что именно получит сервер 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.