В системах интернационализации ключевой проблемой становится масштабирование словаря переводов по мере роста проекта. При увеличении числа экранов, модулей и доменных областей приложения единый файл переводов начинает превращаться в узкое место: он усложняет поддержку, замедляет загрузку и увеличивает риск конфликтов ключей.
В i18next решение этой проблемы строится на концепции namespace (пространств имён) — логического разделения переводов на независимые группы. Каждый namespace представляет собой отдельный набор ключей, относящихся к конкретной части приложения.
Базовая идея заключается в том, что вместо монолитной структуры:
{
"header.title": "Главная",
"header.logout": "Выйти",
"profile.title": "Профиль",
"profile.edit": "Редактировать"
}
используется разбиение:
Каждый файл становится самостоятельной единицей загрузки и обновления.
Внутренне i18next хранит переводы в виде вложенной структуры:
{
en: {
common: {
welcome: "Welcome"
},
header: {
title: "Home"
},
profile: {
title: "Profile"
}
}
}
Здесь:
en — языкcommon, header, profile —
namespaceДоступ к ключам осуществляется через указание namespace либо явно, либо через конфигурацию по умолчанию.
Подключение namespace начинается с настройки i18next:
i18next.init({
lng: 'ru',
fallbackLng: 'en',
ns: ['common', 'header', 'profile'],
defaultNS: 'common',
resources: {
en: {
common: {
welcome: "Welcome"
},
header: {
title: "Home"
},
profile: {
title: "Profile"
}
},
ru: {
common: {
welcome: "Добро пожаловать"
},
header: {
title: "Главная"
},
profile: {
title: "Профиль"
}
}
}
});
Ключевые параметры:
i18next поддерживает два основных способа доступа к переводам.
i18next.t('header:title');
i18next.t('profile:title');
Здесь используется разделитель : между namespace и
ключом.
i18next.t('welcome');
Если defaultNS = 'common', вызов интерпретируется
как:
common.welcome
В реальных приложениях часто требуется переключать активные пространства имён в зависимости от контекста.
i18next.loadNamespaces(['profile', 'settings'], () => {
console.log(i18next.t('profile:title'));
});
Этот механизм позволяет подгружать переводы лениво, снижая начальный объём загружаемых данных.
Практическая архитектура namespace обычно отражает структуру продукта:
Пример структуры файлов:
locales/
ru/
common.json
auth.json
dashboard.json
en/
common.json
auth.json
dashboard.json
Каждый файл содержит только свой домен:
// auth.json
{
"login": "Войти",
"logout": "Выйти",
"email": "Электронная почта"
}
При подключении HTTP backend namespace становятся ключевым элементом загрузки:
import Backend from 'i18next-http-backend';
i18next
.use(Backend)
.init({
ns: ['common', 'auth', 'dashboard'],
defaultNS: 'common',
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json'
}
});
Здесь:
{{lng}} — язык{{ns}} — namespaceПри обращении к auth:login выполняется запрос:
/locales/ru/auth.json
Крупные приложения редко загружают все переводы сразу. Вместо этого применяется стратегия lazy loading:
i18next.loadNamespaces(['dashboard'], () => {
const title = i18next.t('dashboard:title');
});
Это позволяет:
В связке с React используется обёртка:
import { useTranslation } from 'react-i18next';
function Profile() {
const { t } = useTranslation('profile');
return (
<h1>{t('title')}</h1>
);
}
Если требуется несколько namespace:
const { t } = useTranslation(['profile', 'common']);
t('profile:title');
t('common:welcome');
Первый элемент массива становится namespace по умолчанию внутри компонента.
i18next поддерживает цепочку поиска ключей:
Пример:
i18next.init({
ns: ['page', 'common'],
defaultNS: 'page',
fallbackNS: 'common'
});
Если ключ не найден в page, поиск продолжается в
common.
Использование namespace устраняет проблему конфликтов:
Без namespace:
{
"title": "Dashboard"
}
и
{
"title": "Profile"
}
привели бы к перезаписи одного значения другим.
С namespace:
dashboard.title
profile.title
ключи изолированы и не пересекаются.
При использовании TypeScript namespace можно типизировать для повышения безопасности:
interface Resources {
common: {
welcome: string;
};
auth: {
login: string;
};
}
declare module 'i18next' {
interface CustomTypeOptions {
defaultNS: 'common';
resources: Resources;
}
}
Это позволяет ловить ошибки ключей на этапе компиляции.
В крупных системах namespace часто строятся по иерархии:
Иногда применяется нейминг с разделителями:
auth.login
auth.register
profile.settings
Но даже в этом случае физическое разделение файлов остаётся предпочтительным, так как оно упрощает кеширование и сборку.
При использовании backend-решений namespace проходят несколько стадий:
Поведение можно контролировать:
i18next.on('loaded', (loaded) => {
console.log(loaded);
});
В модульных приложениях каждый модуль часто владеет собственным namespace:
// module: comments
i18next.addResources('ru', 'comments', {
add: 'Добавить комментарий',
delete: 'Удалить'
});
Такой подход обеспечивает:
При использовании сборщиков (Webpack, Vite) namespace могут объединяться в чанки:
auth.chunk.js -> auth.json
dashboard.chunk.js -> dashboard.json
Это позволяет синхронизировать структуру переводов с кодовой базой, делая локализацию частью feature-based архитектуры.