В экосистеме i18next переводами управляют как набором ресурсов, обычно представленных JSON-структурами вида:
{
"common": {
"save": "Сохранить",
"cancel": "Отмена"
}
}
В классическом сценарии такие файлы считаются источником истины, однако в реальных проектах часто возникает обратный поток данных: переводы редактируются вне приложения (в CMS, сервисах локализации или через команду переводчиков) и затем должны быть импортированы обратно в приложение без потери структуры, ключей и fallback-логики.
Обратный импорт — это процесс синхронизации внешних переводов с внутренними ресурсами i18next.
При возврате переводов в приложение встречаются несколько распространённых форматов:
JSON (i18next resource bundles) Наиболее прямой формат, совместимый с загрузчиками i18next.
{
"common": {
"save": "Save",
"cancel": "Cancel"
}
}
CSV (табличные выгрузки) Используется в системах локализации и при работе с переводчиками.
| key | en | ru |
|---|---|---|
| common.save | Save | Сохранить |
| common.cancel | Cancel | Отмена |
XLIFF / PO Форматы, характерные для профессиональных переводческих пайплайнов.
При обратном импорте все они должны быть нормализованы в структуру ресурсов i18next.
Типовой процесс включает несколько стадий:
Получение внешнего источника
Нормализация данных
common.save) в вложенные
объектыen,
ru, pt-BR)Слияние с существующими ресурсами
Загрузка в i18next resource store
addResourceBundlei18next предоставляет API для динамического обновления ресурсов без перезапуска приложения.
i18next.addResourceBundle(
'ru',
'common',
{
save: 'Сохранить',
cancel: 'Отмена'
},
true,
true
);
Параметры:
lng — языкnamespace — пространство имёнresources — объект переводовdeep — глубокое слияниеoverwrite — перезапись существующих ключейПри использовании backend-модулей (например, файлового или HTTP) обратный импорт часто реализуется как перезапись файлов или обновление источника данных.
Пример с файловым backend:
import Backend from 'i18next-fs-backend';
i18next.use(Backend).init({
backend: {
loadPath: './locales/{{lng}}/{{ns}}.json'
}
});
После импорта внешняя система может перезаписать:
locales/ru/common.json
Далее i18next либо автоматически подхватит изменения (watch mode), либо выполнит повторную загрузку ресурсов.
При обратном импорте ключевая проблема — конфликт версий переводов.
Используется, когда внешняя система считается единственным источником истины.
i18next.addResourceBundle('ru', 'common', newTranslations, true, true);
Недостаток — возможная потеря локальных изменений.
Сохраняет существующие ключи и добавляет новые.
i18next.addResourceBundle('ru', 'common', newTranslations, false, true);
Поведение:
Используется в системах с несколькими уровнями переводов:
i18next автоматически использует fallbackLng:
i18next.init({
fallbackLng: 'en',
lng: 'ru'
});
Внешние системы часто хранят ключи в плоском виде:
{
"common.save": "Сохранить"
}
Для i18next требуется вложенная структура:
{
"common": {
"save": "Сохранить"
}
}
Функция нормализации:
function normalize(flat) {
const result = {};
for (const key in flat) {
const parts = key.split('.');
let current = result;
parts.forEach((part, index) => {
if (index === parts.length - 1) {
current[part] = flat[key];
} else {
current[part] = current[part] || {};
current = current[part];
}
});
}
return result;
}
В архитектуре микросервисов переводы часто приходят через API:
async function loadTranslations(lng) {
const res = await fetch(`/api/translations/${lng}`);
const data = await res.json();
Object.keys(data).forEach(ns => {
i18next.addResourceBundle(lng, ns, data[ns], true, true);
});
}
Такой подход позволяет:
При интеграции внешних переводов важно учитывать версионность:
{
"version": 12,
"translations": {
"common": {
"save": "Сохранить"
}
}
}
Логика обработки:
Обратный импорт часто приносит избыточные ключи. Для синхронизации используется процедура pruning:
function pruneTranslations(current, incoming) {
const result = {};
for (const key in current) {
if (incoming[key]) {
result[key] = incoming[key];
}
}
return result;
}
Это позволяет:
При динамическом импорте важно учитывать кэширование:
i18next.services.resourceStore.data = {};
i18next.reloadResources();
Также в HTTP-бэкендах применяются:
Типичная схема:
Конфликты возникают при одновременных изменениях:
Стратегии:
Timestamp-based
последняя модификация выигрывает
Namespace isolation
разные источники → разные namespaces
Manual resolution layer
конфликтные ключи помещаются в отдельный файл review.json
Для больших проектов используется частичный импорт:
i18next.addResourceBundle('ru', 'common', {
save: 'Сохранить'
}, true, true);
или выборочное обновление:
i18next.addResource('ru', 'common', 'save', 'Сохранить');
Это снижает риск перезаписи и позволяет обновлять только изменённые строки.
При работе в браузере или Node.js можно организовать фоновую синхронизацию:
setInterval(async () => {
const updates = await fetchUpdates();
updates.forEach(u => {
i18next.addResourceBundle(u.lng, u.ns, u.data, true, true);
});
}, 60000);
Такой подход применяется для: