Переход с других систем интернационализации на i18next требует предварительного анализа существующей модели локализации, структуры ключей и способа интеграции переводов в UI. Основная сложность миграции заключается не в синтаксических различиях API, а в различиях философии: многие библиотеки используют форматированные сообщения или компонентный подход, тогда как i18next опирается на ключ-значение, контексты, интерполяцию и плагины.
Ключевая особенность миграции — сохранение стабильности текстов при постепенном переключении слоёв интернационализации без полной переписки приложения.
Перед переносом переводов необходимо выделить модель, используемую в текущей системе:
Каждая из этих моделей влияет на то, как данные будут преобразованы в формат i18next.
i18next использует следующую структуру:
{
"common": {
"welcome": "Добро пожаловать",
"cart": {
"items": "В корзине {{count}} товаров"
}
}
}
Основные концепции:
Переводы из исходной системы группируются по категориям:
На этом этапе формируется единый словарь, который станет источником для i18next resources.
Разные библиотеки используют разные схемы:
t('WELCOME_MESSAGE')formatMessage({ id: 'welcome.message' })<Trans i18nKey="welcome.message" />В i18next рекомендуется привести всё к единому виду:
auth.login.titlecart.item.counterrors.network.timeoutВажно сохранить иерархию, а не плоскую структуру.
Из JSON или JS-объектов исходной системы данные преобразуются в формат i18next:
Было (например, react-intl):
{
"welcome.message": "Добро пожаловать, {name}"
}
Стало:
{
"welcome": {
"message": "Добро пожаловать, {{name}}"
}
}
Основные изменения:
{name} → {{name}}intl.formatMessage({ id: 'welcome.message' }, { name: 'Alex' });
i18next.t('welcome.message', { name: 'Alex' });
или при иерархии:
i18next.t('welcome.message', { name: 'Alex' });
<FormattedMessage id="cart.items" values={{ count }} />
заменяется на:
import { useTranslation } from 'react-i18next';
const { t } = useTranslation();
t('cart.items', { count });
или:
<Trans i18nKey="cart.items" values={{ count }} />
this.$t('message.welcome', { name })
i18next.t('message.welcome', { name })
или через обёртку:
import { useTranslation } from 'react-i18next';
const { t } = useTranslation();
Особенность миграции с Vue — замена реактивных watch-систем на подписку i18next.
{
"items": "1 item | {count} items"
}
{
"items_one": "{{count}} товар",
"items_few": "{{count}} товара",
"items_many": "{{count}} товаров"
}
или через встроенную pluralization:
{
"items": "{{count}} товар",
"items_plural": "{{count}} товаров"
}
Использование:
i18next.t('items', { count: 5 });
Многие библиотеки используют кастомные форматтеры:
{count, plural, one {# item} other {# items}}
{
"items": "{{count}} item"
}
или с форматами:
i18next.t('price', {
price: 1200,
formatParams: {
price: {
currency: 'USD'
}
}
});
Дополнительно подключаются плагины форматирования.
Многие системы используют плоский JSON:
{
"login.title": "Вход",
"login.button": "Войти"
}
В i18next предпочтительнее:
{
"login": {
"title": "Вход",
"button": "Войти"
}
}
Разделение на namespaces:
import i18next from 'i18next';
i18next.init({
lng: 'ru',
fallbackLng: 'en',
resources: {
ru: {
common: require('./locales/ru/common.json')
}
}
});
import { initReactI18next } from 'react-i18next';
i18next.use(initReactI18next).init({
resources,
lng: 'ru'
});
Старые системы часто используют динамические ключи:
t(`error.${code}`);
В i18next сохраняется аналогичная модель:
i18next.t(`error.${code}`);
Рекомендуется заранее нормализовать список ошибок в namespace:
{
"error": {
"404": "Не найдено",
"500": "Ошибка сервера"
}
}
При переходе часто сохраняются две системы одновременно:
Стратегия:
Пример обёртки:
function t(key, options) {
if (i18next.exists(key)) {
return i18next.t(key, options);
}
return legacyT(key, options);
}
{name} → ломается{{name}} → требуется строгое соответствиеПлоские ключи без namespace приводят к конфликтам:
titlebuttontitle в разных модуляхМногие библиотеки используют ICU, но i18next требует отдельной настройки plural rules.
Без fallback языка возможны “дыры” в UI при неполной миграции.
При использовании TypeScript:
interface Resources {
common: {
welcome: string;
};
}
Дополнительно применяются:
Рекомендуемая структура:
locales/
ru/
common.json
auth.json
cart.json
en/
common.json
auth.json
cart.json
Namespaces подключаются лениво:
i18next.loadNamespaces('cart');
Если исходная система использует ICU:
{count, plural, one {# item} other {# items}}
возможны два подхода:
Backend часто возвращает:
{
"error": "USER_NOT_FOUND"
}
В i18next:
i18next.t(`errors.${errorCode}`);
или через mapping layer:
const message = i18next.t(errorMap[code]);
После завершения переноса: