☰
如何给 Yaak 加上多语言切换能力:从 Settings 字段到 t() 函数
2026/10/9 1:35:07 网站建设 项目流程

如何给 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 的持久化管线,再引入一个带自己配置格式的运行库,改动面比问题本身还大。二是把语言放进atomWithKVStorage(workspaceLayoutAtom就是这种原子):本地 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} modelKey="language" title="Interface language" description="Language 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: Record<string, Record<string, 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(); // <Button>Send</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 快照。全局搜一下英文原文最快的。

我动过哪些文件,什么情况下值得继续扩

实际改动一共五处:

  1. crates/yaak-models/src/models.rs:Settings结构体加language字段
  2. crates/yaak-models/migrations/下新增一个迁移 SQL:settings表加language列
  3. apps/yaak-client/components/Settings/SettingsInterface.tsx:加一行ModelSettingRowSelect
  4. apps/yaak-client/lib/i18n.ts:新增,语言包加t()
  5. 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),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询