Компонент i18n-t

Компонент i18n-t используется для интерполяции и локализации HTML-структур, Vue-компонентов и сложных текстовых шаблонов внутри переводов. Основная задача — безопасно внедрять разметку и динамические элементы в локализованные строки без ручной сборки текста.

В отличие от обычного вызова t(), компонент позволяет:

  • вставлять HTML-теги из шаблона перевода;
  • комбинировать перевод с Vue-компонентами;
  • использовать параметры интерполяции;
  • поддерживать сложные структуры текста;
  • избегать небезопасного использования v-html.

Наиболее часто i18n-t применяется для:

  • ссылок внутри текста;
  • выделения частей текста;
  • встраивания кнопок и компонентов;
  • локализации абзацев со сложной разметкой;
  • мультиязычных уведомлений и предупреждений.

Базовый синтаксис

Простейший пример

<i18n-t keypath="welcome">
</i18n-t>
{
  "welcome": "Добро пожаловать"
}

Компонент рендерит перевод по ключу welcome.


Использование HTML-разметки внутри перевода

Перевод с тегами

{
  "message": "Нажмите <strong>сюда</strong> для продолжения"
}
<i18n-t keypath="message" tag="p" />

Результат:

<p>
  Нажмите <strong>сюда</strong> для продолжения
</p>

Атрибут tag

Атрибут tag задаёт корневой HTML-элемент.

<i18n-t keypath="message" tag="div" />
<div>
  Нажмите <strong>сюда</strong> для продолжения
</div>

Вставка Vue-компонентов

Одно из главных преимуществ i18n-t — возможность внедрять компоненты прямо в перевод.

Перевод со слотами

{
  "terms": "Примите условия {0}"
}
<i18n-t keypath="terms" tag="p">
  <a href="/terms">пользовательского соглашения</a>
</i18n-t>

Результат:

<p>
  Примите условия
  <a href="/terms">пользовательского соглашения</a>
</p>

Позиционные параметры

Компонент использует индексированные плейсхолдеры:

{
  "example": "Это {0} пример"
}
<i18n-t keypath="example">
  <strong>важный</strong>
</i18n-t>

Результат:

Это <strong>важный</strong> пример

Несколько компонентов

{
  "notification": "{0} отправил сообщение {1}"
}
<i18n-t keypath="notification">
  <strong>Администратор</strong>
  <a href="/messages">открыть</a>
</i18n-t>

Именованные слоты

Помимо индексированных параметров поддерживаются именованные слоты.

Перевод

{
  "policy": "Прочитайте {link}"
}

Компонент

<i18n-t keypath="policy">
  <template #link>
    <a href="/policy">политику конфиденциальности</a>
  </template>
</i18n-t>

Результат

Прочитайте
<a href="/policy">политику конфиденциальности</a>

Интерполяция значений

i18n-t поддерживает передачу параметров через атрибут values.

Пример

{
  "balance": "Баланс пользователя {name}: {amount}"
}
<i18n-t
  keypath="balance"
  :values="{
    name: 'Алексей',
    amount: '$250'
  }"
/>

Результат:

Баланс пользователя Алексей: $250

Одновременное использование компонентов и параметров

Перевод

{
  "email": "{0}, ваш email: {email}"
}

Компонент

<i18n-t
  keypath="email"
  :values="{ email: 'admin@example.com' }"
>
  <strong>Администратор</strong>
</i18n-t>

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

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

import { useI18n } from 'vue-i18n'

export default {
  setup() {
    const { locale } = useI18n()

    return {
      locale
    }
  }
}

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

<i18n-t keypath="message" tag="p" />

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

Установка

npm install vue-i18n

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

export default defineNuxtConfig({
  modules: ['@nuxtjs/i18n']
})

Компонент

<i18n-t keypath="welcome" />

Работа с HTML и безопасностью

Проблема v-html

При использовании:

<div v-html="$t('message')"></div>

появляются риски:

  • XSS-уязвимости;
  • внедрение вредоносного HTML;
  • сложность контроля структуры;
  • небезопасная интерполяция данных.

Преимущества i18n-t

Компонент:

  • безопасно рендерит структуру;
  • контролирует вставляемые компоненты;
  • не требует ручного HTML-парсинга;
  • интегрируется со слотами Vue.

Использование с маршрутизацией

Локализация ссылок

{
  "profile": "Перейдите в {0}"
}
<i18n-t keypath="profile">
  <RouterLink to="/profile">
    профиль
  </RouterLink>
</i18n-t>

Использование с UI-компонентами

Пример с кнопкой

{
  "action": "Для продолжения нажмите {0}"
}
<i18n-t keypath="action">
  <BaseButton>
    Продолжить
  </BaseButton>
</i18n-t>

Вложенные компоненты

Пример

{
  "complex": "{0} выполнил действие {1}"
}
<i18n-t keypath="complex">
  <UserAvatar />
  <ActionButton />
</i18n-t>

Использование нескольких языков

Русский

{
  "register": "Создать {0}"
}

Английский

{
  "register": "Create {0}"
}

Компонент

<i18n-t keypath="register">
  <a href="/signup">аккаунт</a>
</i18n-t>

После смены локали структура останется неизменной, изменится только текст перевода.


