Readest 国际化标签重命名工作流:Key-as-Content 架构下的多语言键迁移与命令面板同步

Readest 国际化标签重命名工作流:Key-as-Content 架构下的多语言键迁移与命令面板同步 桌面应用跨平台前端【免费下载链接】readestReadest is a modern, feature-rich ebook reader designed for avid readers offering seamless cross-platform access, powerful tools, and an intuitive interface to elevate your reading experience.项目地址https://gitcode.com/gh_mirrors/re/readest点击查看免费下载导读Readest 采用 key-as-content键即内容的国际化设计设置面板上的英文标签文本本身就是翻译键因此重命名一个 UI 标签等同于重命名全部语言翻译文件中的键。本文档总结的这套工作流源自仓库内 i18n-label-rename-workflow.md 记忆文档解决了三个核心问题如何不靠猜测地为每个语言推导新翻译、如何防止设置搜索面板Command Palette因漏改而静默失同步、以及如何在不产生 diff 噪音的前提下部分回滚。读完本文你将掌握一套可复现的、经过真实 PR#5287、#5301验证的标签重命名流程。一、先理解 Readest 的 Key-as-Content 国际化架构1.1 键就是内容源码里写的是英文文本本身在 Readest 的源码中UI 文案的引用方式不是t(settings.font.overrideBookFont)这种语义键而是直接把英文文本当作键。例如设置面板中LayoutPanel.tsx 中label{_(Additional Margin (%))}commandRegistry.ts 中labelKey: _(Additional Margin (%))。这里的_是 misc.ts 导出的stubTranslation存根可以推断其实现为返回传入的键字符串本身——它只在模块初始化阶段作为占位符真正渲染时由react-i18next的t函数根据当前语言查找翻译。也就是说翻译键与英文默认文案是同一条字符串public/locales/code/translation.json中的 JSON key 就是界面显示的文字。1.2 翻译键在哪些文件中流转键的提取pnpm i18n:extract定义于 package.json调用i18next-scanner扫描src/**/*.{js,jsx,ts,tsx}中的_(...)调用把新键写入各语言文件。语言清单i18n-langs.json 列出全部可翻译语言en是源语言不在此列见 i18n.ts 的注释与SUPPORTED_LNGS [en, ...translatableLngs]。翻译资源public/locales/ 下每个语言目录各有一份translation.json由浏览器端i18next-http-backend按/locales/{{lng}}/{{ns}}.json加载。1.3 关键结论重命名标签 重命名键正因为 key-as-content重命名一个 UI 标签会改变它在所有语言翻译文件中的 JSON key。那些只改界面英文、不同步 30 多种语言文件的 PR 是必然不完整的反过来像 #5287从 Header Footer 行中删掉 Show和 #5301Column Gap 改为 Additional Margin这种看似只改文案的 PR本质上是跨语言文件的 i18n PR。这一点决定了后续所有操作都要以全语言键迁移的视角来执行。二、不要重新翻译从每个语言的旧值机械推导新值2.1 直觉上的错误做法拿到新标签后最直觉的做法是把英文新文本丢给译者/机器翻译得到各语言的新翻译。文档明确警告这不可取——它会把译者自己的术语习惯替换成你的用词破坏既有译文的一致性。例如德语社区习惯用 Verbleibende Zeit若你按英文 Remaining Time 重新翻译可能得到风格不一致的结果。2.2 正确做法git show恢复旧值并剥离被改动的词因为仓库是 git 管理的旧键及其翻译仍完整保存在历史提交中。用下面的命令从基准提交恢复某个语言的旧 translation.jsongit show base:apps/readest-app/public/locales/code/translation.jsonbase是重命名前的基准提交通常是改动前的分支点或合并基。拿到旧文件后找到旧键如Show Remaining Time把被删掉的词从对应翻译中机械剥离即得到新键的翻译。#5287 的真实案例文档原例语言旧键与旧值来自 git show新键与新值机械剥离后德语Show Remaining Time: Verbleibende Zeit anzeigenRemaining Time: Verbleibende Zeit这种剥离是纯机械操作无需任何翻译猜测对全部语言逐一执行一致性由构造过程本身保证。当前仓库 de/translation.json 中已能看到重命名后的结果Remaining Time: Verbleibende Zeit与此工作流的产出完全吻合。其余语言如 arالوقت المتبقي、esTiempo restante、frTemps restant、bnবাকি সময়、elΥπολειπόμενος χρόνος同样只保留了剥离后的核心词验证了这套推导逻辑在真实仓库中的落地效果。提示如果个别语言在剥离后出现语法不完整或词序问题只对该语言做最小修正而不是推倒重译以保持与其他语言的推导结果同构。三、先查重新键可能已经存在在动手修改所有语言文件之前先确认新键是否已经存在于翻译文件中。#5287中 Remaining Time 是已存在的键Reading Progress相关区域已有类似条目提取器在扫描到它时会直接复用已有键于是你少写一个字符串——旧值 Show Remaining Time 下的翻译直接并入既有键无需为每个语言新增条目。实操建议在动手前对public/locales/en/translation.json和任一语言文件 grep 新键文本grep -n Remaining Time apps/readest-app/public/locales/en/translation.json若命中直接复用即可若未命中才需要走第二节的旧值剥离流程。四、别漏掉 commandRegistry.ts设置搜索面板的镜像同步4.1 labelKey 机制与失同步风险Readest 的设置搜索面板Command Palette由 commandRegistry.ts 驱动它镜像了设置面板中的一部分标签作为CommandItem.labelKey。该文件的labelKey值必须与 LayoutPanel.tsx 等面板中使用的_(...)保持一致否则面板与设置界面显示不同的文案搜索也会失准。例如当前commandRegistry.ts中的labelKey: _(Additional Margin (%))L295对应 #5301 的重命名结果labelKey: _(Show Header)、labelKey: _(Show Footer)L319、L325等 Header Footer 相关条目。漏改commandRegistry.ts是静默的不会有编译错误面板上的标签已更新但命令面板仍然引用旧键导致面板与设置页显示不一致且搜索旧名/新名都可能失败。4.2 keywords 数组是独立命名的旧搜索词可以保留每个CommandItem的keywords数组与labelKey相互独立。例如段落间距相关条目的keywords: [paragraph, margin, spacing, gap]。这意味着当标签从 Show Remaining Time 改为 Remaining Time 后保留 keywords 中的show并不会被新标签污染——搜索面板仍能通过旧词show命中该条目。getSearchableTextL41-L52把localizedLabel、labelKey、panel、panelLabel、section、keywords全部拼入 fzf 的搜索文本所以 keywords 相当于给用户保留了老用户习惯的搜索词这是重命名时应当刻意保留的兼容性设计。4.3 相关的搜索与渲染链路搜索searchCommands用fzfsmart-case、normalize、limit 50对组合文本检索命中位置再映射回标签区间用于高亮渲染CommandPalette.tsx 通过_(item.labelKey)实时取当前语言的翻译并高亮命中字符。对这条链路的回归测试参见 command-registry-extended.test.ts其中通过stubTranslation: (key) key让测试聚焦于注册表结构与搜索逻辑本身。五、部分回滚而不制造 diff 噪音5.1 问题i18n:extract只追加不回写原位置i18next-scanner的配置见 i18next-scanner.config.cjs{ sort: false, // 不按键名排序保持文件既有顺序 removeUnusedKeys: true, // 清理源码中不再引用的键 defaultValue: __STRING_NOT_TRANSLATED__, keySeparator: false, nsSeparator: false, func: { list: [_], extensions: [.js, .jsx, .ts, .tsx] }, // ... }关键点在于sort: false重新提取只会把重新出现的键append 到文件末尾。如果你把重命名过的键又改回去直接跑pnpm i18n:extract会得到键被移到文件底部的 diff——键内容没变位置变了在代码评审里表现为一次毫无意义的 move污染 diff。5.2 正确做法按基准提交的键顺序重建 JSON文档给出的无损重建流程建议用脚本执行读取基准提交base中各语言的translation.json拿到该语言文件的键顺序逐键构造新文件存活下来的键取当前值要恢复的键取基准值即撤销重命名带来的改动把真正新增的键 append 到文件末尾重新运行pnpm i18n:extract并确认结果是no-op没有任何改动——这一步证明手工重建的文件与扫描器将要产出的文件完全一致是最有说服力的自检。5.3 为什么no-op能证明正确性因为 scanner 的写入逻辑是确定性的removeUnusedKeys决定删哪些、sort: false决定不重排若重建文件经过提取后 diff 为空就说明所有应存在的键都在、位置正确、不应存在的键如已被移除的旧键已清理。反之若 diff 非空对照差异即可定位是漏了键还是多写了键。六、收尾验证清单合并前建议按以下顺序自查全部通过即可视为重命名完成键覆盖grep新键确认存在于所有可翻译语言文件apps/readest-app/public/locales/*/translation.json旧键不再残留面板镜像确认src/services/commandRegistry.ts中对应labelKey已改为新键且keywords保留旧搜索词提取无副作用运行pnpm i18n:extract后git status干净或 diff 仅含预期改动翻译质量门禁运行pnpm check:translationspackage.json——它扫描各语言文件中是否残留__STRING_NOT_TRANSLATED__占位符scanner 的defaultValue确保没有漏翻的键混入视觉回归设置面板与命令面板分别打开确认文案一致、搜索命中正常仓库 e2e 与 Playwright 测试目录apps/readest-app/e2e/可作参考。七、工作流总结这套工作流可以浓缩为四条纪律重命名即全量迁移——key-as-content 决定了改标签就是改 30 多种语言文件的键不存在只改英文的捷径推导优于翻译——用git show base:apps/readest-app/public/locales/code/translation.json恢复旧值、机械剥离被改词而不是重新翻译先复用再新增——新键若已存在提取器会复用省去全部语言的新增工作镜像与回归并举——同步commandRegistry.ts的labelKey保留keywords兼容旧词回滚时按基准键序重建文件并让pnpm i18n:extract验证为 no-op。它已在 #5287 与 #5301 两次真实重命名中验证过且与仓库内其他国际化记忆如 i18n 提取键清理、反馈翻译中的复数处理、基于 Playwright 的设置面板截图共同构成 Readest 的 i18n 维护方法论。赞分享桌面应用跨平台前端【免费下载链接】readestReadest is a modern, feature-rich ebook reader designed for avid readers offering seamless cross-platform access, powerful tools, and an intuitive interface to elevate your reading experience.项目地址https://gitcode.com/gh_mirrors/re/readest点击查看免费下载相关推荐kbar国际化支持多语言命令面板的实现方法kbar国际化支持多语言命令面板的实现方法 想要为你的网站或应用添加一个功能强大且支持多语言的命令面板吗kbar 提供了完美的解决方案作为一款快速、轻量级前端UI组件OBS Studio插件开发与配置进阶用户完整指南OBS Studio插件开发与配置进阶用户完整指南 OBS Studio作为开源直播录制软件的标杆其强大的插件系统是功能扩展的核心。本文将深入探讨OBS插件音视频直播屏幕录制桌面应用视频终极Lucide图标国际化指南从零开始的多语言语义化命名实践终极Lucide图标国际化指南从零开始的多语言语义化命名实践 Lucide作为一款由社区打造的精美且一致的图标工具包是Feather Icons的衍生开源项前端UI组件设计系统创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考