跳转到内容

多语言支持与阅读切换

EpoCanvas Docs 内置了完整的内容级多语言:导航栏、侧边栏、搜索提示等界面文字,以及每一篇文档的正文,都提供 10 种语言的独立版本。读者在页面右上角选择语言后,会跳转到同一篇文章的目标语言版本,正文与界面一起切换。


支持的语言列表

站点目前提供 10 种语言,其中简体中文是默认语言:

语言语言名称URL 前缀界面缩写
简体中文简体中文(默认)无前缀ZH
繁體中文繁體中文/zh-tw/TW
English英语/en/EN
日本語日语/ja/JA
한국어韩语/ko/KO
Español西班牙语/es/ES
Français法语/fr/FR
Deutsch德语/de/DE
Русский俄语/ru/RU
Português葡萄牙语/pt/PT

语言清单定义在 src/utils/i18n.ts 的 SUPPORTED_LANGUAGES 中,语言路由注册在 astro.config.mjs 的 locales 配置里,两处保持一致。


如何切换语言

  1. 在页面顶部导航栏右侧找到语言按钮(显示当前语言的缩写,如 ZH);
  2. 点击按钮,下拉菜单列出全部 10 种语言;
  3. 点击目标语言(例如 English 或 日本語);
  4. 浏览器跳转到当前这篇文章的目标语言版本,导航、侧边栏、目录与正文一起变成目标语言。

多语言切换下拉菜单展开时的真实标注效果:① 语言按钮 ② 语言列表 ③ 当前语言

图:在《多语言支持与阅读切换》页点击语言按钮后的实际标注效果。① 顶栏右侧的语言切换按钮;② 展开的下拉菜单,列出全部 10 种语言(列表可滚动);③ 当前生效的语言(带对勾标记)。

多语言切换工作机制示意图

图:语言切换的执行流程。构建时每种语言生成一套静态页面,点击语言按钮即跳转到对应语言的同一篇文章;个别页面缺少译文时自动回退显示中文。


URL 规则

每种语言的页面有独立的 URL 前缀,同一篇文章在不同语言下只有前缀不同:

/canvas/i18n/ → 简体中文(默认语言,无前缀)
/en/canvas/i18n/ → 英文
/ja/canvas/i18n/ → 日文
/zh-tw/canvas/i18n/ → 繁体中文
  • 语言信息直接编码在 URL 里,切换后的页面可以直接收藏、分享,搜索引擎也能按语言分别收录;
  • 页面内出现的站内链接(导航、侧边栏、正文中的推荐链接)都会自动带上当前语言的前缀,读者在某一语言内连续阅读时不会跳回中文页;
  • 每个语言版本都是构建期生成的独立静态页面,页面的 <html lang> 属性与语言一致,屏幕阅读器和翻译工具可以正确识别。

译文缺失怎么办

系统按”默认语言兜底”的策略处理缺失译文:某篇文档还没有某语言的译文时,访问该语言的页面会显示默认的简体中文内容,页面其余部分(导航、侧边栏、界面文字)仍然是目标语言,不会出现 404 报错或空白页。

这一策略的好处是:新增文档时不需要等所有语言的译文就绪,先发布中文版,译文补齐后再重新构建即可。


实现原理

多语言能力由 Astro 与 Starlight 的国际化机制承载,构建流程如下:

  1. 内容目录按语言划分:默认语言的文档放在 src/content/docs/(简体中文),其他语言放在 src/content/docs/<语言>/ 下,目录结构与中文完全对应,例如英文版《多语言支持与阅读切换》是 src/content/docs/en/canvas/i18n.md;
  2. 构建期生成全部语言页面:astro build 读取 astro.config.mjs 中的 locales 配置,为每种语言生成一套完整静态页面;
  3. 界面词条按语言渲染:顶栏导航、搜索提示、目录标题等界面文字由各组件调用 getTranslation(key, lang) 在构建期直接输出对应语言,页面里没有运行时替换脚本;
  4. 正文内链自动本地化:构建管线中的 rehypeLocalizeInternalLinks 插件会把译文正文里的 /canvas/xxx 内链自动加上当前语言前缀;
  5. 全文搜索按语言隔离:Pagefind 为每种语言建立独立索引,在英文页面搜索只会返回英文结果。
  6. 正文图片按语言自动回退:rehypeLocalizeDiagramImages 插件把译文正文里的 /images/canvas/<文件名> 改写为 /images/canvas/<语言>/<文件名>,对应语言的图片放在 public/images/canvas/<语言>/ 下;没有同名文件时保留原图,新增一张图不必同时配齐 10 种语言;
  7. 提示框与脚注按语言输出:rehypeLocalizeAsides 与 rehypeLocalizeFootnotes 按源文件所在语言目录改写提示框标题和脚注文案——Starlight 在 remark 阶段按文件绝对路径推断语言,本机构建传给它的是相对路径,不处理就会全部回落成中文;
  8. hreflang 与 404 页:hreflangXDefault() 在构建完成后为每个页面补一条 hreflang="x-default",指向简体中文版本;src/pages/404.astro 复用同一份词条字典按浏览器语言输出提示文案,页面上的语言菜单指向各语言首页。

开发者:如何新增一种语言?

以新增意大利语(Italiano,代码 it)为例:

  1. 注册语言路由:在 astro.config.mjs 的 locales 中加入 it: { label: 'Italiano', lang: 'it' };
  2. 补充侧边栏文案:在同一文件的 SIDEBAR_I18N 中为全部分组与条目补上 it 的翻译;
  3. 补充界面词条:在 src/utils/i18n.ts 的 SUPPORTED_LANGUAGES 中登记语言清单(代码、名称、缩写、URL 前缀),并在 UI_TRANSLATIONS 中补齐该语言的界面词条;
  4. 翻译文档内容:新建 src/content/docs/it/ 目录,按中文版的目录结构放入译文。未翻译的页面会先回退显示中文,可以逐步补齐;如需翻译界面截图与架构图,另建 public/images/canvas/it/ 目录放入同名文件,页面会自动引用。
  5. 重新构建:运行 pnpm run build,新语言页面与搜索索引即生成。构建后运行 node scripts/audit-i18n.mjs 和 node scripts/check-localized-images.mjs 复核译文结构与图片引用,这两项在 CI 中是必过检查。

开发者:如何新增或修改界面词条?

新增界面元素时,在 src/utils/i18n.ts 的 UI_TRANSLATIONS 中补充词条:

src/utils/i18n.ts
// 字典按「语言 → 词条键」两级组织,与 getTranslation 的读取顺序一致
export const UI_TRANSLATIONS: Record<string, Record<string, string>> = {
// 第一步:在默认语言 zh-CN 里加词条(缺失时界面回退显示键名)
'zh-CN': {
'nav.plugins': '插件生态',
// ...其余已有词条
},
// 第二步:给其它语言补同一键名的翻译,没补的语言自动回退中文
en: {
'nav.plugins': 'Plugins',
},
ja: {
'nav.plugins': 'プラグイン',
},
};

然后在组件中调用 getTranslation 并传入当前语言:

---
import { getLangFromLocale, getTranslation } from '../../utils/i18n';
const lang = getLangFromLocale(Astro.currentLocale);
---
<a href={localizedHref('/plugins/', lang)}>{getTranslation('nav.plugins', lang)}</a>

词条在构建期渲染为对应语言的静态文字,没有运行时开销。