添加 i18n 功能
尽管 Astro 没有内置的国际化功能,但你也可以构建你自己的国际化解决方案。
本节示例中,你将看到如何使用内容集合和动态路由来支持不同语言。
假设示例拥有不同语言的子路径,例如面向英语的 example.com/en/blog
和面向法语的 example.com/fr/blog
。
如果你希望默认语言不像其他语言那样在 URL 中可见,后续会有 关于隐藏默认语言的介绍。
操作步骤
标题部分 操作步骤为页面设置多语言
标题部分 为页面设置多语言-
创建一个你想支持的语言的目录。例如,如果你打算支持英语和法语,那么是创建
en/
和fr/
目录:目录src/
目录pages/
目录en/
- about.astro
- index.astro
目录fr/
- 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/');---
为翻译内容创建内容集合
标题部分 为翻译内容创建内容集合-
在
src/content
文件夹中为每种你想要包含的内容创建一个文件夹,同时在其中也创建对应语言的子目录。假设你现在打算为博客文章支持英语和法语:目录src/
目录content/
目录blog/
目录en/ 英语版博客文章
- post-1.md
- post-2.md
目录fr/ 法语版博客文章
- 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 } 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/>在 SSR 模式 下,你可以直接获取到请求的内容:
src/pages/[lang]/blog/[...slug].astro ---import { getEntryBySlug } from 'astro:content';const { lang, slug } = Astro.params;const page = await getEntryBySlug('blog', `${lang}/${slug}`);if (!page) {return Astro.redirect('/404');}const formattedDate = page.data.date.toLocaleString(lang);const { Content, headings } = await page.render();---<h1>{page.data.title}</h1><p>by {page.data.author} • {formattedDate}</p><Content/>📚 你可以在这获取有关更多 动态路由 的内容。
翻译 UI 标签
标题部分 翻译 UI 标签创建术语字典来翻译你网站上用户界面的元素标签,这样可以让访问者在他们的语言环境下更好地体验你的网站。
-
创建一个
src/i18n/ui.ts
文件来存储你翻译后的标签: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; -
创建两个辅助函数:一个用来基于当前 URL 检测页面语言,另一个用来获取不同部分的 UI 标签的翻译版本。
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];}} -
在需要的地方导入辅助函数并使用它们来选择当前语言环境下的 UI 标签。例如,导航组件可能会像下面这样:
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>
允许用户切换不同语言
标题部分 允许用户切换不同语言为你所支持的不同语言创建链接,以便用户能选择他们浏览你网站时所使用的语言。
-
创建一个用以显示每个语言的链接的组件:
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 中隐藏默认语言
标题部分 在 URL 中隐藏默认语言-
除默认语言外,为每种语言创建一个目录。例如,将默认语言的页面直接放在
pages/
中,而将翻译的页面放在fr/
中:目录src/
目录pages/
- about.astro
- index.astro
目录fr/
- 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}`}} -
在需要的地方导入这个辅助函数。例如,一个
nav
组件可能如下所示:src/components/Nav.astro ---import { getLangFromUrl, useTranslations } 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';---<ul>{Object.entries(languages).map(([lang, label]) => (<li><a href={translatePath('/', lang)}>{label}</a></li>))}</ul>
翻译路由
标题部分 翻译路由将你页面中的路由翻译成不同语言。
- 在
src/i18n/ui.ts
中添加路由映射。
export const routes = { de: { 'services': 'leistungen', }, fr: { 'services': 'prestations-de-service', }, }
- Update the
useTranslatedPath
helper function insrc/i18n/utils.ts
to add router translation logic. - 在
src/i18n/utils.ts
中更新useTranslatedPath
辅助函数以添加路由翻译逻辑:
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}` }}
src/i18n/utils.ts
中创建一个辅助函数来获取对应路由,如果该路由存在于当前 URL 的话:
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}
- 这个辅助函数可以用来获取翻译后的路由。例如,当没有定义翻译后的路由时,用户将被重定向到首页:
--- import { languages } from '../i18n/ui'; const route = getRouteFromUrl(Astro.url); ---
<ul> {Object.entries(languages).map(([lang, label]) => ( <li> <a href={translatePath(`/${route ? route : ''}`, lang)}>{label}</a> </li> ))} </ul>
参考
标题部分 参考其他第三方库
标题部分 其他第三方库- astro-i18next — An Astro integration for i18next including some utility components.
- astro-i18n — A TypeScript-first internationalization library for Astro.
- astro-i18n-aut — An Astro integration for i18n that supports the
defaultLocale
without page generation. The integration is adapter agnostic and UI framework agnostic.
更多方案
-
状态共享
学习如何使用 Nano Stores 在框架组件之间共享状态
-
添加 RSS 摘要
通过向你的 Astro 网站添加 RSS 摘要让用户来订阅你的内容。
-
Installing a Vite or Rollup plugin
Learn how you can import YAML data by adding a Rollup plugin to your project.
-
使用 API 路由构建表单
了解如何使用 JavaScript 发送表单到 API 路由
-
在 Astro 页面中构建 HTML 表单
了解如何在 Astro 页面中构建 HTML 表单并在你的 frontmatter 中处理提交请求
-
使用 Bun 和 Astro
学习如何在 Astro 网站上使用 Bun。
-
调用服务器端点
了解如何在 Astro 中调用服务器端点
-
校验验证码
了解如何创建一个 API 路由并从客户端进行获取。
-
用 Docker 来构建你的 Astro 网站
了解如何使用 Docker 来构建你的 Astro 网站。
-
为链接添加图标
了解如何安装 rehype 插件并为你 Markdown 文件中的链接添加图标
-
添加 i18n 功能
通过动态路由和内容集合来让你的 Astro 站点支持国际化。
-
Add Last Modified Time
Build a remark plugin to add the last modified time to your Markdown and MDX.
-
添加阅读时间
构建一个 remark 插件并将其添加到你的 Markdown 或 MDX 文件中。
-
在 Astro 组件中共享状态
了解如何通过 Nano Stores 在 Astro 组件中共享状态