feat: added Traditional Chinese support
This commit is contained in:
@@ -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 音樂創作助手創作愉快!
|
||||
@@ -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`
|
||||
@@ -0,0 +1,11 @@
|
||||
## 再次歡迎
|
||||
|
||||
很高興再次見到您。檢測到您的 LLM 提供方已經配置完成,現在可以直接開始聊天。
|
||||
|
||||
提示:
|
||||
- 隨時輸入 `/clear` 重置當前聊天。
|
||||
- 輸入 `/welcome` 再次查看這條歡迎消息。
|
||||
- 輸入 `/help` 查看幫助說明。
|
||||
- 輸入 `/hotkeys` 查看快捷鍵指南。
|
||||
|
||||
祝你在 K.G.Studio 音樂創作助手中創作順利。
|
||||
@@ -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**。
|
||||
- 切換提供方後,建議開啟一個新的對話,以便聊天會話乾淨地使用新模型。
|
||||
@@ -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 提供方均無隸屬關係。所有數據默認都保存在您的本地設備中;您發送給第三方服務的數據由您自行負責。
|
||||
@@ -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<PianoRollProps> = ({
|
||||
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<PianoRollProps> = ({
|
||||
|
||||
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<PianoRollProps> = ({
|
||||
}
|
||||
|
||||
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<PianoRollProps> = ({
|
||||
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) {
|
||||
|
||||
@@ -354,6 +354,7 @@ const GeneralSettings: React.FC = () => {
|
||||
<option value="auto">{t('settings.general.language.auto')}</option>
|
||||
<option value="en_us">{t('settings.general.language.en_us')}</option>
|
||||
<option value="zh_cn">{t('settings.general.language.zh_cn')}</option>
|
||||
<option value="zh_hk">{t('settings.general.language.zh_hk')}</option>
|
||||
</select>
|
||||
<div className="settings-help" style={{ fontSize: '12px', color: '#888', marginTop: '4px' }}>
|
||||
{t('settings.general.language.help')}
|
||||
|
||||
+15
-4
@@ -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');
|
||||
});
|
||||
});
|
||||
|
||||
+11
-3
@@ -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;
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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<ResolvedLocaleCode, TranslationMessages> = {
|
||||
en_us: enUsMessages,
|
||||
zh_cn: zhCnMessages,
|
||||
zh_hk: zhHkMessages,
|
||||
};
|
||||
|
||||
@@ -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': '区域',
|
||||
|
||||
@@ -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': '將上方播放器拖至音軌以導入片段。拖到<strong>音頻音軌</strong>可作為 WAV 區域導入(推薦),拖到<strong>MIDI 音軌</strong>可作為 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': '將上方播放器拖至<strong>音頻音軌</strong>以導入歌曲。完整歌曲生成不支持拖至 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': '將上方播放器拖至<strong>音頻音軌</strong>以導入混音。',
|
||||
'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': '將上方播放器拖至<strong>音頻音軌</strong>以導入重繪結果。',
|
||||
'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': '將上方各音軌播放器拖至<strong>音頻音軌</strong>以導入。不支持拖至 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}',
|
||||
};
|
||||
+1
-1
@@ -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<LanguageSetting, 'auto'>;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user