логотип PurpleSchool
логотип PurpleSchool

Многоязычные приложения на Vue с i18n

Автор

Алексей Иванов

Введение

Приложения, которые поддерживают несколько языков, становятся стандартом для современных веб-сервисов. Пользователи хотят видеть интерфейс и получать сообщения на своем родном языке, а для разработчика вопрос поддержки нескольких языков больше не роскошь, а востребованная особенность. В экосистеме Vue для этих задач существует мощное и удобное решение — библиотека vue-i18n. С помощью vue-i18n можно буквально за несколько шагов добавить поддержку локализации (интернационализации, или i18n) в Vue-приложение любого масштаба.

В этой статье вы узнаете, как подключить vue-i18n, организовать структуру языковых файлов, менять языки на лету, управлять динамическими переводами и параметрами внутри переводимых строк. Также разберем, какие возможности и подводные камни предоставляет этот популярный инструмент.

Основы интеграции vue-i18n в Vue-приложение

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

Для начала вам потребуется установить саму библиотеку. Сделать это можно через npm или yarn:

npm install vue-i18n@next

Если вы работаете с Vue 2, используйте совместимую версию:

npm install vue-i18n@8

Обратите внимание: для Vue 3 нужен vue-i18n версии 9+, для Vue 2 — версия 8.

Подключение vue-i18n к приложению

Теперь, когда пакет установлен, давайте подключим его к приложению. Смотрите, как это делается для Vue 3 (Composition API):

// main.js
import { createApp } from 'vue'
import { createI18n } from 'vue-i18n'
import App from './App.vue'

// Импортируем объект с переводами
import messages from './locales'

// Инициализируем i18n
const i18n = createI18n({
  locale: 'en', // язык по умолчанию
  fallbackLocale: 'ru', // запасной язык
  messages, // все переводы
})

const app = createApp(App)
app.use(i18n)
app.mount('#app')

Если вы работаете с Vue 2, подключение немного отличается:

// main.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import App from './App.vue'
import messages from './locales'

Vue.use(VueI18n)

const i18n = new VueI18n({
  locale: 'ru',
  fallbackLocale: 'en',
  messages,
})

new Vue({
  i18n,
  render: h => h(App)
}).$mount('#app')

Теперь i18n доступен во всех компонентах.

Организация и хранение переводов

Лучшие практики хранения переводов

Обычно переводы хранят в виде JSON или JS-объектов. На небольших проектах можно все переводы держать внутри одного файла:

// src/locales/index.js
export default {
  en: {
    welcome: "Welcome",
    logout: "Log out",
  },
  ru: {
    welcome: "Добро пожаловать",
    logout: "Выйти",
  }
}

Но если проект масштабируется, стоит разделить переводы по отдельным файлам:

src/
└── locales/
    ├── en.json
    └── ru.json

И собирать их вместе:

import en from './locales/en.json'
import ru from './locales/ru.json'

export default { en, ru }

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

Структура файла с переводом

Используйте вложенность для группировки строк по разделам интерфейса:

{
  "navbar": {
    "home": "Home",
    "about": "About us",
    "contact": "Contacts"
  },
  "auth": {
    "login": "Sign in",
    "logout": "Log out"
  }
}

Эта структура избавляет от конфликтов и дублирования ключей.

Основные возможности vue-i18n

Статические и динамические переводы

Вы легко можете добавить простую статическую строку через директиву $t:

<template>
  <div>{{ $t('welcome') }}</div>
</template>

Если необходимо работать с вложенными ключами, используйте точку:

<template>
  <nav>
    <a>{{ $t('navbar.home') }}</a>
    <a>{{ $t('navbar.about') }}</a>
  </nav>
</template>

Подстановка параметров

Vue-i18n поддерживает шаблоны с параметрами. Пример определения и использования строки с параметром:

{
  "greeting": "Hello, {name}!"
}

Обратите внимание на фигурные скобки для плейсхолдеров. Теперь подставим значение:

<template>
  <div>{{ $t('greeting', { name: userName }) }}</div>
</template>
<script>
export default {
  data() {
    return { userName: 'Alex' }
  }
}
</script>

