Версионирование переводов

Природа проблемы версионирования локализаций

Переводы в многозадачных интерфейсах представляют собой динамический слой данных, который изменяется независимо от бизнес-логики приложения. В отличие от кода, локализационные ресурсы часто обновляются чаще и менее синхронизированно с релизами. Это создаёт проблему рассинхронизации: приложение может ожидать один набор ключей, а на стороне ресурсов присутствует другой.

i18next, как система интернационализации, оперирует JSON-ресурсами, загружаемыми синхронно или асинхронно через backend. Отсутствие контроля версий приводит к следующим классам ошибок:

  • устаревшие ключи, оставшиеся после рефакторинга
  • несовместимость структуры JSON между релизами
  • некорректный кеш браузера или CDN
  • частично загруженные переводы после деплоя

Версионирование решает задачу согласования состояния приложения и состояния переводов.


Версионирование на уровне ключей и структуры ресурсов

Основная единица управления в i18next — ключ перевода. Изменение ключей без стратегии миграции приводит к разрушению обратной совместимости.

Стабильные ключи

Подход со стабильными ключами предполагает неизменность идентификаторов:

{
  "auth.login.title": "Вход",
  "auth.login.button": "Войти"
}

Изменения текста не затрагивают структуру, а значит не требуют версионирования. Однако при масштабировании появляются проблемы:

  • накопление устаревших ключей
  • невозможность понять актуальность значения
  • рост объёма переводов

Версионирование через пространства имён

i18next поддерживает namespaces, что позволяет разделять версии логически:

i18next.init({
  ns: ['common', 'auth_v1', 'auth_v2'],
  defaultNS: 'common'
});

Использование суффиксов версий в namespace позволяет сохранять параллельные состояния переводов без конфликтов.


Версионирование JSON-ресурсов

JSON-файлы переводов часто доставляются через HTTP backend. В этом случае ключевым становится контроль кеширования.

Версионирование через путь файла

Один из распространённых подходов — включение версии в URL:

/locales/en/translation.v1.json
/locales/en/translation.v2.json

или

/locales/v2/en/translation.json

i18next-http-backend позволяет динамически формировать путь:

backend: {
  loadPath: '/locales/{{lng}}/{{ns}}.json?v=2'
}

Версия в query string или path выступает как механизм cache busting.


Инвалидация кеша

Ключевая проблема локализации — агрессивное кеширование браузером и CDN.

Query-based versioning

loadPath: '/locales/{{lng}}/{{ns}}.json?version=2026-05-30'

Версия может соответствовать:

  • релизу приложения
  • коммиту CI/CD
  • hash сборки

Hash-based versioning

Более строгий подход использует content hash:

translation.8f3a1c.json

Генерация hash производится на этапе сборки:

  • вычисление SHA-1 или SHA-256 от JSON
  • переименование файла
  • обновление манифеста ресурсов

Версионирование через backend и API

При использовании серверной загрузки переводов i18next интегрируется с backend API.

Пример конфигурации:

i18next
  .use(HttpBackend)
  .init({
    backend: {
      loadPath: '/api/locales?lng={{lng}}&ns={{ns}}&v=3'
    }
  });

Backend может учитывать версию:

  • через заголовки Accept-Version
  • через query параметр
  • через JWT claims (в корпоративных системах)

Это позволяет централизованно управлять совместимостью переводов.


Синхронизация версий приложения и переводов

Версионирование становится критическим при рассинхронизации фронтенда и локализации.

Привязка к версии приложения

const appVersion = process.env.APP_VERSION;

i18next.init({
  backend: {
    loadPath: `/locales/{{lng}}/{{ns}}.json?v=${appVersion}`
  }
});

При каждом релизе обновляется версия приложения, что автоматически инвалидирует кеш переводов.

Привязка к git commit hash

Более точная стратегия:

v=commit_sha

Преимущество — точная трассируемость соответствия кода и локализации.


Динамическая подгрузка и контроль актуальности

