前端多语言采用策展目录(vue-i18n),拒绝运行时机器翻译
背景
两个 Vue 3 SPA(frontends/user、frontends/admin)此前完全没有 i18n:页面骨架硬编码英文,而共享错误信息目录(src/services/messages/zh-CN.ts)硬编码中文且 DEFAULT_LOCALE = 'zh-CN'——渲染出的界面本身是混语言的。文档站已运行双语言约定(Docusaurus defaultLocale: 'en' + zh-CN 镜像树,同 PR 双写)。在对全部 27 个视图做文案抽取之前,我们评估了自动翻译组件(主要是 translate.js,xnx3/translate)与策展消息目录两条路线。
关于 translate.js(v4,MIT,活跃维护)的关键事实:它在页面加载后遍历 DOM,把页面文本发送到作者私有云(api.translate.zvo.cn 等)做机器翻译;免费通道有每日字数上限;翻译发生在首绘之后(天然 FOUC);Vue 适配有未决 issue(#54、#94);且它会改写 input 的 value 属性——对凭据表单是 UX 隐患。
决策
采用 vue-i18n v11 策展消息目录(Composition API,legacy: false,globalInjection: true)作为两个前端唯一的 i18n 机制。语言集:现在为 en(默认)+ zh-CN,后续可扩展二级集合(zh-TW、ja、de、es、fr、pt-BR、ru)。运行时机翻组件(machine-translation widget)对产品 UI 被明确拒绝。
理由:fulla 是安全敏感的身份提供方——consent/scope/authorization 术语必须人工策展、页面内容不得外发第三方翻译云、自托管/离线部署(IdP 常态)必须可用、渲染文本必须确定性且可在 PR 中评审。translate.js 是 MIT 许可、技术上可以嵌入,但其数据流本身就足以否决。vue-i18n 是 Vue 事实标准(MIT,约 390 万周下载),在当前目录规模下无需额外构建插件(运行时约 10–14 KB brotli),且与错误目录既有的 per-locale 资源文件设计同构。
后果与现状
- 每个应用一个语言切换器同时驱动页面骨架与新产生的错误信息:
getErrorMessage(code)在错误触发时按当前 UI locale 解析。已渲染的消息是一次性快照——切换语言不会重译屏上已有文本(该行为由 e2e 锁定;存错误码、渲染时重解析的完全响应式改造列为受跟踪的后续选项)。DEFAULT_LOCALE变为回退表(en)。 - 语言检测顺序:
localStorage['fulla-locale'](沿用fulla-theme模式)→navigator.languages→en,在 mount 前同步生效(无语言闪烁),并同步<html lang>;index.html内联脚本在首绘前预同步 lang。 - 服务层(
errorAdapter+messages/,两应用镜像文件)保持零依赖:当前语言经services/locale.ts纯模块传递,由 i18n 引导层推送——node 环境单测因此不受影响。 - 目录双写(
en+zh-CN)同 PR 完成,由 key-parity 与调用点 key 存在性单测(i18nKeys.test.ts)强制;镜像服务与components/ui字节同步门禁保持两应用一致。 - 机器翻译仅允许用于翻译工作流(新增语言时预翻译 + 人工评审),绝不允许进入运行时。
- 一次性成本:27 个视图的文案抽取(user 188 键 + admin 306 键);固定中文错误文案的 e2e 断言迁移到英文目录(默认语言),中文路径由每应用专属
i18n.spec.ts覆盖。