Локализация сложных предложений

Разные языки имеют различный порядок слов. i18n-t позволяет не зависеть от фиксированной структуры шаблона.

Русский

{
  "article": "Статья опубликована пользователем {0}"
}

Японский

{
  "article": "{0} が記事を公開しました"
}

Компонент

<i18n-t keypath="article">
  <strong>Admin</strong>
</i18n-t>

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

Перевод

{
  "messages": "Нет сообщений | {count} сообщение | {count} сообщения | {count} сообщений"
}

Компонент

<i18n-t
  keypath="messages"
  :plural="count"
  :values="{ count }"
/>

Атрибут locale

Компонент позволяет явно указывать локаль.

Пример

<i18n-t
  keypath="welcome"
  locale="en"
/>

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

Если перевод отсутствует:

<i18n-t keypath="unknown.key">
  <template #default>
    Текст по умолчанию
  </template>
</i18n-t>

Работа с динамическими ключами

Пример

<i18n-t :keypath="currentKey" />
const currentKey = computed(() => {
  return isAdmin.value
    ? 'admin.message'
    : 'user.message'
})

Использование внутри циклов

<li v-for="item in items" :key="item.id">
  <i18n-t
    :keypath="item.translation"
    :values="item.values"
  />
</li>

Форматирование чисел и дат

Перевод

{
  "date": "Дата: {date}"
}

Компонент

<i18n-t
  keypath="date"
  :values="{
    date: new Intl.DateTimeFormat('ru').format(new Date())
  }"
/>

Использование вместе с n() и d()

Пример

<script setup>
const { n, d } = useI18n()

const price = n(1200, 'currency')
const date = d(new Date(), 'short')
</script>

<template>
  <i18n-t
    keypath="info"
    :values="{ price, date }"
  />
</template>

Комбинирование с условным рендерингом

<i18n-t
  v-if="isAuthorized"
  keypath="authorized"
/>

<i18n-t
  v-else
  keypath="guest"
/>

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

Типизация параметров

interface UserValues {
  name: string
  age: number
}
<script setup lang="ts">
const values: UserValues = {
  name: 'Alex',
  age: 25
}
</script>

<template>
  <i18n-t
    keypath="profile"
    :values="values"
  />
</template>

Производительность

Компонент i18n-t:

  • кэширует переводы;
  • использует реактивность Vue;
  • обновляется при смене локали;
  • поддерживает SSR;
  • оптимизирован для виртуального DOM.

Однако большое количество компонентов внутри циклов может увеличивать стоимость рендера.

Потенциальные проблемы

<div v-for="item in hugeArray">
  <i18n-t :keypath="item.key">
    <ComplexComponent />
  </i18n-t>
</div>

В подобных случаях рекомендуется:

  • минимизировать вложенность;
  • избегать тяжёлых компонентов;
  • использовать мемоизацию;
  • разбивать интерфейс на части.

SSR и Hydration

i18n-t корректно работает при серверном рендеринге.

Особенно важно:

  • синхронизировать локаль между сервером и клиентом;
  • избегать случайной генерации текста;
  • не использовать разные переводы на сервере и клиенте.

Пример ошибки hydration

locale.value = navigator.language

На сервере и клиенте значение может отличаться.


Отличия i18n-t от $t

Возможность $t() i18n-t
Простые строки Да Да
HTML-разметка Ограниченно Да
Vue-компоненты Нет Да
Слоты Нет Да
Безопасный рендер Частично Да
Сложная структура Нет Да

Когда использовать i18n-t

Компонент особенно полезен в следующих случаях:

  • текст содержит ссылки;
  • требуется HTML-разметка;
  • используются UI-компоненты;
  • порядок слов зависит от языка;
  • перевод содержит выделения;
  • нужна безопасная вставка структуры.

Для простых строк предпочтительнее использовать обычный t() или $t().


Типичные ошибки

Использование HTML вместо компонентов

Нежелательно:

{
  "text": "Нажмите <a href='/home'>сюда</a>"
}

Лучше:

{
  "text": "Нажмите {0}"
}
<i18n-t keypath="text">
  <RouterLink to="/home">
    сюда
  </RouterLink>
</i18n-t>

Смешивание бизнес-логики и перевода

Плохо:

<i18n-t :keypath="isAdmin ? 'a' : 'b'" />

Лучше:

const translationKey = computed(() => {
  return isAdmin.value
    ? 'a'
    : 'b'
})

Жёстко зафиксированный порядок слов

Нежелательно:

{
  "message": "Пользователь {0} создал {1}"
}

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

Для таких случаев лучше применять именованные слоты.


Практический пример формы регистрации

Переводы

{
  "register": "Создавая аккаунт, вы принимаете {terms} и {policy}"
}

Компонент

<i18n-t keypath="register" tag="p">
  <template #terms>
    <RouterLink to="/terms">
      условия использования
    </RouterLink>
  </template>

  <template #policy>
    <RouterLink to="/policy">
      политику конфиденциальности
    </RouterLink>
  </template>
</i18n-t>

Результат

<p>
  Создавая аккаунт, вы принимаете
  <a href="/terms">
    условия использования
  </a>
  и
  <a href="/policy">
    политику конфиденциальности
  </a>
</p>