DeepSeek Harness macOS 菜单仍是英文?CFBundleLocalizations 丢失排查与补回

故障排查发布于 2026-10-20作者: DeepSeek Plugin 插件市场
DeepSeek HarnessmacOSCFBundleLocalizationselectron-builder本地化
macOS 简体中文系统下 Desktop 的系统对话框与追加菜单仍是英文,通常是 Info.plist 缺 CFBundleLocalizations。配置文件同一对象出现两个 extendInfo 键,后者覆盖前者。给出合并键、打包后注入 plist、改用 mac.extendInfo 单一来源三条路径。

如果 macOS 已经是简体中文,Desktop 自己的界面也是中文,但「文件」菜单里的系统项和「添加工作区」的文件夹对话框仍是英文,先去读已安装应用的 Info.plist,看 CFBundleLocalizations 在不在。 据讨论 #10343,0.2.1-rc.2 的包里没有这个键,Contents/Resources 也没有 .lproj 目录,系统因此把应用当成纯英文。根因不在 electron-builder,而在构建配置里同一个对象写了两个 extendInfo。

DeepSeek Harness Desktop 的 Info.plist 里有没有 CFBundleLocalizations

验证先跑两条命令,确认事实再改配置,避免把系统语言问题当成打包问题。 装好的应用和构建产物都可以这样查:

  1. 执行 plutil -p <应用路径>/Contents/Info.plist | grep CFBundleLocalizations,把 <应用路径> 换成实际安装位置,预期打印数组或什么都不打印。
  2. 观察输出,预期没有任何输出说明键缺失;若打印出数组,则问题不在这个键。
  3. 执行 ls <应用路径>/Contents/Resources | grep lproj,预期打印 .lproj 目录或什么都不打印。
  4. 观察输出,预期同样为空,说明既没声明语言、也没带本地化目录,系统只能按英文处理。
  5. 再检查系统「语言与地区」里简体中文是否排在首位,预期首选语言确实是中文,排除配置本身的问题。

DeepSeek Harness 缺这个键时哪些文本会变

缺这个键时,受影响的不是应用自己渲染的界面,而是系统代替应用本地化的部分。 据讨论,范围大致如下:

  1. macOS 追加的菜单项,例如「服务」「显示所有窗口」这类系统项,预期显示为英文。
  2. 「添加工作区」打开的文件夹选择对话框,预期按钮与标签由系统按英文提供。
  3. 系统弹出的权限请求、文件位置等标准对话框,预期同样是英文。
  4. 应用自己写的界面文案仍按应用内 i18n 显示,预期出现「一半中文一半英文」。
  5. 把系统语言切回英文再切回中文,预期只有系统代本地化部分跟着切换,应用内文案仍按应用设置走。

DeepSeek Harness 为什么会丢:同一对象里的两个 extendInfo 键

