diff --git a/openless-all/app/src/components/SettingsModal.tsx b/openless-all/app/src/components/SettingsModal.tsx
index d32a4295..0f85aa81 100644
--- a/openless-all/app/src/components/SettingsModal.tsx
+++ b/openless-all/app/src/components/SettingsModal.tsx
@@ -333,6 +333,7 @@ function LanguagePicker() {
>
+
);
diff --git a/openless-all/app/src/i18n/en.ts b/openless-all/app/src/i18n/en.ts
index 069b6f52..00e5c607 100644
--- a/openless-all/app/src/i18n/en.ts
+++ b/openless-all/app/src/i18n/en.ts
@@ -391,6 +391,7 @@ export const en: typeof zhCN = {
labelDesc: 'Choose "Follow system" to match the OS language at launch.',
followSystem: 'Follow system',
zh: '简体中文',
+ zhTW: '繁體中文',
en: 'English',
restartHint: 'Some native menus (system tray, etc.) may require an app restart to fully switch.',
},
diff --git a/openless-all/app/src/i18n/index.ts b/openless-all/app/src/i18n/index.ts
index d326eaa7..b366837a 100644
--- a/openless-all/app/src/i18n/index.ts
+++ b/openless-all/app/src/i18n/index.ts
@@ -12,8 +12,9 @@ import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import { en } from './en';
import { zhCN } from './zh-CN';
+import { zhTW } from './zh-TW';
-export const SUPPORTED_LOCALES = ['zh-CN', 'en'] as const;
+export const SUPPORTED_LOCALES = ['zh-CN', 'zh-TW', 'en'] as const;
export type SupportedLocale = (typeof SUPPORTED_LOCALES)[number];
export const LOCALE_STORAGE_KEY = 'ol.locale';
@@ -22,14 +23,17 @@ const FOLLOW_SYSTEM_VALUE = 'system';
function detectSystemLocale(): SupportedLocale {
if (typeof navigator === 'undefined') return 'zh-CN';
const nav = (navigator.language || '').toLowerCase();
- if (nav.startsWith('zh')) return 'zh-CN';
+ if (nav.startsWith('zh')) {
+ if (nav.includes('hant') || nav.includes('tw') || nav.includes('hk') || nav.includes('mo')) return 'zh-TW';
+ return 'zh-CN';
+ }
return 'en';
}
function getStoredLocale(): SupportedLocale | null {
if (typeof window === 'undefined') return null;
const raw = window.localStorage.getItem(LOCALE_STORAGE_KEY);
- return raw === 'zh-CN' || raw === 'en' ? raw : null;
+ return raw === 'zh-CN' || raw === 'zh-TW' || raw === 'en' ? raw : null;
}
const initialLng: SupportedLocale = getStoredLocale() ?? detectSystemLocale();
@@ -37,6 +41,7 @@ const initialLng: SupportedLocale = getStoredLocale() ?? detectSystemLocale();
void i18n.use(initReactI18next).init({
resources: {
'zh-CN': { translation: zhCN },
+ 'zh-TW': { translation: zhTW },
en: { translation: en },
},
lng: initialLng,
diff --git a/openless-all/app/src/i18n/zh-CN.ts b/openless-all/app/src/i18n/zh-CN.ts
index d156ef1c..9ed8912e 100644
--- a/openless-all/app/src/i18n/zh-CN.ts
+++ b/openless-all/app/src/i18n/zh-CN.ts
@@ -389,6 +389,7 @@ export const zhCN = {
labelDesc: '选择「跟随系统」时按操作系统当前语言显示。',
followSystem: '跟随系统',
zh: '简体中文',
+ zhTW: '繁體中文',
en: 'English',
restartHint: '部分原生菜单(系统托盘等)可能需要重启 App 才会切换。',
},
diff --git a/openless-all/app/src/i18n/zh-TW.ts b/openless-all/app/src/i18n/zh-TW.ts
new file mode 100644
index 00000000..5bd7fa0f
--- /dev/null
+++ b/openless-all/app/src/i18n/zh-TW.ts
@@ -0,0 +1,515 @@
+import type { zhCN } from './zh-CN';
+
+// 繁體中文資源 — 與產品當前文案保持一致。
+// 新增 key 時,必須同步更新 en.ts,避免切換到 English 後出現中文殘留。
+
+export const zhTW: typeof zhCN = {
+ app: {
+ name: 'OpenLess',
+ tagline: '自然說話,完美書寫',
+ },
+ common: {
+ loading: '加載中…',
+ refresh: '刷新',
+ clear: '清空',
+ copy: '複製',
+ delete: '刪除',
+ later: '稍後',
+ cancel: '取消',
+ close: '關閉',
+ show: '顯示',
+ hide: '隱藏',
+ saved: '已保存',
+ saving: '保存中',
+ copied: '已複製',
+ operationFailed: '操作失敗',
+ add: '添加',
+ durationSeconds: '{{value}} 秒',
+ durationMinutes: '{{value}} 分鐘',
+ },
+ capsule: {
+ thinking: '正在思考中',
+ cancelled: '已取消',
+ error: '出錯了',
+ inserted: '已插入 {{count}}',
+ translating: '正在翻譯',
+ },
+ qa: {
+ thinking: '思考中…',
+ error: '出錯了,請稍後再試。',
+ errorRetry: '重試',
+ errorRetryHint: '再按 {{recordHotkey}} 重新提問。',
+ pinTooltip: '固定(不自動關閉)',
+ unpinTooltip: '取消固定',
+ closeTooltip: '關閉',
+ selectionPreview: '基於選中文本:',
+ emptyTitle: '按 {{recordHotkey}} 開始提問',
+ emptyDesc: '在任意 app 選中一段文字後,按一次 {{recordHotkey}} 開始錄音,再按一次結束並提交。回答會顯示在這裏,可以連續多輪追問。',
+ recordingHint: '錄音中…再按一次 {{recordHotkey}} 結束並提問',
+ statusIdle: '按 {{recordHotkey}} 提問',
+ statusRecording: '錄音中',
+ statusThinking: '思考中',
+ statusError: '出錯了',
+ },
+ nav: {
+ overview: '概覽',
+ history: '歷史',
+ vocab: '詞彙表',
+ style: '風格',
+ translation: '翻譯',
+ selectionAsk: '劃詞追問',
+ },
+ shell: {
+ shortcutLabel: '錄音快捷鍵',
+ shortcutHint: '開始 / 停止',
+ betaTag: 'BETA',
+ betaNote: '所有數據都只保存在本機。',
+ footer: {
+ account: '賬戶',
+ feedback: '反饋',
+ settings: '設置',
+ help: '幫助',
+ version: '版本 {{version}}',
+ helpPopover: {
+ tagline: '本地驅動的語音輸入層',
+ releaseNotes: '查看發佈日誌 ↗',
+ docs: '幫助中心 ↗',
+ },
+ },
+ providerPrompt: {
+ title: '設置語音提供商',
+ body: '還沒有配置 ASR 或 LLM 提供商,語音輸入和潤色暫時無法正常工作。',
+ later: '稍後',
+ openSettings: '去設置',
+ },
+ hotkeyModePrompt: {
+ title: '檢查錄音方式',
+ body: '本版本默認改爲“切換式說話”。如果你之前改過快捷鍵觸發方式,請到“錄音”裏手動確認一次。本次更新同時調整了快捷鍵方式的讀取邏輯;如果你更習慣按住說話,可以重新切回“按住說話”。',
+ later: '稍後提醒',
+ openSettings: '去錄音設置',
+ },
+ },
+ onboarding: {
+ welcome: '歡迎使用 OpenLess',
+ intro: '本地說出,本地落字。開始前需要兩個系統權限。',
+ accessibilityTitle: '輔助功能',
+ hotkeyTitle: '全局快捷鍵',
+ accessibilityDesc: '用於監聽全局快捷鍵(默認 {{trigger}})並把識別結果寫入光標位置。',
+ hotkeyDesc: '用於確認全局快捷鍵監聽可用。',
+ micTitle: '麥克風',
+ micDesc: '用於捕獲你的語音輸入。',
+ actionNotApplicable: '無需授權',
+ actionGranted: '已授權',
+ actionOpenSystem: '打開系統設置',
+ actionGrant: '授權',
+ actionRequestMic: '彈出授權',
+ accessibilityHint: '授權後必須**完全退出 OpenLess** 再重新打開(macOS TCC 規則)。',
+ footerHint: '授權全部完成後此引導自動關閉。如果一直不消失,從菜單欄 OpenLess → 退出,重新打開 App。',
+ },
+ overview: {
+ kicker: 'DASHBOARD',
+ title: '今日概覽',
+ desc: '本地說出,本地落字。下面是你今日的口述節奏與系統狀態。',
+ pressPrefix: '按',
+ pressSuffix: '開始錄音',
+ asrKind: 'ASR 語音',
+ llmKind: 'LLM 模型',
+ asrName: '火山引擎',
+ asrSubname: 'bigmodel',
+ llmName: 'OpenAI 兼容',
+ llmConfigured: '已配置 active LLM',
+ llmNotConfigured: '未配置',
+ statusConfigured: '已配置',
+ statusNotConfigured: '未配置',
+ metricChars: '今日字數',
+ metricSegments: '{{count}} 段',
+ metricDuration: '今日總時長',
+ metricAvg: '平均段落',
+ metricAvgTrend: '今日均值',
+ metricNoData: '暫無數據',
+ metricTotal: '累計記錄',
+ metricTotalTrend: '本機存檔 (上限 200)',
+ weekTitle: '近 7 天',
+ weekUnit: '條數 / 天',
+ recentTitle: '最近識別',
+ recentAll: '全部記錄 →',
+ recentEmpty: '還沒有記錄。按 {{trigger}} 開始第一次錄音。',
+ weekDays: ['日', '一', '二', '三', '四', '五', '六'],
+ },
+ history: {
+ kicker: 'HISTORY',
+ title: '歷史記錄',
+ desc: '最近的識別結果只保存在本機。左側爲時間線,右側爲原文與潤色對比。',
+ filterAll: '全部',
+ summary: '共 {{total}} 條 · 顯示 {{shown}}',
+ empty: '還沒有歷史記錄。按 {{trigger}} 錄一段試試。',
+ rawLabel: '原文',
+ rawEmpty: '(空)',
+ selectHint: '左側選一條查看詳情。',
+ insertedTo: '插入到',
+ chars: '{{count}} 字',
+ vocabHits: '{{count}} 個熱詞',
+ inserted: '已插入',
+ pasteSent: '已嘗試粘貼',
+ copiedFallback: '已複製(需 {{shortcut}})',
+ insertFailed: '插入失敗',
+ confirmClear: '確定清空全部 {{count}} 條記錄?此操作不可恢復。',
+ },
+ vocab: {
+ kicker: 'VOCABULARY',
+ title: '詞彙表',
+ desc: '告訴模型識別前可能出現的詞——生詞、新詞或專業詞彙。同時進入 ASR 熱詞與後期模型上下文。',
+ placeholder: '輸入詞語,按 Enter 或點添加…',
+ tip: '支持中英混合 · 數字開頭按字面識別 · 命中次數自動計數',
+ loadFailed: '加載失敗:{{err}}',
+ empty: '還沒有詞條。在上面輸入一個生詞或專業術語,讓模型在聽寫時優先匹配。',
+ tipDisabled: '點擊禁用此詞條',
+ tipEnabled: '點擊啓用此詞條',
+ removeAria: '刪除',
+ presets: {
+ title: '場景預設',
+ tip: '可多選後批量啓用;支持編輯和新建,已爲後續導入導出預留本地結構。',
+ create: '新建預設',
+ apply: '啓用所選',
+ save: '保存預設',
+ edit: '編輯 {{name}}',
+ newPreset: '新預設',
+ namePlaceholder: '預設名稱',
+ wordsPlaceholder: '詞條(用逗號或換行分隔)',
+ },
+ },
+ style: {
+ kicker: 'STYLE',
+ title: '輸出風格',
+ desc: '選擇默認風格用於全局錄音。每張卡可單獨啓停;啓停的風格不會出現在歷史記錄的「重新潤色」切換中。',
+ masterToggle: '整體啓用',
+ currentDefault: '當前默認',
+ ariaSetDefault: '設爲默認',
+ modes: {
+ raw: { name: '原文', desc: '只補標點和必要分句,不改寫不擴寫。', sample: '保留原始口語;嗯、那個等口癖會被去除,但不會重組語句。' },
+ light: { name: '輕度潤色', desc: '去口癖、補標點,整理爲可發送的自然文字。', sample: '讓轉寫聽起來不像念稿——保留語氣和表達習慣,但行文流暢。' },
+ structured: { name: '清晰結構', desc: '多個主題或步驟時,自動組織爲分點列表。', sample: '1. 主題一\na. 要點\nb. 要點\n2. 主題二\na. 要點\nb. 要點' },
+ formal: { name: '正式表達', desc: '工作溝通和郵件場景,更專業更完整。', sample: '郵件場景自動識別問候 / 落款;不引入空泛客套。' },
+ },
+ },
+ translation: {
+ kicker: 'TRANSLATION',
+ title: '翻譯',
+ desc: '把口述的內容自動翻譯成目標語言後再插入。目標語言、工作語言、觸發方式都在這裏配置。',
+ statusEnabled: '已啓用',
+ statusDisabled: '未啓用',
+ working: {
+ title: '工作語言',
+ desc: '勾選你日常會用到的語言(多選)。這組語言會作爲前提注入 LLM 的 system prompt 頭部,影響潤色與翻譯的判斷(專名拼寫、語氣、行文習慣)。',
+ },
+ target: {
+ title: '翻譯目標語言',
+ desc: '選了某個語言後,錄音過程中任意時刻按一下 Shift,停止後就會把轉寫翻譯成該語言再插入到光標位置。選「不啓用」則 Shift 沒有任何效果,走普通潤色管線。',
+ disabled: '不啓用(Shift 按下不觸發翻譯)',
+ },
+ howto: {
+ title: '使用方法',
+ step1: '在另一個 app 的輸入框裏聚焦光標(備忘錄、郵件、聊天窗口都行)。',
+ step2: '按一下"錄音快捷鍵"(當前是 {{trigger}}),開始錄音。',
+ step3: '在錄音過程中任意時刻按一下 Shift——按一下即可,不需要按住,可以在開口前、說到一半、快說完時按。',
+ step4: '再按一下"錄音快捷鍵"停止錄音。',
+ step5: '系統會把轉寫交給大模型翻譯成上面選的目標語言,然後插入到一開始那個輸入框光標位置。',
+ indicatorTitle: '怎麼知道翻譯模式生效了',
+ indicatorDesc: '一旦按下 Shift,屏幕底部錄音膠囊的上方會立刻懸浮一個藍色"● 正在翻譯"小藥丸——它會一直顯示到本次插入完成,讓你確認這次輸出會走翻譯管線。',
+ fallbackTitle: '安全兜底',
+ fallbackDesc: '翻譯模式選「不啓用」時 Shift 是沒作用的;翻譯過程中如果大模型調用失敗,會回退到把原始中文轉寫直接插入,不會丟字。詳見 issue #4。',
+ },
+ },
+ selectionAsk: {
+ kicker: 'SELECTION ASK',
+ title: '劃詞追問',
+ desc: '選中任意 app 裏的一段文字,按 {{hotkey}} 彈出浮窗,再按 {{recordHotkey}} 錄音提問。支持多輪追問,浮窗一直保留直到你手動關。',
+ statusEnabled: '已啓用',
+ statusDisabled: '未啓用',
+ hotkey: {
+ title: '彈出浮窗的快捷鍵',
+ desc: '只決定「打開 / 關閉」浮窗。浮窗裏錄音 / 提問統一用 {{recordHotkey}}(與你的主聽寫鍵複用)。選「不啓用」則關閉整個功能。',
+ optionDisabled: '不啓用',
+ chordWarning: '',
+ },
+ history: {
+ title: '保存歷史',
+ desc: '勾上則把每次追問的「選中文本 + 你的語音問題 + AI 答案」寫入本地存檔(不上雲)。默認關,關閉時浮窗一關問答即遺忘,更注重隱私。',
+ },
+ howto: {
+ title: '使用方法',
+ step1: '按「{{hotkey}}」在任意時刻打開浮窗(不需要先選文字)。',
+ step2: '在任意 app(瀏覽器、Mail、IDE、PDF reader…)裏選中一段文字。',
+ step3: '按一下 **{{recordHotkey}}**——開始錄音;再按一下 {{recordHotkey}},停止並提交,AI 答案顯示在浮窗裏。',
+ step4: '同一個浮窗裏可繼續多輪追問:再按 {{recordHotkey}} 錄音 → 再按 {{recordHotkey}} 提交。可以重新選文字讓下一輪帶新選區,也可以不選直接對話。',
+ step5: '按 Esc 或浮窗右上角 ✕ 關閉,關閉即清空所有多輪歷史。再按「{{hotkey}}」就是一段新的對話。',
+ windowTitle: '浮窗位置 + 拖動 + 釘住',
+ windowDesc: '浮窗第一次打開在屏幕底部錄音膠囊正上方;標題欄可拖動,移到任意位置後下一次打開會保留位置(同一次啓動期間)。右上角 📌 釘住時即使重新提問也保留窗口;不釘住按 Esc 即關。',
+ privacyTitle: '隱私契約',
+ privacyDesc: '選中的文本只在內存裏活到浮窗關閉,**絕不**寫入歷史存檔(保存歷史開關只控制問答 metadata);超過 4000 字符會截首+尾各 2000 後再上送大模型,避免泄露太多。LLM 調用走你已配的 ARK / DeepSeek 等 OpenAI 兼容 endpoint。',
+ },
+ },
+ settings: {
+ kicker: 'SETTINGS',
+ title: '設置',
+ desc: '錄音方式、模型與語音提供商、快捷鍵、權限與關於信息——全部在這裏。',
+ sections: {
+ recording: '錄音',
+ providers: '提供商',
+ shortcuts: '快捷鍵',
+ permissions: '權限',
+ language: '語言',
+ about: '關於',
+ },
+ recording: {
+ title: '錄音',
+ desc: '定義全局錄音的快捷鍵與觸發方式。',
+ hotkeyLabel: '錄音快捷鍵',
+ hotkeyDescAcc: '按下即開始捕獲語音,全局生效。需要授予輔助功能權限。',
+ hotkeyDescNoAcc: '按下即開始捕獲語音,全局生效。無需額外輔助功能授權。',
+ modeLabel: '錄音方式',
+ modeDesc: '切換式 = 按一次開始、再按一次結束;按住說話 = 按住開始、鬆開結束。',
+ modeToggle: '切換式',
+ modeHold: '按住說話',
+ migrationNoticeTitle: '默認已改爲切換式說話',
+ migrationNoticeDesc: '如果你之前改過快捷鍵觸發方式,請在這裏手動確認一次。本次更新調整了快捷鍵方式的默認值與讀取邏輯;如果你更習慣按住說話,可以重新切回“按住說話”。',
+ capsuleLabel: '錄音膠囊',
+ capsuleDesc: '錄音 / 轉寫時在屏幕底部顯示半透明膠囊。',
+ restoreClipboardLabel: '插入後恢復剪貼板',
+ restoreClipboardDesc: '僅 Windows / Linux:粘貼成功後恢復你原來的剪貼板內容(默認開)。關掉就把聽寫文本留在剪貼板,模擬粘貼沒真正落地時可以手動 Ctrl+V 找回。詳見 issue #111。',
+ allowNonTsfFallbackLabel: '允許非 TSF 兜底',
+ allowNonTsfFallbackDesc: '僅 Windows:TSF 直接上屏失敗後,允許改用 Unicode SendInput、快捷鍵粘貼或 WM_PASTE。關閉後可驗證是否真實使用 TSF 輸入。',
+ startupAtBoot: '開機自啓',
+ startupAtBootDesc: '登錄後自動啓動 OpenLess。macOS 寫 LaunchAgent,Linux 寫 ~/.config/autostart,Windows 寫 HKCU\\Run(不需要管理員)。詳見 issue #194。',
+ startupAtBootError: '開機自啓切換失敗:{{message}}',
+ },
+ providers: {
+ llmTitle: 'LLM 模型(潤色)',
+ llmDesc: 'OpenAI 兼容協議,支持多家供應商切換。',
+ providerLabel: '供應商',
+ llmProviderDesc: '選擇後將自動填入 Base URL 默認值。',
+ asrProviderDesc: '切換後將自動選用對應憑據。',
+ asrTitle: 'ASR 語音(轉寫)',
+ asrDesc: '用於將口述實時轉寫爲文本。',
+ presets: {
+ ark: 'ARK(火山方舟)',
+ deepseek: 'DeepSeek',
+ siliconflow: '硅基流動',
+ openai: 'OpenAI',
+ custom: '自定義',
+ asrVolcengine: '火山引擎 bigasr',
+ asrSiliconflow: '硅基流動 SenseVoice',
+ asrZhipu: '智譜 GLM-ASR',
+ asrGroq: 'Groq Whisper-large-v3',
+ asrWhisper: 'OpenAI Whisper(兼容)',
+ },
+ volcengineAppKeyLabel: 'APP ID',
+ volcengineAccessKeyLabel: 'Access Token',
+ volcengineResourceIdLabel: 'Resource ID',
+ volcengineMappingNote: 'Secret Key 當前無需填寫。Resource ID 默認使用 volc.bigasr.sauc.duration。',
+ fillDefault: '填入默認值',
+ readFailed: '讀取失敗',
+ apiKeyLabel: 'API 密鑰',
+ baseUrlLabel: '接口地址',
+ modelLabel: '模型',
+ appIdLabel: 'App ID(應用 ID)',
+ accessKeyLabel: 'Access Key',
+ resourceIdLabel: '資源 ID',
+ toolsLabel: '連接檢查',
+ toolsDesc: '先保存上方配置,再驗證當前模型連通性或拉取模型;失敗時仍可手動填寫模型 ID。',
+ validate: '驗證',
+ validating: '驗證中…',
+ fetchModels: '拉取模型',
+ loadingModels: '拉取模型中…',
+ modelMissing: '未配置模型,請先填寫模型 ID。',
+ modelsEmpty: '鑑權成功,但沒有返回可用模型。',
+ modelsLoaded: '已拉取 {{count}} 個模型。',
+ selectModel: '選擇一個模型寫入上方字段',
+ modelSaved: '已保存模型 {{model}}。',
+ validateSuccess: '連接檢查通過。',
+ providerHttpStatus: '供應商接口返回 {{status}},請檢查 API Key 權限或 Endpoint。',
+ endpointMustUseHttps: 'Endpoint 必須使用 HTTPS(本地 localhost/127.0.0.1 測試除外)。',
+ endpointInvalid: 'Endpoint 格式不合法。',
+ responseTooLarge: '供應商響應過大,已停止驗證以保證安全。',
+ asrInvalidJson: 'ASR 響應不是有效 JSON。',
+ asrMissingTextField: 'ASR 響應缺少 text 字段。',
+ apiKeyMissing: 'API Key 爲空。',
+ endpointMissing: 'Endpoint 爲空。',
+ requestTimeout: '請求超時,請稍後重試。',
+ },
+ shortcuts: {
+ title: '快捷鍵速查',
+ descAcc: '所有快捷鍵全局生效,需要在權限設置中開啓輔助功能。',
+ descNoAcc: '所有快捷鍵全局生效。若無響應,請在權限頁查看全局快捷鍵監聽狀態。',
+ startStop: '開始 / 停止錄音',
+ cancel: '取消本次錄音',
+ confirm: '膠囊確認插入',
+ switchStyle: '切換上一次風格',
+ openApp: '打開 OpenLess',
+ confirmHint: '點擊右側 ✓',
+ notSupported: '暫未支持',
+ },
+ permissions: {
+ title: '權限',
+ descAcc: 'OpenLess 需要以下系統權限才能正常工作。授權後通常需要完全退出 App 重啓一次才生效。',
+ descNoAcc: 'OpenLess 需要麥克風可用,並依賴全局快捷鍵監聽狀態判斷 native hook 是否正常工作。',
+ micLabel: '麥克風',
+ micDesc: '用於捕獲你的語音輸入。',
+ accLabel: '輔助功能',
+ accDesc: '用於監聽全局快捷鍵並將識別結果寫入光標位置。',
+ hotkeyLabel: '全局快捷鍵',
+ hotkeyDescWithAdapter: '當前適配器:{{adapter}}。用於判斷快捷鍵監聽是否已經安裝。',
+ hotkeyDescPlain: '用於判斷快捷鍵監聽是否已經安裝。',
+ networkLabel: '網絡',
+ networkDesc: '雲端 ASR / LLM 調用所必需。本地模式可關閉。',
+ networkOk: '可用',
+ checking: '檢查中…',
+ granted: '已授權',
+ notApplicable: '無需授權',
+ denied: '未授權',
+ indeterminate: '未確定',
+ openSystem: '打開系統設置',
+ grant: '授權',
+ hotkeyInstalled: '已安裝',
+ hotkeyStarting: '安裝中…',
+ hotkeyFailed: '監聽失敗',
+ windowsImeLabel: 'Windows 輸入法後端',
+ windowsImeDesc: '用於在語音會話期間臨時切換到 OpenLess TSF 輸入法,避免剪貼板插入限制。',
+ windowsImeInstalled: '已安裝',
+ windowsImeUnavailable: '不可用',
+ windowsIme: {
+ installed: '已安裝。語音輸入時會臨時切換到 OpenLess 輸入法。',
+ notInstalled: '未安裝。OpenLess 正在使用剪貼板 / WM_PASTE 兜底。',
+ registrationBroken: '註冊已損壞。請重新安裝 OpenLess 輸入法。',
+ notWindows: '僅 Windows 可用。',
+ },
+ },
+ language: {
+ title: '界面語言',
+ desc: '切換 UI 顯示語言。當前會話即時生效,下次啓動自動沿用。',
+ label: '語言',
+ labelDesc: '選擇「跟隨系統」時按操作系統當前語言顯示。',
+ followSystem: '跟隨系統',
+ zh: '簡體中文',
+ zhTW: '繁體中文',
+ en: 'English',
+ restartHint: '部分原生菜單(系統托盤等)可能需要重啓 App 纔會切換。',
+ },
+ about: {
+ tagline: '自然說話,完美書寫',
+ checkUpdate: '檢查更新',
+ checkUpdateBtn: '檢查',
+ checkingUpdate: '檢查中…',
+ upToDate: '當前已是最新版本。',
+ updateError: '檢查或更新失敗,請稍後重試。',
+ openReleases: '打開 Releases',
+ source: '源碼',
+ docs: '文檔',
+ feedback: '反饋',
+ qq: '社區 QQ 羣',
+ qqDesc: '使用 QQ 搜索羣號加入,或掃碼進羣。',
+ copyQq: '複製羣號',
+ privacy: '隱私',
+ privacyDesc: '所有識別結果僅保存在本機。雲端 API 僅用於實時轉寫與潤色,不會保留你的錄音。',
+ localFirst: '本地優先',
+ updateDialog: {
+ available: {
+ title: '發現新版本',
+ desc: '發現 OpenLess {{version}},是否現在更新?',
+ },
+ downloading: {
+ title: '正在下載更新',
+ desc: '正在下載 OpenLess {{version}},請保持應用打開。',
+ },
+ downloaded: {
+ title: '更新已準備好',
+ desc: 'OpenLess {{version}} 已安裝完成。是否現在自動重啓以應用更新?',
+ },
+ installing: {
+ title: '正在安裝更新',
+ desc: '正在安裝 OpenLess {{version}},請保持應用打開。',
+ },
+ install: '現在更新',
+ downloadingLabel: '下載中…',
+ installingLabel: '安裝中…',
+ later: '稍後手動重啓',
+ restartNow: '現在重啓',
+ progress: '{{progress}}% · {{downloaded}} / {{total}}',
+ progressUnknown: '已下載 {{downloaded}}',
+ },
+ },
+ },
+ modal: {
+ sections: {
+ account: '賬戶',
+ settings: '設置',
+ personalize: '個性化',
+ about: '關於',
+ helpCenter: '幫助中心',
+ releaseNotes: '版本說明',
+ },
+ account: {
+ localUser: '本地用戶',
+ localUserDesc: '未登錄 · 所有數據保存在本機',
+ loginSync: '登錄 / 同步',
+ footer: 'OpenLess 默認完全本地運行。登錄後可在多設備間同步詞彙表與風格預設,識別仍在本機或你配置的 Provider 上完成。',
+ },
+ personalize: {
+ appearance: '外觀',
+ appearanceDesc: '跟隨系統 / 淺色 / 深色',
+ appearanceSystem: '跟隨系統',
+ appearanceLight: '淺色',
+ appearanceDark: '深色',
+ language: '界面語言',
+ font: '字體大小',
+ fontDesc: '整體縮放界面字號,立即生效。',
+ fontSmall: '小',
+ fontMedium: '中',
+ fontLarge: '大',
+ blur: '毛玻璃強度',
+ blurDesc: '影響窗口內層 backdrop-filter 強度(macOS 系統磨砂層無法運行時調)。',
+ startupOpen: '啓動時打開',
+ startupOverview: '概覽',
+ startupLast: '上次位置',
+ startupAtBoot: '開機自啓',
+ },
+ about: {
+ tagline: '自然說話,完美書寫',
+ checkUpdate: '檢查更新',
+ checkUpdateBtn: '檢查',
+ docs: '文檔',
+ docsBtn: 'openless.app/docs ↗',
+ feedback: '反饋渠道',
+ feedbackBtn: 'GitHub Issues ↗',
+ privacy: '隱私',
+ privacyDesc: '所有識別結果只保存在本機,雲端 API 僅用於實時調用。',
+ localFirst: '本地優先',
+ },
+ },
+ windowChrome: {
+ minimize: '最小化',
+ maximize: '最大化',
+ close: '關閉',
+ },
+ hotkey: {
+ triggers: {
+ rightOption: '右 Option',
+ leftOption: '左 Option',
+ rightControl: '右 Control',
+ leftControl: '左 Control',
+ rightCommand: '右 Command',
+ fn: 'Fn (地球鍵)',
+ rightAlt: '右 Alt',
+ },
+ fallback: '全局快捷鍵',
+ modeHoldSuffix: '(按住說話)',
+ modeToggleSuffix: '(開始 / 停止)',
+ usageHold: '按住 {{trigger}} 說話,鬆開結束。',
+ usageToggle: '按 {{trigger}} 開始錄音,再按一次結束。',
+ adapter: {
+ macEventTap: 'macOS Event Tap',
+ windowsLowLevel: 'Windows 低層鍵盤 hook',
+ rdev: 'rdev 監聽器',
+ },
+ },
+};
diff --git a/openless-all/app/src/pages/Settings.tsx b/openless-all/app/src/pages/Settings.tsx
index 71c1abfd..574d02b9 100644
--- a/openless-all/app/src/pages/Settings.tsx
+++ b/openless-all/app/src/pages/Settings.tsx
@@ -1096,6 +1096,7 @@ function LanguageSection() {
>
+