Skip to content

Реализация функций i18n

В этом руководстве вы узнаете, как использовать коллекции контента и динамическую маршрутизацию для создания собственного решения интернационализации (i18n) и отображения контента на разных языках.

В этом примере для каждого языка используется свой путь, например, example.com/en/blog для английского и example.com/ru/blog для русского.

Если вы хотите, чтобы язык по умолчанию не отображался в URL (в отличие от других языков), ниже вы найдете инструкции по скрытию языка по умолчанию в URL.

Ознакомьтесь с разделом ресурсов, где вы найдете внешние ссылки на связанные темы, такие как стили для письма справа налево (RTL) и выбор языковых тегов.

Настройка страниц для каждого языка

Section titled Настройка страниц для каждого языка
  1. Создайте директорию для каждого языка, который вы хотите поддерживать. Например, en/ и ru/ для поддержки английского и русского:

    • Directorysrc/
      • Directorypages/
        • Directoryen/
          • about.astro
          • index.astro
        • Directoryru/
          • about.astro
          • index.astro
        • index.astro
  2. Настройте src/pages/index.astro для перенаправления на язык по умолчанию.

    src/pages/index.astro
    <meta http-equiv="refresh" content="0;url=/en/" />

    Этот подход использует meta refresh и будет работать независимо от способа развертывания вашего сайта. На некоторых статических хостингах можно также настроить серверную переадресацию через специальный конфигурационный файл. Подробнее об этом читайте в документации вашей платформы развертывания.

Организация переводов с помощью коллекций

Section titled Организация переводов с помощью коллекций
  1. Создайте в src/content/ отдельные каталоги для каждого типа контента и добавьте в них подкаталоги для всех поддерживаемых языков. Например, для блог-постов на английском и русском:

    • Directorysrc/
      • Directorycontent/
        • Directoryblog/
          • Directoryen/ Посты на английском
            • post-1.md
            • post-2.md
          • Directoryru/ Посты на русском
            • post-1.md
            • post-2.md
  2. Создайте файл 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
    };
    Подробнее о Коллекциях контента.
  3. Используйте динамические маршруты для получения и отображения контента в зависимости от параметров 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/>

Перевод элементов интерфейса

Section titled Перевод элементов интерфейса

Создайте словари с переводами для всех текстовых элементов интерфейса вашего сайта. Это позволит посетителям использовать сайт полностью на их родном языке.

  1. Создайте файл 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;
  2. Создайте в файле 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];
    }
    }
  3. Импортируйте нужные вспомогательные функции и используйте их для отображения текста на соответствующем языке. Например, компонент навигации может выглядеть так:

    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>
  4. На каждой странице элемент <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 Позвольте пользователям переключать языки

Создайте ссылки на все поддерживаемые языки, чтобы пользователи могли выбрать, на каком языке они хотят просматривать ваш сайт.

  1. Создайте компонент для переключения языков:

    src/components/LanguagePicker.astro
    ---
    import { languages } from '../i18n/ui';
    ---
    <ul>
    {Object.entries(languages).map(([lang, label]) => (
    <li>
    <a href={`/${lang}/`}>{label}</a>
    </li>
    ))}
    </ul>
  2. Добавьте компонент <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
  1. Создайте директорию для каждого языка, кроме языка по умолчанию. Например, храните страницы на языке по умолчанию непосредственно в pages/, а переведенные страницы в ru/:

    • Directorysrc/
      • Directorypages/
        • about.astro
        • index.astro
        • Directoryru/
          • about.astro
          • index.astro
  2. Добавьте еще одну строку в src/i18n/ui.ts для управления отображением языка по умолчанию:

    src/i18n/ui.ts
    export const showDefaultLang = false;
  3. Добавьте в файл 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}`
    }
    }
  4. Импортируйте эту функцию там, где она нужна, например, в компонент навигации:

    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>
  5. Эту же функцию можно использовать в переключателе языков:

    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 Перевод маршрутов

Переведите маршруты ваших страниц для каждого языка.

  1. Добавьте соответствия маршрутов в src/i18n/ui.ts:

    src/i18n/ui.ts
    export const routes = {
    de: {
    'services': 'leistungen',
    },
    ru: {
    'services': 'услуги',
    },
    }
  2. Обновите функцию 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] !== undefined
    const translatedPath = hasTranslation ? '/' + routes[l][pathName] : path
    return !showDefaultLang && l === defaultLang ? translatedPath : `/${l}${translatedPath}`
    }
    }
  3. Создайте вспомогательную функцию в 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;
    }
  4. Теперь эту функцию можно использовать для получения переведенного маршрута. Например, если перевод маршрута не определен, пользователь будет перенаправлен на главную страницу:

    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 Библиотеки сообщества
  • 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.
Contribute Community Sponsor