Перейти к основному контенту
Tech Path Finder
КурсыИнтервьюКод-ревьюБлог
Tech Path Finder

Персонализированный путеводитель в IT. Квизы, мок-интервью, код ревью и аналитика прогресса.

@potapov_me

Платформа

  • Курсы
  • Прогресс
  • Мок-интервью
  • Код ревью
  • Живое ревью с ИИ
  • Тренажёр переговоров
  • Закладки

Контент

  • Блог
  • Главная
  • Обратная связь

Компания

  • О проекте
  • Тарифы
  • Условия использования
  • Конфиденциальность
  • Согласие на обработку данных
  • Cookie
  • Реквизиты

Аккаунт

  • Войти
  • Зарегистрироваться
  • Профиль

© 2026 Tech Path Finder. Все права защищены.

·ИП Потапов К.С.·Политика конфиденциальности·
Сделано с ❤️ в России
  1. Интернационализация (i18n)
nuxt_i18n

Интернационализация (i18n)

Модуль @nuxtjs/i18n, стратегии локализации URL, useI18n composable, pluralization и lazy-загрузка переводов

Интернационализация с @nuxtjs/i18n

Интернационализация (i18n) — это архитектурный выбор, который лучше сделать в начале проекта. Добавить поддержку нескольких языков постфактум значительно сложнее, чем изначально строить с учётом локализации.

#Зачем нужна i18n и что она решает

i18n решает три класса проблем:

Переводы — замена строк в интерфейсе в зависимости от языка. Это не просто подстановка слов — разные языки имеют разный порядок слов, грамматические формы, длину.

Форматирование — дата 2026-04-03 в русской локали выглядит как 3 апреля 2026 г., в американской как April 3, 2026. Числа, валюты, единицы измерения — всё это локалезависимо.

Маршрутизация — URL может включать код языка: /ru/about, /en/about. Это влияет на SEO и пользовательский опыт.

#Установка и базовая конфигурация

npm install @nuxtjs/i18n

Конфигурация в nuxt.config.ts:

