Установка vue-i18next

Библиотека 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.


Инициализация i18next

Создаётся отдельный файл конфигурации.

src/i18n/index.js

import 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

Подключение в Vue-приложение

Vue 3

main.js

import { 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>

Использование Composition API

Во 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.


Использование HTTP Backend

В крупных приложениях переводы обычно загружаются динамически.

Инициализация backend

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.


Автоматическое определение языка браузера

Подключение detector

import LanguageDetector from 'i18next-browser-languagedetector'

Конфигурация

i18next
  .use(LanguageDetector)
  .use(initVueI18next)
  .init({
    fallbackLng: 'en'
  })

Теперь язык определяется автоматически:

  • navigator.language
  • cookie
  • localStorage
  • query parameters
  • html lang

Использование namespaces

Namespaces позволяют разделять переводы по модулям.

Структура

locales/
├── en/
│   ├── common.json
│   └── auth.json
└── ru/
    ├── common.json
    └── auth.json

Конфигурация namespaces

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
    }
  }
})

Использование namespace в переводах

<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-языки

Fallback используется при отсутствии перевода.

i18next.init({
  lng: 'kk',

  fallbackLng: 'ru'
})

Если казахский перевод отсутствует, будет использован русский.


Отключение экранирования HTML

По умолчанию Vue безопасно обрабатывает HTML, поэтому в большинстве случаев можно отключить escape.

interpolation: {
  escapeValue: false
}

Lazy Loading переводов

Для крупных приложений используется асинхронная подгрузка.

Конфигурация

import Backend from 'i18next-http-backend'

i18next
  .use(Backend)
  .init({
    backend: {
      loadPath: '/locales/{{lng}}/{{ns}}.json'
    }
  })

Переводы будут загружаться только при необходимости.


Использование Suspense во Vue 3

При асинхронной загрузке переводов удобно использовать Suspense.

<Suspense>
  <App />
</Suspense>

Интеграция с TypeScript

Типизация переводов

import 'i18next'

declare module 'i18next' {
  interface CustomTypeOptions {
    defaultNS: 'translation'
  }
}

Типизированный перевод

const text = t('welcome')

TypeScript сможет проверять корректность ключей при правильной настройке типов.


Использование в Options API

Компонент

<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'
})

Поддержка SSR

При использовании Nuxt или серверного рендеринга важно:

  • избегать глобального singleton i18next;
  • создавать отдельный instance на каждый request;
  • синхронизировать язык между сервером и клиентом.

Пример создания экземпляра:

import i18next from 'i18next'

export function createI18n() {
  return i18next.createInstance()
}

Распространённые ошибки

Отсутствие plugin registration

Ошибка:

$t is not a function

Причина:

app.use(i18next)

не был вызван.


Неверный namespace

Ошибка:

missingKey

Причина:

$t('auth:login')

при отсутствии namespace auth.


Неверный путь загрузки

Ошибка HTTP 404 при загрузке переводов.

Причина:

loadPath: '/locales/{{lng}}/{{ns}}.json'

не соответствует реальной структуре каталогов.


Рекомендуемая архитектура

Для больших приложений обычно используются:

  • отдельные namespaces для модулей;
  • lazy loading;
  • backend-загрузка;
  • автоматическое определение языка;
  • fallback language;
  • хранение локали в localStorage;
  • типизация переводов через TypeScript.

Пример структуры:

src/
├── i18n/
│   ├── index.js
│   ├── config.js
│   └── plugins.js
├── locales/
│   ├── en/
│   ├── ru/
│   └── kk/
└── modules/

Полная конфигурация production-уровня

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