Плоская vs вложенная структура

Структура ключей переводов в i18next напрямую влияет на читаемость, масштабируемость и удобство сопровождения интернационализации приложения. Одним из базовых архитектурных решений становится выбор между плоской (flat) и вложенной (nested) структурой ресурсов. Эти подходы не являются взаимоисключающими в рамках библиотеки, однако различия между ними существенно влияют на организацию кода и поведение функций интерполяции, пространств имён и fallback-механизмов.


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

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

{
  "home_title": "Главная",
  "home_description": "Описание главной страницы",
  "button_save": "Сохранить",
  "button_cancel": "Отмена"
}

Особенности плоской структуры

1. Отсутствие вложенности

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

2. Линейная адресация

Доступ к значениям осуществляется напрямую:

i18next.t('home_title');

3. Простота генерации и миграции

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

4. Сложность масштабирования

При росте приложения количество ключей быстро увеличивается, и отсутствие структуры приводит к появлению длинных и семантически перегруженных идентификаторов:

{
  "dashboard_user_profile_settings_privacy_toggle": "Приватность"
}

Такие ключи ухудшают читаемость и увеличивают риск ошибок при обращении.


Вложенная структура переводов

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

Пример вложенной структуры:

{
  "home": {
    "title": "Главная",
    "description": "Описание главной страницы"
  },
  "button": {
    "save": "Сохранить",
    "cancel": "Отмена"
  }
}

Особенности вложенной структуры

1. Иерархическая организация

Ключи группируются по смыслу, что делает структуру ближе к архитектуре приложения.

Доступ осуществляется через точечную нотацию:

i18next.t('home.title');

2. Логическая группировка

Переводы объединяются по доменам, компонентам или страницам, что облегчает сопровождение и поиск:

{
  "profile": {
    "settings": {
      "privacy": {
        "title": "Приватность",
        "description": "Управление настройками приватности"
      }
    }
  }
}

3. Улучшенная масштабируемость

При росте проекта добавление новых ключей не нарушает общую структуру, а расширяет существующие узлы.

4. Сложность частичного доступа

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

const section = 'profile.settings.privacy.title';
i18next.t(section);

Поддержка вложенности в i18next

i18next нативно поддерживает вложенные структуры и автоматически резолвит ключи с точечной нотацией.

Важный механизм — разворачивание ключей (key resolution):

i18next.t('profile.settings.privacy.title');

Библиотека последовательно проходит по объекту:

  • profile
  • settings
  • privacy
  • title

и возвращает конечное значение.


Экранирование точек в плоской структуре

Плоская структура часто использует точки как часть ключа, что может конфликтовать с механизмом вложенности. Для предотвращения неоднозначности применяется экранирование.

{
  "button.save": "Сохранить"
}

Доступ:

i18next.t('button.save', { nsSeparator: false });

или через настройку:

i18next.init({
  keySeparator: false
});

При отключённом разделителе точка перестаёт интерпретироваться как вложенность, и ключи становятся строго плоскими.


Гибридный подход

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

{
  "button": {
    "save": "Сохранить",
    "cancel": "Отмена"
  },
  "errors.network.timeout": "Превышено время ожидания"
}

Такой подход сочетает преимущества:

  • вложенность для крупных доменов (UI, страницы, модули)
  • плоские ключи для исключений, ошибок или системных сообщений

Интерполяция и структура ключей

Структура ключей влияет на читаемость интерполяции, особенно в вложенных объектах.

Плоский вариант:

{
  "welcome_user": "Добро пожаловать, {{name}}"
}
i18next.t('welcome_user', { name: 'Алексей' });

Вложенный вариант:

{
  "welcome": {
    "user": "Добро пожаловать, {{name}}"
  }
}
i18next.t('welcome.user', { name: 'Алексей' });

Во втором случае интерполяция сохраняет ту же семантику, но ключ становится более структурированным и контекстным.


Влияние на пространства имён (namespaces)

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

Пример:

// common.json
{
  "button": {
    "save": "Сохранить"
  }
}
// profile.json
{
  "settings": {
    "privacy": "Приватность"
  }
}

В этом случае вложенность внутри namespace снижает необходимость в глобально уникальных ключах, чего требует плоская модель.


Производительность и размер ресурсов

С точки зрения runtime-разрешения ключей различия между подходами минимальны, однако влияние проявляется на уровне:

  • размера JSON-файлов
  • компрессии (gzip лучше сжимает повторяющиеся структуры)
  • кеширования и диффов обновлений переводов

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

{
  "profile_edit_title": "...",
  "profile_edit_button_save": "...",
  "profile_edit_button_cancel": "..."
}

Вложенность уменьшает дублирование:

{
  "profile": {
    "edit": {
      "title": "...",
      "button": {
        "save": "...",
        "cancel": "..."
      }
    }
  }
}

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

1. Чрезмерная вложенность

Глубина более 4–5 уровней усложняет поддержку и делает ключи громоздкими:

i18next.t('app.profile.settings.privacy.security.level.high.title');

2. Псевдовложенность в плоских ключах

Использование точек без реальной структуры создаёт иллюзию иерархии:

{
  "user.profile.edit.save.button.text": "Сохранить"
}

Такие ключи совмещают недостатки обеих моделей.

3. Смешение стратегий без правил

Отсутствие стандарта приводит к хаосу:

{
  "button_save": "...",
  "button.cancel": "...",
  "profileSettingsPrivacy": "..."
}

Практическая модель выбора структуры

Плоская структура оправдана при:

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

Вложенная структура предпочтительна при:

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

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