Библиотека vue-i18next используется для интеграции
системы интернационализации i18next в приложения на Vue.js.
Она предоставляет удобную связку между реактивностью Vue и мощной
системой переводов i18next: поддержкой namespaces, pluralization,
interpolation, lazy loading, fallback-языков и динамического
переключения локалей.
Для базовой интеграции необходимы следующие пакеты:
npm install i18next vue-i18next
При использовании загрузки переводов через HTTP дополнительно устанавливается backend:
npm install i18next-http-backend
Для автоматического определения языка браузера:
npm install i18next-browser-languagedetector
Полный набор зависимостей для типичного SPA:
npm install i18next vue-i18next i18next-http-backend i18next-browser-languagedetector
Для Yarn:
yarn add i18next vue-i18next
Для pnpm:
pnpm add i18next vue-i18next
Перед установкой необходимо учитывать совместимость между версиями Vue и библиотекой интеграции.
| Vue | Библиотека |
|---|---|
| Vue 3 | vue-i18next |
| Vue 2 | vue-i18next + vue-demi |
Современные проекты обычно используют Vue 3.
Проверка версии Vue:
npm list vue
Типичная структура каталогов для локализации:
src/
├── locales/
│ ├── en/
│ │ └── translation.json
│ └── ru/
│ └── translation.json
├── i18n/
│ └── index.js
├── App.vue
└── main.js
Разделение переводов по папкам облегчает поддержку крупных проектов.
{
"welcome": "Добро пожаловать",
"auth": {
"login": "Войти",
"logout": "Выйти"
}
}
{
"welcome": "Welcome",
"auth": {
"login": "Login",
"logout": "Logout"
}
}
Формат JSON является стандартным способом хранения переводов в i18next.
Создаётся отдельный файл конфигурации.
src/i18n/index.jsimport i18next from 'i18next'
import { initVueI18next } from 'vue-i18next'
import ru from '../locales/ru/translation.json'
import en from '../locales/en/translation.json'
i18next.use(initVueI18next).init({
lng: 'ru',
fallbackLng: 'en',
resources: {
ru: {
translation: ru
},
en: {
translation: en
}
},
interpolation: {
escapeValue: false
}
})
export default i18next
main.jsimport { createApp } from 'vue'
import App from './App.vue'
import i18next from './i18n'
const app = createApp(App)
app.use(i18next)
app.mount('#app')
После регистрации plugin переводчик становится доступным во всех компонентах.
$t<template>
<h1>{{ $t('welcome') }}</h1>
</template>
<template>
<button>
{{ $t('auth.login') }}
</button>
</template>
Во Vue 3 часто используется Composition API.
<script setup>
import { useTranslation } from 'vue-i18next'
const { t } = useTranslation()
</script>
<template>
<h1>{{ t('welcome') }}</h1>
</template>
import i18next from 'i18next'
function changeLanguage(lang) {
i18next.changeLanguage(lang)
}
<script setup>
import i18next from 'i18next'
const setRussian = () => {
i18next.changeLanguage('ru')
}
const setEnglish = () => {
i18next.changeLanguage('en')
}
</script>
<template>
<button @click="setRussian">
RU
</button>
<button @click="setEnglish">
EN
</button>
</template>
При изменении языка компоненты автоматически обновляют переводы благодаря реактивности Vue.
В крупных приложениях переводы обычно загружаются динамически.
import i18next from 'i18next'
import Backend from 'i18next-http-backend'
import { initVueI18next } from 'vue-i18next'
i18next
.use(Backend)
.use(initVueI18next)
.init({
lng: 'ru',
fallbackLng: 'en',
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json'
}
})
public/
└── locales/
├── ru/
│ └── translation.json
└── en/
└── translation.json
Такой подход уменьшает размер первоначального bundle.
import LanguageDetector from 'i18next-browser-languagedetector'
i18next
.use(LanguageDetector)
.use(initVueI18next)
.init({
fallbackLng: 'en'
})
Теперь язык определяется автоматически:
navigator.languageNamespaces позволяют разделять переводы по модулям.
locales/
├── en/
│ ├── common.json
│ └── auth.json
└── ru/
├── common.json
└── auth.json
import commonRu from '../locales/ru/common.json'
import authRu from '../locales/ru/auth.json'
import commonEn from '../locales/en/common.json'
import authEn from '../locales/en/auth.json'
i18next.init({
lng: 'ru',
ns: ['common', 'auth'],
defaultNS: 'common',
resources: {
ru: {
common: commonRu,
auth: authRu
},
en: {
common: commonEn,
auth: authEn
}
}
})
<template>
<h1>{{ $t('title') }}</h1>
<button>
{{ $t('auth:login') }}
</button>
</template>
Префикс auth: указывает namespace.
i18next поддерживает подстановку динамических данных.
{
"greeting": "Привет, {{name}}"
}
<template>
<p>{{ $t('greeting', { name: 'Алексей' }) }}</p>
</template>
Результат:
Привет, Алексей
{
"item_one": "{{count}} товар",
"item_few": "{{count}} товара",
"item_many": "{{count}} товаров"
}
<template>
<p>{{ $t('item', { count: 5 }) }}</p>
</template>
i18next автоматически выбирает нужную форму.
Fallback используется при отсутствии перевода.
i18next.init({
lng: 'kk',
fallbackLng: 'ru'
})
Если казахский перевод отсутствует, будет использован русский.
По умолчанию Vue безопасно обрабатывает HTML, поэтому в большинстве случаев можно отключить escape.
interpolation: {
escapeValue: false
}
Для крупных приложений используется асинхронная подгрузка.
import Backend from 'i18next-http-backend'
i18next
.use(Backend)
.init({
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json'
}
})
Переводы будут загружаться только при необходимости.
При асинхронной загрузке переводов удобно использовать
Suspense.
<Suspense>
<App />
</Suspense>
import 'i18next'
declare module 'i18next' {
interface CustomTypeOptions {
defaultNS: 'translation'
}
}
const text = t('welcome')
TypeScript сможет проверять корректность ключей при правильной настройке типов.
<script>
export default {
methods: {
getMessage() {
return this.$t('welcome')
}
}
}
</script>
Иногда перевод требуется в обычных JS-модулях.
import i18next from './i18n'
const message = i18next.t('welcome')
localStorage.setItem('language', 'ru')
const language = localStorage.getItem('language')
i18next.init({
lng: language || 'en'
})
При использовании Nuxt или серверного рендеринга важно:
Пример создания экземпляра:
import i18next from 'i18next'
export function createI18n() {
return i18next.createInstance()
}
Ошибка:
$t is not a function
Причина:
app.use(i18next)
не был вызван.
Ошибка:
missingKey
Причина:
$t('auth:login')
при отсутствии namespace auth.
Ошибка HTTP 404 при загрузке переводов.
Причина:
loadPath: '/locales/{{lng}}/{{ns}}.json'
не соответствует реальной структуре каталогов.
Для больших приложений обычно используются:
Пример структуры:
src/
├── i18n/
│ ├── index.js
│ ├── config.js
│ └── plugins.js
├── locales/
│ ├── en/
│ ├── ru/
│ └── kk/
└── modules/
import i18next from 'i18next'
import Backend from 'i18next-http-backend'
import LanguageDetector from 'i18next-browser-languagedetector'
import { initVueI18next } from 'vue-i18next'
i18next
.use(Backend)
.use(LanguageDetector)
.use(initVueI18next)
.init({
fallbackLng: 'en',
supportedLngs: ['en', 'ru', 'kk'],
ns: ['common', 'auth'],
defaultNS: 'common',
backend: {
loadPath: '/locales/{{lng}}/{{ns}}.json'
},
detection: {
order: [
'localStorage',
'cookie',
'navigator'
],
caches: ['localStorage']
},
interpolation: {
escapeValue: false
},
debug: false
})
export default i18next