20. Интернационализация
🌍 Интернационализация с @nuxtjs/i18n
Заголовок раздела «🌍 Интернационализация с @nuxtjs/i18n»@nuxtjs/i18n — официальный модуль Nuxt для интернационализации приложений. Он обеспечивает маршрутизацию на нескольких языках, ленивую загрузку переводов и полную TypeScript-поддержку.
🚀 Установка
Заголовок раздела «🚀 Установка»npx nuxi module add i18nexport default defineNuxtConfig({ modules: ['@nuxtjs/i18n'], i18n: { // Доступные языки locales: [ { code: 'ru', language: 'ru-RU', name: 'Русский', file: 'ru.json' }, { code: 'en', language: 'en-US', name: 'English', file: 'en.json' }, { code: 'de', language: 'de-DE', name: 'Deutsch', file: 'de.json' }, ], // Язык по умолчанию defaultLocale: 'ru', // Стратегия URL strategy: 'prefix_except_default', // Директория с переводами langDir: 'locales/', // Ленивая загрузка lazy: true, // SEO мета-теги detectBrowserLanguage: { useCookie: true, cookieKey: 'i18n_redirected', redirectOn: 'root', alwaysRedirect: false } }})📁 Структура файлов переводов
Заголовок раздела «📁 Структура файлов переводов»locales/├── ru.json # Русский├── en.json # Английский└── de.json # Немецкий{ "nav": { "home": "Главная", "about": "О нас", "blog": "Блог", "contact": "Контакты" }, "hero": { "title": "Добро пожаловать в {appName}", "subtitle": "Современное веб-приложение на Nuxt 3", "cta": "Начать сейчас" }, "auth": { "login": "Войти", "logout": "Выйти", "register": "Зарегистрироваться", "email": "Email", "password": "Пароль" }, "messages": { "items": "Нет элементов | {count} элемент | {count} элемента | {count} элементов" }, "errors": { "notFound": "Страница не найдена", "serverError": "Ошибка сервера" }}{ "nav": { "home": "Home", "about": "About", "blog": "Blog", "contact": "Contact" }, "hero": { "title": "Welcome to {appName}", "subtitle": "Modern web application with Nuxt 3", "cta": "Get Started" }, "auth": { "login": "Sign In", "logout": "Sign Out", "register": "Sign Up", "email": "Email", "password": "Password" }, "messages": { "items": "No items | {count} item | {count} items" }, "errors": { "notFound": "Page not found", "serverError": "Server error" }}🪝 useI18n()
Заголовок раздела «🪝 useI18n()»Основной composable для работы с переводами:
<script setup lang="ts">const { t, locale, locales, setLocale, availableLocales } = useI18n()
// t() — получение переводаconst title = t('hero.title', { appName: 'MyApp' })const loginText = t('auth.login')
// locale — текущая локаль (ref)console.log(locale.value) // 'ru'
// Смена языкаconst changeLocale = (code: string) => setLocale(code)
// Плюрализацияconst itemCount = t('messages.items', 3)</script>
<template> <h1>{{ t('hero.title', { appName: 'MyApp' }) }}</h1> <p>{{ t('hero.subtitle') }}</p> <button>{{ t('auth.login') }}</button></template>🗺️ useLocalePath()
Заголовок раздела «🗺️ useLocalePath()»Генерация локализованных путей:
<script setup lang="ts">const localePath = useLocalePath()
// Локализованный путьconst homePath = localePath('/') // ru: '/', en: '/en/'const aboutPath = localePath('/about') // ru: '/about', en: '/en/about'const blogPath = localePath({ name: 'blog' })</script>
<template> <!-- Автоматически добавляет языковой префикс --> <NuxtLink :to="localePath('/')"> {{ t('nav.home') }} </NuxtLink>
<NuxtLink :to="localePath('/about')"> {{ t('nav.about') }} </NuxtLink>
<!-- Именованный маршрут --> <NuxtLink :to="localePath({ name: 'blog-slug', params: { slug: 'my-post' } })"> Статья </NuxtLink></template>🔄 useSwitchLocalePath()
Заголовок раздела «🔄 useSwitchLocalePath()»Переключение языка для текущей страницы:
<script setup lang="ts">const switchLocalePath = useSwitchLocalePath()const { locale, locales } = useI18n()</script>
<template> <nav> <NuxtLink v-for="loc in locales" :key="loc.code" :to="switchLocalePath(loc.code)" :class="{ active: locale === loc.code }" > {{ loc.name }} </NuxtLink> </nav></template>🛣️ Стратегии маршрутизации
Заголовок раздела «🛣️ Стратегии маршрутизации»Nuxt i18n поддерживает 4 стратегии:
no_prefix
Заголовок раздела «no_prefix»/ → все языкиprefix_except_default
Заголовок раздела «prefix_except_default»/ → русский (по умолчанию)/en/ → английский/de/ → немецкий/ru/ → русский/en/ → английский/de/ → немецкийprefix_and_default
Заголовок раздела «prefix_and_default»/ → перенаправление на /ru//ru/ → русский/en/ → английскийexport default defineNuxtConfig({ i18n: { strategy: 'prefix_except_default', defaultLocale: 'ru' }})🐌 Ленивая загрузка переводов
Заголовок раздела «🐌 Ленивая загрузка переводов»export default defineNuxtConfig({ i18n: { lazy: true, langDir: 'locales/', locales: [ { code: 'ru', file: 'ru.json' }, { code: 'en', file: 'en.json' } ] }})Для разделения переводов на части:
// locales/ru/index.ts — динамическая загрузкаexport default async () => { const messages = await import('./messages.json') const common = await import('./common.json') return { ...messages.default, ...common.default }}🔢 Плюрализация
Заголовок раздела «🔢 Плюрализация»{ "apples": "Нет яблок | {count} яблоко | {count} яблока | {count} яблок"}<script setup>const { t } = useI18n()</script>
<template> <p>{{ t('apples', 0) }}</p> <!-- Нет яблок --> <p>{{ t('apples', 1) }}</p> <!-- 1 яблоко --> <p>{{ t('apples', 3) }}</p> <!-- 3 яблока --> <p>{{ t('apples', 11) }}</p> <!-- 11 яблок --></template>📅 Форматирование дат и чисел
Заголовок раздела «📅 Форматирование дат и чисел»<script setup>const { d, n } = useI18n()
// Форматирование датыconst date = d(new Date(), 'long') // "15 января 2024 г."
// Форматирование числаconst price = n(1234567.89, 'currency') // "1 234 567,89 ₽"</script>// nuxt.config.ts — настройка форматовi18n: { datetimeFormats: { ru: { short: { year: 'numeric', month: 'short', day: 'numeric' }, long: { year: 'numeric', month: 'long', day: 'numeric' } } }, numberFormats: { ru: { currency: { style: 'currency', currency: 'RUB' }, decimal: { style: 'decimal', minimumFractionDigits: 2 } } }}🔍 SEO с i18n
Заголовок раздела «🔍 SEO с i18n»<script setup>useHead({ htmlAttrs: { lang: useI18n().locale.value }})</script>i18n: { // Автоматически добавляет hreflang теги seo: true, baseUrl: 'https://myapp.com'}