ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

如何给 Yaak 加上多语言切换能力:从 Settings 字段到 t() 函数

如何给 Yaak 加上多语言切换能力:从 Settings 字段到 t() 函数 如何给 Yaak 加上多语言切换能力从 Settings 字段到 t() 函数【免费下载链接】yaakThe most intuitive desktop API client. Organize and execute REST, GraphQL, WebSockets, Server Sent Events, and gRPC 项目地址: https://gitcode.com/GitHub_Trending/ya/yaak我在 Yaak 的 Settings 页面找了一圈从 Appearance 到 Update Channel 全是英文没有任何入口能改界面语言——界面文本是直接写死在各组件的 JSX 里的。本文补上这个 Yaak 多语言切换的缺口给 Settings 模型加一个语言字段在 Interface 标签页放出选项让 Send 这类界面文本跟随所选语言。做完之后的效果是在设置页点一下就能在中英文之间切换重启后选择保留。先把按钮到数据库的路径反查一遍确定改动落点我没有从创建语言包文件开始动手而是先反查了一次数据流。设置页上任何一个选项的写入路径都是组件调用patchModel(settings, {...})→ guest-js 层的 store 落库 → SQLite 的settings表。类型源头则在 Rust 侧crates/yaak-models/src/models.rs 里的Settings结构体用 ts-rs 注解同时生成前端用的 TS 类型。所以反查下来改动落点一共四处结构体加字段、数据库表加列、设置页加一行、运行时加一个翻译函数。项目里最近的先例是主题切换appearance就是Settings结构体上的一个字符串字段SettingsTheme.tsx用ModelSettingRowSelect把它写成下拉框运行时再用 hook 解析。语言跟它是同一个形状我就照抄这个形状。这里先记两个被我否决的方案。一是引入 i18next 这类完整 i18n 库Yaak 是 local-first 应用界面文本量不大而且它已经有 patchModel 到 SQLite 的持久化管线再引入一个带自己配置格式的运行库改动面比问题本身还大。二是把语言放进atomWithKVStorageworkspaceLayoutAtom就是这种原子本地 KV 存储不跟着数据导出和文件系统同步走跟其他界面设置不一致。⚙️ 第一步给 Settings 结构体和数据库表加 language在Settings结构体里加一个字段放在appearance旁边// crates/yaak-models/src/models.rs 的 Settings 结构体 // 结构体已有 #[serde(default)]缺字段的旧数据走默认值 pub language: String,用字符串而不是枚举理由和appearance一致选项列表放前端 UI 里维护后端只管存。数据库侧加一个迁移文件放进 crates/yaak-models/migrations/也可以直接跑仓库自带的scripts/create-migration.cjs生成空文件-- 文件名时间戳必须晚于现有最新迁移20260902000000 那一批 ALTER TABLE settings ADD COLUMN language TEXT NOT NULL DEFAULT en;为什么这两步必须配套Settings上挂着#[ts(export, export_to gen_models.ts)]下次 Rust 构建会把含language的 TS 类型回写前端settingsAtom读到的模型就自动带上了这个字段。第二步在设置的 Interface 标签页加一行语言选择改动落在 apps/yaak-client/components/Settings/ 下的SettingsInterface.tsx在Editor那个SettingsSection附近插一行// SettingsInterface.tsx其余与原文件一致 ModelSettingRowSelect model{settings} modelKeylanguage titleInterface language descriptionLanguage used for the Yaak interface. options{[ { label: English, value: en }, { label: 简体中文, value: zh-CN }, ]} /选ModelSettingRowSelect这个现成组件是因为它自己会从model里按modelKey读值、变更时走patchModel回写不用我手写任何状态同步逻辑。这一步之所以放在结构体之后modelKey是泛型约束的Settings类型里没有language时这一行直接编译不过顺序反了会白报错。第三步写 useTranslation把 Send 按钮的文本换掉翻译函数做成纯函数语言值直接从settingsAtom取不另开 Context// apps/yaak-client/lib/i18n.ts const messages: Recordstring, Recordstring, string { en: { send: Send }, zh-CN: { send: 发送 }, }; export function t(lang: string, key: string): string { return messages[lang]?.[key] ?? messages.en[key] ?? key; // 缺 key 回退英文 }Hook 放在 apps/yaak-client/hooks/ 目录和旁边的useResolvedAppearance.ts归为一类// apps/yaak-client/hooks/useTranslation.ts import { useAtomValue } from jotai; import { settingsAtom } from yaakapp-internal/models; import { t } from ../lib/i18n; export function useTranslation() { const settings useAtomValue(settingsAtom); return (key: string) t(settings?.language || en, key); }最后找一个最显眼的组件试刀UrlBar.tsx里的 Send 按钮// UrlBar.tsx只贴关键行其余与原文件一致 const translate useTranslation(); // ButtonSend/Button 改为 Button onClick{onSend}{translate(send)}/Button这个写法有点绕t明明接收lang参数hook 里却把语言闭包掉了。但这样调用方只面对一个函数后面往messages里加语言不用动任何组件。跑一遍验证切换不生效先查这三处先npm install再跑根package.json里的开发入口启动仓库用scripts/run-dev.mjs拉起的整套 dev 环境具体脚本名以scripts字段为准。验证顺序打开 Settings → Interface确认出现 Interface language 一行切到简体中文UrlBar 的 Send 变成发送退出应用重开选择仍在——这一步能证明值确实走了 SQLite 而不是只活在内存里。想看数据库侧在 Settings 的 App Info 一节里找到 Data Directory进去找到.db文件用sqlite3查settings表就能看到language列。三个我实际踩过的坑TS 报language不在Settings类型上gen_models.ts是 ts-rs 从 Rust 侧生成的只改前端不会更新它重新触发一次 Rust 构建让它回写。设置行显示正常但落库报no such column: language迁移文件时间戳没排进顺序。文件名前缀必须晚于crates/yaak-models/migrations/里现有的最新一批否则跑不到。切完语言个别组件还是英文那处文本没走useTranslation还是硬编码或者组件在别处又读了一份旧的 atom 快照。全局搜一下英文原文最快的。我动过哪些文件什么情况下值得继续扩实际改动一共五处crates/yaak-models/src/models.rsSettings结构体加language字段crates/yaak-models/migrations/下新增一个迁移 SQLsettings表加language列apps/yaak-client/components/Settings/SettingsInterface.tsx加一行ModelSettingRowSelectapps/yaak-client/lib/i18n.ts新增语言包加t()apps/yaak-client/hooks/useTranslation.ts新增使用方组件各加一次 import 和一处文本替换值得继续扩的场景有两个。一是翻译 key 涨到几十个以后把messages拆成按模块分文件再写一个构建时脚本比对英文 key 全集防漏翻。二是插件侧要 i18n 时把当前语言透给packages/plugin-runtime的插件 API让第三方插件的提示文本也能跟随。反过来说如果只想自己用中文系统语言自动检测这一步可以直接跳过——默认值en加一行navigator.language判断就够了不值得为它单独建模块。【免费下载链接】yaakThe most intuitive desktop API client. Organize and execute REST, GraphQL, WebSockets, Server Sent Events, and gRPC 项目地址: https://gitcode.com/GitHub_Trending/ya/yaak创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进