Nuxt.js предоставляет гибридную модель рендеринга (SSR + CSR), что делает задачу интернационализации принципиально сложнее, чем в чисто клиентских SPA. Любая система перевода должна учитывать два контекста выполнения: серверный и браузерный, синхронизировать состояние языков между ними и сохранять согласованность маршрутов.
i18next выступает как независимый слой локализации, не привязанный к Vue или Nuxt напрямую. Это позволяет использовать его как универсальный движок переводов внутри Nuxt через плагины и адаптеры, сохраняя контроль над конфигурацией и стратегией загрузки ресурсов.
Интеграция строится вокруг создания единственного экземпляра i18next и его проброса в контекст приложения Nuxt.
Ключевая цель архитектуры:
В SSR-режиме Nuxt обрабатывает каждый запрос отдельно. Это означает:
Типичная проблема:
Решение заключается в фабричной функции создания i18next:
export function createI18nInstance(locale) {
const i18n = i18next.createInstance();
i18n.init({
lng: locale,
fallbackLng: 'en',
interpolation: {
escapeValue: false
}
});
return i18n;
}
В Nuxt архитектура расширяется через plugins слой. Плагин отвечает за:
export default defineNuxtPlugin((nuxtApp) => {
const locale = useCookie('locale').value || 'en';
const i18n = createI18nInstance(locale);
nuxtApp.provide('i18n', i18n);
});
Доступ в компонентах:
const { $i18n } = useNuxtApp();
$i18n.t('common.title');
i18next строится на концепции namespaces, позволяющей разделять переводы по доменам:
Lazy-loading реализуется через backend загрузчик:
import HttpBackend from 'i18next-http-backend';
i18next
.use(HttpBackend)
.init({
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json'
},
ns: ['common'],
defaultNS: 'common'
});
Nuxt требует предварительной загрузки переводов на сервере до рендера HTML.
Ключевой момент:
Пример server hook:
export default defineNuxtPlugin(async (nuxtApp) => {
const event = useRequestEvent();
const acceptLang = event.node.req.headers['accept-language'];
const locale = detectLocale(acceptLang);
const i18n = createI18nInstance(locale);
await i18n.loadNamespaces(['common']);
nuxtApp.provide('i18n', i18n);
});
Nuxt маршруты должны учитывать язык как часть URL:
Реализация обычно строится через middleware:
export default defineNuxtRouteMiddleware((to) => {
const locale = to.params.locale || 'en';
const i18n = useNuxtApp().$i18n;
i18n.changeLanguage(locale);
});
Проблема синхронизации:
Решение — единый источник истины: URL.
i18next сам по себе не реактивен. Для Nuxt/Vue необходимо оборачивать состояние языка:
const locale = ref(i18n.language);
watch(locale, (lng) => {
i18n.changeLanguage(lng);
});
Изменение языка должно:
Сохранение выбора языка:
const localeCookie = useCookie('locale');
localeCookie.value = lng;
При SSR cookie становится источником языка на сервере.
Приоритет:
i18next поддерживает интерполяцию переменных:
i18n.t('welcome', {
name: 'Alex'
});
JSON:
{
"welcome": "Hello {{name}}"
}
Расширенные кейсы:
{
"item": "{{count}} item",
"item_plural": "{{count}} items"
}
Использование:
i18n.t('item', { count: 5 });
i18next автоматически выбирает форму на основе языка.
SSR + i18next часто вызывает расхождения HTML:
Причины:
Решение:
if (process.server) {
nuxtApp.payload.i18n = {
language: i18n.language,
resources: i18n.services.resourceStore.data
};
}
На клиенте:
i18next.init({
lng: nuxtApp.payload.i18n.language,
resources: nuxtApp.payload.i18n.resources
});
Типичная организация:
/locales
/en
common.json
auth.json
/ru
common.json
auth.json
/plugins
i18n.js
/utils
i18n.js
Для повышения производительности:
i18next backend поддерживает caching layer:
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json',
requestOptions: {
cache: 'force-cache'
}
}
Nuxt 3 использует Composition API:
export function useI18n() {
const { $i18n } = useNuxtApp();
return {
t: $i18n.t.bind($i18n),
language: $i18n.language
};
}
Nuxt SSR позволяет генерировать уникальные HTML для каждого языка:
i18next используется только как слой перевода, не как SEO-движок, но влияет на генерацию контента сервера.
Оптимизационные техники: