Skip to content

添加国际化功能

在本秘诀中,你将学习如何使用内容集合和动态路由来构建你自己的国际化 (i18n) 解决方案并以不同的语言提供你的内容。

¥In this recipe, you will learn how to use content collections and dynamic routing to build your own internationalization (i18n) solution and serve your content in different languages.

此示例在其自己的子路径上为每种语言提供服务,例如 example.com/en/blog 代表英语,example.com/fr/blog 代表法语。

¥This example serves each language at its own subpath, e.g. example.com/en/blog for English and example.com/fr/blog for French.

如果你希望默认语言不像其他语言那样在 URL 中可见,则可以使用下面的 隐藏默认语言的说明

¥If you prefer the default language to not be visible in the URL unlike other languages, there are instructions to hide the default language below.

See the resources section for external links to related topics such as right-to-left (RTL) styling and choosing language tags.

¥Recipe

¥Set up pages for each language

  1. Create a directory for each language you want to support. For example, en/ and fr/ if you are supporting English and French:

    • Directorysrc/
      • Directorypages/
        • Directoryen/
          • about.astro
          • index.astro
        • Directoryfr/
          • about.astro
          • index.astro
        • index.astro
  2. Set up src/pages/index.astro to redirect to your default language.

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

    This approach uses a meta refresh and will work however you deploy your site. Some static hosts also let you configure server redirects with a custom configuration file. See your deploy platform’s documentation for more details.

¥Use collections for translated content

  1. Create a folder in src/content/ for each type of content you want to include and add subdirectories for each supported language. For example, to support English and French blog posts:

    • Directorysrc/
      • Directorycontent/
        • Directoryblog/
          • Directoryen/ Blog posts in English
            • post-1.md
            • post-2.md
          • Directoryfr/ Blog posts in French
            • post-1.md
            • post-2.md
  2. Create a src/content/config.ts file and export a collection for each type of content.

    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
    };
    Read more about Content Collections.
  3. Use dynamic routes to fetch and render content based on a lang and a slug parameter.

    In static rendering mode, use getStaticPaths to map each content entry to a page:

    src/pages/[lang]/blog/[...slug].astro
    ---
    import { getCollection } from 'astro:content';
    export async function getStaticPaths() {
    const pages = await getCollection('blog');
    const paths = pages.map(page => {
    const [lang, ...slug] = page.slug.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 page.render();
    ---
    <h1>{page.data.title}</h1>
    <p>by {page.data.author}{formattedDate}</p>
    <Content/>
    Read more about dynamic routing.

¥Translate UI strings

创建术语词典来翻译站点周围 UI 元素的标签。这使你的访问者可以用他们的语言充分体验你的网站。

¥Create dictionaries of terms to translate the labels for UI elements around your site. This allows your visitors to experience your site fully in their language.

  1. Create a src/i18n/ui.ts file to store your translation strings:

    src/i18n/ui.ts
    export const languages = {
    en: 'English',
    fr: 'Français',
    };
    export const defaultLang = 'en';
    export const ui = {
    en: {
    'nav.home': 'Home',
    'nav.about': 'About',
    'nav.twitter': 'Twitter',
    },
    fr: {
    'nav.home': 'Accueil',
    'nav.about': 'À propos',
    },
    } as const;
  2. Create two helper functions: one to detect the page language based on the current URL, and one to get translations strings for different parts of the UI in src/i18n/utils.ts:

    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. Import the helpers where needed and use them to choose the UI string that corresponds to the current language. For example, a nav component might look like:

    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. Each page must have a lang attribute on the <html> element that matches the language on the page. In this example, a reusable layout extracts the language from the current route:

    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>

    You can then use this base layout to ensure that pages use the correct lang attribute automatically.

    src/pages/en/about.astro
    ---
    import Base from '../../layouts/Base.astro';
    ---
    <Base>
    <h1>About me</h1>
    ...
    </Base>

让用户在语言之间切换

Section titled 让用户在语言之间切换

¥Let users switch between languages

创建指向你支持的不同语言的链接,以便用户可以选择他们想要使用的语言来阅读你的网站。

¥Create links to the different languages you support so users can choose the language they want to read your site in.

  1. Create a component to show a link for each language:

    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. Add <LanguagePicker /> to your site so it is shown on every page. The example below adds it to the site footer in a base layout:

    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>

¥Hide default language in the URL

  1. Create a directory for each language except the default language. For example, store your default language pages directly in pages/, and your translated pages in fr/:

    • Directorysrc/
      • Directorypages/
        • about.astro
        • index.astro
        • Directoryfr/
          • about.astro
          • index.astro
  2. Add another line to the src/i18n/ui.ts file to toggle the feature:

    src/i18n/ui.ts
    export const showDefaultLang = false;
  3. Add a helper function to src/i18n/utils.ts, to translate paths based on the current language:

    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. Import the helper where needed. For example, a nav component might look like:

    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. The helper function can also be used to translate paths for a specific language. For example, when users switch between languages:

    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>

¥Translate Routes

翻译每种语言的页面路径。

¥Translate the routes of your pages for each language.

  1. Add route mappings to src/i18n/ui.ts:

    src/i18n/ui.ts
    export const routes = {
    de: {
    'services': 'leistungen',
    },
    fr: {
    'services': 'prestations-de-service',
    },
    }
  2. Update the useTranslatedPath helper function in src/i18n/utils.ts to add router translation logic.

    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. Create a helper function to get the route, if it exists based on the current URL, in src/i18n/utils.ts:

    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. The helper function can be used to get a translated route. For example, when no translated route is defined, the user will be redirected to the home page:

    src/components/LanguagePicker.astro
    ---
    import { languages } from '../i18n/ui';
    import { getRouteFromUrl } from '../i18n/utils';
    const route = getRouteFromUrl(Astro.url);
    ---
    <ul>
    {Object.entries(languages).map(([lang, label]) => (
    <li>
    <a href={translatePath(`/${route ? route : ''}`, lang)}>{label}</a>
    </li>
    ))}
    </ul>

¥Resources

¥Community libraries

  • astro-i18next — 一个 Astro 集成 i18next,包括一些实用程序组件。

  • astro-i18n — Astro 的 TypeScript 优先国际化库。

  • astro-i18n-aut — 一个 Astro 集成 i18n,支持 defaultLocale 而无需生成页面。该集成与适配器和 UI 框架无关。

  • paraglide — 一个完全类型安全的 i18n 库,专为部分水合模式(如 Astro 岛)而设计。

Astro 中文网 - 粤ICP备13048890号