В крупных приложениях интернационализация перестаёт быть набором строковых переводов и превращается в систему модулей, где каждая часть интерфейса управляет собственным словарём. Библиотека i18next поддерживает архитектуру, в которой переводы можно разделять по функциональным областям, сокращая связность, ускоряя загрузку и упрощая сопровождение.
Функциональное разделение переводов основано на идее, что каждая область приложения должна иметь собственный изолированный набор ключей. Вместо единого глобального JSON-файла используется набор ресурсов, сгруппированных по смыслу:
Такое разделение снижает вероятность коллизий ключей и позволяет масштабировать проект без роста сложности словарей.
В i18next ключевым механизмом разделения переводов выступают пространства имён (namespaces). Каждый namespace представляет собой логически обособленный блок переводов.
Пример структуры:
{
"auth": {
"login": "Войти",
"logout": "Выйти",
"error": {
"invalid_password": "Неверный пароль"
}
},
"profile": {
"title": "Профиль пользователя",
"edit": "Редактировать"
}
}
Однако при использовании namespaces структура становится более модульной:
locales/
en/
auth.json
profile.json
cart.json
ru/
auth.json
profile.json
cart.json
Каждый файл представляет отдельный функциональный блок.
i18next позволяет явно указывать используемые пространства имён:
i18next.init({
lng: 'ru',
fallbackLng: 'en',
ns: ['auth', 'profile', 'cart'],
defaultNS: 'auth',
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json'
}
});
Такой подход обеспечивает:
Разделение переводов становится особенно эффективным при lazy-loading. При переходе в новую часть приложения соответствующий namespace подгружается отдельно.
i18next.loadNamespaces('cart', () => {
// теперь доступны переводы корзины
});
В SPA архитектуре это позволяет связывать перевод напрямую с маршрутом:
/auth → namespace auth/profile → namespace profile/checkout → namespace cartКаждый маршрут получает только необходимые ресурсы.
При отсутствии разделения ключей возникает проблема пересечений:
"title": "..."
Такой ключ может означать заголовок профиля, страницы товара или модального окна.
Namespace устраняет неоднозначность:
auth:title
profile:title
product:title
или через разделённые файлы:
auth.title
profile.title
product.title
Внутренняя реализация i18next поддерживает оба подхода через точечную нотацию и вложенные структуры.
Оптимальная степень дробления переводов зависит от архитектуры приложения:
Подход подходит для небольших и средних приложений.
Позволяет разделять даже части одного функционального блока.
Используется в системах с большим количеством переиспользуемых компонентов.
Компонентный подход предполагает, что каждый UI-компонент имеет собственный namespace:
components/
button/
en.json
ru.json
modal/
en.json
ru.json
Использование:
const { t } = useTranslation('button');
t('submit');
Такой подход уменьшает зависимость компонентов друг от друга и делает их переносимыми между проектами.
При разделении переводов неизбежно возникает ситуация отсутствия ключа в конкретном модуле. В i18next предусмотрена цепочка fallback:
i18next.init({
ns: ['checkout', 'common'],
defaultNS: 'checkout',
fallbackNS: 'common'
});
Стратегия поиска ключа:
Это позволяет хранить общие строки отдельно:
Даже при строгом разделении функциональности существует слой общих
переводов. Обычно он выделяется в namespace common.
Содержимое:
common.json
{
"ok": "ОК",
"cancel": "Отмена",
"error": "Ошибка",
"loading": "Загрузка"
}
Этот слой используется всеми частями приложения и не привязан к конкретной функциональности.
Функциональные namespaces тесно связаны с динамическими импортами:
const loadCheckout = async () => {
await i18next.loadNamespaces('checkout');
const module = await import('./checkout');
return module;
};
Это синхронизирует:
Исключается ситуация, когда интерфейс отрисован, а переводы ещё не загружены.
В масштабных системах структура часто принимает вид:
locales/
ru/
common.json
auth.json
checkout.json
dashboard.json
admin/
users.json
roles.json
en/
...
Дополнительно вводится уровень доменов:
Это позволяет отделять пользовательскую и административную части интерфейса.
Разделение переводов по функциональности упрощает обновление отдельных частей системы без риска влияния на другие области.
Изменения в checkout.json не затрагивают:
auth.jsonprofile.jsondashboard.jsonТакое поведение критично для командной разработки, где разные группы работают над разными доменами.
Внутри одного namespace важно поддерживать единый стиль ключей:
save, delete,
updatetitle, description,
labelloading, error,
successi18next обрабатывает вложенные структуры, что позволяет сохранять семантическую читаемость:
{
"button": {
"save": "Сохранить",
"cancel": "Отмена"
}
}
При росте приложения функциональное разделение становится основным механизмом управления сложностью. Добавление новой фичи означает создание нового namespace, а не модификацию существующих файлов.
Это приводит к следующим свойствам архитектуры:
Разделение переводов по функциональности превращает интернационализацию из монолитного слоя в набор автономных модулей, тесно связанных с бизнес-логикой приложения.