据讨论中社区核对源码,electron-builder-config.mjs 的同一个对象字面量里出现了两个 extendInfo,后一个把前一个覆盖了。 这不是 electron-builder 的合并 bug,而是普通 JavaScript 语义。打包链路的其他排查可以对照 macOS Desktop 故障说明 看:

  1. 第一个 extendInfo 带着 CFBundleLocalizations 和麦克风用途说明,预期它本来会进入构建输入。
  2. 后续提交把它拆成两个键,第二个只保留 NSMicrophoneUsageDescription,预期前一个键被同名属性覆盖。
  3. 对象字面量求值时,后面的同名键替换前面的,预期 CFBundleLocalizations 从未进入构建输入。
  4. .oxlintrc.json 忽略了 **/*.mjs,预期 no-dupe-keys 规则不会在这个文件上运行。
  5. 现有签名测试只断言麦克风描述,预期两个检查都没拦住。

DeepSeek Harness Desktop 补回 CFBundleLocalizations 的三条路径

三条路都能把语言声明补回包里,差别在改构建配置、补已生成的产物,还是收敛配置来源。 这不是 DSH插件 的问题,而是 Desktop 打包配置的问题,按你能否重新打包来选。

路径一:合并 extendInfo,保留原注释

这是最直接的根修:一个 extendInfo 同时带两个键,并把解释「为什么必须放在 bundle 里」的注释一起留下。 改动只在构建配置,不涉及运行时。

  1. 打开 apps/desktop/scripts/electron-builder-config.mjs,定位两处 extendInfo,预期两处都能找到。
  2. 删除第二处,把 NSMicrophoneUsageDescription 并入第一处,预期只剩一个键且带两个属性。
  3. 保留原来关于 macOS 按 bundle 匹配语言、而不是读 Electron Framework 资源的注释,预期理由仍留在代码里。
  4. 重新打包,按上一节的两条命令检查产物,预期 Info.plist 同时出现 CFBundleLocalizations 与麦克风描述。
  5. 在中文系统下启动,预期系统菜单变为中文。
  6. 把产物装到另一台中文 Mac 上复测,预期菜单语言同样跟随首选语言,而不是只在开发机生效。
js
extendInfo: {
  CFBundleLocalizations: ['en', 'zh_CN'],
  NSMicrophoneUsageDescription:
    'DeepSeek Harness uses your microphone to transcribe speech into message drafts.',
},

路径二:打包后脚本注入 plist

如果暂时不能改构建配置,可以在产物上补键,但必须重新签名,否则应用会被系统拒绝。 适合本机验证或内部测试包:

  1. 先复制一份产物做备份,预期注入失败后还能恢复原包。
  2. 用 /usr/libexec/PlistBuddy -c "Add :CFBundleLocalizations array" 创建数组,预期键以空数组存在。
  3. 继续用 -c "Add :CFBundleLocalizations:0 string en" 和 -c "Add :CFBundleLocalizations:1 string zh_CN" 写入两项,预期数组包含 en 与 zh_CN。
  4. 执行 plutil -lint,预期 plist 语法正确、解析无错误。
  5. 用原有身份重新执行 codesign,保留 hardened runtime 与 entitlements,预期签名覆盖修改后的 plist。
  6. 预期结果:codesign --verify 通过,应用可启动,系统菜单语言跟随首选语言。

路径三:改用 mac.extendInfo 单一来源

要防止下次再出现重复键,就把这段配置收敛成一个变量,只在一个位置引用。 这样即使以后再加键,也不会出现两个同名属性:

  1. 在配置模块顶部定义 const macExtendInfo = { ... },集中放语言与权限描述,预期一个对象持有全部 mac bundle 键。
  2. 在 mac 配置里只写一次 extendInfo: macExtendInfo,预期配置里只有一个 extendInfo 属性。
  3. 若构建脚本还有其他分支需要扩展,用对象展开合并,而不是再写一个 extendInfo 键,预期扩展都并进同一个对象。
  4. 给 .mjs 文件补上 lint 覆盖,或单独对构建脚本跑一次 no-dupe-keys,预期重复键在打包前被报出来。
  5. 重新打包并读一次产物,预期 extendInfo 仍只出现一次,新增键没有覆盖旧键。

DeepSeek Harness 本地化判别的回归与签名检查

同样显示英文,修法不同:前者改 plist,后者改翻译资源。 先用四个对照区分来源,再按后面的顺序验收:

  1. 应用主界面是中文、系统菜单是英文,预期指向 bundle 语言声明。
  2. 应用主界面本身也有英文,预期指向应用内翻译缺条目,与 plist 无关。
  3. 只有某个第三方对话框英文,预期是该组件自己的语言包,不要改 CFBundleLocalizations。
  4. 换一个 macOS 用户、首选语言设为中文再测,预期结果随系统变,进一步确认是系统代本地化。
  5. 增加一个测试读取构建后的 Info.plist,断言 CFBundleLocalizations 等于预期数组,同时断言 NSMicrophoneUsageDescription 仍存在,预期两个键都被覆盖。
  6. 在简体中文系统上启动,检查文件夹对话框与系统追加菜单,再跑 codesign --verify --deep --strict 与 spctl,预期语言、权限描述、签名三项同时通过。
  7. 把 Info.plist 检查放进打包脚本尾部,预期构建失败时也能看到缺了哪个键。

来源:Discussion #10343。

常见问题

macOS 简体中文系统下 DeepSeek Harness Desktop 菜单仍是英文,是应用没做中文本地化吗?

DeepSeek Harness Desktop 多半不是界面没翻译,而是 bundle 没声明支持的语言。据讨论 #10343,Desktop 自身界面可以是中文,但 macOS 和 Chromium 代本地化的系统菜单、文件夹对话框会跟随应用的 CFBundleLocalizations;这个键缺失时系统按英文应用处理,所以追加菜单保持英文。

DeepSeek Harness 的 CFBundleLocalizations 怎么确认是丢了,而不是系统语言没生效?

DeepSeek Harness 直接读已安装应用的 Info.plist 最准。执行 plutil -p /Applications/DeepSeek\ Harness.app/Contents/Info.plist 后查找 CFBundleLocalizations,没有这个键就是 bundle 没声明;再看 Contents/Resources 下有没有 .lproj 目录,两者都缺即可排除系统语言因素。

DeepSeek Harness 里 electron-builder-config.mjs 写了键,打包后为什么没有?

DeepSeek Harness 的 electron-builder-config.mjs 在同一个对象字面量里出现了两个 extendInfo 键。JavaScript 后写的键覆盖先写的,只有麦克风描述那一个生效,CFBundleLocalizations 被丢掉;electron-builder 本身没收到两条记录。同时 .oxlintrc.json 忽略了 *.mjs,no-dupe-keys 规则没有机会报错。

DeepSeek Harness 打包后直接改 Info.plist 注入键,为什么应用会打不开?

DeepSeek Harness 的 Info.plist 属于签名内容的一部分,改动会让原有签名失效。修改后必须重新签名并保留 hardened runtime 设置,再在目标机器上验证;临时脚本注入适合本机验证或内部构建,不适合直接替换正式签名产物。

DeepSeek Harness 只改应用内翻译,能解决系统菜单英文吗?

DeepSeek Harness 只改应用内翻译不能解决系统菜单英文,因为系统菜单和文件夹对话框走的是 CFBundleLocalizations,不是应用内 i18n。要把语言声明补进 Info.plist 并重新签名,改翻译资源只会影响应用自己渲染的文案。

相关术语

CFBundleLocalizations
CFBundleLocalizations 是 Info.plist 中声明应用支持语言的数组键。macOS 用它匹配系统首选语言,决定系统菜单、对话框等框架代本地化内容显示哪种语言。— https://github.com/deepseek-ai/deepseek-harness/discussions/10343
extendInfo
extendInfo 是 electron-builder 的 mac 配置项,用于把自定义键合并进生成的 Info.plist。它是构建配置里的一个 JavaScript 对象属性,同名属性重复时只有最后一个生效。— https://github.com/deepseek-ai/deepseek-harness/discussions/10343
Info.plist
Info.plist 是 macOS 应用包根部的属性列表文件,记录 bundle 标识、版本、支持语言与权限用途说明。系统读取它来决定应用的本地化与权限行为。— https://github.com/deepseek-ai/deepseek-harness/discussions/10343
.lproj
.lproj 是 macOS 应用包内按语言存放资源的目录后缀,例如 zh_CN.lproj。目录存在只说明带了资源,系统是否采用仍取决于 Info.plist 的语言声明。— https://github.com/deepseek-ai/deepseek-harness/discussions/10343

来源