Repository Wiki
Naptie/endfield-docmaker

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)的双语文档站需要在三个层面同时做到正确:

  1. URL 层:每种语言要有独立、可收藏、可被搜索引擎索引的 URL(/ 为中文,/en/... 为英文);
  2. 路由层:SvelteKit 的路由文件(src/routes/+page.svelte 等)不应为每种语言复制一份;
  3. 文案层:组件内的界面文案要按当前语言渲染,且在构建期完成编译以避免运行时解析开销。

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)
src/lib/components