Библиотека i18next используется для интернационализации приложений и
предоставляет гибкий механизм управления переводами, переключения
языков, форматирования и локализации интерфейса. В экосистеме Vue
библиотека часто применяется совместно с адаптером
react-i18next для React или напрямую через API
i18next. Для Vue обычно используется связка с
vue-i18next либо собственный composable-слой.
Структура проекта с поддержкой нескольких языков часто выглядит следующим образом:
src/
├── i18n/
│ ├── index.js
│ ├── locales/
│ │ ├── ru.json
│ │ ├── en.json
│ │ └── kz.json
├── components/
├── App.vue
└── main.js
Установка зависимостей:
npm install i18next
Базовая конфигурация:
// src/i18n/index.js
import i18next from 'i18next'
import ru from './locales/ru.json'
import en from './locales/en.json'
import kz from './locales/kz.json'
i18next.init({
lng: 'ru',
fallbackLng: 'en',
resources: {
ru: {
translation: ru
},
en: {
translation: en
},
kz: {
translation: kz
}
}
})
export default i18next
Файлы переводов:
// ru.json
{
"title": "Главная страница",
"welcome": "Добро пожаловать"
}
// en.json
{
"title": "Home page",
"welcome": "Welcome"
}
После настройки экземпляр i18next импортируется в
компоненты.
Пример компонента:
<script setup>
import i18next from '@/i18n'
</script>
<template>
<div>
<h1>{{ i18next.t('title') }}</h1>
<p>{{ i18next.t('welcome') }}</p>
</div>
</template>
Метод t() выполняет поиск перевода по ключу.
Основная функция переключения языка —
changeLanguage().
Простейший пример:
<script setup>
import i18next from '@/i18n'
const switchLanguage = (lang) => {
i18next.changeLanguage(lang)
}
</script>
<template>
<div>
<button @click="switchLanguage('ru')">
Русский
</button>
<button @click="switchLanguage('en')">
English
</button>
<button @click="switchLanguage('kz')">
Қазақша
</button>
</div>
</template>
При вызове метода:
i18next.changeLanguage('en')
библиотека:
Проблема прямого использования i18next.t() внутри
шаблона Vue заключается в отсутствии реактивности. Vue не отслеживает
внутренние изменения состояния i18next.
Без дополнительной логики интерфейс может не обновиться автоматически после переключения языка.
Распространённое решение — использовать ref.
// composables/useI18n.js
import { ref } from 'vue'
import i18next from '@/i18n'
const currentLanguage = ref(i18next.language)
i18next.on('languageChanged', (lng) => {
currentLanguage.value = lng
})
export function useI18n() {
const t = (key) => {
currentLanguage.value
return i18next.t(key)
}
const changeLanguage = (lng) => {
i18next.changeLanguage(lng)
}
return {
t,
changeLanguage,
currentLanguage
}
}
Использование:
<script setup>
import { useI18n } from '@/composables/useI18n'
const {
t,
changeLanguage,
currentLanguage
} = useI18n()
</script>
<template>
<div>
<h1>{{ t('title') }}</h1>
<p>
Текущий язык:
{{ currentLanguage }}
</p>
<button @click="changeLanguage('ru')">
RU
</button>
<button @click="changeLanguage('en')">
EN
</button>
</div>
</template>
Теперь Vue реагирует на изменение языка и автоматически перерисовывает компонент.
После обновления страницы язык по умолчанию обычно сбрасывается. Для
сохранения выбранной локали применяется localStorage.
const changeLanguage = (lng) => {
localStorage.setItem('language', lng)
i18next.changeLanguage(lng)
}
const savedLanguage =
localStorage.getItem('language') || 'ru'
i18next.init({
lng: savedLanguage,
fallbackLng: 'en',
resources: {
ru: {
translation: ru
},
en: {
translation: en
}
}
})
Теперь приложение восстанавливает язык пользователя автоматически.
i18next умеет определять язык браузера через плагин
i18next-browser-languagedetector.
Установка:
npm install i18next-browser-languagedetector
Настройка:
import i18next from 'i18next'
import LanguageDetector from 'i18next-browser-languagedetector'
i18next
.use(LanguageDetector)
.init({
fallbackLng: 'en',
resources: {
ru: {
translation: ru
},
en: {
translation: en
}
}
})
Если браузер пользователя работает на русском языке, автоматически
будет выбрана локаль ru.
Часто применяется следующая схема:
localStorage;Настройка:
detection: {
order: ['localStorage', 'navigator'],
caches: ['localStorage']
}
Полная конфигурация:
i18next
.use(LanguageDetector)
.init({
fallbackLng: 'en',
detection: {
order: ['localStorage', 'navigator'],
caches: ['localStorage']
},
resources: {
ru: {
translation: ru
},
en: {
translation: en
}
}
})
В больших приложениях хранение всех переводов в одном бандле увеличивает размер JavaScript-файлов.
Оптимальное решение — ленивое подключение локалей.
async function loadLanguage(lang) {
const translations = await import(
`./locales/${lang}.json`
)
i18next.addResourceBundle(
lang,
'translation',
translations.default
)
await i18next.changeLanguage(lang)
}
Использование:
await loadLanguage('en')
Распространённый интерфейс — <select>.
<script setup>
import { useI18n } from '@/composables/useI18n'
const {
currentLanguage,
changeLanguage
} = useI18n()
</script>
<template>
<sel ect
:value="currentLanguage"
@change="changeLanguage($event.target.value)"
>
<option value="ru">
Русский
</option>
<option value="en">
English
</option>
<option value="kz">
Қазақша
</option>
</select>
</template>
Во многих приложениях язык хранится в адресной строке:
/ru/products
/en/products
Это полезно для:
import { useRoute } fr om 'vue-router'
const route = useRoute()
const lang = route.params.lang
i18next.changeLanguage(lang)
router.beforeEach((to, from, next) => {
const lang = to.params.lang
if (lang) {
i18next.changeLanguage(lang)
}
next()
})
Чтобы не импортировать i18next в каждый компонент,
создаётся глобальный плагин.
// plugins/i18n.js
import i18next from '@/i18n'
export default {
install(app) {
app.config.globalProperties.$t =
i18next.t.bind(i18next)
app.config.globalProperties.$i18n =
i18next
}
}
Подключение:
import { createApp } from 'vue'
import App from './App.vue'
import i18nPlugin from './plugins/i18n'
const app = createApp(App)
app.use(i18nPlugin)
app.mount('#app')
Использование:
<template>
<h1>{{ $t('title') }}</h1>
</template>
Если ключ отсутствует:
i18next.t('unknown_key')
будет возвращён сам ключ:
unknown_key
Для отслеживания ошибок локализации используется параметр:
saveMissing: true
Также можно подключить собственный обработчик:
missingKeyHandler: function (
lng,
ns,
key
) {
console.log('Отсутствует перевод:', key)
}
Если перевод отсутствует в текущей локали, i18next может
использовать запасной язык.
Пример:
fallbackLng: 'en'
Сценарий:
// ru.json
{
"title": "Главная"
}
// en.json
{
"title": "Home",
"profile": "Profile"
}
Вызов:
i18next.t('profile')
вернёт:
Profile
поскольку ключ отсутствует в русском переводе.
В крупных проектах переводы разделяются по namespace.
Пример:
locales/
├── ru/
│ ├── common.json
│ ├── auth.json
│ └── profile.json
Настройка:
i18next.init({
ns: ['common', 'auth'],
defaultNS: 'common'
})
Использование:
i18next.t('auth:login')
changeLanguage() возвращает Promise.
await i18next.changeLanguage('en')
Это важно при:
Пример:
const switchLanguage = async (lang) => {
await i18next.changeLanguage(lang)
document.title =
i18next.t('title')
}
После переключения языка желательно обновлять атрибут:
<html lang="ru">
Реализация:
i18next.on('languageChanged', (lng) => {
document.documentElement.lang = lng
})
Это важно для:
Для арабского и иврита требуется направление текста справа налево.
const rtlLanguages = ['ar', 'he']
i18next.on('languageChanged', (lng) => {
document.documentElement.dir =
rtlLanguages.includes(lng)
? 'rtl'
: 'ltr'
})
Ошибка:
{{ i18next.t('title') }}
без отслеживания состояния языка.
Решение — использование ref, composable или интеграции с
реактивной системой Vue.
Причина — отсутствие сохранения в localStorage.
Решение:
localStorage.setItem('language', lng)
Ошибка:
i18next.t('titles')
при наличии:
{
"title": "Главная"
}
Рекомендуется использовать строгую структуру ключей:
{
"pages": {
"home": {
"title": "Главная"
}
}
}
Часто применяется следующая структура:
src/
├── i18n/
│ ├── index.js
│ ├── plugins/
│ ├── composables/
│ ├── locales/
│ │ ├── ru/
│ │ ├── en/
│ │ └── kz/
│ └── helpers/
Где:
locales/ — переводы;composables/ — реактивные хуки;helpers/ — функции локализации;plugins/ — интеграция с Vue.Подобная архитектура упрощает: