Реализация функций i18n
В этом руководстве вы узнаете, как использовать коллекции контента и динамическую маршрутизацию для создания собственного решения интернационализации (i18n) и отображения контента на разных языках.
В версии 4.0 Astro добавил встроенную поддержку i18n-маршрутизации, позволяющую указать основной язык и список поддерживаемых языков, а также предоставляющую полезные вспомогательные функции для работы с международной аудиторией. Если вы предпочитаете этот подход, ознакомьтесь с нашим руководством по интернационализации.
В этом примере для каждого языка используется свой путь, например, example.com/en/blog
для английского и example.com/ru/blog
для русского.
Если вы хотите, чтобы язык по умолчанию не отображался в URL (в отличие от других языков), ниже вы найдете инструкции по скрытию языка по умолчанию в URL.
Рецепт
Section titled РецептНастройка страниц для каждого языка
Section titled Настройка страниц для каждого языка-
Создайте директорию для каждого языка, который вы хотите поддерживать. Например,
en/
иru/
для поддержки английского и русского:Directorysrc/
Directorypages/
Directoryen/
- about.astro
- index.astro
Directoryru/
- about.astro
- index.astro
- index.astro
-
Настройте
src/pages/index.astro
для перенаправления на язык по умолчанию.src/pages/index.astro <meta http-equiv="refresh" content="0;url=/en/" />Этот подход использует meta refresh и будет работать независимо от способа развертывания вашего сайта. На некоторых статических хостингах можно также настроить серверную переадресацию через специальный конфигурационный файл. Подробнее об этом читайте в документации вашей платформы развертывания.
Если вы используете SSR-адаптер, вы можете использовать
Astro.redirect
для серверной переадресации на язык по умолчанию.src/pages/index.astro ---return Astro.redirect('/en/');---
Организация переводов с помощью коллекций
Section titled Организация переводов с помощью коллекций-
Создайте в
src/content/
отдельные каталоги для каждого типа контента и добавьте в них подкаталоги для всех поддерживаемых языков. Например, для блог-постов на английском и русском:Directorysrc/
Directorycontent/
Directoryblog/
Directoryen/ Посты на английском
- post-1.md
- post-2.md
Directoryru/ Посты на русском
- post-1.md
- post-2.md
-
Создайте файл
src/content.config.ts
и экспортируйте коллекцию для каждого типа контента.src/content.config.ts import { defineCollection, z } from 'astro:content';const blogCollection = defineCollection({schema: z.object({title: z.string(),author: z.string(),date: z.date()})});export const collections = {'blog': blogCollection};Подробнее о Коллекциях контента. -
Используйте динамические маршруты для получения и отображения контента в зависимости от параметров
lang
иslug
.В режиме статической генерации используйте функцию
getStaticPaths
, чтобы создать отдельную страницу для каждой записи контента:src/pages/[lang]/blog/[...slug].astro ---import { getCollection, render } from 'astro:content';export async function getStaticPaths() {const pages = await getCollection('blog');const paths = pages.map(page => {const [lang, ...slug] = page.id.split('/');return { params: { lang, slug: slug.join('/') || undefined }, props: page };});return paths;}const { lang, slug } = Astro.params;const page = Astro.props;const formattedDate = page.data.date.toLocaleString(lang);const { Content } = await render(page);---<h1>{page.data.title}</h1><p>by {page.data.author} • {formattedDate}</p><Content/>В режиме SSR (EN) запрашивайте нужную запись напрямую:
src/pages/[lang]/blog/[...slug].astro ---import { getEntry, render } from 'astro:content';const { lang, slug } = Astro.params;const page = await getEntry('blog', `${lang}/${slug}`);if (!page) {return Astro.redirect('/404');}const formattedDate = page.data.date.toLocaleString(lang);const { Content, headings } = await render(page);---<h1>{page.data.title}</h1><p>by {page.data.author} • {formattedDate}</p><Content/>Подробнее о динамических маршрутах.В примере выше используется встроенный метод
toLocaleString()
для создания удобочитаемой строки из даты в метаданных. Это гарантирует, что дата и время будут отформатированы в соответствии с языком пользователя.
Перевод элементов интерфейса
Section titled Перевод элементов интерфейсаСоздайте словари с переводами для всех текстовых элементов интерфейса вашего сайта. Это позволит посетителям использовать сайт полностью на их родном языке.
-
Создайте файл
src/i18n/ui.ts
для хранения переводов интерфейса:src/i18n/ui.ts export const languages = {en: 'English',ru: 'Русский',};export const defaultLang = 'en';export const ui = {en: {'nav.home': 'Home','nav.about': 'About','nav.twitter': 'Twitter',},ru: {'nav.home': 'Главная','nav.about': 'О нас',},} as const; -
Создайте в файле
src/i18n/utils.ts
две вспомогательные функции: первую для определения языка страницы по текущему URL и вторую для получения переводов элементов интерфейса:src/i18n/utils.ts import { ui, defaultLang } from './ui';export function getLangFromUrl(url: URL) {const [, lang] = url.pathname.split('/');if (lang in ui) return lang as keyof typeof ui;return defaultLang;}export function useTranslations(lang: keyof typeof ui) {return function t(key: keyof typeof ui[typeof defaultLang]) {return ui[lang][key] || ui[defaultLang][key];}}В первом шаге строка
nav.twitter
не была переведена на русский. Иногда это именно то, что нужно — не переводить имена собственные или общепринятые термины. Когда перевод для ключа отсутствует,useTranslations
автоматически возвращает значение из языка по умолчанию. Поэтому русские пользователи тоже увидят “Twitter” в навигационной панели. -
Импортируйте нужные вспомогательные функции и используйте их для отображения текста на соответствующем языке. Например, компонент навигации может выглядеть так:
src/components/Nav.astro ---import { getLangFromUrl, useTranslations } from '../i18n/utils';const lang = getLangFromUrl(Astro.url);const t = useTranslations(lang);---<ul><li><a href={`/${lang}/home/`}>{t('nav.home')}</a></li><li><a href={`/${lang}/about/`}>{t('nav.about')}</a></li><li><a href="https://twitter.com/astrodotbuild">{t('nav.twitter')}</a></li></ul> -
На каждой странице элемент
<html>
должен содержать атрибутlang
с указанием используемого языка. В этом примере базовый макет определяет язык из текущего маршрута:src/layouts/Base.astro ---import { getLangFromUrl } from '../i18n/utils';const lang = getLangFromUrl(Astro.url);---<html lang={lang}><head><meta charset="utf-8" /><link rel="icon" type="image/svg+xml" href="/favicon.svg" /><meta name="viewport" content="width=device-width" /><title>Astro</title></head><body><slot /></body></html>Затем вы можете использовать этот макет, чтобы страницы автоматически получали правильный атрибут
lang
.src/pages/en/about.astro ---import Base from '../../layouts/Base.astro';---<Base><h1>About me</h1>...</Base>
Позвольте пользователям переключать языки
Section titled Позвольте пользователям переключать языкиСоздайте ссылки на все поддерживаемые языки, чтобы пользователи могли выбрать, на каком языке они хотят просматривать ваш сайт.
-
Создайте компонент для переключения языков:
src/components/LanguagePicker.astro ---import { languages } from '../i18n/ui';---<ul>{Object.entries(languages).map(([lang, label]) => (<li><a href={`/${lang}/`}>{label}</a></li>))}</ul> -
Добавьте компонент
<LanguagePicker />
на сайт так, чтобы он отображался на каждой странице. В примере ниже он добавлен в футер сайта в базовом макете:src/layouts/Base.astro ---import LanguagePicker from '../components/LanguagePicker.astro';import { getLangFromUrl } from '../i18n/utils';const lang = getLangFromUrl(Astro.url);---<html lang={lang}><head><meta charset="utf-8" /><link rel="icon" type="image/svg+xml" href="/favicon.svg" /><meta name="viewport" content="width=device-width" /><title>Astro</title></head><body><slot /><footer><LanguagePicker /></footer></body></html>
Скрытие языка по умолчанию в URL
Section titled Скрытие языка по умолчанию в URL-
Создайте директорию для каждого языка, кроме языка по умолчанию. Например, храните страницы на языке по умолчанию непосредственно в
pages/
, а переведенные страницы вru/
:Directorysrc/
Directorypages/
- about.astro
- index.astro
Directoryru/
- about.astro
- index.astro
-
Добавьте еще одну строку в
src/i18n/ui.ts
для управления отображением языка по умолчанию:src/i18n/ui.ts export const showDefaultLang = false; -
Добавьте в файл
src/i18n/utils.ts
функцию, которая будет формировать правильные пути с учетом текущего языка:src/i18n/utils.ts import { ui, defaultLang, showDefaultLang } from './ui';export function useTranslatedPath(lang: keyof typeof ui) {return function translatePath(path: string, l: string = lang) {return !showDefaultLang && l === defaultLang ? path : `/${l}${path}`}} -
Импортируйте эту функцию там, где она нужна, например, в компонент навигации:
src/components/Nav.astro ---import { getLangFromUrl, useTranslations, useTranslatedPath } from '../i18n/utils';const lang = getLangFromUrl(Astro.url);const t = useTranslations(lang);const translatePath = useTranslatedPath(lang);---<ul><li><a href={translatePath('/home/')}>{t('nav.home')}</a></li><li><a href={translatePath('/about/')}>{t('nav.about')}</a></li><li><a href="https://twitter.com/astrodotbuild">{t('nav.twitter')}</a></li></ul> -
Эту же функцию можно использовать в переключателе языков:
src/components/LanguagePicker.astro ---import { languages } from '../i18n/ui';import { getLangFromUrl, useTranslatedPath } from '../i18n/utils';const lang = getLangFromUrl(Astro.url);const translatePath = useTranslatedPath(lang);---<ul>{Object.entries(languages).map(([lang, label]) => (<li><a href={translatePath('/', lang)}>{label}</a></li>))}</ul>
Перевод маршрутов
Section titled Перевод маршрутовПереведите маршруты ваших страниц для каждого языка.
-
Добавьте соответствия маршрутов в
src/i18n/ui.ts
:src/i18n/ui.ts export const routes = {de: {'services': 'leistungen',},ru: {'services': 'услуги',},} -
Обновите функцию
useTranslatedPath
вsrc/i18n/utils.ts
, добавив логику перевода маршрутов.src/i18n/utils.ts import { ui, defaultLang, showDefaultLang, routes } from './ui';export function useTranslatedPath(lang: keyof typeof ui) {return function translatePath(path: string, l: string = lang) {const pathName = path.replaceAll('/', '')const hasTranslation = defaultLang !== l && routes[l] !== undefined && routes[l][pathName] !== undefinedconst translatedPath = hasTranslation ? '/' + routes[l][pathName] : pathreturn !showDefaultLang && l === defaultLang ? translatedPath : `/${l}${translatedPath}`}} -
Создайте вспомогательную функцию в
src/i18n/utils.ts
, чтобы получить маршрут на основе текущего URL (если он существует):src/i18n/utils.ts import { ui, defaultLang, showDefaultLang, routes } from './ui';export function getRouteFromUrl(url: URL): string | undefined {const pathname = new URL(url).pathname;const parts = pathname?.split('/');const path = parts.pop() || parts.pop();if (path === undefined) {return undefined;}const currentLang = getLangFromUrl(url);if (defaultLang === currentLang) {const route = Object.values(routes)[0];return route[path] !== undefined ? route[path] : undefined;}const getKeyByValue = (obj: Record<string, string>, value: string): string | undefined => {return Object.keys(obj).find((key) => obj[key] === value);}const reversedKey = getKeyByValue(routes[currentLang], path);if (reversedKey !== undefined) {return reversedKey;}return undefined;} -
Теперь эту функцию можно использовать для получения переведенного маршрута. Например, если перевод маршрута не определен, пользователь будет перенаправлен на главную страницу:
src/components/LanguagePicker.astro ---import { languages } from '../i18n/ui';import { getRouteFromUrl, useTranslatedPath } from '../i18n/utils';const route = getRouteFromUrl(Astro.url);---<ul>{Object.entries(languages).map(([lang, label]) => {const translatePath = useTranslatedPath(lang);return (<li><a href={translatePath(`/${route ? route : ''}`)}>{label}</a></li>)})}</ul>
Ресурсы
Section titled РесурсыБиблиотеки сообщества
Section titled Библиотеки сообщества- astro-i18next — Интеграция Astro для i18next, включающая ряд полезных компонентов.
- astro-i18n — Библиотека интернационализации для Astro с упором на TypeScript.
- astro-i18n-aut — Интеграция Astro для i18n, которая поддерживает
defaultLocale
без генерации страниц. Интеграция не зависит от адаптера и UI-фреймворка. - astro-react-i18next — Интеграция Astro, которая позволяет легко использовать i18next и react-i18next в React-компонентах на сайтах Astro.
- paraglide — Полностью типобезопасная библиотека i18n, специально разработанная для паттернов частичной гидратации, таких как островки Astro.