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

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

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

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

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

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

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

Когда Adapty ищет локализацию, соответствующую локали пользователя, происходит следующее:

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

Таким образом, 'pt_BR', pt-BR и pt-br — все они разрешаются в одну и ту же локализацию.

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

В SDK v4 при получении флоу передавать код локали не нужно.

  • Пейволы из Flow Builder и Paywall Builder: Adapty автоматически определяет локализацию по настройкам устройства и тем локализациям, которые вы настроили в билдере. Отрендерите флоу с помощью createFlowView — код локали не нужен.
  • Кастомные пейволы (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, хранящиеся в каждом Remote Config.

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

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

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

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

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

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

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

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