Результат: "Hello, Alex!"

Плюральные формы

Число может влиять на форму слова. Vue-i18n позволяет определять множественные формы:

{
  "car": "No cars | One car | {count} cars"
}

Вызовите с параметром count:

<div>{{ $tc('car', 0) }}</div> <!-- No cars -->
<div>{{ $tc('car', 1) }}</div> <!-- One car -->
<div>{{ $tc('car', 5) }}</div> <!-- 5 cars -->

Если нужны параметры, комбинируйте их:

<div>{{ $tc('car', carCount, { count: carCount }) }}</div>

А если проект требует поддержки сложных правил множественного числа (например, для русского или польского языков), подключите специальные правила pluralizationRules.

Смена языка на лету

Покажу, как сделать переключатель языков:

<template>
  <select v-model="$i18n.locale">
    <option value="en">English</option>
    <option value="ru">Русский</option>
  </select>
</template>

Обратите внимание: $i18n.locale — реактивное свойство. Изменение его значения тут же переключает все тексты на другой язык.

Если вы используете Composition API, доступ к locale выглядит так:

import { useI18n } from 'vue-i18n'

setup() {
  const { locale } = useI18n()
  // Теперь можно менять locale.value
  return { locale }
}

Загрузка переводов по требованию (Lazy loading)

В больших приложениях грузить все языки сразу — не лучшая идея. Реализуем асинхронную подгрузку языковых файлов:

// Предположим, что мы динамически импортируем нужный язык
async function loadLocaleMessages(i18n, locale) {
  const messages = await import(`./locales/${locale}.json`)
  i18n.global.setLocaleMessage(locale, messages.default)
  i18n.global.locale.value = locale
}

Такой подход позволяет разбивать бандл и не загружать пользователю лишний контент.

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

Кроме шаблонов, переводы доступны в JS-коде компонентов:

methods: {
  showAlert() {
    alert(this.$t('auth.login')) // "Sign in"
  }
}

Или с Composition API:

import { useI18n } from 'vue-i18n'

setup() {
  const { t } = useI18n()
  function showMessage() {
    alert(t('auth.login'))
  }
  return { showMessage }
}

Локализация дат, времени и чисел

Vue-i18n умеет форматировать даты и числа согласно выбранной локали. Для этого надо дополнительно определить форматы:

const i18n = createI18n({
  // ...
  datetimeFormats: {
    en: { short: { year: 'numeric', month: 'short', day: 'numeric' } },
    ru: { short: { year: 'numeric', month: 'short', day: 'numeric' } }
  },
  numberFormats: {
    en: { currency: { style: 'currency', currency: 'USD' } },
    ru: { currency: { style: 'currency', currency: 'RUB' } }
  }
})

Использование в шаблоне:

<div>{{ $d(new Date(), 'short') }}</div> <!-- 18 Mar 2024 -->
<div>{{ $n(2100, 'currency') }}</div> <!-- $2,100.00 / 2 100,00 ₽ -->

Работа с fallback и кастомной логикой

Vue-i18n позволяет назначать fallbackLocale — язык, на который будет переключаться приложение, если для основного не найден перевод. Например, если для 'es' (испанский) нет нужной строки, будет взята версия из 'en' (английский).

Если хотите расширить эту логику (например, fallback только для некоторых ключей), используйте обработчики отсутствующих переводов — missing handlers.

const i18n = createI18n({
  // ...
  missing(locale, key, vm) {
    // Например, выводим в консоль все пропущенные ключи
    console.warn(`Missing translation for key "${key}" in locale "${locale}"`)
    return key // Можно вернуть ключ как fallback
  }
})

Это полезно при разработке и локализации на множество языков.

Локализация на уровне отдельных компонентов

Вы можете определить переводы прямо внутри компонента. Смотрите, как это делается:

export default {
  i18n: {
    messages: {
      en: { hello: "Hello from component" },
      ru: { hello: "Привет из компонента" }
    }
  }
}

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

