# Round 2 PRD:语言检测与语言版本设置 状态:v0.1 启动稿 所属子项目:`02-P-NBL` Round:Round 2 日期:2026-06-03 关联文档: - [`../product-plan.md`](../product-plan.md) - [`../engineering-plan.md`](../engineering-plan.md) - [`../plan.md`](../plan.md) - [`../round-1/prd.md`](../round-1/prd.md) ## 1. 背景 Round 1 已经把现有 SenseVoice 纳入 `ModelProfile` 架构,并通过 QA 验证: - 老用户旧模型目录 `models/sensevoice` 可 fallback。 - 新用户模型可安装到 `models/sensevoice-zh`。 - `SelectedModelID=sensevoice-zh` 已成为隐藏配置。 - `LanguageProfile` 已有最小中文实现,但尚未真正参与语言选择和 UI 设置。 后续产品路线要求: | 语言版本 | 默认轻量模型 | 高级模型 | |---|---|---| | 中文 | SenseVoice | Qwen3-ASR | | English | Moonshine English | Parakeet | Round 2 的任务不是接入英文模型,而是先建立语言版本机制,让后续 Round 3 可以把 English 默认模型切到 Moonshine。 ## 2. 本轮目标 Round 2 完成后,系统内部应从: ```text 前端按 navigator.language 自行判断 zh/en 后端没有语言版本配置 ``` 演进为: ```text Config.LanguageMode / Config.LanguageID -> LanguageRegistry -> EffectiveLanguage -> UI locale -> 未来默认模型选择 ``` 用户可见目标: - 中文系统首次启动默认中文界面。 - English 系统首次启动默认 English 界面。 - 其他系统语言暂时默认 English。 - 设置页可手动选择中文或 English。 - 选择“自动”时跟随系统语言。 - 语言选择持久化,重启后仍生效。 ## 3. 本轮非目标 明确不做: - 不接入 Moonshine。 - 不接入 Parakeet。 - 不接入 Qwen3-ASR。 - 不做模型选择页。 - 不做模型删除、回滚、清理。 - 不做 30+ 语言列表。 - 不做粤语作为独立语言版本。 - 不做法语、德语、西班牙语、意大利语、葡萄牙语、俄语、乌克兰语等欧洲语言 UI。 - 不根据语言切换自动删除任何模型。 - 不强制老用户重新下载 SenseVoice。 - 不改变用户词库、历史记录格式。 - 不做正式安装包签名和 notarization。 ## 4. 用户故事 ### 4.1 中文系统的新用户 作为中文 macOS 用户,我第一次安装 VoiceSnap 后,应用应默认使用中文界面,并继续使用 SenseVoice。 验收点: - `LanguageMode=auto`。 - 有效语言解析为 `zh-CN`。 - UI 显示中文。 - 默认模型仍为 `sensevoice-zh`。 - 新用户模型下载路径不退化。 ### 4.2 English 系统的新用户 作为 English macOS 用户,我第一次安装 VoiceSnap 后,应用应默认显示 English UI。 验收点: - `LanguageMode=auto`。 - 有效语言解析为 `en`。 - UI 显示 English。 - 本轮模型仍为 `sensevoice-zh`。 - 不出现 Moonshine 或 Parakeet。 说明: Round 2 的 English 只是语言版本和 UI 准备。真正的 English 默认 Moonshine 在 Round 3 交付。 ### 4.3 其他系统语言的新用户 作为法语、德语、日语等系统语言用户,本轮应用应默认进入 English UI,避免误判为中文。 验收点: - 系统语言不是中文且不是 English 时,有效语言为 `en`。 - UI 显示 English。 - 模型仍为 `sensevoice-zh`。 ### 4.4 老用户升级 作为已有 VoiceSnap 用户,我升级到 Round 2 后,不应因为新增语言配置而丢失设置、词库、历史或模型。 验收点: - 旧 `config.json` 缺少 `LanguageMode` / `LanguageID` 时可以正常加载。 - 默认补齐为 `LanguageMode=auto`。 - 中文用户有效语言为 `zh-CN`。 - 旧 `SelectedModelID` 兼容。 - `userdict.json`、`history.json`、模型目录不被修改。 ### 4.5 用户手动切换语言 作为用户,我可以在设置页选择“自动、中文、English”。 验收点: - 选择中文后 UI 切到中文。 - 选择 English 后 UI 切到 English。 - 选择自动后根据系统语言解析有效语言。 - 重启后保持选择结果。 - 切换语言不触发模型下载、删除或迁移。 ## 5. 配置规则 新增配置字段: ```json { "LanguageMode": "auto", "LanguageID": "zh-CN" } ``` ### 5.1 LanguageMode 允许值: | 值 | 含义 | |---|---| | `auto` | 跟随系统语言 | | `manual` | 使用用户手动选择的语言 | 非法值处理: - 回退为 `auto`。 - 记录日志。 - 不中断 App 启动。 ### 5.2 LanguageID 当前允许值: | 值 | 含义 | UI locale | |---|---|---| | `zh-CN` | 中文 | `zh` | | `en` | English | `en` | 非法值处理: - 在 `manual` 模式下回退为 `zh-CN` 或 `en`,具体规则由默认有效语言决定。 - 在 `auto` 模式下忽略非法 `LanguageID`,按系统语言重新解析。 - 不中断 App 启动。 ### 5.3 有效语言 后端需要提供一个确定性的有效语言解析: ```text EffectiveLanguage = Resolve(LanguageMode, LanguageID, SystemLocale) ``` 规则: | 条件 | 有效语言 | |---|---| | `LanguageMode=manual` 且 `LanguageID=zh-CN` | `zh-CN` | | `LanguageMode=manual` 且 `LanguageID=en` | `en` | | `LanguageMode=auto` 且系统语言为中文 | `zh-CN` | | `LanguageMode=auto` 且系统语言为 English | `en` | | `LanguageMode=auto` 且系统语言为其他语言 | `en` | ## 6. LanguageProfile Round 2 需要把 Round 1 的最小 language registry 扩展为: ```text zh-CN en ``` 建议字段: ```go type LanguageProfile struct { ID string DisplayName string NativeName string UILocale string SystemMatchers []string DefaultModelID string } ``` Round 2 profile: | ID | DisplayName | NativeName | UILocale | SystemMatchers | DefaultModelID | |---|---|---|---|---|---| | `zh-CN` | Chinese | 中文 | `zh` | `zh`, `zh-CN`, `zh-Hans`, `zh-Hant` | `sensevoice-zh` | | `en` | English | English | `en` | `en`, `en-US`, `en-GB`, `en-AU`, `en-CA` | `sensevoice-zh` | 注意: - `en` 的默认模型本轮仍是 `sensevoice-zh`。 - Round 3 再把 English 默认模型切换为 `moonshine-en`。 ## 7. 系统语言检测 系统语言检测必须在后端有确定实现,不能只依赖前端 `navigator.language`。 优先级建议: 1. macOS 系统 locale,例如 `AppleLocale` / `AppleLanguages`。 2. 环境变量 `LANG` / `LC_ALL` / `LC_MESSAGES`。 3. 无法读取时默认 `en`。 研发实现时应提供可 mock 的检测接口,便于单元测试覆盖: ```go type SystemLocaleDetector interface { Detect() string } ``` ## 8. 前端交互 设置页新增“语言版本”区域。 中文 UI: ```text 语言版本 自动(跟随系统) 中文 English ``` English UI: ```text Language Automatic Chinese English ``` 交互规则: - 使用单选或分段控制,不使用自由输入。 - 当前选择必须有清晰选中状态。 - 切换后立即保存。 - 保存成功后 UI 文案立即切换。 - 保存失败时保留原选择并显示现有风格的错误提示或静默回退日志,本轮不新增复杂弹窗。 ## 9. 后端 API 建议新增或扩展 `ConfigService`: ```go GetLanguageSettings() LanguageSettings SetLanguageAuto() LanguageSettings SetLanguageManual(languageID string) LanguageSettings ``` 建议返回结构: ```go type LanguageSettings struct { LanguageMode string LanguageID string EffectiveLanguageID string UILocale string Options []LanguageOption } ``` 其中 `Options` 只包含: - `zh-CN` - `en` 如果研发希望直接扩展 `GetConfig()`,必须保证旧前端调用不破坏。 ## 10. 验收标准 Round 2 必须全部满足: - 老配置缺少语言字段时可正常加载。 - 新配置保存后包含 `LanguageMode` 和 `LanguageID`。 - 中文系统 auto 解析为 `zh-CN`。 - English 系统 auto 解析为 `en`。 - 其他系统语言 auto 解析为 `en`。 - 手动选择中文后 UI 立即切到中文。 - 手动选择 English 后 UI 立即切到 English。 - 手动选择重启后仍生效。 - 切换语言不删除、不迁移、不重新下载模型。 - `SelectedModelID` 保持 `sensevoice-zh`。 - 词库、历史、热键、音频设备、Dock 设置不丢失。 - 不出现 Moonshine、Parakeet、Qwen3-ASR。 ## 11. 已知风险 | 风险 | 说明 | 处理 | |---|---|---| | 系统语言检测不稳定 | macOS locale 来源可能有多个 | 使用 detector 抽象和 mock 测试 | | 前后端语言源冲突 | 当前前端自己用 `navigator.language` | Round 2 以后以前端从后端 effective language 为准 | | English UI 文案缺失 | 现有 i18n 已有 en.json,但可能不完整 | QA 检查关键设置页和 onboarding | | 用户误解为英文模型 | Round 2 English UI 仍用 SenseVoice | 文档和 UI 不展示模型升级承诺 | | 语言切换引发模型切换 | 本轮不应发生 | QA 明确验证模型目录和 SelectedModelID 不变 |