Организация кодовой базы

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

Ключевые принципы:

  • Разделение по функциональным областям, а не по языку как единственному критерию
  • Использование namespaces как базовой единицы модуля перевода
  • Ленивая загрузка переводов вместо единого большого файла
  • Изоляция UI-слоя от локализационных деталей
  • Предсказуемая структура файлов для автоматизации и CI

Структура каталогов переводов

Базовая организация ресурсов строится вокруг языков и namespaces одновременно. Наиболее распространённая схема:

/locales
  /en
    common.json
    auth.json
    dashboard.json
  /ru
    common.json
    auth.json
    dashboard.json
  /kk
    common.json
    auth.json
    dashboard.json

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

Альтернативная структура для более крупных проектов:

/locales
  /en
    /common
      header.json
      footer.json
    /auth
      login.json
      register.json
  /ru
    /common
      header.json
      footer.json
    /auth
      login.json
      register.json

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


Namespaces как основа модульности

Namespaces в i18next позволяют разделять переводы по функциональным блокам приложения. Каждый namespace соответствует доменной области.

Инициализация i18next с namespaces:

import i18n from 'i18next';

i18n.init({
  lng: 'en',
  fallbackLng: 'en',
  ns: ['common', 'auth', 'dashboard'],
  defaultNS: 'common',
  resources: {}
});

Использование namespace в коде:

i18n.t('login.title', { ns: 'auth' });

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

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

Разделение переводов по функциональным модулям

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

Пример структуры feature-based архитектуры:

/src
  /features
    /auth
      login.js
      auth.i18n.js
      /locales
        en.json
        ru.json
    /dashboard
      dashboard.js
      dashboard.i18n.js
      /locales
        en.json
        ru.json

Подход позволяет:

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

Регистрация ресурсов:

import i18n from 'i18next';
import en from './locales/en.json';
import ru from './locales/ru.json';

i18n.addResourceBundle('en', 'auth', en);
i18n.addResourceBundle('ru', 'auth', ru);

Формат хранения переводов

Переводы обычно хранятся в JSON с вложенной структурой ключей.

Пример:

{
  "login": {
    "title": "Sign in",
    "submit": "Login",
    "error": {
      "invalid": "Invalid credentials"
    }
  }
}

Рекомендации по структуре:

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

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

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

Использование i18next-http-backend:

import i18n from 'i18next';
import HttpBackend from 'i18next-http-backend';

i18n
  .use(HttpBackend)
  .init({
    lng: 'en',
    fallbackLng: 'en',
    backend: {
      loadPath: '/locales/{{lng}}/{{ns}}.json'
    }
  });

Такая схема позволяет:

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

Связь с code splitting

При использовании bundler’ов (Webpack, Vite, Rollup) переводы можно привязывать к динамическим импортам.

Пример:

async function loadAuthModule() {
  const module = await import('./features/auth/auth.js');

  await i18n.loadNamespaces('auth');

  return module;
}

Логика:

  • модуль загружается
  • затем подгружается соответствующий namespace
  • UI отображается только после готовности переводов

Мульти-язычная архитектура приложения

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

Основные элементы:

  • единый instance i18next
  • централизованное управление языком
  • синхронизация состояния UI

Пример смены языка:

i18n.changeLanguage('ru');

Структурно важно:

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

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

При активной разработке возникает необходимость синхронизации изменений между языками.

Практики версионирования:

  • хранение JSON в Git с обязательным review
  • использование CI-проверок на отсутствие ключей
  • автоматическая генерация недостающих переводов

Пример проверки:

const missingKeys = i18n.getMissingKeys('ru');
console.log(missingKeys);

Дополнительная практика — использование базового языка как источника истины:

en (source of truth)
ru (derived)
kk (derived)

Fallback-структура и устойчивость системы

Fallback-настройки определяют поведение при отсутствии перевода:

i18n.init({
  fallbackLng: ['en', 'ru'],
  fallbackNS: 'common'
});

Организационно это влияет на структуру:

  • общий namespace common содержит универсальные строки
  • доменные namespaces не дублируют общие тексты
  • минимизируется разрастание ключей

Тестирование структуры переводов

Кодовая база должна включать проверки консистентности переводов.

Типовые тесты:

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

import en from './locales/en/common.json';
import ru from './locales/ru/common.json';

function diffKeys(a, b) {
  return Object.keys(a).filter(k => !b[k]);
}

console.log(diffKeys(en, ru));

Проверка использования ключей в коде:

  • статический анализ AST
  • поиск неиспользуемых ключей
  • контроль отсутствующих переводов в runtime

Пример комплексной структуры проекта

/src
  /app
    i18n.js
  /features
    /auth
      auth.service.js
      auth.view.js
      auth.i18n.js
      /locales
        en.json
        ru.json
    /dashboard
      dashboard.controller.js
      dashboard.view.js
      /locales
        en.json
        ru.json
  /shared
    /components
    /locales
      common.en.json
      common.ru.json
/locales
  /en
    common.json
  /ru
    common.json

Архитектура сочетает:

  • глобальные переводы (shared, common)
  • feature-level переводы
  • централизованную инициализацию i18n

Управление ростом кодовой базы

По мере увеличения приложения критически важным становится контроль структуры переводов:

  • ограничение количества namespaces на модуль
  • запрет на кросс-ссылки между feature-переводами
  • унификация ключей через соглашения (snake_case или dot notation)
  • регулярная очистка неиспользуемых ключей

Пример соглашения:

feature.section.element.state
auth.login.button.submit
dashboard.stats.chart.title

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