Организация файлов переводов

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


Базовая структура локализационных ресурсов

Типичная организация переводов в проекте с Globalize строится вокруг разделения по локалям и типам данных.

/locales
  /en
    messages.json
    numbers.json
    date.json
  /ru
    messages.json
    numbers.json
    date.json
  /kk
    messages.json
    numbers.json
    date.json

Такой подход обеспечивает:

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

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


Разделение типов данных

Globalize использует несколько категорий данных, которые логически разделяются на разные файлы.

Сообщения интерфейса

Файл messages.json содержит ключи переводов пользовательского интерфейса.

{
  "app.title": "My Application",
  "button.save": "Save",
  "button.cancel": "Cancel"
}

Особенность организации сообщений заключается в их ключевой структуре. Используются:

  • плоские ключи (button.save)
  • либо группировка по пространствам имён (button.*, form.*)

Плоская структура упрощает кэширование и динамическую подмену.


Числовые форматы

Файл numbers.json содержит CLDR-данные, необходимые для форматирования чисел.

{
  "minimumGroupingDigits": 1,
  "symbols": {
    "decimal": ".",
    "group": ","
  },
  "numberFormat": {
    "standard": "#,##0.###"
  }
}

Данный файл часто генерируется автоматически из CLDR-источников и не редактируется вручную.


Форматы дат и времени

Файл date.json включает данные о календарях, периодах и форматах отображения дат.

{
  "months": {
    "wide": ["January", "February", "March"]
  },
  "days": {
    "wide": ["Sunday", "Monday", "Tuesday"]
  },
  "dateFormats": {
    "short": "MM/dd/yy",
    "long": "MMMM d, y"
  }
}

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


Модульная структура и namespaces

При масштабировании приложения применяется разбиение сообщений на доменные модули.

/locales
  /ru
    auth.json
    profile.json
    dashboard.json

Пример auth.json:

{
  "login.title": "Вход",
  "login.button": "Войти",
  "error.invalidCredentials": "Неверный логин или пароль"
}

Такое разделение позволяет:

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

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

Одним из ключевых подходов является lazy loading языковых ресурсов.

import Globalize from "globalize";

async function loadLocale(locale) {
  const messages = await import(`./locales/${locale}/messages.json`);
  const numbers = await import(`./locales/${locale}/numbers.json`);
  const date = await import(`./locales/${locale}/date.json`);

  Globalize.load(messages.default);
  Globalize.load(numbers.default);
  Globalize.load(date.default);

  return Globalize(locale);
}

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


Использование CLDR и организация данных

Globalize зависит от CLDR (Common Locale Data Repository). В проектной структуре обычно выделяется отдельный слой для CLDR-данных:

/cldr
  /supplemental
    likelySubtags.json
    plurals.json
  /main
    /ru
      numbers.json
      dates.json

Рекомендуемая стратегия:

  • supplemental данные загружаются один раз
  • локальные данные загружаются по мере необходимости
  • данные не дублируются между локалями

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

При больших проектах важно контролировать изменения переводов.

Распространённый подход:

/locales
  /ru
    v1
      messages.json
    v2
      messages.json

Или использование версий в ключах:

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

Версионирование позволяет:

  • безопасно обновлять интерфейс
  • сохранять обратную совместимость
  • проводить A/B тестирование переводов

Сборка и бандлинг переводов

При использовании Webpack или Vite применяется стратегия разделения чанков.

export default {
  resolve: {
    alias: {
      "@locales": "/src/locales"
    }
  }
};

Далее языковые файлы автоматически попадают в отдельные чанки:

  • locale-ru.chunk.js
  • locale-en.chunk.js

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


Нормализация структуры сообщений

При росте проекта возникает необходимость унификации ключей.

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

<domain>.<component>.<element>

Примеры:

auth.login.title
auth.login.error
profile.settings.save
dashboard.stats.users

Преимущества:

  • отсутствие конфликтов ключей
  • ясная структура ответственности
  • простота поиска переводов

Хранение переводов вне кода

В крупных системах переводы часто выносятся в отдельные репозитории или CMS.

Варианты хранения:

  • JSON-файлы в репозитории
  • отдельный i18n-репозиторий
  • внешняя система локализации (API)

Пример загрузки через API:

async function fetchMessages(locale) {
  const res = await fetch(`/i18n/${locale}/messages`);
  return res.json();
}

Такой подход позволяет обновлять переводы без релиза приложения.


Слияние и переопределение переводов

Globalize допускает многослойную загрузку данных.

Globalize.load(baseMessages);
Globalize.load(featureMessages);
Globalize.load(overrides);

Порядок важен: последующие слои переопределяют предыдущие.

Типичная схема:

  1. базовые переводы
  2. переводы модуля
  3. пользовательские или экспериментальные значения

Организация fallback-локалей

При отсутствии полной локализации используется цепочка fallback:

ru-KZ → ru → en

Файловая структура отражает это:

/locales
  /ru
  /en

Частичные локали содержат только отличающиеся значения, остальные наследуются.


Оптимизация структуры файлов

Для уменьшения количества запросов применяется объединение данных:

ru.bundle.json
en.bundle.json

Однако при этом теряется гранулярная загрузка. Баланс достигается через гибридную модель:

  • core-бандл (общие данные)
  • feature-бандлы (модули)
  • locale-бандлы (язык)

Инвалидация и обновление переводов

При изменении переводов важно управлять кэшированием.

Практика:

  • добавление хэша в имя файла

    messages.ru.8f3a1.json
  • либо версия в URL

    /locales/ru/messages?v=12

Это предотвращает использование устаревших переводов в браузере.


Стандартизация формата сообщений

Для обеспечения совместимости между системами используется единый формат:

  • JSON как основной контейнер
  • UTF-8 кодировка
  • отсутствие логики внутри переводов
  • чистые строки без HTML-разметки

Допустимая параметризация:

{
  "welcome.user": "Hello, {name}"
}

Масштабирование структуры при росте проекта

При увеличении количества локалей и модулей структура эволюционирует в сторону доменно-ориентированной модели:

/i18n
  /core
  /auth
  /billing
  /profile
  /shared

Каждый домен содержит собственные переводы для всех локалей.

Это позволяет:

  • параллельную работу команд
  • независимые релизы модулей
  • снижение конфликтов в переводах