i18next позволяет загружать переводы асинхронно, что усиливает необходимость контроля версий.

reload ресурсов

i18next.reloadResources(['en', 'ru'], ['common']);

Используется при переключении версии без перезагрузки страницы.

detect missing keys

Встроенный механизм позволяет фиксировать расхождения:

i18next.init({
  saveMissing: true,
  missingKeyHandler: (lng, ns, key) => {
    console.log('Missing key:', lng, ns, key);
  }
});

При изменении версии это помогает выявить несовместимости.


Миграция между версиями переводов

При изменении структуры JSON необходима стратегия миграции.

Переименование ключей

// v1
{
  "login_button": "Войти"
}

// v2
{
  "auth.login.button": "Войти"
}

Для сохранения совместимости применяются маппинги:

const keyMap = {
  login_button: 'auth.login.button'
};

i18next позволяет использовать кастомные интерцепторы через missingKeyHandler или postProcessor.


Фолбэки и совместимость версий

Версионирование неизбежно требует fallback-стратегий.

i18next.init({
  fallbackLng: 'en',
  fallbackNS: ['common_v1'],
  load: 'currentOnly'
});

При отсутствии ключа в новой версии используется старая версия namespace или базовый язык.


Версионирование и pluralization

Сложность возрастает при работе с множественными формами.

{
  "item_one": "1 элемент",
  "item_few": "{{count}} элемента",
  "item_many": "{{count}} элементов"
}

При изменении правил pluralization (например, ICU переход) версия переводов должна быть синхронизирована с движком интернационализации.


ICU и форматирование как фактор версионирования

При переходе на ICU MessageFormat структура переводов меняется радикально:

{
  "item": "{count, plural, one {# элемент} few {# элемента} other {# элементов}}"
}

Это требует отдельной версии namespace или полного обновления ресурсов.


CDN и стратегии доставки переводов

При использовании CDN возникают дополнительные требования:

  • невозможность мгновенной инвалидации
  • необходимость immutable assets
  • необходимость версионированных путей

Рекомендуемая модель:

/cdn/locales/v{major}/{lng}/{ns}.json

Major-версия отделяет несовместимые изменения структуры.


Feature flags и локализационные версии

Версионирование переводов часто связывается с feature flags:

if (featureFlags.newCheckout) {
  i18next.loadNamespaces('checkout_v2');
}

Это позволяет параллельно поддерживать несколько продуктовых потоков с разными наборами переводов.


Логирование и контроль расхождений версий

Системы мониторинга фиксируют:

  • отсутствие ключей
  • устаревшие namespace
  • несовпадение hash ресурсов

Пример структуры логирования:

logger.warn({
  type: 'i18n_mismatch',
  lng: 'ru',
  ns: 'auth_v2',
  key: 'login.button',
  expectedVersion: 3,
  actualVersion: 2
});

Консистентность версий в распределённых системах

В микрофронтенд-архитектуре разные части интерфейса могут использовать разные версии переводов. Это приводит к необходимости:

  • централизованного registry версий
  • единого i18next instance через shared module
  • контрактов между сервисами локализации

Типовая проблема возникает при независимых деплоях модулей, где namespace становится единственным гарантом совместимости.


Генерация переводов и автоматическое версионирование

При использовании автоматических систем генерации переводов (TMS) версии часто формируются автоматически:

  • изменение source string → новая версия key mapping
  • обновление machine translation → инкремент версии namespace
  • re-export из Figma/JSON → hash-based version

Пайплайн обычно включает:

  1. извлечение ключей из кода
  2. сравнение с предыдущей версией
  3. генерацию diff
  4. публикацию новой версии ресурсов

Стратегии выбора модели версионирования

Различные модели решают разные классы задач:

  • version-in-path — строгая изоляция ресурсов
  • query-based versioning — простота внедрения
  • namespace versioning — гибкость миграций
  • hash-based assets — максимальная точность и кеш-контроль

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