添加国际化功能
在本秘诀中,你将学习如何使用内容集合和动态路由来构建你自己的国际化 (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.
¥Recipe
为每种语言设置页面
Section titled “为每种语言设置页面”¥Set up pages for each language
-
Create a directory for each language you want to support. For example,
en/andfr/if you are supporting English and French:Directorysrc/
Directorypages/
Directoryen/
- about.astro
- index.astro
Directoryfr/
- about.astro
- index.astro
- index.astro
-
Set up
src/pages/index.astroto 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.
If you are using an SSR adapter, you can use
Astro.redirectto redirect to the default language on the server.src/pages/index.astro ---return Astro.redirect('/en/');---
使用翻译内容的集合
Section titled “使用翻译内容的集合”¥Use collections for translated content
-
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
-
Create a
src/content.config.tsfile 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. -
Use dynamic routes to fetch and render content based on a
langand aslugparameter.In static rendering mode, use
getStaticPathsto map each content entry to a page: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/>In SSR mode, fetch the requested entry directly:
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/>Read more about dynamic routing.
翻译 UI 字符串
Section titled “翻译 UI 字符串”¥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.
-
Create a
src/i18n/ui.tsfile 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; -
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];}} -
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> -
Each page must have a
langattribute 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
langattribute 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.
-
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> -
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>
隐藏 URL 中的默认语言
Section titled “隐藏 URL 中的默认语言”¥Hide default language in the URL
-
Create a directory for each language except the default language. For example, store your default language pages directly in
pages/, and your translated pages infr/:Directorysrc/
Directorypages/
- about.astro
- index.astro
Directoryfr/
- about.astro
- index.astro
-
Add another line to the
src/i18n/ui.tsfile to toggle the feature:src/i18n/ui.ts export const showDefaultLang = false; -
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}`}} -
Import the helper where needed. For example, a
navcomponent 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> -
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.
-
Add route mappings to
src/i18n/ui.ts:src/i18n/ui.ts export const routes = {de: {'services': 'leistungen',},fr: {'services': 'prestations-de-service',},} -
Update the
useTranslatedPathhelper function insrc/i18n/utils.tsto 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] !== undefinedconst translatedPath = hasTranslation ? '/' + routes[l][pathName] : pathreturn !showDefaultLang && l === defaultLang ? translatedPath : `/${l}${translatedPath}`}} -
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;} -
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, 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>
¥Resources
¥Community libraries
-
astro-i18next — 一个 Astro 集成 i18next,包括一些实用程序组件。
-
astro-i18n — Astro 的 TypeScript 优先国际化库。
-
astro-i18n-aut — 一个 Astro 集成 i18n,支持
defaultLocale而无需生成页面。该集成与适配器和 UI 框架无关。 -
astro-react-i18next — Astro 集成,无缝支持在 Astro 网站上的 React 组件中使用 i18next 和 react-i18next。
-
paraglide — 一个完全类型安全的 i18n 库,专为部分水合模式(如 Astro 岛)而设计。
-
astro-loader-i18n — Astro glob 内容加载器,用于支持 i18n 文件和文件夹结构,并支持路由转换。