首页 / Astro 教程 / 国际化 i18n 路由

Astro 教程

国际化 i18n 路由

本教程共 56 篇 · 第 52 篇 · 更新于 2026-08-07 · 约 12 分钟阅读

AstroAstro 教程国际化i18n多语言路由URL 前缀Astro i18n

本节目标:学会用 Astro 自带的 i18n 路由功能,为同一个网站配置多语言版本,并生成、校验、跳转到正确的本地化 URL。

做一个面向多国用户的网站,最麻烦的事往往是「同一份内容,怎么按语言分门别类」。Astro 从 v3 起就内置了一套 i18n 路由能力,不需要你额外装库,只要改一处配置,就能让 /about//en/about//es/about/ 各归各位。这一章就把它讲透。

什么是 i18n 路由

i18n 是 internationalization(国际化)的缩写,因为首尾字母之间夹了 18 个字母,所以戏称 i18n。在 Astro 里,i18n 路由指的是一套帮你在多个语言版本之间「生成链接、校验链接、按浏览器偏好跳转」的工具。

它解决的其实是三件事:第一,告诉 Astro 你的网站支持哪些语言;第二,根据文件结构自动产出带语言前缀的 URL;第三,当某个语言缺页时,决定是跳走还是补一个回退页。

Astro 是用一个内置的中间件来实现这套逻辑的。它排在中间件链的第一位,会等你自己的中间件和页面逻辑都跑完、路由渲染出来之后,再做一遍 URL 校验。也就是说,你在自己的中间件里做的重定向、页面里写的逻辑都先执行,最后才是 i18n 的中间件收尾。

在 astro.config 里声明语言

配置写在 astro.config.mjsi18n 块里。最基础的写法,就是列出你支持的所有语言(locales),并指定其中一个为默认语言(defaultLocale)。

// astro.config.mjs
import { defineConfig } from "astro/config"

export default defineConfig({
  i18n: {
    locales: ["es", "en", "pt-br"],
    defaultLocale: "en",
  }
})

locales 里的每一项都是一个字符串,通常就是语言代码,比如 enesfrpt-br。这个字符串有两个作用:一是对应你 src/pages/ 下的文件夹名,二是作为 URL 里的前缀。所以一定要想清楚命名,后面文件夹必须和这里完全一致。

用文件夹组织多语言页面

声明好语言之后,下一步是在 src/pages/ 里按语言建文件夹。Astro 的文件路由会把这些文件夹直接映射成 URL。文件夹名字必须和 locales 里的字符串一模一样。

假设 defaultLocaleen,且你没有开启 prefixDefaultLocale(下面会讲),目录大概是这样:

src/
  pages/
    about.astro          // 对应 example.com/about/
    index.astro          // 对应 example.com/
    es/
      about.astro        // 对应 example.com/es/about/
      index.astro        // 对应 example.com/es/
    pt-br/
      about.astro        // 对应 example.com/pt-br/about/
      index.astro        // 对应 example.com/pt-br/

注意一个细节:默认语言 en 的页面放在 src/pages/ 根下,不带 /en/ 文件夹;其他语言才放进各自的文件夹。这是因为默认语言是否带前缀,是由 prefixDefaultLocale 决定的,详见下一节。

控制默认语言要不要前缀

routing.prefixDefaultLocale 决定默认语言的 URL 是否带语言前缀。它有两个取值。

prefixDefaultLocale: false 是默认值。这种情况下,默认语言不带前缀,页面文件放在 src/pages/ 根目录;其他语言带前缀,放在各自文件夹。

// astro.config.mjs
export default defineConfig({
  i18n: {
    locales: ["es", "en", "fr"],
    defaultLocale: "en",
    routing: {
      prefixDefaultLocale: false
    }
  }
})

prefixDefaultLocale: true 则要求所有语言都带前缀,包括默认语言。这时连默认语言的页面也要放进 /en/ 文件夹,文件结构必须和 URL 结构完全对齐。

// astro.config.mjs
export default defineConfig({
  i18n: {
    locales: ["es", "en", "fr"],
    defaultLocale: "en",
    routing: {
      prefixDefaultLocale: true
    }
  }
})

开启 true 之后,目录变成这样:

src/
  pages/
    index.astro          // 始终需要:站点入口
    en/
      index.astro
      about.astro
    es/
      about.astro
      index.astro
    pt-br/
      about.astro
      index.astro

有个小坑要提醒:prefixDefaultLocale: true 时,不带前缀的 URL(比如 example.com/about/)会直接返回 404,除非你配置了回退策略。另外,即便默认语言加了前缀,站点首页 / 默认仍不会被强制跳转到 /en/——如果你想让首页也重定向到带前缀的默认语言,可以再加上 redirectToDefaultLocale: true

