diff --git a/public/chat/help-zh_hk.md b/public/chat/help-zh_hk.md new file mode 100644 index 0000000..426e1ad --- /dev/null +++ b/public/chat/help-zh_hk.md @@ -0,0 +1,126 @@ +## 幫助 + +歡迎使用 **K.G.Studio 音樂創作助手**。這是您的 AI 音樂創作與編曲助手,可以幫助您構思、編排和修改音樂內容。 + +您可以選擇使用 **內置本地瀏覽器 LLM**(無需 API Key),也可以配置外部 LLM 提供方。下面是常用的入門方式。 + +--- + +### 使用本地 LLM(瀏覽器)——無需 API Key + +K.G.Studio 可以通過 WebGPU 在瀏覽器中直接運行 **Gemma 4 E4B**。不會產生外部 API 調用費用,數據也不會離開您的設備。 + +1. 進入 **設置 ⚙️ → 通用 → LLM 提供方**,選擇 **本地 LLM(瀏覽器)**(默認選項)。 +2. 第一次打開聊天時會自動下載模型(約 2.8 GB),後續會緩存在本地瀏覽器中。 +3. 可選設置 **上下文長度**(32k / 64k / 128k tokens);越大越佔顯存。 +4. 現在就可以開始聊天,無需 Key、帳號或持續聯網。 + +**要求:** Chrome 113+ 或 Edge 113+,安全上下文(HTTPS 或 localhost),以及至少 8 GB 顯存的 GPU 或至少 16 GB 統一內存的系統。 + +> 注意:本地模型效果無法與 GPT 或 Claude 這類商業模型完全相比。複雜任務通常更適合外部提供方。 + +--- + +### 配置外部 LLM 提供方 + +進入 **設置 ⚙️ → 通用 → LLM 提供方**。根據所選提供方,您需要填寫對應的 API Key,以及在需要時填寫 Base URL(例如 Ollama、OpenRouter 等 OpenAI 兼容服務)。 + +--- + +### 使用 OpenAI GPT 系列 + +1. 在 [**OpenAI**](https://platform.openai.com/account/api-keys) 獲取 API Key。您可能需要先注冊帳號並添加支付方式。 +2. 在 **設置 ⚙️ → 通用 → LLM 提供方** 中選擇 **OpenAI**。 +3. 在 **OpenAI → 密鑰** 中填入 API Key。 +4. 在 **OpenAI → 模型** 下拉中選擇模型。若希望在成本和效果之間取得平衡,推薦 `gpt-5.4-mini`。 +5. 可選:在 **OpenAI → Flex 模式** 中啟用 Flex Mode。它可能降低成本,但也可能帶來更高延遲或更多服務端錯誤。 + +--- + +### 使用 OpenRouter + +OpenRouter 提供統一接口,可訪問多個語言模型提供方的模型,其中也包含免費選項,適合比較不同模型表現。 + +1. 在 [**OpenRouter**](https://openrouter.ai/keys) 獲取 API Key。需要注冊;使用付費模型時可能還需要綁定支付方式。 +2. 在 **設置 ⚙️ → 通用 → LLM 提供方** 中選擇 **OpenAI 兼容服務**。 +3. 在 **OpenAI 兼容服務 → 密鑰** 中填入 API Key。 +4. 在 [**OpenRouter Models Page**](https://openrouter.ai/models) 瀏覽可用模型,並可使用 "Prompt Pricing" 過濾免費模型。 + **注意:** 各模型提供方的數據保留與隱私策略可能不同,使用前請自行查看。 +5. 在 **OpenAI 兼容服務 → 模型** 中填入模型名。推薦系列包括: + - `Anthropic: Claude Sonnet 4.6`(`anthropic/claude-sonnet-4.6`)—— Claude 系列裡質量和成本平衡較好 + - `Qwen: Qwen3.5-35B-A3B`(`qwen/qwen3.5-35b-a3b`)—— 推薦開源模型 + - `Qwen: Qwen3-Next-80B-A3B`(免費:`qwen/qwen3-next-80b-a3b-instruct:free`)—— 推薦免費模型 + - `OpenAI: GPT-OSS 120B`(免費:`openai/gpt-oss-120b:free`)—— 推薦免費模型 + - 注意:免費模型會經常變化,請以 OpenRouter 模型頁中的 **Prompt Pricing** 過濾結果為準 + - 注意:免費模型提供方可能會收集您的數據,使用前請先查看模型頁面說明 +6. 在 **OpenAI 兼容服務 → 基礎 URL** 中填寫 `https://openrouter.ai/api/v1`。 + +### 基本 DAW 操作 + +聊天命令:`/clear`、`/welcome`、`/help`、`/hotkeys` + +- 音軌 + - 在軌道信息面板中添加、重命名和重排音軌。 + - 通過樂器按鈕(鋼琴圖標)切換樂器;可調整 Solo(S)、Mute(M)和 Volume。 + - 通過音軌設置菜單刪除音軌(位於樂器按鈕右側)。 + - **音頻錄音**:點擊工具欄裡的 Rec 按鈕,可直接從麥克風錄到音頻軌。 + +- 區域 + - 創建區域:使用 Pointer 工具時可雙擊,或按住 Ctrl/Cmd 再點擊;使用 Pencil 工具時單擊即可。 + - 移動/縮放:拖動區域主體可移動,拖動邊緣可調整長度。 + - 通過區域左上角的小鉛筆打開鋼琴卷簾窗口。 + +- 鋼琴卷簾(MIDI 音符) + - 工具:Select 與 Pencil。 + - 創建音符:在 Select 模式下雙擊或 Ctrl/Cmd+點擊;在 Pencil 模式下單擊。 + - 選擇音符:單擊;Shift+單擊多選;拖拽框選。 + - 移動/縮放:拖動音符主體可移動已選音符;拖動邊緣可調整長度。 + - **五線譜視圖**:可在鋼琴卷簾工具欄中切換鋼琴卷簾與五線譜視圖。支持自動譜號、調號顯示、連音線和符桿分組。啟用 **Track Scope** 後會連續顯示整條 MIDI 音軌上的內容。 + - **自動化軌道**:可在下方自動化區域繪製和編輯 Pitch Bend 與 MIDI CC 曲線(Modulation、Breath、Volume、Expression、Sustain)。 + - **頻譜模式**:可在鋼琴卷簾中查看音頻區域的頻譜,作為 MIDI 編輯參考層。 + - **事件列表面板**:提供 Notes / Pitch Bend / Controller 標籤頁,用於查看並內聯編輯當前 MIDI 區域中的事件。 + - 可通過 X 或 ESC 關閉鋼琴卷簾窗口。 + +- 智能和弦助手 + - 在鋼琴卷簾工具欄中使用 `⊘`、`T`、`S`、`D` 啟用和弦指導。 + - 和弦候選會根據播放頭當前位置的有效調號來推斷:大調使用 Ionian,小調使用 Aeolian。 + - 將鼠標懸停在任意琴鍵上時,會以紅色高亮顯示上下文相關的和弦建議。 + - 按 `g` 循環切換和弦指導模式,按 `Tab` 切換到下一個候選和弦,按 `Shift+Tab` 切換到上一個。 + - 雙擊(或 Ctrl/Cmd+點擊)高亮和弦可一次性創建整組音符。 + - 和弦長度會自動匹配您最近編輯的音符長度,以保持節奏一致。 + +- 全局軌系統 + - 時間線上方固定有四條軌道:**Marker**、**Tempo**、**Key Signature**、**Chord**。 + - 創建全局區域:在全局軌中雙擊或 Ctrl/Cmd+點擊;拖動可移動,拖動邊緣可調整長度。 + - 這些軌道會影響播放時序、和弦指導、五線譜調號顯示,以及和弦檢測結果。 + +- 音頻分析功能 + - **Detect Chords**:在音頻區域打開鋼琴卷簾窗口後,點擊 **...** → **Detect Chords**,即可自動分析並把結果寫入全局 Chord Track。可配置靈敏度、穩定性和七和弦檢測。 + - **Detect Tempo**:在音頻區域打開鋼琴卷簾窗口後,點擊 **...** → **Detect Tempo**,即可分析 BPM,並可選擇自動對齊項目中的 Tempo Track。 + +- K.G.One 音樂生成器 + - 點擊工具欄中的 **✦**(魔杖)按鈕打開生成器面板。 + - **Full Song Generation**:根據文本描述和可選歌詞生成整首歌曲(需要 K.G.One 服務器)。 + - **Clip Generation**:根據文本提示生成短音頻片段和 MIDI loop(需要 K.G.One 服務器)。 + - **Stem Separation(瀏覽器)**:可完全在瀏覽器中把任意音頻區域分離成 stems,無需服務器。支持兩種模型:**UVR-MDX-NET-Inst_HQ_3**(2 stem:Vocals / Instrumental,約 64 MB)和 **Demucs htdemucs_4s**(4 stem:Vocals / Drums / Bass / Others,約 172 MB)。在生成器面板中下載模型後,點擊 **Separate Stems** 即可。需要 WebGPU(Chrome 113+ / Edge 113+)。 + +- 吸附與量化 + - 在右上角的 NO SNAP 菜單中設置吸附。 + - 使用 Qua. Pos.(起始)和 Qua. Len.(長度)進行量化。 + +- 播放與播放頭 + - 工具欄可回到開頭,並執行 Play/Pause。 + - 在主時間網格點擊小節編號可設置播放頭;在鋼琴卷簾中點擊頂部時間軸也可設置,並遵守當前吸附設置。 + - BPM、拍號和調號都可以通過工具欄中的數值直接調整。 + +--- + +### 說明 + +出於安全考慮,在非本地主機場景下,K.G.Studio 默認不會把 API Key 持久化到 IndexedDB 中(以降低 XSS 風險)。您每次打開 K.G.Studio 時都需要重新輸入。若您確實需要在非本地主機場景下持久化,請在設置中啟用 "Persist API Keys on Non-Localhost"(不建議在共享或生產環境下啟用)。 + +### 免責聲明 + +K.G.Studio 不提供也不托管上述任何模型,也不隸屬於任何模型提供方。所有數據默認都保存在您的本地設備中;您向第三方模型提供方發送的任何數據,都由您自行負責。 + +祝您使用 K.G.Studio 音樂創作助手創作愉快! diff --git a/public/chat/hotkeys-zh_hk.md b/public/chat/hotkeys-zh_hk.md new file mode 100644 index 0000000..5302800 --- /dev/null +++ b/public/chat/hotkeys-zh_hk.md @@ -0,0 +1,59 @@ +## 快捷鍵 + +本指南列出了當前在 K.G.Studio 中已經實現的鍵盤快捷鍵,以及部分點擊時配合修飾鍵的操作。 + +### 平台相關的創建操作 + +- 在 **macOS** 上,使用 Pointer 工具時按 `Cmd+click` 可創建區域、音符或自動化點。 +- 在 **Windows/Linux** 上,使用 Pointer 工具時按 `Ctrl+click` 可創建區域、音符或自動化點。 +- 在主時間線中,Pointer 工具激活時雙擊空白處可創建區域。 +- 在鋼琴卷簾與自動化軌中,Pencil 工具支持單擊創建。 + +### 主快捷鍵 + +- `Space` — 播放 / 停止 +- `C` — 切換循環 +- `R` — 開始 / 停止錄音 +- `Cmd/Ctrl+Z` — 撤銷 +- `Cmd/Ctrl+Shift+Z` — 重做 +- `Cmd/Ctrl+C` — 複製所選項目 +- `Cmd/Ctrl+V` — 粘貼 +- `Cmd/Ctrl+S` — 保存項目 +- `Cmd/Ctrl+A` — 選中當前 MIDI 區域中的全部音符 +- `Cmd/Ctrl+T` — 在播放頭處分割所選區域 +- `Cmd/Ctrl+J` — 合併選中的 MIDI 區域 + +### 區域與鋼琴卷簾窗口快捷鍵 + +- `E` — 為選中的 MIDI 區域打開鋼琴卷簾窗口,或為選中的音頻區域打開波形編輯器;如果編輯器已經打開,`E` 會關閉它。 +- `N` — 以五線譜視圖打開選中的 MIDI 區域。 +- `Delete` / `Backspace` — 刪除選中的區域、音符或自動化點。 +- `Esc` — 關閉鋼琴卷簾窗口。 + +### 選擇操作 + +- `Shift+click` — 加入當前選擇。 +- 在空白區域套索拖拽 — 框選區域或自動化點。 +- 在鋼琴卷簾窗口中 `Shift+套索` — 切換套索範圍內項目的選中狀態。 + +### 鋼琴卷簾快捷鍵 + +- `G` — 循環切換和弦指導模式 +- `Tab` — 跳到下一個和弦候選 +- `Shift+Tab` — 跳到上一個和弦候選 / 轉位 + +### 鋼琴卷簾吸附快捷鍵 + +- `1` — 不吸附 +- `2` — 吸附到 `1/4` +- `3` — 吸附到 `1/8` +- `4` — 吸附到 `1/16` + +### 鋼琴卷簾量化快捷鍵 + +- `5` — 起始量化到 `1/4` +- `6` — 起始量化到 `1/8` +- `7` — 起始量化到 `1/16` +- `8` — 長度量化到 `1/4` +- `9` — 長度量化到 `1/8` +- `0` — 長度量化到 `1/16` diff --git a/public/chat/welcome_again-zh_hk.md b/public/chat/welcome_again-zh_hk.md new file mode 100644 index 0000000..00e9677 --- /dev/null +++ b/public/chat/welcome_again-zh_hk.md @@ -0,0 +1,11 @@ +## 再次歡迎 + +很高興再次見到您。檢測到您的 LLM 提供方已經配置完成,現在可以直接開始聊天。 + +提示: +- 隨時輸入 `/clear` 重置當前聊天。 +- 輸入 `/welcome` 再次查看這條歡迎消息。 +- 輸入 `/help` 查看幫助說明。 +- 輸入 `/hotkeys` 查看快捷鍵指南。 + +祝你在 K.G.Studio 音樂創作助手中創作順利。 diff --git a/public/chat/welcome_local_llm-zh_hk.md b/public/chat/welcome_local_llm-zh_hk.md new file mode 100644 index 0000000..d5a1749 --- /dev/null +++ b/public/chat/welcome_local_llm-zh_hk.md @@ -0,0 +1,29 @@ +## 本地 LLM 模式 + +歡迎使用 **K.G.Studio 音樂創作助手** 的本地 LLM 模式。 + +- 無需外部 API 調用。所有內容都直接在您的瀏覽器中運行,不會產生額外 API 成本。 +- 此模式通過 **LiteRT-LM** 和 **WebGPU** 加速運行 **Gemma 4 E4B**。 +- 推薦硬件:至少 **8 GB 顯存**的 GPU,或至少 **16 GB 統一內存**的系統。 +- 相比更大的雲端模型,本地模式在複雜規劃、長鏈路編輯和多步驟任務上能力會更有限。 + +### 推薦工作方式 +- 保持請求小而明確。 +- 分步驟引導模型逐步完成目標。 +- 盡量針對較小的音樂區域工作,而不是一次處理整首歌。 +- 優先選擇更簡單的音樂編排。 +- 每個獨立任務盡量開啟新的對話。 + +示例: +- 對較複雜的請求,建議拆成多個小步驟。 +- 例如,如果您想讓模型基於當前音樂寫一段和弦進行,可以先說:`請讀取當前音樂。` +- 等這一步完成後,再繼續說:`請基於它寫一段和弦進行。` +- 在本地 LLM 模式下,這種漸進式流程通常更可靠。 +- 在整個流程中,保持您希望 agent 更新的 MIDI 區域處於選中狀態。 +- 僅僅為了執行 `read_music`,您不需要切換到別的區域,因為 `read_music` 可以讀取所有音軌的音樂內容。 + +### 改用外部 LLM +- 如果您想使用更大的雲端模型或自托管模型,請打開 **設置 -> 通用 -> LLM 提供方**,切換到 **本地 LLM(瀏覽器)** 以外的提供方。 +- 如果要用雲端模型,可以選擇 **OpenAI**,或者選擇 **OpenAI Compatible** 並填寫 OpenRouter 等提供方。 +- 如果要用自托管模型,也請選擇 **OpenAI Compatible**,然後填寫你的服務器 **Base URL** 和 **Model**。 +- 切換提供方後,建議開啟一個新的對話,以便聊天會話乾淨地使用新模型。 diff --git a/public/chat/welcome_new-zh_hk.md b/public/chat/welcome_new-zh_hk.md new file mode 100644 index 0000000..cd992b6 --- /dev/null +++ b/public/chat/welcome_new-zh_hk.md @@ -0,0 +1,25 @@ +## 歡迎 + +歡迎使用 **K.G.Studio 音樂創作助手**。這是您在瀏覽器中的 AI 音樂創作與編曲助手。 + +### 快速開始 +**配置 K.G.Studio 音樂創作助手** +- [點擊這裡獲取免費的 OpenRouter API Key](https://openrouter.ai/keys)(您可能需要一個 OpenRouter 帳號)。 +- 進入 **設置 ⚙️ → 通用 → LLM 提供方**,選擇 **OpenAI 兼容服務**。 +- 在 **OpenAI 兼容服務 → 密鑰** 中粘貼您的 Key。注意:在非 localhost 環境下,Key 默認不會持久化,刷新後需要重新輸入,請妥善保管。 +- 在 **OpenAI 兼容服務 → 模型** 中輸入 `qwen/qwen3-30b-a3b:free`。注意:非免費模型可能需要計費。 +- 在 **OpenAI 兼容服務 → 基礎 URL** 中輸入 `https://openrouter.ai/api/v1`。 + +*(或者,您也可以使用官方 OpenAI API,或者任何 OpenAI 兼容服務。)* + +**基本 DAW 操作** +- 在音軌上雙擊(或按住 Ctrl/Cmd 再點擊)可創建區域。 +- 拖動區域邊緣可調整長度;拖動區域主體可移動。 +- 點擊區域左上角的小鉛筆可打開鋼琴卷簾窗口。 +- 在鋼琴卷簾中,雙擊(或 Ctrl/Cmd+點擊)可創建音符。 +- 單擊選擇;Shift+單擊多選;拖拽框選。 +- 拖動音符邊緣可調整長度;拖動音符主體可移動所選音符。 +- 使用鋼琴卷簾工具欄右上角的 Snapping 控制網格量化。 + +### 免責聲明 +K.G.Studio 與任何 LLM 提供方均無隸屬關係。所有數據默認都保存在您的本地設備中;您發送給第三方服務的數據由您自行負責。 diff --git a/src/components/piano-roll/PianoRoll.tsx b/src/components/piano-roll/PianoRoll.tsx index ca8ff1a..312defd 100644 --- a/src/components/piano-roll/PianoRoll.tsx +++ b/src/components/piano-roll/PianoRoll.tsx @@ -69,6 +69,7 @@ import { type PendingModeSwitchRequest, } from './pianoRollViewport'; import { getNextChordGuideSelection, resolveChordGuideContext, type ChordGuideFunction } from './chordGuideUtil'; +import { useI18n } from '../../i18n/useI18n'; interface PianoRollProps { onClose: () => void; @@ -101,6 +102,7 @@ const PianoRoll: React.FC = ({ const isAudioOnly = isAudioWaveform || isSpectrogram; const isHybrid = currentMode === 'hybrid'; const { maxBars, tracks, updateTrack, timeSignature, showChatBox, showKGOnePanel, showEventListPanel, showInstrumentSelection, keySignature, selectedMode, setSelectedMode, playheadPosition, isPlaying, autoScrollEnabled, bpm, pianoRollScrollRequest, selectedNoteIds, automationRedrawVersion, refreshProjectState, setBpm } = useProjectStore(); + const { t } = useI18n(); // Tool state for piano roll const [activeTool, setActiveTool] = useState<'pointer' | 'pencil'>('pointer'); @@ -510,7 +512,7 @@ const PianoRoll: React.FC = ({ const detectionOptions = audioRegion ? await showChordDetectionOptions( - 'Tune audio chord detection settings before processing.', + t('dialog.message.chordDetection'), DEFAULT_AUDIO_CHORD_DETECTION_OPTIONS, ) : await showMidiChordDetectionOptions( @@ -628,7 +630,7 @@ const PianoRoll: React.FC = ({ } const detectionOptions = await showTempoDetectionOptions( - 'Tune audio tempo detection settings before processing.', + t('dialog.message.tempoDetection'), DEFAULT_AUDIO_TEMPO_DETECTION_OPTIONS, ); if (!detectionOptions) { @@ -659,8 +661,8 @@ const PianoRoll: React.FC = ({ const applyResult = await showTempoApply( buildDetectedTempoChoiceMessage(result.bpm), [ - { label: 'Update Current Tempo', value: DETECTED_TEMPO_ACTION_UPDATE_CURRENT }, - { label: 'Insert Tempo Change', value: DETECTED_TEMPO_ACTION_INSERT_REGION }, + { label: t('dialog.action.updateCurrentTempo'), value: DETECTED_TEMPO_ACTION_UPDATE_CURRENT }, + { label: t('dialog.action.insertTempoChange'), value: DETECTED_TEMPO_ACTION_INSERT_REGION }, ], ); if (!applyResult) { diff --git a/src/components/settings/sections/GeneralSettings.tsx b/src/components/settings/sections/GeneralSettings.tsx index f870171..0cec24a 100644 --- a/src/components/settings/sections/GeneralSettings.tsx +++ b/src/components/settings/sections/GeneralSettings.tsx @@ -354,6 +354,7 @@ const GeneralSettings: React.FC = () => { +
{t('settings.general.language.help')} diff --git a/src/i18n/locale.test.ts b/src/i18n/locale.test.ts index dc45c8b..d54335f 100644 --- a/src/i18n/locale.test.ts +++ b/src/i18n/locale.test.ts @@ -6,6 +6,7 @@ describe('locale resolution', () => { expect(normalizeLanguageSetting('auto')).toBe('auto'); expect(normalizeLanguageSetting('en_us')).toBe('en_us'); expect(normalizeLanguageSetting('zh_cn')).toBe('zh_cn'); + expect(normalizeLanguageSetting('zh_hk')).toBe('zh_hk'); expect(normalizeLanguageSetting('unknown')).toBe('auto'); }); @@ -13,12 +14,20 @@ describe('locale resolution', () => { expect(resolveLanguageSetting('auto', ['en-US'])).toBe('en_us'); }); - it('resolves auto to Simplified Chinese for Chinese locales', () => { + it('resolves auto to Simplified Chinese for Simplified Chinese locales', () => { expect(resolveLanguageSetting('auto', ['zh-CN'])).toBe('zh_cn'); expect(resolveLanguageSetting('auto', ['zh-SG'])).toBe('zh_cn'); - expect(resolveLanguageSetting('auto', ['zh-TW'])).toBe('zh_cn'); - expect(resolveLanguageSetting('auto', ['zh-HK'])).toBe('zh_cn'); - expect(resolveLanguageSetting('auto', ['zh-MO'])).toBe('zh_cn'); + expect(resolveLanguageSetting('auto', ['zh-Hans'])).toBe('zh_cn'); + expect(resolveLanguageSetting('auto', ['zh'])).toBe('zh_cn'); + }); + + it('resolves auto to Traditional Chinese for Traditional Chinese locales', () => { + expect(resolveLanguageSetting('auto', ['zh-HK'])).toBe('zh_hk'); + expect(resolveLanguageSetting('auto', ['zh-TW'])).toBe('zh_hk'); + expect(resolveLanguageSetting('auto', ['zh-MO'])).toBe('zh_hk'); + expect(resolveLanguageSetting('auto', ['zh-Hant'])).toBe('zh_hk'); + expect(resolveLanguageSetting('auto', ['zh-Hant-HK'])).toBe('zh_hk'); + expect(resolveLanguageSetting('auto', ['zh-Hant-TW'])).toBe('zh_hk'); }); it('falls back to English when locale list is empty or unknown', () => { @@ -29,5 +38,7 @@ describe('locale resolution', () => { it('keeps explicit locale selections', () => { expect(resolveLanguageSetting('en_us', ['zh-CN'])).toBe('en_us'); expect(resolveLanguageSetting('zh_cn', ['en-US'])).toBe('zh_cn'); + expect(resolveLanguageSetting('zh_hk', ['en-US'])).toBe('zh_hk'); + expect(resolveLanguageSetting('zh_hk', ['zh-CN'])).toBe('zh_hk'); }); }); diff --git a/src/i18n/locale.ts b/src/i18n/locale.ts index 3d8f1f4..e2db223 100644 --- a/src/i18n/locale.ts +++ b/src/i18n/locale.ts @@ -3,8 +3,11 @@ import type { LanguageSetting, ResolvedLocaleCode } from './types'; const CHINESE_LANGUAGE_PREFIX = 'zh'; const DEFAULT_LOCALE: ResolvedLocaleCode = 'en_us'; +// BCP 47 subtags that indicate Traditional Chinese (Hant script or HK/TW/MO regions) +const TRADITIONAL_CHINESE_SUBTAGS = new Set(['hk', 'tw', 'mo', 'hant']); + export function normalizeLanguageSetting(value: unknown): LanguageSetting { - return value === 'zh_cn' || value === 'en_us' || value === 'auto' ? value : 'auto'; + return value === 'zh_cn' || value === 'en_us' || value === 'zh_hk' || value === 'auto' ? value : 'auto'; } export function getBrowserLocales(): string[] { @@ -25,15 +28,20 @@ export function resolveLanguageSetting( setting: LanguageSetting, browserLocales: string[] = getBrowserLocales(), ): ResolvedLocaleCode { - if (setting === 'en_us' || setting === 'zh_cn') { + if (setting === 'en_us' || setting === 'zh_cn' || setting === 'zh_hk') { return setting; } for (const locale of browserLocales) { const normalizedLocale = locale.trim().toLowerCase(); - if (normalizedLocale === CHINESE_LANGUAGE_PREFIX || normalizedLocale.startsWith(`${CHINESE_LANGUAGE_PREFIX}-`)) { + if (normalizedLocale === CHINESE_LANGUAGE_PREFIX) { return 'zh_cn'; } + if (normalizedLocale.startsWith(`${CHINESE_LANGUAGE_PREFIX}-`)) { + const subtags = normalizedLocale.slice(CHINESE_LANGUAGE_PREFIX.length + 1).split('-'); + const isTraditional = subtags.some((tag) => TRADITIONAL_CHINESE_SUBTAGS.has(tag)); + return isTraditional ? 'zh_hk' : 'zh_cn'; + } } return DEFAULT_LOCALE; diff --git a/src/i18n/messages/en_us.ts b/src/i18n/messages/en_us.ts index 6d7c01a..2fc9eb9 100644 --- a/src/i18n/messages/en_us.ts +++ b/src/i18n/messages/en_us.ts @@ -18,6 +18,7 @@ export const enUsMessages: TranslationMessages = { 'settings.general.language.auto': 'Auto', 'settings.general.language.en_us': 'English', 'settings.general.language.zh_cn': 'Simplified Chinese', + 'settings.general.language.zh_hk': 'Traditional Chinese', 'settings.yes': 'Yes', 'settings.no': 'No', 'settings.restoreDefault': 'Restore default', @@ -103,6 +104,11 @@ export const enUsMessages: TranslationMessages = { 'dialog.label.minimumBpm': 'Minimum BPM', 'dialog.label.maximumBpm': 'Maximum BPM', 'dialog.label.autoAlignRegionToBeat': 'Auto-align region to beat', + 'dialog.message.chordDetection': 'Tune audio chord detection settings before processing.', + 'dialog.message.tempoDetection': 'Tune audio tempo detection settings before processing.', + 'dialog.message.tempoApply': 'Detected tempo: {bpm} BPM. Choose how to apply it.\n\nUpdate Current Tempo changes the active tempo at this clip location. Insert Tempo Change adds a new tempo region at the nearest bar before the clip starts.', + 'dialog.action.updateCurrentTempo': 'Update Current Tempo', + 'dialog.action.insertTempoChange': 'Insert Tempo Change', 'eventList.title': 'Event List', 'eventList.scopeTabs': 'Event list scopes', 'eventList.scope.region': 'Region', diff --git a/src/i18n/messages/index.ts b/src/i18n/messages/index.ts index 0074bef..71e41b6 100644 --- a/src/i18n/messages/index.ts +++ b/src/i18n/messages/index.ts @@ -1,8 +1,10 @@ import type { ResolvedLocaleCode, TranslationMessages } from '../types'; import { enUsMessages } from './en_us'; import { zhCnMessages } from './zh_cn'; +import { zhHkMessages } from './zh_hk'; export const messagesByLocale: Record = { en_us: enUsMessages, zh_cn: zhCnMessages, + zh_hk: zhHkMessages, }; diff --git a/src/i18n/messages/zh_cn.ts b/src/i18n/messages/zh_cn.ts index 71f1dcd..c54921b 100644 --- a/src/i18n/messages/zh_cn.ts +++ b/src/i18n/messages/zh_cn.ts @@ -18,6 +18,7 @@ export const zhCnMessages: TranslationMessages = { 'settings.general.language.auto': '自动', 'settings.general.language.en_us': 'English', 'settings.general.language.zh_cn': '简体中文', + 'settings.general.language.zh_hk': '繁体中文', 'settings.yes': '是', 'settings.no': '否', 'settings.restoreDefault': '恢复默认', @@ -103,6 +104,11 @@ export const zhCnMessages: TranslationMessages = { 'dialog.label.minimumBpm': '最小 BPM', 'dialog.label.maximumBpm': '最大 BPM', 'dialog.label.autoAlignRegionToBeat': '自动将片段对齐到拍点', + 'dialog.message.chordDetection': '在处理前调整音频和弦检测设置。', + 'dialog.message.tempoDetection': '在处理前调整音频速度检测设置。', + 'dialog.message.tempoApply': '检测到速度:{bpm} BPM。请选择应用方式。\n\n"更新当前速度"将修改此片段位置的有效速度。"插入速度变更"将在片段起始点前最近的小节处添加新的速度区域。', + 'dialog.action.updateCurrentTempo': '更新当前速度', + 'dialog.action.insertTempoChange': '插入速度变更', 'eventList.title': '事件列表', 'eventList.scopeTabs': '事件列表范围', 'eventList.scope.region': '区域', diff --git a/src/i18n/messages/zh_hk.ts b/src/i18n/messages/zh_hk.ts new file mode 100644 index 0000000..65cdd62 --- /dev/null +++ b/src/i18n/messages/zh_hk.ts @@ -0,0 +1,698 @@ +import type { TranslationMessages } from '../types'; + +export const zhHkMessages: TranslationMessages = { + 'app.loading': '加載中...', + 'assistant.displayName': 'K.G.Studio 音樂創作助手', + 'assistant.welcomeFallback': '歡迎使用 K.G.Studio 音樂創作助手。', + 'status.chordGuideCandidate': '和弦指導候選: {name} - {notes} - {note}', + 'settings.sidebar.title': '設置', + 'settings.sidebar.close': '關閉設置', + 'settings.sidebar.general': '通用', + 'settings.sidebar.audioIo': '音頻 I/O', + 'settings.sidebar.behavior': '行為', + 'settings.sidebar.templates': '模板', + 'settings.sidebar.chordGuide': '和弦指導', + 'settings.general.title': '通用', + 'settings.general.language.label': '語言', + 'settings.general.language.help': '自動會使用你的系統語言。', + 'settings.general.language.auto': '自動', + 'settings.general.language.en_us': 'English', + 'settings.general.language.zh_cn': '簡體中文', + 'settings.general.language.zh_hk': '繁體中文', + 'settings.yes': '是', + 'settings.no': '否', + 'settings.restoreDefault': '恢復默認', + 'settings.deleteCachedModel': '刪除已緩存模型', + 'settings.deleting': '刪除中...', + 'settings.general.llmProvider.section': 'LLM 提供方', + 'settings.general.llmProvider.label': 'LLM 提供方', + 'settings.general.llmProvider.local': '本地 LLM(瀏覽器)', + 'settings.general.llmProvider.openai': 'OpenAI', + 'settings.general.llmProvider.claudeOpenRouter': 'Claude(通過 OpenRouter)', + 'settings.general.llmProvider.openaiCompatible': 'OpenAI 兼容服務(例如 OpenRouter、Ollama)', + 'settings.general.persistKeys.label': '在非 localhost 環境持久化 API Key', + 'settings.general.persistKeys.help': '啟用後,即使在非 localhost 環境下,API Key 也會保存到瀏覽器存儲中。警告:這可能增加 XSS 攻擊帶來的安全風險。', + 'settings.general.localRuntime.cachedStatus': '緩存模型狀態', + 'settings.general.localRuntime.cacheChecking': '正在檢查本地模型緩存...', + 'settings.general.localRuntime.cacheDownloaded': '已下載到瀏覽器緩存。', + 'settings.general.localRuntime.cacheMissing': '尚未下載。', + 'settings.general.localRuntime.contextLength': '上下文長度', + 'settings.general.localRuntime.contextHelp': '更大的上下文長度需要更多顯存,並且在對話變長後也可能降低性能。', + 'settings.general.localRuntime.downloadUrl': '下載地址', + 'settings.general.localRuntime.downloadHelp': '修改這個地址可能導致下載失敗,或指向不兼容的模型文件。', + 'settings.general.localRuntime.autoDownload': '下次你使用 `Local LLM (Browser)` 聊天時,本地模型會自動下載。', + 'settings.general.uvr5.section': 'UVR5 Web 運行時', + 'settings.general.uvr5.downloadUrl': 'UVR-MDX-NET-Inst_HQ_3 下載地址', + 'settings.general.htdemucs.downloadUrl': 'htdemucs_4s 下載地址', + 'settings.general.modelUrl.help': '修改這個地址可能導致下載失敗,或指向不兼容的模型文件。', + 'settings.general.openai.section': 'OpenAI', + 'settings.general.openai.key': '密鑰', + 'settings.general.openai.keyPlaceholder': '輸入你的 OpenAI API Key', + 'settings.general.openai.model': '模型', + 'settings.general.openai.flexMode': 'Flex 模式', + 'settings.general.openai.flexHelp': 'Flex 模式使用 OpenAI 的彈性服務層。優點:繁忙時段可能節省成本並提高吞吐。缺點:延遲可能波動,也可能排隊或被降優先級。僅對 OpenAI 提供方生效,對 OpenAI 兼容服務無影響。', + 'settings.general.keys.persisted': '密鑰會保存在本地(瀏覽器中的 IndexedDB)。', + 'settings.general.keys.sessionOnly': '出於安全考慮,在非本地主機環境下密鑰不會持久化,只保留在當前會話內存中。', + 'settings.general.claudeOpenRouter.section': 'Anthropic Claude(通過 OpenRouter)', + 'settings.general.claudeOpenRouter.keyPlaceholder': '輸入你的 Claude API Key', + 'settings.general.baseUrl': '基礎 URL', + 'settings.general.claudeOpenRouter.baseUrlHelp': '這是 OpenRouter API 的基礎 URL。除非你明確知道自己在做什麼,否則不要修改。', + 'settings.general.openaiCompatible.section': 'OpenAI 兼容服務', + 'settings.general.openaiCompatible.keyPlaceholder': '輸入你的 API Key', + 'settings.general.openaiCompatible.baseUrlPlaceholder': '例如 https://openrouter.ai/api/v1', + 'settings.general.openaiCompatible.baseUrlHelp': '快捷預設:', + 'settings.general.openaiCompatible.modelPlaceholder': '例如 qwen3:30b', + 'settings.general.soundfont.section': 'Soundfont 設置', + 'settings.general.soundfont.managed': 'Soundfont 配置由服務器(kgone-server.json)管理,當前設置為只讀。', + 'settings.general.soundfont.baseUrl': '基礎 URL', + 'settings.general.soundfont.baseUrlHelp': '如果改成不兼容的 soundfont 源,某些樂器可能發聲錯誤或無法播放。', + 'settings.general.kgone.section': 'K.G.One 設置', + 'settings.general.kgone.managed': 'K.G.One 配置由服務器(kgone-server.json)管理,當前設置為只讀。', + 'settings.general.kgone.enabled': '啟用 K.G.One 集成', + 'settings.general.kgone.disabled': '禁用', + 'settings.general.kgone.enabledOption': '啟用', + 'settings.general.kgone.serverBaseUrl': '服務器基礎 URL', + 'settings.general.kgone.serverBaseUrlHelp': '正在運行的 K.G.One Music Studio 服務器基礎 URL。用於整曲生成、片段生成和分軌。', + 'dialog.title.notice': '提示', + 'dialog.title.timeSignature': '拍號', + 'dialog.title.tempoDetection': '速度檢測', + 'dialog.title.applyTempo': '應用速度', + 'dialog.title.chordDetection': '和弦檢測', + 'dialog.title.input': '輸入', + 'dialog.title.confirm': '確認', + 'dialog.close': '關閉對話框', + 'dialog.cancel': '取消', + 'dialog.ok': '確定', + 'dialog.experimentalFeature': '實驗性功能', + 'dialog.recommendedSource': '推薦源素材', + 'dialog.chordHint.audio': '和弦分析仍屬實驗性功能。和聲內容、編配密度以及瞬態較強的素材都可能影響準確率。建議先使用默認設置,再逐步微調靈敏度和穩定性,讓檢測結果更貼近實際樂句。', + 'dialog.chordHint.midi': '和弦分析仍屬實驗性功能。和弦堆疊密度、重疊以及裝飾音都會影響結果。建議先使用默認設置,再調整短音抑制和和聲關注方式,以匹配該段音樂的和聲作用。', + 'dialog.chordHint.tempo': '速度分析仍屬實驗性功能。自由速度、瞬態稀疏以及多層打擊樂都可能降低準確率。建議先使用默認 BPM 範圍,如果第一次結果不理想,再把分析範圍收窄到更可能的速度區間。', + 'dialog.sourceHint.kgone': '為了獲得更可靠的和弦標籤,建議分析已經削弱或移除了人聲和打擊樂的 stem。如果已啟用 K.G.One Music Studio 服務器集成,請使用帶有 "Vocal, Drums, Bass, Guitar, Piano, and Others" 的 Separator 模型,並優先分析 Piano 或 Others stem。', + 'dialog.sourceHint.local': '為了獲得更可靠的和弦標籤,建議分析已經削弱或移除了人聲和打擊樂的 stem。如果你使用本地分軌器,請選擇 "Vocal, Drums, Bass, and Others" 模型,並使用 Others stem 進行分析。', + 'dialog.label.sensitivity': '靈敏度', + 'dialog.label.stability': '穩定性', + 'dialog.label.noChordThreshold': '無和弦閾值', + 'dialog.label.enableSevenths': '和弦細節:啟用七和弦', + 'dialog.label.shortNotes': '短音', + 'dialog.option.suppressionLow': '低抑制', + 'dialog.option.suppressionMedium': '中抑制', + 'dialog.option.suppressionHigh': '高抑制', + 'dialog.label.harmonicFocus': '和聲關注', + 'dialog.option.harmonicBalanced': '均衡', + 'dialog.option.harmonicSustained': '偏向持續音', + 'dialog.label.minimumBpm': '最小 BPM', + 'dialog.label.maximumBpm': '最大 BPM', + 'dialog.label.autoAlignRegionToBeat': '自動將片段對齊到拍點', + 'dialog.message.chordDetection': '在處理前調整音頻和弦檢測設置。', + 'dialog.message.tempoDetection': '在處理前調整音頻速度檢測設置。', + 'dialog.message.tempoApply': '檢測到速度:{bpm} BPM。請選擇應用方式。\n\n"更新當前速度"將修改此片段位置的有效速度。"插入速度變更"將在片段起始點前最近的小節處添加新的速度區域。', + 'dialog.action.updateCurrentTempo': '更新當前速度', + 'dialog.action.insertTempoChange': '插入速度變更', + 'eventList.title': '事件列表', + 'eventList.scopeTabs': '事件列表範圍', + 'eventList.scope.region': '區域', + 'eventList.scope.track': '音軌', + 'eventList.scope.global': '全局', + 'eventList.deleteVisibleSelectedRows': '刪除當前可見的已選行', + 'eventList.table.position': '位置', + 'eventList.table.status': '狀態', + 'eventList.table.num': '編號', + 'eventList.table.val': '數值', + 'eventList.table.lengthInfo': '長度/信息', + 'eventList.region.filters': '區域事件篩選', + 'eventList.region.filter.notes': '音符', + 'eventList.region.filter.pitchBends': '彎音', + 'eventList.region.filter.controller': '控制器', + 'eventList.region.empty': '請選擇一個 MIDI 區域,或先在鋼琴卷簾中打開一個區域,再查看事件列表。', + 'eventList.region.add.label': '添加', + 'eventList.region.add.noteTitle': '在播放頭位置添加音符', + 'eventList.region.add.pitchBendTitle': '在播放頭位置添加彎音', + 'eventList.region.add.controllerTitle': '在播放頭位置添加控制器事件', + 'eventList.region.addType.note': '音符', + 'eventList.region.addType.pitchBend': '彎音', + 'eventList.region.addType.controller': '控制器', + 'eventList.region.status.note': '音符', + 'eventList.region.status.pitchBend': '彎音', + 'eventList.region.status.controller': '控制器', + 'eventList.track.filters': '音軌列表模式', + 'eventList.track.filter.regions': '區域', + 'eventList.track.filter.volume': '音量', + 'eventList.track.filter.pan': '聲像', + 'eventList.track.empty': '請選擇一條音軌以查看區域和音軌自動化。', + 'eventList.track.add.label': '添加', + 'eventList.track.add.midiRegionTitle': '在播放頭位置添加 MIDI 區域', + 'eventList.track.add.volumeTitle': '在播放頭位置添加音量自動化點', + 'eventList.track.add.panTitle': '在播放頭位置添加聲像自動化點', + 'eventList.track.addType.midiRegion': 'MIDI 區域', + 'eventList.track.status.audio': '音頻', + 'eventList.track.status.midi': 'MIDI', + 'eventList.track.status.volume': '音量', + 'eventList.track.status.pan': '聲像', + 'eventList.global.filters': '全局事件篩選', + 'eventList.global.filter.marker': '標記', + 'eventList.global.filter.tempo': '速度', + 'eventList.global.filter.keySignature': '調號', + 'eventList.global.filter.chord': '和弦', + 'eventList.global.add.label': '添加', + 'eventList.global.add.markerTitle': '在播放頭位置添加標記區段', + 'eventList.global.add.tempoTitle': '在播放頭位置添加速度區段', + 'eventList.global.add.keySignatureTitle': '在播放頭位置添加調號區段', + 'eventList.global.add.chordTitle': '在播放頭位置添加和弦區段', + 'eventList.global.addType.marker': '標記', + 'eventList.global.addType.tempo': '速度', + 'eventList.global.addType.keySignature': '調號', + 'eventList.global.addType.chord': '和弦', + 'eventList.global.status.marker': '標記', + 'eventList.global.status.tempo': '速度', + 'eventList.global.status.keySignature': '調號', + 'eventList.global.status.chord': '和弦', + 'eventList.global.validation.marker': '請輸入標記標籤。需要非空文本標籤。示例:前奏', + 'eventList.global.validation.tempo': '請輸入僅包含數字的 BPM 值。需要介於 {min} 和 {max} 之間的整數。示例:128', + 'eventList.global.validation.keySignature': '請輸入一個精確的調號名稱。需要規範值,例如 "C major" 或 "F# minor"。示例:F# minor', + 'eventList.global.validation.chord': '請輸入有效的和弦符號。需要應用可解析的和弦表示。示例:Bm7b5', + 'eventList.global.validation.position': '請輸入項目起點或之後的位置。需要非負位置。示例:1 1 0', + 'eventList.global.validation.length': '請輸入正長度。需要大於零的時值。示例:4 0', + 'globalTracks.label': '全局軌道', + 'globalTracks.marker': '標記', + 'globalTracks.tempo': '速度', + 'globalTracks.signature': '調號', + 'globalTracks.chord': '和弦', + 'globalTracks.addItem': '添加 {label} 全局軌道項目', + 'pianoRoll.power.linear': '線性', + 'pianoRoll.power.sqrtDefault': '√(默認)', + 'pianoRoll.power.mild': '輕度', + 'pianoRoll.power.strong': '強烈', + 'pianoRoll.chordGuide.off': '關閉和弦指導', + 'pianoRoll.chordGuide.tonic': '主功能和弦指導', + 'pianoRoll.chordGuide.subdominant': '下屬功能和弦指導', + 'pianoRoll.chordGuide.dominant': '屬功能和弦指導', + 'pianoRoll.spectrogramView': '頻譜視圖', + 'pianoRoll.sheetMusicView': '五線譜視圖', + 'pianoRoll.pointerTool': '指針工具', + 'pianoRoll.pencilTool': '鉛筆工具', + 'pianoRoll.toggleAutomationLane': '切換自動化軌', + 'pianoRoll.automation': '自動化', + 'pianoRoll.automationLane': '{label} 自動化軌', + 'pianoRoll.automationType.pitchBend': '彎音', + 'pianoRoll.automationType.cc1': 'CC1', + 'pianoRoll.automationType.cc2': 'CC2', + 'pianoRoll.automationType.cc7': 'CC7', + 'pianoRoll.automationType.cc11': 'CC11', + 'pianoRoll.automationType.cc64': 'CC64', + 'pianoRoll.mode': '調式', + 'pianoRoll.modeOption.ionian': '伊奧尼亞', + 'pianoRoll.modeOption.dorian': '多利亞', + 'pianoRoll.modeOption.phrygian': '弗里吉亞', + 'pianoRoll.modeOption.lydian': '利底亞', + 'pianoRoll.modeOption.mixolydian': '混合利底亞', + 'pianoRoll.modeOption.aeolian': '愛奧利亞', + 'pianoRoll.modeOption.locrian': '洛克里亞', + 'pianoRoll.modeOption.harmonic_minor': '和聲小調', + 'pianoRoll.modeOption.melodic_minor': '旋律小調', + 'pianoRoll.modeOption.phrygian_dominant': '弗里吉亞屬調式', + 'pianoRoll.modeOption.harmonic_major': '和聲大調', + 'pianoRoll.chordGuide': '和弦指導', + 'pianoRoll.sheetQuantization': '譜面量化', + 'pianoRoll.snap': '吸附', + 'pianoRoll.snap.none': '無吸附', + 'pianoRoll.quantizePositionCompact': '位置量化', + 'pianoRoll.quantizeLengthCompact': '長度量化', + 'pianoRoll.floor': '底噪', + 'pianoRoll.curve': '曲線', + 'pianoRoll.zoom': '縮放', + 'pianoRoll.moreOptions': '更多選項', + 'pianoRoll.quantize.1/1': '1/1', + 'pianoRoll.quantize.1/2': '1/2', + 'pianoRoll.quantize.1/3': '1/3', + 'pianoRoll.quantize.1/4': '1/4', + 'pianoRoll.quantize.1/6': '1/6', + 'pianoRoll.quantize.1/8': '1/8', + 'pianoRoll.quantize.1/12': '1/12', + 'pianoRoll.quantize.1/16': '1/16', + 'pianoRoll.quantize.1/24': '1/24', + 'pianoRoll.quantize.1/32': '1/32', + 'pianoRoll.showActiveRegionOnly': '僅顯示當前區域', + 'pianoRoll.showEntireTrack': '顯示整條音軌', + 'pianoRoll.detectChords': '檢測和弦...', + 'pianoRoll.detectingChords': '正在檢測和弦...', + 'pianoRoll.detectTempo': '檢測速度...', + 'pianoRoll.detectingTempo': '正在檢測速度...', + 'percussion.short.35': '原底鼓', + 'percussion.short.36': '底鼓', + 'percussion.short.37': '邊擊', + 'percussion.short.38': '原軍鼓', + 'percussion.short.39': '拍手', + 'percussion.short.40': '電軍鼓', + 'percussion.short.41': '低落地鼓', + 'percussion.short.42': '閉鑔', + 'percussion.short.43': '高落地鼓', + 'percussion.short.44': '踏鑔', + 'percussion.short.45': '低嗵鼓', + 'percussion.short.46': '開鑔', + 'percussion.short.47': '中低嗵鼓', + 'percussion.short.48': '中高嗵鼓', + 'percussion.short.49': '碎音鑔1', + 'percussion.short.50': '高嗵鼓', + 'percussion.short.51': '叮叮鑔1', + 'percussion.short.52': '中國鑔', + 'percussion.short.53': '叮叮鑔鈴', + 'percussion.short.54': '鈴鼓', + 'percussion.short.55': '飛濺鑔', + 'percussion.short.56': '牛鈴', + 'percussion.short.57': '碎音鑔2', + 'percussion.short.58': '振響器', + 'percussion.short.59': '叮叮鑔2', + 'percussion.short.60': '高邦戈', + 'percussion.short.61': '低邦戈', + 'percussion.short.62': '悶高康加', + 'percussion.short.63': '開高康加', + 'percussion.short.64': '低康加', + 'percussion.short.65': '高廷巴勒', + 'percussion.short.66': '低廷巴勒', + 'percussion.short.67': '高阿哥哥', + 'percussion.short.68': '低阿哥哥', + 'percussion.short.69': '卡巴薩', + 'percussion.short.70': '沙鎚', + 'percussion.short.71': '短哨', + 'percussion.short.72': '長哨', + 'percussion.short.73': '短刮瓜', + 'percussion.short.74': '長刮瓜', + 'percussion.short.75': '響棒', + 'percussion.short.76': '高木塊', + 'percussion.short.77': '低木塊', + 'percussion.short.78': '悶庫卡', + 'percussion.short.79': '開庫卡', + 'percussion.short.80': '悶三角鐵', + 'percussion.short.81': '開三角鐵', + 'percussion.full.35': '原聲底鼓', + 'percussion.full.36': '底鼓 1', + 'percussion.full.37': '邊擊', + 'percussion.full.38': '原聲軍鼓', + 'percussion.full.39': '拍手', + 'percussion.full.40': '電軍鼓', + 'percussion.full.41': '低落地嗵鼓', + 'percussion.full.42': '閉合踩鑔', + 'percussion.full.43': '高落地嗵鼓', + 'percussion.full.44': '踏踩鑔', + 'percussion.full.45': '低嗵鼓', + 'percussion.full.46': '開放踩鑔', + 'percussion.full.47': '中低嗵鼓', + 'percussion.full.48': '中高嗵鼓', + 'percussion.full.49': '碎音鑔 1', + 'percussion.full.50': '高嗵鼓', + 'percussion.full.51': '叮叮鑔 1', + 'percussion.full.52': '中國鑔', + 'percussion.full.53': '叮叮鑔鈴', + 'percussion.full.54': '鈴鼓', + 'percussion.full.55': '飛濺鑔', + 'percussion.full.56': '牛鈴', + 'percussion.full.57': '碎音鑔 2', + 'percussion.full.58': '振響器', + 'percussion.full.59': '叮叮鑔 2', + 'percussion.full.60': '高邦戈', + 'percussion.full.61': '低邦戈', + 'percussion.full.62': '悶高康加', + 'percussion.full.63': '開高康加', + 'percussion.full.64': '低康加', + 'percussion.full.65': '高廷巴勒', + 'percussion.full.66': '低廷巴勒', + 'percussion.full.67': '高阿哥哥', + 'percussion.full.68': '低阿哥哥', + 'percussion.full.69': '卡巴薩', + 'percussion.full.70': '沙鎚', + 'percussion.full.71': '短哨', + 'percussion.full.72': '長哨', + 'percussion.full.73': '短刮瓜', + 'percussion.full.74': '長刮瓜', + 'percussion.full.75': '響棒', + 'percussion.full.76': '高木塊', + 'percussion.full.77': '低木塊', + 'percussion.full.78': '悶庫卡', + 'percussion.full.79': '開庫卡', + 'percussion.full.80': '悶三角鐵', + 'percussion.full.81': '開三角鐵', + 'toolbar.export.kgstudio': '導出為 KGStudio 文件', + 'toolbar.export.midi': '導出為 MIDI 文件', + 'toolbar.export.wav': '導出為 WAV', + 'toolbar.export.mp3': '導出為 MP3', + 'toolbar.projectName.prompt': '輸入項目名稱:', + 'toolbar.projectName.invalid': '無效的項目名稱。只允許字母、數字、空格、連字符、下劃線、句點和括號。', + 'toolbar.projectName.reserved': '"{name}" 是保留項目名稱。請選擇其他名稱。', + 'toolbar.projectName.overwrite': '項目 "{name}" 已存在。要覆蓋它嗎?', + 'toolbar.projectName.renameOrCopy': '你想重命名這個項目,還是另存為一個副本?', + 'toolbar.projectName.saveAsCopy': '另存為副本', + 'toolbar.projectName.rename': '重命名', + 'toolbar.save.error': '保存時發生錯誤:{error}', + 'toolbar.load.error': '加載項目時發生錯誤:{error}', + 'toolbar.newProject.confirm': '確定要創建新項目嗎?當前未保存的更改將會丟失。', + 'toolbar.project.notFound': '未找到項目 "{name}"。', + 'toolbar.openProject.confirm': '要打開這個項目嗎?當前項目中未保存的更改將會丟失。', + 'toolbar.status.loadFailed': '加載項目失敗:{error}', + 'toolbar.status.newProjectCreated': '已創建新項目 "{name}"', + 'toolbar.status.projectLoaded': '{description} 加載成功', + 'toolbar.status.projectExportedKgstudio': '項目 "{name}" 已導出為 KGStudio 文件', + 'toolbar.status.exportProjectError': '導出項目出錯:{error}', + 'toolbar.export.failedProject': '導出項目失敗:{error}', + 'toolbar.status.projectExportedMidi': '項目 "{name}" 已導出為 MIDI 文件', + 'toolbar.status.exportMidiError': '導出 MIDI 出錯:{error}', + 'toolbar.export.failedMidi': '導出項目為 MIDI 失敗:{error}', + 'toolbar.status.projectExportedWav': '項目 "{name}" 已導出為 WAV 文件', + 'toolbar.status.exportWavError': '導出 WAV 出錯:{error}', + 'toolbar.export.failedWav': '導出項目為 WAV 失敗:{error}', + 'toolbar.status.projectExportedMp3': '項目 "{name}" 已導出為 MP3 文件', + 'toolbar.status.exportMp3Error': '導出 MP3 出錯:{error}', + 'toolbar.export.failedMp3': '導出項目為 MP3 失敗:{error}', + 'toolbar.status.importFailed': '導入文件失敗:{error}', + 'toolbar.import.failedProjectFile': '導入項目文件失敗:{error}', + 'toolbar.import.corruptedKgstudio': '.kgstudio 文件已損壞或無效:{error}', + 'toolbar.status.importingMidi': '正在導入 MIDI 文件 "{name}"...', + 'toolbar.status.failedImportMidi': '導入 MIDI 文件失敗:{error}', + 'toolbar.status.playbackFailedStart': '播放啟動失敗', + 'toolbar.status.recordingStoppedCommitted': '錄音已停止 - 音符已提交', + 'toolbar.status.failedStopPlayback': '停止播放失敗', + 'toolbar.maxBars.prompt': '輸入新的最大小節數(>= {min}):', + 'toolbar.input.invalidNumber': '輸入無效。請輸入有效數字。', + 'toolbar.maxBars.invalid': '數值無效。請輸入一個 >= {min} 的數字。', + 'toolbar.status.maxBarsChanged': '最大小節數已改為 {value}', + 'toolbar.bpm.prompt': '輸入新的 BPM({min}-{max}):', + 'toolbar.bpm.invalid': 'BPM 無效。請輸入 {min} 到 {max} 之間的值。', + 'toolbar.status.bpmChanged': 'BPM 已改為 {value}', + 'toolbar.timeSignature.prompt': '設置拍號:', + 'toolbar.status.timeSignatureChanged': '拍號已改為 {value}', + 'toolbar.status.keySignatureChanged': '調號已改為 {value}', + 'toolbar.status.copied': '項目已複製到剪貼板', + 'toolbar.status.copyNone': '沒有可複製的已選項目', + 'toolbar.status.pasted': '已從剪貼板粘貼項目', + 'toolbar.status.pasteFailed': '無法粘貼 - 沒有有效的剪貼板內容或上下文', + 'toolbar.status.deleted': '已刪除所選區域', + 'toolbar.status.deleteNone': '沒有選中可刪除的區域', + 'toolbar.undo.none': '沒有可撤銷的操作', + 'toolbar.redo.none': '沒有可重做的操作', + 'toolbar.status.undid': '已撤銷:{description}', + 'toolbar.status.redid': '已重做:{description}', + 'toolbar.action': '操作', + 'toolbar.status.chatToggled': '聊天窗口已切換', + 'toolbar.status.settingsToggled': '設置面板已切換', + 'toolbar.pianoRoll.selectMidiRegion': '請先選擇一個 MIDI 區域再打開鋼琴卷簾。', + 'toolbar.status.recordingStopped': '錄音已停止', + 'toolbar.status.audioRecordingStarted': '音頻錄音已開始...', + 'toolbar.recording.openMidiRegion': '開始錄音前,請先在鋼琴卷簾中打開一個 MIDI 區域。', + 'toolbar.recording.selectMidiRegion': '開始錄音前,請先選擇一個 MIDI 區域。', + 'toolbar.recording.noMidiDevice': '未檢測到 MIDI 設備。請連接 MIDI 鍵盤後重試。', + 'toolbar.status.recordingStarted': '錄音已開始...', + 'toolbar.button.new': '新建', + 'toolbar.button.load': '打開', + 'toolbar.button.save': '保存', + 'toolbar.button.export': '導出', + 'toolbar.button.import': '導入', + 'toolbar.button.settings': '設置', + 'toolbar.button.undo': '撤銷', + 'toolbar.button.redo': '重做', + 'toolbar.button.select': '選擇', + 'toolbar.button.pencil': '鉛筆', + 'toolbar.button.splitRegion': '在播放頭處分割區域', + 'toolbar.button.mergeRegions': '合併選中的 MIDI 區域', + 'toolbar.button.snapToGrid': '吸附到網格', + 'toolbar.button.copy': '複製', + 'toolbar.button.paste': '粘貼', + 'toolbar.button.delete': '刪除', + 'toolbar.button.backToBeginning': '回到開頭', + 'toolbar.button.play': '播放', + 'toolbar.button.pause': '暫停', + 'toolbar.button.stopRecording': '停止錄音', + 'toolbar.button.record': '錄音', + 'toolbar.button.loop': '循環', + 'toolbar.button.metronome': '節拍器', + 'toolbar.button.piano': '鋼琴', + 'toolbar.button.kgone': 'K.G.One 音樂生成器', + 'toolbar.button.chat': '聊天', + 'toolbar.button.eventList': '事件列表編輯器', + 'toolbar.keySignatureChooser': '選擇調號,當前為 {value}', + 'toolbar.export.label': '導出', + 'toolbar.importProject.title': '導入項目', + 'toolbar.importProject.description': '將項目文件拖放到這裡', + 'toolbar.openingProject': '正在打開項目...', + 'fileImport.invalidType': '文件類型無效。請選擇以下擴展名之一的文件:{extensions}', + 'fileImport.close': '關閉導入彈窗', + 'fileImport.supportedFormats': '支持的格式:{formats}', + 'fileImport.or': '或', + 'fileImport.browse': '瀏覽文件', + 'instrument.group.PIANO_AND_KEYBOARDS': '鋼琴與鍵盤', + 'instrument.group.GUITAR': '吉他', + 'instrument.group.BASS': '貝斯', + 'instrument.group.STRINGS': '弦樂', + 'instrument.group.BRASS': '銅管', + 'instrument.group.WOODWIND': '木管', + 'instrument.group.PERCUSSION_KIT': '打擊樂套件', + 'instrument.group.SYNTH': '合成器', + 'instrument.name.acoustic_grand_piano': '原聲大鋼琴', + 'instrument.name.bright_acoustic_piano': '明亮原聲鋼琴', + 'instrument.name.electric_grand_piano': '電大鋼琴', + 'instrument.name.electric_piano_1': '電鋼琴 1', + 'instrument.name.electric_piano_2': '電鋼琴 2', + 'instrument.name.drawbar_organ': '擊桿風琴', + 'instrument.name.clavinet': '擊弦電鋼琴', + 'instrument.name.harpsichord': '羽管鍵琴', + 'instrument.name.accordion': '手風琴', + 'instrument.name.acoustic_guitar_nylon': '原聲吉他(尼龍弦)', + 'instrument.name.acoustic_guitar_steel': '原聲吉他(鋼弦)', + 'instrument.name.electric_guitar_clean': '電吉他(清音)', + 'instrument.name.overdriven_guitar': '電吉他(過載)', + 'instrument.name.distortion_guitar': '電吉他(失真)', + 'instrument.name.acoustic_bass': '原聲貝斯', + 'instrument.name.electric_bass_finger': '電貝斯(指彈)', + 'instrument.name.electric_bass_pick': '電貝斯(撥片)', + 'instrument.name.slap_bass_1': '擊勾貝斯', + 'instrument.name.synth_bass_1': '合成貝斯 1', + 'instrument.name.violin': '小提琴', + 'instrument.name.viola': '中提琴', + 'instrument.name.cello': '大提琴', + 'instrument.name.contrabass': '低音提琴', + 'instrument.name.string_ensemble_1': '弦樂合奏', + 'instrument.name.string_ensemble_2': '弦樂合奏 2', + 'instrument.name.pizzicato_strings': '撥奏弦樂', + 'instrument.name.tremolo_strings': '震音弦樂', + 'instrument.name.orchestral_harp': '管弦豎琴', + 'instrument.name.trumpet': '小號', + 'instrument.name.trombone': '長號', + 'instrument.name.tuba': '大號', + 'instrument.name.french_horn': '圓號', + 'instrument.name.brass_section': '銅管組', + 'instrument.name.soprano_sax': '高音薩克斯', + 'instrument.name.alto_sax': '中音薩克斯', + 'instrument.name.tenor_sax': '次中音薩克斯', + 'instrument.name.baritone_sax': '上低音薩克斯', + 'instrument.name.oboe': '雙簧管', + 'instrument.name.english_horn': '英國管', + 'instrument.name.bassoon': '巴松', + 'instrument.name.clarinet': '單簧管', + 'instrument.name.piccolo': '短笛', + 'instrument.name.flute': '長笛', + 'instrument.name.shakuhachi': '尺八', + 'instrument.name.harmonica': '口琴', + 'instrument.name.standard': '標準鼓組', + 'instrument.name.orchestra_kit': '管弦打擊樂組', + 'instrument.name.marimba': '馬林巴', + 'instrument.name.timpani': '定音鼓', + 'instrument.name.taiko_drum': '太鼓', + 'instrument.name.woodblock': '木魚', + 'instrument.name.lead_1_square': '主音 1(方波)', + 'instrument.name.lead_2_sawtooth': '主音 2(鋸齒波)', + 'instrument.name.pad_1_new_age': '鋪底 1(新世紀)', + 'instrument.name.pad_2_warm': '鋪底 2(溫暖)', + 'instrument.name.pad_3_polysynth': '鋪底 3(複音合成)', + 'instrument.name.pad_4_choir': '鋪底 4(合唱)', + 'instrument.name.pad_5_bowed': '鋪底 5(弓弦玻璃)', + 'instrument.name.pad_6_metallic': '鋪底 6(金屬)', + 'instrument.name.pad_7_halo': '鋪底 7(光暈)', + 'instrument.name.pad_8_sweep': '鋪底 8(掃頻)', + 'instrument.name.fx_1_rain': '特效 1(雨)', + 'instrument.name.fx_2_soundtrack': '特效 2(配樂)', + 'instrument.name.fx_3_crystal': '特效 3(水晶)', + 'instrument.name.fx_4_atmosphere': '特效 4(氛圍)', + 'instrument.name.fx_5_brightness': '特效 5(明亮)', + 'instrument.name.fx_6_goblins': '特效 6(妖精)', + 'instrument.name.fx_7_echoes': '特效 7(回聲)', + 'instrument.name.fx_8_scifi': '特效 8(科幻)', + // ─── K.G.One / Music Generator Panel ───────────────────────────────────────── + 'kgone.panel.title.server': 'K.G.One 音樂生成器', + 'kgone.panel.title.local': '音樂生成器', + 'kgone.tab.fullSong': '完整歌曲', + 'kgone.tab.remix': '混音', + 'kgone.tab.repaint': '重繪', + 'kgone.tab.separator': '分離', + 'kgone.tab.requiresServer': '需要 K.G.One Music Studio 服務器集成', + // Shared across tabs + 'kgone.shared.advancedSettings': '高級設置', + 'kgone.shared.caption': '描述', + 'kgone.shared.lyrics': '歌詞', + 'kgone.shared.lyricsPlaceholder': '[Verse 1]\n在這裡輸入歌詞...\n\n[Chorus]\n...', + 'kgone.shared.instrumental': '純音樂(無人聲)', + 'kgone.shared.inferenceSteps': '推理步數', + 'kgone.shared.guidanceScale': '引導強度', + 'kgone.shared.useRandomSeed': '使用隨機種子', + 'kgone.shared.seed': '種子', + 'kgone.shared.thinking': '思考模式(CoT 元數據生成)', + 'kgone.shared.selectedRegion': '已選區域', + 'kgone.shared.track': '音軌', + 'kgone.shared.poweredBy': '技術支持:', + 'kgone.shared.btn.loadingModel': '加載模型中...', + 'kgone.shared.btn.generating': '生成中...', + 'kgone.shared.btn.processing': '處理中...', + 'kgone.shared.btn.downloading': '下載中...', + 'kgone.shared.btn.preparingUpload': '準備上傳...', + 'kgone.shared.btn.importing': '導入中...', + 'kgone.shared.hint.loadingModel': '正在加載模型——此過程可能需要 60 秒以上,請耐心等待...', + 'kgone.shared.hint.submitting': '正在提交生成請求...', + 'kgone.shared.hint.downloadingAudio': '正在下載音頻...', + // Clip tab + 'kgone.clip.field.prompt': '提示詞', + 'kgone.clip.field.promptPlaceholder': '例:粗糙, 酸性, 貝斯線, 303, 合成主音, FM, 次低音, 中高頻, 高頻相位, 高頻混響, 彎音, 8小節, 140 BPM, E 小調', + 'kgone.clip.field.promptHint': '用逗號分隔的標籤描述片段:樂器類別、子類型、音色、效果、小節數、BPM、調性。', + 'kgone.clip.field.negativePrompt': '負向提示詞', + 'kgone.clip.field.negativePromptPlaceholder': '例:失真, 噪音', + 'kgone.clip.field.bars': '小節數', + 'kgone.clip.field.bars4': '4 小節', + 'kgone.clip.field.bars8': '8 小節', + 'kgone.clip.field.note': '音符', + 'kgone.clip.field.scale': '音階', + 'kgone.clip.field.scaleMajor': '大調', + 'kgone.clip.field.scaleMinor': '小調', + 'kgone.clip.field.bpm': 'BPM', + 'kgone.clip.field.steps': '步數', + 'kgone.clip.field.cfgScale': 'CFG 強度', + 'kgone.clip.field.seedLabel': '種子(-1 = 隨機)', + 'kgone.clip.field.samplerType': '採樣器類型', + 'kgone.clip.field.sigmaMin': 'Sigma 最小值', + 'kgone.clip.field.sigmaMax': 'Sigma 最大值', + 'kgone.clip.field.cfgRescale': 'CFG 重縮放', + 'kgone.clip.hint.generating': '正在生成片段...', + 'kgone.clip.hint.drag': '將上方播放器拖至音軌以導入片段。拖到音頻音軌可作為 WAV 區域導入(推薦),拖到MIDI 音軌可作為 MIDI 區域導入。注意:MIDI 是從音頻轉錄的,可能不完全準確。', + 'kgone.clip.btn.generate': '生成片段', + 'kgone.clip.poweredBy': 'Foundation-1', + // Full Song tab + 'kgone.fullSong.field.captionPlaceholder': '例:風格:歐洲舞曲,90年代流行電子。感覺:朗朗上口、充滿活力... 速度:約 130 BPM。編排:強勁底鼓、歐洲舞曲貝斯線...', + 'kgone.fullSong.field.captionHint': '用自然語言描述歌曲風格、情緒、速度、編排和結構。', + 'kgone.fullSong.field.lyricsHint': '使用 [Intro]、[Verse]、[Chorus]、[Bridge] 標籤標注段落。', + 'kgone.fullSong.hint.generating': '生成中...', + 'kgone.fullSong.hint.generatingProgress': '生成中... {pct}% — {stage}', + 'kgone.fullSong.hint.generatingProgressNoStage': '生成中... {pct}%', + 'kgone.fullSong.hint.drag': '將上方播放器拖至音頻音軌以導入歌曲。完整歌曲生成不支持拖至 MIDI 音軌。', + 'kgone.fullSong.btn.generate': '生成歌曲', + 'kgone.fullSong.poweredBy': 'ACE-Step 1.5', + // Remix tab + 'kgone.remix.field.captionPlaceholder': '例:風格:爵士,感覺:流暢悠揚,編排:鋼琴、立式貝斯、刷式鼓...', + 'kgone.remix.field.captionHint': '描述混音的目標風格、情緒和編排。', + 'kgone.remix.field.lyricsHint': '留空則保留原歌詞,或提供新歌詞。使用 [Verse]、[Chorus]、[Bridge] 標籤。', + 'kgone.remix.field.coverStrength': '翻唱強度', + 'kgone.remix.field.coverStrengthHint': '0 = 創意改編,1 = 忠實於原曲結構', + 'kgone.remix.field.noiseStrength': '噪聲強度', + 'kgone.remix.field.noiseStrengthHint': '0 = 純風格遷移,推薦 0.1–0.25', + 'kgone.remix.hint.submitting': '正在提交混音請求...', + 'kgone.remix.hint.generating': '正在生成混音...', + 'kgone.remix.hint.generatingProgress': '正在生成混音... {pct}% — {stage}', + 'kgone.remix.hint.generatingProgressNoStage': '正在生成混音... {pct}%', + 'kgone.remix.hint.drag': '將上方播放器拖至音頻音軌以導入混音。', + 'kgone.remix.hint.noRegion': '請在時間軸上選擇一個音頻區域以進行混音。僅支持音頻區域——MIDI 區域不可混音。', + 'kgone.remix.btn.generate': '生成混音', + 'kgone.remix.btn.processingRemix': '處理混音中...', + 'kgone.remix.btn.importAligned': '對齊源區域並導入', + 'kgone.remix.poweredBy': 'ACE-Step 1.5', + // Repaint tab + 'kgone.repaint.field.repaintRange': '重繪範圍', + 'kgone.repaint.field.loopModeOff': '循環模式已關閉。請啟用工具欄上的循環按鈕並設置循環範圍以定義重繪窗口。', + 'kgone.repaint.field.start': '開始', + 'kgone.repaint.field.end': '結束', + 'kgone.repaint.field.untilEnd': '直至音頻末尾', + 'kgone.repaint.field.captionPlaceholder': '例:風格:爵士,感覺:流暢悠揚,編排:鋼琴、立式貝斯、刷式鼓...', + 'kgone.repaint.field.captionHint': '描述重繪段落的目標風格和編排。', + 'kgone.repaint.field.lyricsHint': '留空則保留原歌詞,或為重繪段落提供新歌詞。', + 'kgone.repaint.field.repaintStrength': '重繪強度', + 'kgone.repaint.field.repaintStrengthHint': '0 = 保留原音,1 = 完全重新生成', + 'kgone.repaint.hint.submitting': '正在提交重繪請求...', + 'kgone.repaint.hint.generating': '正在生成重繪...', + 'kgone.repaint.hint.generatingProgress': '正在生成重繪... {pct}% — {stage}', + 'kgone.repaint.hint.generatingProgressNoStage': '正在生成重繪... {pct}%', + 'kgone.repaint.hint.drag': '將上方播放器拖至音頻音軌以導入重繪結果。', + 'kgone.repaint.hint.noRegion': '請在時間軸上選擇一個音頻區域以進行重繪。僅支持音頻區域——MIDI 區域不可重繪。', + 'kgone.repaint.btn.generate': '生成重繪', + 'kgone.repaint.btn.processingRepaint': '處理重繪中...', + 'kgone.repaint.btn.importAligned': '對齊源區域並導入', + 'kgone.repaint.poweredBy': 'ACE-Step 1.5', + // Separator tab + 'kgone.separator.field.separationModel': '分離模型', + 'kgone.separator.field.chunkDuration': '可選音頻分塊時長(秒)', + 'kgone.separator.field.chunkDurationPlaceholder': '留空則處理整個區域', + 'kgone.separator.field.modelOverlap': '模型重疊', + 'kgone.separator.hint.drag': '將上方各音軌播放器拖至音頻音軌以導入。不支持拖至 MIDI 音軌進行音軌分離。', + 'kgone.separator.hint.noRegion.download': '請下載 {model},然後在時間軸上選擇一個音頻區域以提取音軌。', + 'kgone.separator.hint.noRegion.select': '請在時間軸上選擇一個音頻區域以提取音軌。僅支持音頻區域——MIDI 區域不可分離。', + 'kgone.separator.btn.separateStems': '分離音軌', + 'kgone.separator.btn.importAllStems': '將所有音軌導入時間軸', + 'kgone.separator.poweredBy': 'UVR5 CLI', + 'kgone.separator.model.vocalInstMedium': '人聲與樂器(中等精度)', + 'kgone.separator.model.vocalInstHigh': '人聲與樂器(高精度)', + 'kgone.separator.model.6stem': '人聲、鼓、貝斯、吉他、鋼琴及其他', + 'kgone.separator.model.htdemucs4s': '人聲、鼓、貝斯及其他', + 'kgone.separator.btn.server.loadingModel': '加載模型中...', + 'kgone.separator.btn.server.generating': '準備上傳...', + 'kgone.separator.btn.server.polling': '正在分離音軌...', + 'kgone.separator.btn.server.downloading': '下載中...', + 'kgone.separator.btn.local.loadingModel': '準備本地模型...', + 'kgone.separator.btn.local.generating': '準備音頻...', + 'kgone.separator.btn.local.polling': '本地分離中...', + 'kgone.separator.btn.local.downloading': '收尾處理...', + 'kgone.separator.local.title': '本地分離模式', + 'kgone.separator.local.description': '如果 K.G.One Music Studio 不可用,本地分離模式提供了一種直接在瀏覽器中提取音軌的內置替代方案。提供兩種本地模型:人聲與樂器(中等精度),以及人聲、鼓、貝斯及其他。下方的下載狀態反映當前選定的模型。人聲與樂器(中等精度)的處理時間通常比人聲、鼓、貝斯及其他更長,總處理時間仍取決於您的硬件配置。如果處理回退至 CPU,分離運行期間頁面響應可能暫時變慢。', + 'kgone.separator.local.learnMore': '了解更多關於 K.G.One Music Studio 服務器集成的信息。', + 'kgone.separator.local.provider': '當前設備:', + 'kgone.separator.local.model': '模型:', + 'kgone.separator.local.downloaded': '已下載', + 'kgone.separator.local.notDownloaded': '未下載', + 'kgone.separator.local.checkingCache': '正在檢查本地模型緩存...', + 'kgone.separator.local.btn.download': '下載所選模型', + 'kgone.separator.local.btn.downloading': '模型下載中...', + 'kgone.separator.local.btn.redownload': '重新下載模型', + 'kgone.separator.local.btn.redownloading': '重新下載中...', + 'kgone.separator.local.btn.deleteCache': '刪除已緩存模型', + 'kgone.separator.local.btn.deleting': '刪除中...', + 'kgone.separator.local.progress.downloading': '正在下載 {name}...', + 'kgone.separator.local.progress.downloadingWithSize': '正在下載 {name}... {received} / {total} MB', + 'kgone.separator.local.progress.downloadingMbOnly': '正在下載 {name}... {received} MB', + 'kgone.separator.local.progress.ready': '{name} 已就緒。', + 'kgone.separator.local.progress.preparingRuntime': '正在準備 ONNX 運行時環境...', + 'kgone.separator.local.progress.readingAudio': '正在讀取音頻文件...', + 'kgone.separator.local.progress.running': '正在瀏覽器中執行分離...', + 'kgone.separator.local.progress.complete': '分離完成。', + + 'openProjectModal.title': '打開項目', + 'openProjectModal.close': '關閉', + 'openProjectModal.loading': '正在加載項目...', + 'openProjectModal.date.unknown': '未知', + 'openProjectModal.filter.placeholder': '篩選項目...', + 'openProjectModal.viewToggle.projects': '項目', + 'openProjectModal.viewToggle.trash': '回收站', + 'openProjectModal.trash.hint': '已刪除的項目將在 30 天後自動移除。', + 'openProjectModal.sort.name': '名稱', + 'openProjectModal.sort.created': '創建時間', + 'openProjectModal.sort.updated': '更新時間', + 'openProjectModal.empty.trash': '回收站為空', + 'openProjectModal.empty.noProjects': '暫無已保存的項目', + 'openProjectModal.empty.noMatch': '沒有符合篩選條件的項目', + 'openProjectModal.meta.created': '創建於 {date}', + 'openProjectModal.meta.updated': '更新於 {date}', + 'openProjectModal.meta.deleted': '刪除於 {date}', + 'openProjectModal.action.open': '打開', + 'openProjectModal.action.duplicate': '複製', + 'openProjectModal.action.delete': '刪除', + 'openProjectModal.action.restore': '恢復', + 'openProjectModal.action.deletePermanently': '永久刪除', + 'openProjectModal.dialog.duplicateName': '請輸入複製項目的名稱:', + 'openProjectModal.dialog.invalidName': '項目名稱無效。只允許使用字母、數字、空格、連字符、下劃線、句號和括號。', + 'openProjectModal.dialog.duplicateError': '複製項目失敗:{error}', + 'openProjectModal.dialog.deleteError': '刪除項目失敗:{error}', + 'openProjectModal.dialog.restoreError': '恢復項目失敗:{error}', + 'openProjectModal.dialog.confirmPermanentDelete': '確定要永久刪除"{name}"嗎?\n\n此操作無法撤銷。', + 'openProjectModal.dialog.permanentDeleteError': '永久刪除項目失敗:{error}', +}; diff --git a/src/i18n/types.ts b/src/i18n/types.ts index 8769321..35d577c 100644 --- a/src/i18n/types.ts +++ b/src/i18n/types.ts @@ -1,4 +1,4 @@ -export type LanguageSetting = 'auto' | 'en_us' | 'zh_cn'; +export type LanguageSetting = 'auto' | 'en_us' | 'zh_cn' | 'zh_hk'; export type ResolvedLocaleCode = Exclude; diff --git a/src/util/audioTempoDetectionActions.ts b/src/util/audioTempoDetectionActions.ts index 52838e0..dd5defd 100644 --- a/src/util/audioTempoDetectionActions.ts +++ b/src/util/audioTempoDetectionActions.ts @@ -7,6 +7,7 @@ import { findTempoRegionAtBar, findTempoRegionAtBeat, } from './globalTrackUtil'; +import { translate } from '../i18n/translate'; export const DETECTED_TEMPO_ACTION_UPDATE_CURRENT = 'update-current-tempo'; export const DETECTED_TEMPO_ACTION_INSERT_REGION = 'insert-tempo-change'; @@ -16,7 +17,7 @@ export type DetectedTempoAction = | typeof DETECTED_TEMPO_ACTION_INSERT_REGION; export function buildDetectedTempoChoiceMessage(bpm: number): string { - return `Detected tempo: ${bpm} BPM. Choose how to apply it.\n\nUpdate Current Tempo changes the active tempo at this clip location. Insert Tempo Change adds a new tempo region at the nearest bar before the clip starts.`; + return translate('dialog.message.tempoApply', { bpm }); } const ALIGNMENT_EPSILON = 1e-6;