c2f07bd8d2
Introduce a standalone macOS menu-bar app (OSGKeyboardMac) that reuses the platform-agnostic OSGKeyboardShared core: record -> cloud/local ASR -> polish -> insert. Local mode uses Qwen3-ASR via mlx-swift-asr (macOS 15+, Apple Silicon); iOS targets stay zero-SPM. Harden iCloud sync for multi-device correctness: - Per-field settings merge (appSettings.v2) so concurrent edits no longer clobber each other's unrelated fields. - Per-device usage statistics (G-Counter) that sum instead of max(). - Tombstoned dictionary/history merge so deletes propagate and entries can't resurrect. - API keys replicate via iCloud Keychain, never iCloud KVS JSON; pulling a legacy blob without key fields no longer wipes local Keychain entries. - Add a low-risk "Sync Now" action in Settings. Fix Flow keyboard mic state: stay orange until the host publishes a real ready contract, share a single MicVoiceAvailability gate, and self-heal stale cross-process heartbeat jitter instead of getting stuck. Extract shared storage (SpeechHistoryStore/UsageStatisticsStore, ConfigurationStore) into OSGKeyboardShared and add tests for the new sync/merge logic.
204 lines
9.6 KiB
Plaintext
204 lines
9.6 KiB
Plaintext
/* Engine status labels */
|
||
"engine.summary.local" = "本地 · %@";
|
||
"engine.summary.cloud" = "当前:%@";
|
||
"engine.summary.cloudWithModel" = "当前:%1$@ · %2$@";
|
||
"engine.asr.appleSpeech" = "Apple 语音识别";
|
||
|
||
/* v0.2.0: flow-level warnings surfaced alongside the final transcript. */
|
||
"flow.warning.cloudPolishMissingKey" = "云端润色需要先在设置中填写 API Key,本次已插入原始识别结果。";
|
||
"flow.warning.localPolishUnavailable" = "内置润色暂不可用,本次已插入原始识别结果。";
|
||
"flow.warning.polishDegraded" = "弱网识别,本次未润色,已插入原始识别结果。";
|
||
|
||
/* LLM providers */
|
||
"provider.openai" = "OpenAI";
|
||
"provider.deepseek" = "DeepSeek";
|
||
"provider.qwen" = "通义千问";
|
||
"provider.zhipu" = "智谱 GLM";
|
||
"provider.moonshot" = "月之暗面";
|
||
"provider.mimo" = "小米 MiMo";
|
||
"provider.custom" = "自定义";
|
||
|
||
/* LLM errors */
|
||
"error.llm.invalidURL" = "API 地址无效。请在设置中检查 Base URL。";
|
||
"error.llm.noAPIKey" = "未填写 API Key。";
|
||
"error.llm.http" = "API 返回 HTTP %lld。请稍后重试或联系服务方。";
|
||
"error.llm.decoding" = "解析 API 响应失败。";
|
||
"error.llm.transport" = "网络错误,请检查连接后重试。";
|
||
"error.llm.rateLimited" = "API 调用过于频繁,请稍候再试。";
|
||
"error.llm.cancelled" = "请求已取消。";
|
||
|
||
/* ASR errors */
|
||
"error.asr.localeUnsupported" = "当前系统未分配可用语音语言模型,请稍后重试或切换语言。";
|
||
"error.asr.assetsNotReady" = "语音语言资源未就绪,请稍后重试。";
|
||
"error.asr.formatUnsupported" = "当前设备不支持该语音输入格式。";
|
||
"error.asr.noSpeech" = "未识别到语音内容,请重试。";
|
||
"error.asr.chunkFailed" = "第 %lld 段识别失败:%@";
|
||
|
||
/* Cloud ASR errors */
|
||
"error.cloudASR.noAPIKey" = "未填写 API Key。";
|
||
"error.cloudASR.invalidURL" = "云端识别接口地址无效。";
|
||
"error.cloudASR.http" = "云端识别返回 HTTP %lld。";
|
||
"error.cloudASR.httpWithMessage" = "云端识别返回 HTTP %lld:%@";
|
||
"error.cloudASR.decoding" = "解析云端识别响应失败:%@";
|
||
"error.cloudASR.transport" = "云端识别网络错误:%@";
|
||
"error.cloudASR.emptyTranscript" = "云端识别返回了空文本。";
|
||
"error.cloudASR.audioTooLong" = "音频片段超过该云端识别服务的时长限制。";
|
||
"error.cloudASR.providerUnsupported" = "该服务商暂不支持云端语音识别。";
|
||
|
||
/* 润色场景 */
|
||
"polishScenario.daily_chat" = "日常聊天";
|
||
"polishScenario.social_lifestyle" = "小红书";
|
||
"polishScenario.social_short" = "微博";
|
||
"polishScenario.goofy" = "逗比";
|
||
"polishScenario.work" = "工作";
|
||
"polishScenario.document" = "文档写作";
|
||
"polishScenario.todo" = "TODO 记录";
|
||
"polishScenario.custom" = "自定义";
|
||
"polishScenario.chip.daily_chat" = "聊天";
|
||
"polishScenario.chip.social_lifestyle" = "小红书";
|
||
"polishScenario.chip.social_short" = "微博";
|
||
"polishScenario.chip.goofy" = "逗比";
|
||
"polishScenario.chip.work" = "工作";
|
||
"polishScenario.chip.document" = "文档";
|
||
"polishScenario.chip.todo" = "TODO";
|
||
"polishScenario.chip.custom" = "自定义";
|
||
|
||
/* v0.3.0: 润色档位 */
|
||
"polish.intensity.off" = "关闭";
|
||
"polish.intensity.light" = "轻度";
|
||
"polish.intensity.medium" = "中度";
|
||
"polish.intensity.heavy" = "深度";
|
||
"polish.intensity.off.desc" = "不润色;词库为空时直接插入识别原文,有词条时仅做 ASR 纠错。";
|
||
"polish.intensity.light.desc" = "仅清除孤立语气词和重复口误;标点和结构化仍会生效。";
|
||
"polish.intensity.medium.desc" = "纠正识别错误、清除语气词、润色语句;始终补标点与结构化。推荐默认。";
|
||
"polish.intensity.heavy.desc" = "可重组段落、拆长句、自动编号。适合会议纪要与报告。";
|
||
|
||
/* v0.3.0: 输入场景标签 */
|
||
"appContext.code" = "代码";
|
||
"appContext.email" = "邮件";
|
||
"appContext.chat" = "聊天";
|
||
"appContext.document" = "长文";
|
||
"appContext.unknown" = "通用";
|
||
|
||
/* v0.3.0: 词库类别 */
|
||
"dict.category.properNoun" = "人名地名";
|
||
"dict.category.technical" = "技术名词";
|
||
"dict.category.acronym" = "缩写";
|
||
"dict.category.productName" = "产品名";
|
||
"dict.category.custom" = "自定义";
|
||
|
||
/* v0.3.0: 词库来源 */
|
||
"dict.source.manual" = "手动添加";
|
||
"dict.source.history" = "自动学习";
|
||
"dict.source.contacts" = "来自通讯录";
|
||
"dict.source.recentEdit" = "来自最近编辑";
|
||
|
||
/* 键盘 UI(扩展与预览共用) */
|
||
"keyboard.tapToTalkA11y" = "点击说话";
|
||
"keyboard.translation.chip" = "翻译";
|
||
"keyboard.translation.offMenu" = "不翻译";
|
||
"keyboard.translation.a11y" = "翻译";
|
||
"keyboard.translation.a11yHint" = "切换翻译或更改目标语言。";
|
||
|
||
/* macOS 应用 */
|
||
"mac.section.dashboard" = "仪表盘";
|
||
"mac.section.history" = "历史";
|
||
"mac.section.dictionary" = "个性词库";
|
||
"mac.section.settings" = "设置";
|
||
"mac.brand.subtitle" = "AI 听写";
|
||
"mac.devices" = "设备";
|
||
"mac.status.ready" = "准备听写…";
|
||
"mac.status.listening" = "录音中…";
|
||
"mac.status.transcribing" = "识别中…";
|
||
"mac.status.copied" = "已复制到剪贴板";
|
||
"mac.status.pasted" = "已插入前台应用";
|
||
"mac.status.copiedAndPasted" = "已复制并插入";
|
||
"mac.stat.dictationTime" = "听写时长";
|
||
"mac.stat.words" = "听写字数";
|
||
"mac.stat.translation" = "翻译字数";
|
||
"mac.stat.dictionary" = "词库";
|
||
"mac.stat.cumulativeDuration" = "累计时长";
|
||
"mac.stat.transcribed" = "累计转写";
|
||
"mac.stat.cumulativeTranslation" = "累计翻译";
|
||
"mac.stat.customTerms" = "自定义词条";
|
||
"mac.status.chipReady" = "就绪";
|
||
"mac.status.chipProcessing" = "处理中";
|
||
"mac.record.start" = "开始录音";
|
||
"mac.record.stop" = "停止";
|
||
"mac.record.pressStop" = "点击停止";
|
||
"mac.copy" = "复制";
|
||
"mac.openWindow" = "打开主窗口";
|
||
"mac.quit" = "退出";
|
||
"mac.mode.cloud" = "云端模式";
|
||
"mac.mode.local" = "本地模式";
|
||
"mac.connected" = "已连接";
|
||
"mac.offline" = "离线";
|
||
"mac.history.recent" = "最近";
|
||
"mac.history.empty" = "还没有语音识别记录。";
|
||
"mac.history.select" = "选择一条记录";
|
||
"mac.history.clearTitle" = "清空全部历史?";
|
||
"mac.history.clearMessage" = "此操作无法撤销。";
|
||
"mac.history.clearConfirm" = "全部清空";
|
||
"mac.dict.health" = "词库健康度";
|
||
"mac.dict.healthDesc" = "影响识别偏置且润色时不会被改写的自定义词条。";
|
||
"mac.dict.search" = "搜索词条";
|
||
"mac.dict.empty" = "还没有词条";
|
||
"mac.dict.emptyBody" = "在 iPhone 或 iPad 上添加词条以提升识别准确性,它们会通过 iCloud 同步到这里。";
|
||
"mac.dict.noMatch" = "无匹配结果";
|
||
"mac.cancel" = "取消";
|
||
"mac.delete" = "删除";
|
||
"mac.dict.deleteTitle" = "删除该词条?";
|
||
"mac.dict.deleteMessage" = "此操作无法撤销。";
|
||
"mac.hint.holdOption" = "长按 Option 开始听写";
|
||
"mac.settings.cloudProvider" = "云端服务商";
|
||
"mac.settings.service" = "服务商";
|
||
"mac.settings.apiKey" = "API 密钥";
|
||
"mac.settings.model" = "模型";
|
||
"mac.settings.recognition" = "识别方式";
|
||
"mac.settings.cloudEngine" = "云端引擎与 AI 润色";
|
||
"mac.settings.cloudEngineDesc" = "通过服务商 API 进行高质量转写,并自动润色语法与风格。";
|
||
"mac.settings.localEngine" = "本地识别(Qwen3-ASR)";
|
||
"mac.settings.localEngineDesc" = "使用 Qwen3-ASR 1.7B(MLX)本地转写,高隐私、低延迟。";
|
||
"mac.settings.localSpeechFallback" = "本地识别(Apple Speech)";
|
||
"mac.settings.localSpeechFallbackDesc" = "未安装 Qwen3 模型时,使用 Apple 本地语音识别。";
|
||
"mac.settings.about" = "关于";
|
||
"mac.settings.general" = "通用";
|
||
"mac.settings.input" = "输入与快捷键";
|
||
"mac.settings.recognitionLanguage" = "识别语言";
|
||
"mac.settings.interfaceLanguage" = "界面语言";
|
||
"mac.settings.autoPaste" = "听写后自动粘贴";
|
||
"mac.settings.autoPasteDesc" = "转写完成后向前台应用模拟 ⌘V(需辅助功能权限)。";
|
||
"mac.settings.hotkey" = "全局快捷键";
|
||
"mac.settings.hotkeyDesc" = "按住 Option (⌥) 键即可从任意应用开始听写。";
|
||
"mac.settings.qwen3Model" = "Qwen3 模型目录";
|
||
"mac.settings.qwen3ModelDesc" = "需包含 config.json、model.safetensors、vocab.json 与 merges.txt。";
|
||
"mac.settings.qwen3Browse" = "选择文件夹…";
|
||
"mac.settings.qwen3Missing" = "未找到 Qwen3 模型,暂时使用 Apple Speech。";
|
||
"mac.settings.accessibility" = "辅助功能";
|
||
"mac.settings.accessibilityDesc" = "全局快捷键与自动粘贴需要此权限。";
|
||
"mac.settings.openAccessibility" = "打开系统设置";
|
||
"mac.settings.appearance" = "外观";
|
||
"mac.settings.appearanceDesc" = "跟随系统,或强制使用浅色 / 深色。";
|
||
"mac.appearance.system" = "跟随系统";
|
||
"mac.appearance.light" = "浅色";
|
||
"mac.appearance.dark" = "深色";
|
||
"mac.error.noAudio" = "没有捕获到音频";
|
||
"mac.error.noCloudASR" = "当前服务商不支持云端语音识别";
|
||
"mac.error.emptyTranscript" = "没有识别到语音";
|
||
"mac.error.qwen3ModelMissing" = "未安装 Qwen3-ASR 模型";
|
||
"mac.error.qwen3LoadFailed" = "Qwen3 模型加载失败:%@";
|
||
"mac.error.qwen3InferenceFailed" = "Qwen3 转写失败:%@";
|
||
"mac.error.accessibilityRequired" = "请在系统设置中为 OSGKeyboard 启用辅助功能";
|
||
"mac.foregroundApp" = "前台应用:%@";
|
||
"mac.sync.settingsTitle" = "iCloud 同步";
|
||
"mac.sync.settingsSubtitle" = "通过 iCloud 在多台设备间同步设置、使用统计、语音历史与 API 密钥。";
|
||
"mac.sync.syncNow" = "立即同步";
|
||
"mac.sync.dictTitle" = "个人词库 iCloud 同步";
|
||
"mac.sync.dictSubtitle" = "在所有设备间同步个人词库。";
|
||
"mac.sync.error.generic" = "无法与 iCloud 同步,请稍后重试。";
|
||
"mac.sync.error.dictTooLarge" = "词库过大,无法通过 iCloud 同步。";
|
||
|
||
"settings.appLanguage.auto" = "自动";
|
||
"settings.appLanguage.english" = "英文";
|
||
"settings.appLanguage.chinese" = "中文";
|