export default defineNuxtConfig({ modules: ['@nuxtjs/i18n'], i18n: { // Список поддерживаемых локалей locales: [ { code: 'ru', name: 'Русский', file: 'ru.json' }, { code: 'en', name: 'English', file: 'en.json' }, { code: 'de', name: 'Deutsch', file: 'de.json' } ], defaultLocale: 'ru', // Путь к файлам переводов langDir: 'locales/', // Стратегия маршрутизации strategy: 'prefix_except_default', // Lazy loading — загружаем переводы по запросу lazy: true } })

#Стратегии маршрутизации

Стратегия определяет как язык отражается в URL:

no_prefix — нет префикса вообще. Язык определяется через cookie или Accept-Language. URL: /about для всех языков. Плохо для SEO — у страниц нет канонических URL по языку.

prefix_except_default — префикс для всех языков кроме дефолтного. URL: /about (русский), /en/about (английский). Компромисс: дефолтный язык не получает дополнительный сегмент URL.

prefix — префикс для всех языков включая дефолтный. URL: /ru/about, /en/about. Идеально для SEO и multilingual сайтов — все URL однозначны.

prefix_and_default — как prefix, но дефолтный язык доступен и с, и без префикса (редирект). Полезно при миграции с существующего сайта.

#Файлы переводов

Создайте директорию locales/ в корне проекта:

// locales/ru.json { "common": { "save": "Сохранить", "cancel": "Отмена", "loading": "Загрузка...", "error": "Произошла ошибка" }, "nav": { "home": "Главная", "about": "О нас", "courses": "Курсы", "profile": "Профиль" }, "auth": { "login": "Войти", "logout": "Выйти", "register": "Зарегистрироваться", "email": "Email", "password": "Пароль", "forgot_password": "Забыли пароль?" }, "course": { "title": "Название курса", "lessons_count": "{count} урок | {count} урока | {count} уроков" } }
// locales/en.json { "common": { "save": "Save", "cancel": "Cancel", "loading": "Loading...", "error": "An error occurred" }, "nav": { "home": "Home", "about": "About", "courses": "Courses", "profile": "Profile" }, "auth": { "login": "Sign In", "logout": "Sign Out", "register": "Sign Up", "email": "Email", "password": "Password", "forgot_password": "Forgot password?" }, "course": { "title": "Course Title", "lessons_count": "{count} lesson | {count} lessons" } }

#useI18n: основной composable

useI18n() предоставляет все инструменты для работы с переводами в компонентах:

<script setup lang="ts"> const { t, // функция перевода locale, // текущая локаль (ref) locales, // все доступные локали setLocale // программное переключение языка } = useI18n() // Переключение языка async function switchToEnglish() { await setLocale('en') } </script> <template> <nav> <NuxtLink to="/">{{ t('nav.home') }}</NuxtLink> <NuxtLink to="/about">{{ t('nav.about') }}</NuxtLink> </nav> <p>Текущий язык: {{ locale }}</p> <button @click="switchToEnglish">Switch to English</button> </template>

#Переводы с параметрами

Параметры в переводах позволяют вставлять динамические значения:

{ "greeting": "Привет, {name}!", "items_count": "У вас {count} {item} в корзине", "last_seen": "Последний визит: {date}" }
<script setup lang="ts"> const { t } = useI18n() const userName = ref('Иван') const cartItems = ref(3) </script> <template> <!-- t('greeting', { name: 'Иван' }) → "Привет, Иван!" --> <p>{{ t('greeting', { name: userName }) }}</p> <!-- Несколько параметров --> <p>{{ t('items_count', { count: cartItems, item: 'товара' }) }}</p> </template>

#Plural forms: особенности русского языка

Русский язык имеет три формы множественного числа (не две как в английском). @nuxtjs/i18n поддерживает это через символ |:

{ "lessons": "0 уроков | {count} урок | {count} урока | {count} уроков", "days": "{count} день | {count} дня | {count} дней", "files": "{count} файл | {count} файла | {count} файлов" }

Правило для русских плюралей:

  • Форма 1: числа оканчивающиеся на 1 (кроме 11): 1, 21, 31...
  • Форма 2: числа оканчивающиеся на 2, 3, 4 (кроме 12, 13, 14): 2, 3, 4, 22, 23...
  • Форма 3: все остальные: 0, 5-20, 11-14, 25-30...
<script setup lang="ts"> const { t } = useI18n() // t('lessons', 1) → "1 урок" // t('lessons', 3) → "3 урока" // t('lessons', 5) → "5 уроков" // t('lessons', 21) → "21 урок" </script> <template> <p>{{ t('lessons', lessonsCount) }}</p> </template>

#Локализованные ссылки: localePath

Для навигации с учётом текущего языка используйте localePath:

<script setup lang="ts"> const localePath = useLocalePath() </script> <template> <!-- Автоматически добавляет /en/ префикс при необходимости --> <NuxtLink :to="localePath('/about')">О нас</NuxtLink> <NuxtLink :to="localePath('/courses/vue')">Курс по Vue</NuxtLink> <!-- Или через name маршрута --> <NuxtLink :to="localePath({ name: 'courses-id', params: { id: '123' } })"> Курс </NuxtLink> </template>

Никогда не хардкодьте /ru/about или /en/about — это сломается при смене стратегии маршрутизации.

#Переключатель языка: useSwitchLocalePath

<script setup lang="ts"> const { locales } = useI18n() const switchLocalePath = useSwitchLocalePath() </script> <template> <div class="language-switcher"> <NuxtLink v-for="locale in locales" :key="locale.code" :to="switchLocalePath(locale.code)" > {{ locale.name }} </NuxtLink> </div> </template>

switchLocalePath генерирует URL текущей страницы для другого языка. При переходе пользователь остаётся на той же странице, но в другой локали.

#SEO: hreflang и useLocaleHead

@nuxtjs/i18n автоматически генерирует hreflang теги, которые помогают Google показывать правильную языковую версию страницы:

<!-- pages/about.vue --> <script setup lang="ts"> const { t } = useI18n() // Генерирует SEO теги для всех языков const head = useLocaleHead({ addDirAttribute: true, // Добавляет dir="ltr/rtl" addSeoAttributes: true // Добавляет hreflang и canonical }) useHead(head) // Или вручную через useSeoMeta useSeoMeta({ title: t('about.meta_title'), description: t('about.meta_description') }) </script>

Результирующий HTML будет содержать:

<html lang="ru" dir="ltr"> <head> <link rel="canonical" href="https://example.com/about" /> <link rel="alternate" hreflang="ru" href="https://example.com/about" /> <link rel="alternate" hreflang="en" href="https://example.com/en/about" /> <link rel="alternate" hreflang="x-default" href="https://example.com/about" /> </head>

#Lazy loading переводов

При lazy: true файлы переводов загружаются только по необходимости. Для языков с большими файлами переводов это значительно уменьшает initial bundle:

// nuxt.config.ts i18n: { lazy: true, langDir: 'locales/', locales: [ { code: 'ru', file: 'ru.json' }, { code: 'en', file: 'en.json' } ] }

С lazy: true при инициализации загружается только дефолтная локаль. Переключение на другой язык инициирует загрузку соответствующего файла.

#TypeScript: типизация ключей переводов

Для автодополнения ключей переводов в TypeScript создайте декларацию типов:

// types/i18n.d.ts import ru from '../locales/ru.json' // Типизируем структуру переводов declare module '#i18n' { export interface NuxtI18nOptions { messages: { ru: typeof ru en: typeof ru // En использует ту же структуру что ru } } }

После этого t('nav.home') будет проверяться TypeScript — опечатки в ключах станут ошибками компиляции.

#Форматирование дат и чисел через Intl API

Для форматирования дат и чисел не нужны дополнительные библиотеки — JavaScript Intl API прекрасно справляется:

<script setup lang="ts"> const { locale } = useI18n() // Форматирование числа под текущую локаль function formatNumber(value: number): string { return new Intl.NumberFormat(locale.value, { minimumFractionDigits: 0, maximumFractionDigits: 2 }).format(value) } // Форматирование цены function formatPrice(value: number, currency: string = 'RUB'): string { return new Intl.NumberFormat(locale.value, { style: 'currency', currency }).format(value) } // Форматирование даты function formatDate(date: Date | string): string { return new Intl.DateTimeFormat(locale.value, { year: 'numeric', month: 'long', day: 'numeric' }).format(new Date(date)) } </script> <template> <p>Цена: {{ formatPrice(1990) }}</p> <!-- ru: "1 990 ₽", en: "₽1,990.00" --> <p>Дата: {{ formatDate('2026-04-03') }}</p> <!-- ru: "3 апреля 2026 г.", en: "April 3, 2026" --> </template>

Создайте composable useFormatters() для переиспользования этих функций во всём приложении — и не повторяйте одинаковую логику форматирования в каждом компоненте.

Далее: Продвинутый Nuxt 3