用辅助函数生成链接

配置好之后,最实用的就是 astro:i18n 模块提供的辅助函数。在组件或页面里,用 getRelativeLocaleUrl() 就能算出某个语言下某条路由的正确相对 URL,再也不用自己手拼字符串,也避免了前缀规则改了之后忘了同步。

---
// src/pages/es/index.astro
import { getRelativeLocaleUrl } from 'astro:i18n';

// defaultLocale 是 "es"
const aboutURL = getRelativeLocaleUrl("es", "about");
---

<a href="/get-started/">¡Vamos!</a>
<a href={getRelativeLocaleUrl('es', 'blog')}>Blog</a>
<a href={aboutURL}>Acerca</a>

这里第二个参数 "about" 是页面在 src/pages/ 下的相对路径(不含语言前缀、不含扩展名)。函数会按你的 prefixDefaultLocale 设置,自动补上或不补前缀。

如果你的站点配置了自定义域名(domains,后面会简单提),还可以用 getAbsoluteLocaleUrl() 拿到带完整域名的绝对 URL,比如 https://fr.example.com/about

语言切换器怎么写

最常见的需求,是在页面上放一个语言下拉或按钮组,让用户切到同一篇文章的其他语言版本。思路很简单:拿到当前路径,去掉语言前缀,再用 getRelativeLocaleUrl 拼出目标语言链接。

---
import { getRelativeLocaleUrl } from 'astro:i18n';

// Astro.currentLocale 是当前 URL 推导出的语言
const current = Astro.currentLocale;

// 去掉前缀后的相对路径,例如 /about/
const path = Astro.url.pathname.replace(/^\/(en|es|fr)(?=\/|$)/, '') || '/';

const langs = ["en", "es", "fr"];
---

<nav>
  {langs.map((l) => (
    <a href={getRelativeLocaleUrl(l, path)} aria-current={l === current}>
      {l.toUpperCase()}
    </a>
  ))}
</nav>

Astro.currentLocale 是 Astro 帮你算好的「当前语言」,从 URL 前缀推导而来;如果 URL 没有语言前缀,它就回退到 defaultLocale。在静态预渲染页面里它也能用,非常方便。

回退语言 fallback

翻译网站常遇到的情况:某篇 es 文章还没翻,但 fr 版本缺页了。与其给访客一个冷冰冰的 404,不如让 Astro 用另一种语言的内容顶上,这就是回退。

回退分两部分配置。第一部分 i18n.fallback 指定「哪种语言缺页时,用哪种语言补」;第二部分 i18n.routing.fallbackType 指定「补的方式是重定向还是改写」。

// astro.config.mjs
export default defineConfig({
  i18n: {
    locales: ["es", "en", "fr"],
    defaultLocale: "en",
    fallback: {
      fr: "es"   // 任何缺失的 fr 页面,都用 es 版本兜底
    },
    routing: {
      fallbackType: "rewrite"  // 直接显示 es 内容,不跳转
    }
  }
})

fallbackType 有两个取值:redirect(默认)会跳转到对应的 es 路由;rewrite 则原地显示 es 页面的内容,URL 还是 fr 的,访客无感。如果你只想简单跳走,用默认的 redirect 即可。

把 i18n 和浏览器偏好结合

Astro 还帮你读浏览器的 Accept-Language 头,算出访客可能想看的语言。在按需渲染的页面里,你能拿到两个值:

  • Astro.preferredLocale:浏览器偏好语言且正好在你的 locales 里,才算匹配;没有就返回 undefined
  • Astro.preferredLocaleList:浏览器要求的所有语言里、你的站点又支持的,全部列成数组。

所有页面(含静态预渲染)都能用 Astro.currentLocale 拿到「当前 URL 对应的语言」。如果你在 locales 里用了对象形式自定义 codes(比如把 frfr-BRfr-CA 都映射到 /fr/),那浏览器偏好匹配会更准。

i18n 与内容集合配合

如果你用内容集合管理文章(第 20、21 章讲过),多语言内容通常有两种做法。一种是给每种语言各建一个集合,比如 blog-enblog-es;另一种是在同一条目里用字段区分语言,再按 Astro.currentLocale 过滤。Astro 的 i18n 路由本身不绑定内容集合,它只管 URL 和前缀,内容从哪来由你决定。只需要保证生成链接时,路径和你的集合查询对得上即可。

小结

Astro 的 i18n 路由是一套「配置 + 辅助函数」的组合拳:在 astro.config 里声明 localesdefaultLocalerouting 行为,用文件夹组织各语言页面,用 getRelativeLocaleUrl 等函数生成链接,用 fallback 兜住缺页。它不替你翻译内容,只把多语言的「路由骨架」搭好,剩下的翻译工作还是你自己的。