При интеграции i18next и vue-i18next во
Vue-приложение часто возникает необходимость быстро подставлять переводы
прямо в шаблонах компонентов. Для этой задачи используется директива
v-t.
Директива v-t позволяет:
Базовый пример:
<p v-t="'welcome'"></p>
Если для текущего языка существует ключ:
{
"welcome": "Добро пожаловать"
}
то итоговый HTML будет выглядеть так:
<p>Добро пожаловать</p>
После установки vue-i18next директива обычно
регистрируется автоматически вместе с экземпляром
i18next.
Пример конфигурации:
import Vue from 'vue'
import i18next from 'i18next'
import VueI18Next from '@panter/vue-i18next'
Vue.use(VueI18Next)
i18next.init({
lng: 'ru',
resources: {
ru: {
translation: {
hello: 'Привет'
}
}
}
})
const i18n = new VueI18Next(i18next)
new Vue({
i18n,
render: h => h(App)
}).$mount('#app')
После этого директива становится доступной во всех компонентах.
<span v-t="'hello'"></span>
Результат:
<span>Привет</span>
<button v-t="'buttons.save'"></button>
JSON:
{
"buttons": {
"save": "Сохранить"
}
}
<h1 v-t="'page.title'"></h1>
v-t от
$tВ i18next для Vue существует два основных подхода:
$t<p>{{ $t('hello') }}</p>
v-t<p v-t="'hello'"></p>
Оба варианта дают одинаковый результат, однако между ними есть важные различия.
v-t<li v-t="'menu.profile'"></li>
вместо:
<li>{{ $t('menu.profile') }}</li>
Директива напрямую изменяет содержимое элемента.
<div v-t="'loading'"></div>
В длинных шаблонах директива делает структуру более компактной.
<ul>
<li v-t="'menu.home'"></li>
<li v-t="'menu.about'"></li>
<li v-t="'menu.contacts'"></li>
</ul>
v-t поддерживает interpolation — подстановку значений
внутрь строки.
{
"greeting": "Привет, {{name}}"
}
<p v-t="{ path: 'greeting', args: { name: userName } }"></p>
<p>Привет, Алексей</p>
Директива может принимать объект с параметрами.
<p
v-t="{
path: 'message',
args: { count: 5 },
locale: 'ru'
}"
></p>
pathОпределяет ключ перевода.
<p v-t="{ path: 'auth.login' }"></p>
argsПередаёт параметры интерполяции.
{
"cart": {
"items": "Товаров: {{count}}"
}
}
<p v-t="{ path: 'cart.items', args: { count: total } }"></p>
localeПозволяет явно указать язык.
<p
v-t="{
path: 'hello',
locale: 'en'
}"
></p>
Даже если текущий язык приложения — русский, текст будет выведен на английском.
i18next поддерживает множественные формы.
{
"item_one": "{{count}} товар",
"item_few": "{{count}} товара",
"item_many": "{{count}} товаров"
}
<p
v-t="{
path: 'item',
args: { count: itemsCount }
}"
></p>
Иногда переводы содержат HTML-разметку.
{
"warning": "Нажмите <strong>сюда</strong>"
}
<div v-html="$t('warning')"></div>
Для v-t подобный подход не подходит напрямую, потому что
директива устанавливает текстовое содержимое.
Следовательно:
<div v-t="'warning'"></div>
выведет:
<div>Нажмите <strong>сюда</strong></div>
без интерпретации HTML.
v-t изменяет только текст внутри элемента. Атрибуты она
не локализует.
Неправильно:
<input v-t="'placeholder.name'">
Для атрибутов используется привязка:
<input :placeholder="$t('placeholder.name')">
title<button :title="$t('tooltips.save')">
Save
</button>
alt<img
src="logo.png"
:alt="$t('logoAlt')"
>
v-t и Vue-выраженийДопустимо использовать директиву вместе с другими возможностями Vue.
<p
v-if="isAuthorized"
v-t="'profile.authorized'"
></p>
<li
v-for="item in menu"
:key="item.id"
v-t="item.translationKey"
></li>
Ключ перевода может вычисляться динамически.
<p v-t="currentKey"></p>
data() {
return {
currentKey: 'messages.success'
}
}
При изменении языка директива автоматически обновляет содержимое элементов.
i18next.changeLanguage('en')
После вызова все элементы с v-t будут перерисованы.
Если ключ отсутствует:
<p v-t="'unknown.key'"></p>
i18next может:
i18next.init({
lng: 'ru',
fallbackLng: 'en'
})
namespaces)В крупных приложениях переводы разделяются по namespace.
i18next.init({
ns: ['common', 'auth'],
defaultNS: 'common'
})
<p v-t="'auth:login.title'"></p>
При больших объёмах локализации переводы загружаются динамически.
i18next.loadNamespaces('dashboard')
После загрузки namespace директива начнёт использовать новые переводы автоматически.
v-t особенно популярна в проектах на Vue 2, где
директивы активно применяются для работы с DOM.
Пример:
<template>
<section>
<h1 v-t="'home.title'"></h1>
<p v-t="'home.description'"></p>
</section>
</template>
В Vue 3 чаще используется Composition API и функция t,
однако v-t по-прежнему может применяться в совместимых
библиотеках.
Директива может быть немного эффективнее интерполяции через
{{ }}, поскольку обновляет DOM напрямую.
Особенно заметно это при:
v-t<div v-t="'html.content'"></div>
HTML будет экранирован.
Для placeholder, title,
aria-label и других атрибутов требуется
$t.
Иногда интерполяция удобнее:
<p>{{ $t(dynamicKey) }}</p>
computed: {
statusKey() {
return `statuses.${this.status}`
}
}
<p v-t="statusKey"></p>
{
"title1": "Главная",
"title2": "Контакты"
}
{
"pages": {
"home": {
"title": "Главная"
},
"contacts": {
"title": "Контакты"
}
}
}
export const I18N_KEYS = {
SAVE_BUTTON: 'buttons.save',
DELETE_BUTTON: 'buttons.delete'
}
<button v-t="I18N_KEYS.SAVE_BUTTON"></button>
v-t<p v-t="'missing.key'"></p>
Результат:
<p>missing.key</p>
Неправильно:
<p v-t="{ key: 'hello' }"></p>
Правильно:
<p v-t="{ path: 'hello' }"></p>
<p v-t="'login.title'"></p>
если перевод находится в auth, ключ не будет найден.
Правильно:
<p v-t="'auth:login.title'"></p>
В enterprise-проектах директива часто применяется:
<template>
<div class="profile">
<h1 v-t="'profile.title'"></h1>
<p
v-t="{
path: 'profile.messages',
args: { count: messagesCount }
}"
></p>
<button v-t="'buttons.logout'"></button>
</div>
</template>
<script>
export default {
data() {
return {
messagesCount: 12
}
}
}
</script>
| Подход | Преимущества | Недостатки |
|---|---|---|
v-t |
Компактность, прямое обновление DOM | Не работает с HTML и атрибутами |
$t() |
Гибкость, поддержка атрибутов | Более многословный синтаксис |
v-t,
когда:$t, когда:<button v-t="'buttons.submit'"></button>
<li v-t="'menu.settings'"></li>
<h2 v-t="'pages.dashboard.title'"></h2>
<span v-t="'status.loading'"></span>
При серверном рендеринге директива работает корректно, если:
i18next синхронизирован между сервером и
клиентом.Для повышения надёжности можно типизировать ключи переводов.
type TranslationKeys =
| 'buttons.save'
| 'buttons.cancel'
| 'profile.title'
<button v-t="translationKey"></button>
locales/
├── ru/
│ ├── auth.json
│ ├── dashboard.json
│ └── common.json
└── en/
├── auth.json
├── dashboard.json
└── common.json
Хороший стиль:
{
"buttons": {
"save": "Сохранить"
}
}
Плохой стиль:
{
"SaveButtonText": "Сохранить"
}
Для диагностики удобно включать режим debug.
i18next.init({
debug: true
})
i18next начнёт выводить в консоль:
<p>
{{ $t('unknown.key', 'Текст по умолчанию') }}
</p>
Для v-t подобный fallback обычно настраивается глобально
через конфигурацию i18next.
v-if<p
v-if="hasError"
v-t="'errors.network'"
></p>
v-show<p
v-show="isLoading"
v-t="'loading'"
></p>
<BaseButton v-t="'buttons.ok'" />
Корректная работа зависит от того, как компонент обрабатывает содержимое слота и DOM-элементы.
Во время монтирования директива:
i18next;textContent элемента;При смене локали выполняется повторное обновление текста.