Советы по архитектуре и оптимизации

  • Старайтесь создавать переводимые строки с уникальными ключами и структурой, чтобы избежать конфликтов между командами.
  • Разносите общие и проектные переводы по разным секциям, чтобы их было легко обновлять.
  • Используйте проверку покрытия переводами (i18n-линтеры или собственные скрипты), чтобы не пропустить строки на новых языках.
  • Актуализируйте переводы одновременно с обновлениями приложения.

Тестирование многоязычного приложения

Убедитесь, что ваше приложение корректно отображает все тексты для разных языков, а переключение языков не вызывает ошибок. Для автоматического тестирования можно использовать Jest с мок-объектами переводов или cypress/end-to-end тесты для проверки UI.

Заключение

Vue-i18n предоставляет мощный, удобный и гибкий инструмент для поддержки многоязычности в приложениях на Vue любого масштаба. Вы можете хранить переводы так, как удобно команде, легко управлять сменой языка, подставлять параметры, обрабатывать plural-формы, локализовывать даты и числа. Подключить и использовать i18n довольно просто, как показано в примерах. Если вы поддерживаете или создаёте многоязычный продукт — не игнорируйте такие возможности, чтобы ваши приложения были удобны для пользователей по всему миру.

Частозадаваемые технические вопросы по теме статьи и ответы на них

Как добавить поддержку RTL (right-to-left) языков (например, арабский или иврит)?

Чтобы корректно отображать RTL-языки, в момент смены языка проверьте, является ли язык RTL, и добавьте в body тег <body dir="rtl">. Пример:

const rtlLanguages = ['ar', 'he']
watch(locale, (val) => {
  document.body.dir = rtlLanguages.includes(val) ? 'rtl' : 'ltr'
})

Используйте подходящие шрифты в CSS и при необходимости меняйте layout компонентов для RTL.

Как собрать отдельные чанки переводов при сборке (code splitting)?

Воспользуйтесь динамическим импортом языковых файлов, как показано выше, и не забудьте указывать алиасы для Webpack/Vite, чтобы импортировать языки по пути вида @/locales/en.json.

Как добавить поддержку локализации маршрутов в Vue Router?

Для route-based переводов создайте отдельные объекты с переведенными путями, и подключайте их к роутеру через функцию-обертку, подставляя актуальный префикс маршрута (например /ru/about).

Как обновлять переводы без перезапуска приложения (например, при редактировании в админке)?

Используйте метод setLocaleMessage для обновления переводов на лету. После загрузки изменений вызовите:

i18n.global.setLocaleMessage(localeCode, newMessages)

и переводы в интерфейсе применятся сразу.

Как вывести список всех ключей с отсутствующими переводами?

Переберите все ключи базового языка, сравните с другими переводами, выведите разницу в консоль или в отчет. Можно использовать готовые linter-плагины (eslint-plugin-i18n-json) или модульные Node.js скрипты для проверки наличия ключей в файлах.

Стрелочка влевоИнтеграция Node.js и Vue.js для разработки приложений

Все гайды по Vue

Работа с пользовательскими интерфейсами и UI библиотеками во VueОрганизация и структура исходных файлов в проектах VueОбзор популярных шаблонов и стартовых проектов на VueКак организовать страницы и маршруты в проекте на VueСоздание серверных приложений на Vue с помощью Nuxt jsРабота со стилями и CSS в Vue js для красивых интерфейсовСоздание и структурирование Vue.js приложенияНастройка и сборка проектов Vue с использованием современных инструментов
Управление переменными и реактивными свойствами во VueИспользование v for и slot в VueТипизация и использование TypeScript в VuejsИспользование шаблонов в Vue js для построения интерфейсовПередача данных между компонентами с помощью props в Vue jsУправление property и функциями во Vue.jsОсновы работы с объектами в VueИспользование метода map в Vue для обработки массивовПонимание жизненного цикла компонента Vue js на примере mountedОбработка пользовательского ввода в Vue.jsОрганизация файлов и структура проекта Vue.jsКомпоненты Vue создание передача данных события и emitИспользование директив и их особенности на Vue с помощью defineСоздание и использование компонентов в Vue JSОбработка кликов и пользовательских событий в Vue
Открыть базу знаний