Paraglide JS 双语实现
本项目(endfield-docmaker)基于 Paraglide JS 实现中文/英文双语站点:中文作为基础语言(base locale)直接挂载在根路径,英文通过 /en/ URL 前缀区分。Paraglide 在构建期把文案编译为类型安全的 TypeScript 函数,在请求期通过 SvelteKit 的 reroute 钩子剥离语言前缀,使两套语言共享同一棵无语言前缀的路由树。
目的与范围
本页完整覆盖该双语机制的端到端实现:
- 构建期:
vite.config.ts中paraglideVitePlugin的配置(project、outdir、strategy、urlPatterns)及BASE_PATH环境变量对 URL 模板的影响; - 请求期:
src/hooks.ts的reroute钩子如何用deLocalizeUrl把/en/...映射回无前缀路由; - 运行时:生成代码
src/lib/paraglide/(runtime 与 messages)暴露的getLocale、locales、localizeHref、m.*的实际消费方式; - 语言切换 UI:
src/lib/components/LocaleSwitch.svelte的切换逻辑、SEO 隐藏链接与 Bilibili Toy 部署的边界处理; - 语言协商策略:
['url', 'cookie', 'baseLocale']的解析顺序。
以下内容有意留给兄弟页面,不在本页展开:文案条目的增删改与 scripts/manage-messages.ts 的日常维护流程(README 标注其为「i18n 文案管理脚本」)、Bilibili Toy 部署环境的整体探测机制($lib/toy.svelte)、以及具体页面的业务内容。
概述
要解决的问题
一个静态优先(prerender)的双语文档站需要在三个层面同时做到正确:
- URL 层:每种语言要有独立、可收藏、可被搜索引擎索引的 URL(
/为中文,/en/...为英文); - 路由层:SvelteKit 的路由文件(
src/routes/+page.svelte等)不应为每种语言复制一份; - 文案层:组件内的界面文案要按当前语言渲染,且在构建期完成编译以避免运行时解析开销。
Paraglide JS(@inlang/paraglide-js)针对第 3 点提供编译期方案,并通过其 URL 策略与 SvelteKit reroute 钩子的组合解决前两点。
关键概念
| 概念 | 在本项目中的含义 |
|---|---|
| base locale(基础语言) | 中文 zh。urlPatterns 中 zh 绑定到无前缀的基础模式,因此根路径即中文 |
| URL 策略(strategy: url) | 从 URL 路径推断语言,是策略数组中的第一优先级 |
| cookie 策略 | URL 无法判定时的回退(Paraglide 默认读取其约定 Cookie) |
| baseLocale 回退 | 前两者都未命中时落到基础语言 zh |
| 去本地化(de-localize) | 把 /en/docs/ 还原为 /docs/,供内部路由匹配使用 |
| 本地化(localize) | 反向操作,把无前缀路径加上 /en 前缀生成对英文用户可见的 URL |
何时触发整页导航
语言切换会引发 window.location.href 整页跳转(而非客户端路由),因为 URL 前缀是语言状态的唯一持久载体——详见「核心流程」一节的设计权衡说明。
架构
整体分为三层:构建期编译层、请求期路由层、运行时渲染与 UI 层。生成代码 src/lib/paraglide/ 是三层之间的唯一契约。
Loading diagram...
分层说明:
- 构建期:
paraglideVitePlugin读取./project.inlang文案项目,把消息编译为类型安全的函数输出到./src/lib/paraglide;同时依据BASE_PATH环境变量生成带部署基路径的 URL 模式(这是 GitHub Pages 子路径部署的关键)。 - 请求期:所有请求先经过
src/hooks.ts的reroute钩子。它调用生成代码中的deLocalizeUrl,把带/en前缀的 URL 还原为无前缀 pathname,因此 SvelteKit 只需要一棵路由树即可同时服务两种语言。 - 运行时渲染与 UI:页面组件从生成代码导入
m(消息函数集合)与getLocale渲染对应语言;LocaleSwitch.svelte通过localizeHref计算目标语言的 URL 并整页跳转,形成闭环。
设计意图:把「语言」从业务代码中彻底解耦。业务组件永远面向无前缀路由与 m.*() 抽象编程,语言的判定、URL 的改写全部下沉到插件与钩子这一层,新增语言时业务代码几乎不需要修改(见「扩展点」)。
Sources
(2 files)(root)
src/lib/components