Работа с i18next в крупных приложениях почти всегда приводит к разделению переводов на namespaces. Это позволяет разбивать локализационные файлы по функциональным областям: страницы, модули, компоненты. Однако при росте приложения становится критичным не загружать все переводы сразу, а подгружать их по мере необходимости. Именно это и составляет основу динамической загрузки namespace.
Namespaces в i18next представляют собой логическое разделение переводов:
common — общие строки интерфейсаauth — авторизация и регистрацияprofile — профиль пользователяdashboard — панель управленияКаждый namespace соответствует отдельному файлу или источнику данных.
Структура ресурсов:
{
"common": {
"save": "Сохранить",
"cancel": "Отмена"
},
"auth": {
"login": "Вход",
"logout": "Выход"
}
}
При статической конфигурации все namespaces загружаются сразу, что увеличивает начальный вес приложения.
Динамическая загрузка namespaces заключается в том, что перевод подгружается только при первом обращении к нему.
Ключевые цели:
Чаще всего используется i18next-http-backend, который
позволяет загружать JSON-файлы по HTTP.
import i18n from 'i18next';
import HttpBackend from 'i18next-http-backend';
import { initReactI18next } from 'react-i18next';
i18n
.use(HttpBackend)
.use(initReactI18next)
.init({
lng: 'ru',
fallbackLng: 'en',
ns: ['common'],
defaultNS: 'common',
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json'
},
partialBundledLanguages: true,
react: {
useSuspense: true
}
});
В этой конфигурации важны два момента:
ns задаёт начально загруженные namespacesОсновной механизм динамической загрузки — метод
loadNamespaces.
i18n.loadNamespaces('auth').then(() => {
console.log('namespace auth загружен');
});
После загрузки можно безопасно использовать ключи:
i18n.t('auth:login');
Если namespace ещё не загружен, i18next инициирует загрузку через backend.
В реальных приложениях часто требуется несколько namespaces на одном экране:
i18n.loadNamespaces(['common', 'profile']).then(() => {
console.log('переводы загружены');
});
И использование:
i18n.t('common:save');
i18n.t('profile:username');
Такой подход позволяет изолировать перевод каждого домена.
В связке с React применяется подход загрузки namespace при монтировании компонента.
import { useTranslation } from 'react-i18next';
import { useEffect } from 'react';
function ProfilePage() {
const { t, i18n } = useTranslation('profile');
useEffect(() => {
i18n.loadNamespaces('profile');
}, [i18n]);
return <div>{t('title')}</div>;
}
Здесь важно, что useTranslation('profile') уже
определяет namespace, но загрузка может быть отложена до момента
использования.
При включённом useSuspense: true загрузка namespace
становится синхронной с точки зрения UI.
const ProfilePage = React.lazy(() => import('./ProfilePage'));
И внутри i18next:
react: {
useSuspense: true
}
Если namespace не загружен, React приостанавливает рендер до завершения загрузки ресурсов.
Помимо HTTP backend, можно управлять namespaces вручную:
i18n.addResourceBundle(
'ru',
'dashboard',
{
title: 'Панель управления',
stats: 'Статистика'
},
true,
true
);
Параметры:
true — deep mergetrue — перезапись существующих ключейЭтот подход используется при динамической генерации переводов или получении их с API.
Для контроля состояния используется:
i18n.hasLoadedNamespace('auth');
Возвращает true или false.
Также можно подписаться на событие загрузки:
i18n.on('loaded', (loaded) => {
console.log('загружено:', loaded);
});
Если namespace не найден, поведение зависит от конфигурации:
{
fallbackLng: 'en',
saveMissing: true
}
При отсутствии ключа возможны сценарии:
Каждый route соответствует набору namespaces:
const routeNamespaces = {
'/profile': ['profile', 'common'],
'/dashboard': ['dashboard', 'common']
};
Перед переходом:
i18n.loadNamespaces(routeNamespaces[path]);
Некоторые namespaces загружаются сразу:
ns: ['common', 'auth']
Остальные — лениво.
При использовании HTTP backend файлы кешируются браузером:
/locales/ru/auth.json
/locales/ru/profile.json
Важно правильно настроить заголовки:
В серверном рендеринге namespaces должны быть загружены до рендера HTML:
await i18n.loadNamespaces(['common', 'profile']);
После этого выполняется:
i18n.init({
lng: 'ru'
});
Это предотвращает гидрационные расхождения.
t('profile:title') // может вернуть ключ
Причина — namespace ещё не загружен.
Без backend.loadPath динамическая загрузка
невозможна:
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json'
}
Если один ключ существует в нескольких namespaces, приоритет определяется порядком загрузки и defaultNS.
При росте приложения структура namespaces обычно эволюционирует:
Feature-based модель чаще предпочтительна для динамической загрузки, так как соответствует маршрутизации и lazy loading.
i18next предотвращает дублирующие запросы:
Это особенно важно при одновременном рендере нескольких компонентов.
При использовании dynamic import:
const loadProfile = async () => {
await i18n.loadNamespaces('profile');
return import('./ProfilePage');
};
Это позволяет синхронизировать загрузку кода и переводов.
Можно построить слой мониторинга:
i18n.on('failedLoading', (lng, ns, msg) => {
console.error(lng, ns, msg);
});
Используется для отслеживания проблем CDN или отсутствующих файлов.
Практика разделения переводов влияет на производительность:
Баланс достигается группировкой по функциональным зонам, а не по отдельным компонентам.
При смене языка все namespaces пересобираются:
await i18n.changeLanguage('en');
Если namespaces уже были загружены для нового языка, повторная загрузка не происходит.