@goodandready/dsh-tts 0.4.6 → 0.4.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -2
- package/README.ru.md +10 -1
- package/README.zh.md +23 -4
- package/lib/client-src/20-player.js +33 -0
- package/lib/client-src/30-locales.js +205 -195
- package/lib/client-src/40-editors.js +2 -1
- package/lib/client-src/50-settings.js +1 -0
- package/lib/client-src/60-card-dock.js +11 -1
- package/lib/client.js +253 -198
- package/lib/index.js +8 -2
- package/lib/text.js +28 -7
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -21,6 +21,16 @@
|
|
|
21
21
|
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
|
|
22
22
|
</p>
|
|
23
23
|
|
|
24
|
+
<table align="center">
|
|
25
|
+
<tr>
|
|
26
|
+
<td align="center">
|
|
27
|
+
⭐ <strong>If you like this plugin, please star it on GitHub</strong> — it shows me that the plugin is useful to you and motivates me to keep developing it.
|
|
28
|
+
<br><br>
|
|
29
|
+
🐛 <strong>If you find a bug or would like to request a feature</strong>, open a GitHub issue in any language — I will review your proposal and implement useful suggestions in a future plugin version.
|
|
30
|
+
</td>
|
|
31
|
+
</tr>
|
|
32
|
+
</table>
|
|
33
|
+
|
|
24
34
|
</div>
|
|
25
35
|
|
|
26
36
|
---
|
|
@@ -100,12 +110,21 @@ graph LR
|
|
|
100
110
|
|
|
101
111
|
### 5. 👥 Multi-Agent Personas & Subagent Voice Overrides
|
|
102
112
|
* Assign distinct voices, providers, models, and audio chimes to individual subagents (e.g. `coder`, `reviewer`, `planner`, `tester`).
|
|
103
|
-
*
|
|
113
|
+
* **Subagent Auto-Detection**: With `autoDetectSubagent` enabled, incoming turns automatically match subagents by message metadata (`subagent`, `agent`, `author`, `name`) and dynamically apply voice, rate, and SSML style presets.
|
|
114
|
+
|
|
115
|
+
### 6. 💾 Audio Clip Export & Speech History
|
|
116
|
+
* Export any spoken utterance directly to an audio file (`.wav` / `.mp3`) via `exportAudioClip(text)`.
|
|
117
|
+
* Instant download buttons (`⤓`) integrated directly into the input dock speaker control and recent utterances dropdown list.
|
|
104
118
|
|
|
105
|
-
###
|
|
119
|
+
### 7. 💬 Messenger Voice Notes Integration (with `@goodandready/dsh-messenger-gateway`)
|
|
106
120
|
* Generates voice audio for Telegram and Discord bot replies via `POST /dsh-tts/speak`.
|
|
107
121
|
* Protective dependency check with installation hint when gateway plugin is missing.
|
|
108
122
|
|
|
123
|
+
### 8. 🌐 Canonical English & Chinese Localization (EN + ZH)
|
|
124
|
+
* Complete built-in English (`en`) and Chinese (`zh`) UI and speech template dictionaries.
|
|
125
|
+
* Centralized Russian localization provided via `@goodandready/dsh-russian-lang` through Gitea issue tracking.
|
|
126
|
+
* Smart boundary tokenizer supporting CJK full-width punctuation (`。!?`), abbreviations, file extensions, and IP/version numbers.
|
|
127
|
+
|
|
109
128
|
---
|
|
110
129
|
|
|
111
130
|
## 🛠️ Complete Supported Providers Matrix (18 Backends)
|
package/README.ru.md
CHANGED
|
@@ -21,6 +21,16 @@
|
|
|
21
21
|
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
|
|
22
22
|
</p>
|
|
23
23
|
|
|
24
|
+
<table align="center">
|
|
25
|
+
<tr>
|
|
26
|
+
<td align="center">
|
|
27
|
+
⭐ <strong>Если вам нравится этот плагин, поставьте ему звезду на GitHub</strong> — это покажет мне, что плагин вам полезен, и будет мотивировать меня развивать его дальше.
|
|
28
|
+
<br><br>
|
|
29
|
+
🐛 <strong>Если вы нашли баг или хотите предложить новый функционал</strong>, создайте issue на GitHub на любом языке — я рассмотрю ваше предложение и реализую полезные идеи в одной из следующих версий плагина.
|
|
30
|
+
</td>
|
|
31
|
+
</tr>
|
|
32
|
+
</table>
|
|
33
|
+
|
|
24
34
|
</div>
|
|
25
35
|
|
|
26
36
|
---
|
|
@@ -29,7 +39,6 @@
|
|
|
29
39
|
|
|
30
40
|
> **Честно про локальные нейросети:** Kokoro/F5 — только статус и загрузка весов. Inference **не входит** в пакет; эти провайдеры падают с понятной причиной, цепочка идёт дальше. Рабочий offline: Edge TTS, Piper, eSpeak.
|
|
31
41
|
|
|
32
|
-
|
|
33
42
|
**`dsh-tts`** обеспечивает качественное голосовое озвучивание ответов ассистента в веб-интерфейсе **DeepSeek Harness**. При включённой опции **«Читать ответы агента»**, каждая готовая реплика или потоковый фрагмент синтезируется на хосте и мгновенно воспроизводится в браузере.
|
|
34
43
|
|
|
35
44
|
API-ключи никогда не передаются в браузер: синтез аудио выполняется на стороне хоста через **независимые цепочки отказоустойчивости (фолбеков)**, включая системные offline-движки (Edge TTS, Piper, eSpeak). Kokoro/F5 — только загрузка весов; inference не входит в пакет.
|
package/README.zh.md
CHANGED
|
@@ -21,6 +21,16 @@
|
|
|
21
21
|
<a href="README.zh.md"><b>🇨🇳 中文说明</b></a>
|
|
22
22
|
</p>
|
|
23
23
|
|
|
24
|
+
<table align="center">
|
|
25
|
+
<tr>
|
|
26
|
+
<td align="center">
|
|
27
|
+
⭐ <strong>如果您喜欢这个插件,请在 GitHub 上为它点亮 Star</strong> — 这能让我知道插件对您有用,并鼓励我继续开发和维护它。
|
|
28
|
+
<br><br>
|
|
29
|
+
🐛 <strong>如果您发现 Bug 或希望增加功能</strong>,请使用任意语言在 GitHub 上提交 Issue — 我会评估您的建议,并在后续版本中实现有价值的改进。
|
|
30
|
+
</td>
|
|
31
|
+
</tr>
|
|
32
|
+
</table>
|
|
33
|
+
|
|
24
34
|
</div>
|
|
25
35
|
|
|
26
36
|
---
|
|
@@ -93,14 +103,23 @@ graph LR
|
|
|
93
103
|
* **预配置词汇表**:为常见技术缩写和开发者术语提供正确语音替换(SQL、Nginx、Kubernetes/K8s、Docker、API、JSON、YAML、GUI、CLI、CI/CD 等 25 个术语)。
|
|
94
104
|
* **交互式编辑器**:编辑规则,逐条点击 **▶ 试听** 按钮预听效果,一键加载全部 IT 术语。
|
|
95
105
|
|
|
96
|
-
### 5. 👥
|
|
97
|
-
* 为不同子智能体(`coder`、`reviewer`、`planner`、`tester`
|
|
98
|
-
*
|
|
106
|
+
### 5. 👥 多智能体角色个性化语音与自动识别
|
|
107
|
+
* 为不同子智能体(`coder`、`reviewer`、`planner`、`tester` 等)分配独立的语音、服务商、模型、提示音与 SSML 风格。
|
|
108
|
+
* **智能体自动识别**:开启 `autoDetectSubagent` 后,自动根据消息元数据(`subagent`、`agent`、`author`、`name`)匹配对应的专属音色与语速。
|
|
109
|
+
|
|
110
|
+
### 6. 💾 音频片段导出与历史回放
|
|
111
|
+
* 任意朗读过的回复均可通过 `exportAudioClip(text)` 导出为标准音频文件(`.wav` / `.mp3`)。
|
|
112
|
+
* 输入框控制栏及最近朗读列表中内置即时下载按钮(`⤓`)。
|
|
99
113
|
|
|
100
|
-
###
|
|
114
|
+
### 7. 💬 即时通讯语音消息集成(配合 `@goodandready/dsh-messenger-gateway`)
|
|
101
115
|
* 通过 `POST /dsh-tts/speak` 为 Telegram 和 Discord 机器人回复生成语音消息。
|
|
102
116
|
* 缺少网关插件时自动显示安装提示,防护性依赖检查。
|
|
103
117
|
|
|
118
|
+
### 8. 🌐 深度中文本土化支持与智能断句分词器
|
|
119
|
+
* 完整内置英文 (`en`) 与中文 (`zh`) 双语界面及全量语音模版。
|
|
120
|
+
* 针对 CJK 中文全角标点(`。!?`)与自适应分词长度特别优化,避免长句卡顿。
|
|
121
|
+
* 保护缩写、文件名(`package.json`)、网址域名(`goodandready.app`)及 IP / 版本号不被误切。
|
|
122
|
+
|
|
104
123
|
---
|
|
105
124
|
|
|
106
125
|
## 🛠️ 支持的 18 大服务商矩阵
|
|
@@ -141,6 +141,7 @@
|
|
|
141
141
|
const { audio, url } = item.prepared
|
|
142
142
|
audio.playbackRate = Math.max(0.5, Math.min(2, player.rate || 1))
|
|
143
143
|
player.audio = audio
|
|
144
|
+
if (item.text) player.lastText = item.text
|
|
144
145
|
playerChanged()
|
|
145
146
|
prebufferNext()
|
|
146
147
|
let doneCalled = false
|
|
@@ -322,6 +323,38 @@
|
|
|
322
323
|
.catch(() => { /* chime fetch failed or not found */ })
|
|
323
324
|
}
|
|
324
325
|
|
|
326
|
+
function exportAudioClip(text) {
|
|
327
|
+
let phrase = text || player.lastText
|
|
328
|
+
if (!phrase) {
|
|
329
|
+
const list = recent.list()
|
|
330
|
+
if (list && list.items && list.items.length) phrase = list.items[0]
|
|
331
|
+
}
|
|
332
|
+
if (!phrase) return
|
|
333
|
+
fetch('/dsh-tts/speak', {
|
|
334
|
+
method: 'POST',
|
|
335
|
+
headers: { 'Content-Type': 'application/json' },
|
|
336
|
+
body: JSON.stringify({ text: phrase }),
|
|
337
|
+
})
|
|
338
|
+
.then((r) => r.json())
|
|
339
|
+
.then((d) => {
|
|
340
|
+
if (!d || !d.ok || !d.audioBase64) return
|
|
341
|
+
const bin = atob(d.audioBase64)
|
|
342
|
+
const bytes = new Uint8Array(bin.length)
|
|
343
|
+
for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i)
|
|
344
|
+
const mime = d.mime || 'audio/mpeg'
|
|
345
|
+
const blob = new Blob([bytes], { type: mime })
|
|
346
|
+
const url = URL.createObjectURL(blob)
|
|
347
|
+
const a = document.createElement('a')
|
|
348
|
+
a.href = url
|
|
349
|
+
a.download = 'dsh-tts-' + Date.now() + (mime.indexOf('wav') !== -1 ? '.wav' : '.mp3')
|
|
350
|
+
document.body.appendChild(a)
|
|
351
|
+
a.click()
|
|
352
|
+
document.body.removeChild(a)
|
|
353
|
+
setTimeout(() => { try { URL.revokeObjectURL(url) } catch {} }, 2000)
|
|
354
|
+
})
|
|
355
|
+
.catch(() => {})
|
|
356
|
+
}
|
|
357
|
+
|
|
325
358
|
function credOf(map, provider) {
|
|
326
359
|
return (map && map[provider]) || { configured: false, writable: true, ref: '' }
|
|
327
360
|
}
|
|
@@ -1,4 +1,18 @@
|
|
|
1
1
|
const en = {
|
|
2
|
+
'dockLabel': 'Speech',
|
|
3
|
+
'dockPause': 'Pause',
|
|
4
|
+
'dockResume': 'Resume speech',
|
|
5
|
+
'dockStop': 'Stop speech',
|
|
6
|
+
'dockCurrent': 'Now speaking',
|
|
7
|
+
'dockNone': 'Waiting for speech',
|
|
8
|
+
'dockQueue': 'Queue',
|
|
9
|
+
'dockClearQueue': 'Clear queue',
|
|
10
|
+
'dockReplay': 'Replay',
|
|
11
|
+
'dockFavorite': 'Favorite',
|
|
12
|
+
'dockUnfavorite': 'Unfavorite',
|
|
13
|
+
'dockFavorites': 'Favorite clips',
|
|
14
|
+
'exportAudio': 'Export audio clip',
|
|
15
|
+
'exportSuccess': 'Audio exported',
|
|
2
16
|
'statusOnline': 'Host online',
|
|
3
17
|
'recent': 'Recent',
|
|
4
18
|
'recentEmpty': 'No recent speech yet',
|
|
@@ -17,12 +31,10 @@
|
|
|
17
31
|
'queueLen': 'Queue',
|
|
18
32
|
'sseState': 'SSE',
|
|
19
33
|
'currentVoice': 'Voice',
|
|
20
|
-
|
|
21
34
|
'statusOffline': 'Host unreachable',
|
|
22
35
|
'cacheStats': 'Cache',
|
|
23
|
-
|
|
24
36
|
'general': 'General',
|
|
25
|
-
'loading': 'Loading
|
|
37
|
+
'loading': 'Loading…',
|
|
26
38
|
'chainHintCard': 'Pick a provider, paste its API key, and Save (or leave the key field). The key is written to the host credentials store immediately. The browser never reads it back.',
|
|
27
39
|
'speakRepliesHint': 'When on, each finished reply is synthesized on the host and played in this browser. Audio is not sent back to the model.',
|
|
28
40
|
'chainHint': 'Top to bottom is the fallback order. Cloud keys are saved to the host credentials store, never into plugin settings.',
|
|
@@ -104,6 +116,8 @@
|
|
|
104
116
|
'roleReply': 'Reply',
|
|
105
117
|
'roleApproval': 'Approval',
|
|
106
118
|
'roleError': 'Error',
|
|
119
|
+
'ssmlStyle': 'SSML tone style',
|
|
120
|
+
'ssmlStyleHint': 'Expressive tone (e.g. cheerful, serious, empathetic).',
|
|
107
121
|
'longReply': 'Long replies',
|
|
108
122
|
'longReplyHint': 'truncate: cut at the limit; summarize: short retelling; full: read everything.',
|
|
109
123
|
'summaryModel': 'Summary model',
|
|
@@ -116,9 +130,8 @@
|
|
|
116
130
|
'statHits': 'Cache hits',
|
|
117
131
|
'statErrors': 'Errors',
|
|
118
132
|
'statReset': 'Reset stats',
|
|
119
|
-
|
|
120
133
|
'autoDetect': 'Auto language per piece',
|
|
121
|
-
'autoDetectHint': 'Guess
|
|
134
|
+
'autoDetectHint': 'Guess zh/en per spoken piece instead of the language setting.',
|
|
122
135
|
'narrateQuotesOnly': 'Only quotes',
|
|
123
136
|
'narrateQuotesHint': 'Read only «quoted» fragments of a reply.',
|
|
124
137
|
'skipActions': 'Skip *actions*',
|
|
@@ -129,7 +142,6 @@
|
|
|
129
142
|
'customBaseUrlHint': 'OpenAI-compatible origin, e.g. http://localhost:8880/v1; empty = off.',
|
|
130
143
|
'customKeyEnv': 'Custom key env',
|
|
131
144
|
'customKeyEnvHint': 'Credential name for the custom endpoint bearer key.',
|
|
132
|
-
|
|
133
145
|
'localEnginesTitle': 'Local Engines (Offline)',
|
|
134
146
|
'localEnginesHint': 'Run speech synthesis 100% offline without API keys.',
|
|
135
147
|
'enableLocalEngines': 'Enable local engines',
|
|
@@ -146,7 +158,6 @@
|
|
|
146
158
|
'installKokoro': 'Install (350 MB)',
|
|
147
159
|
'installF5': 'Install (2.1 GB)',
|
|
148
160
|
'deleteModel': 'Delete',
|
|
149
|
-
|
|
150
161
|
'voiceDuplexTitle': 'Voice Duplex (Voice-to-Voice)',
|
|
151
162
|
'voiceDuplexHint': 'Interactive voice conversation integrated with dsh-voice.',
|
|
152
163
|
'voiceNotInstalledTitle': 'Required plugin @goodandready/dsh-voice is not installed',
|
|
@@ -157,205 +168,204 @@
|
|
|
157
168
|
'vadBargeInHint': 'Mute speech synthesis immediately when user voice is detected.',
|
|
158
169
|
'newSubagentRole': 'Subagent name (e.g. coder, reviewer)',
|
|
159
170
|
'addSubagentRole': 'Add Subagent',
|
|
171
|
+
'autoDetectSubagent': 'Auto-detect subagent roles',
|
|
160
172
|
'role_coder': 'Coder',
|
|
161
173
|
'role_reviewer': 'Reviewer',
|
|
162
174
|
'role_planner': 'Planner',
|
|
163
|
-
|
|
164
175
|
'enableItDictionary': 'Built-in IT dictionary',
|
|
165
176
|
'enableItDictionaryHint': 'Auto-correct pronunciation for SQL, Nginx, K8s, Docker, API, JSON, YAML, etc.',
|
|
166
177
|
'loadItDictionary': 'Populate IT terms',
|
|
167
178
|
'previewRule': 'Listen',
|
|
168
|
-
|
|
169
179
|
'messengerIntegrationTitle': 'Messenger Integration (Telegram, Discord)',
|
|
170
180
|
'messengerIntegrationHint': 'Voice replies and notes sent through @goodandready/dsh-messenger-gateway.',
|
|
171
181
|
'messengerNotInstalledTitle': 'Required plugin @goodandready/dsh-messenger-gateway is not installed',
|
|
172
|
-
'messengerInstallHint': 'To enable voice
|
|
182
|
+
'messengerInstallHint': 'To enable voice replies in messengers, install the plugin with',
|
|
173
183
|
'messengerTtsEnabled': 'Voice replies in messengers',
|
|
174
|
-
'messengerTtsEnabledHint': 'Allow messenger
|
|
175
|
-
|
|
176
|
-
'dockLabel': 'Speech',
|
|
177
|
-
'dockPause': 'Pause',
|
|
178
|
-
'dockResume': 'Resume speech',
|
|
179
|
-
'dockStop': 'Stop speech',
|
|
184
|
+
'messengerTtsEnabledHint': 'Allow the messenger gateway to deliver replies as voice notes.',
|
|
180
185
|
}
|
|
181
|
-
const ru = {
|
|
182
|
-
'statusOnline': 'Хост на связи',
|
|
183
|
-
'recent': 'Недавние',
|
|
184
|
-
'recentEmpty': 'Пока нет реплик',
|
|
185
|
-
'retry': 'Повторить',
|
|
186
|
-
'advancedCollapsed': 'Дополнительно',
|
|
187
|
-
'confirmClearCache': 'Очистить кэш синтеза? Действие необратимо.',
|
|
188
|
-
'confirmDeleteModel': 'Удалить скачанные веса движка?',
|
|
189
|
-
'autoplayBlocked': 'Звук заблокирован — один раз кликните или коснитесь экрана',
|
|
190
|
-
'enableSound': 'Включить звук',
|
|
191
|
-
'runtimeNotBundled': 'Рантайм нейросинтеза не входит в пакет',
|
|
192
|
-
'runtimeNotBundledHint': 'Установка весов не включает речь. Для offline используйте Edge, Piper или eSpeak.',
|
|
193
|
-
'testProvider': 'Проверить провайдера',
|
|
194
|
-
'breakerOpen': 'Переключатель открыт',
|
|
195
|
-
'lastError': 'Последняя ошибка',
|
|
196
|
-
'activeProvider': 'Активный провайдер',
|
|
197
|
-
'queueLen': 'Очередь',
|
|
198
|
-
'sseState': 'SSE',
|
|
199
|
-
'currentVoice': 'Голос',
|
|
200
|
-
|
|
201
|
-
'statusOffline': 'Хост недоступен',
|
|
202
|
-
'cacheStats': 'Кэш',
|
|
203
|
-
|
|
204
|
-
'dockLabel': 'Чтение',
|
|
205
|
-
'dockPause': 'Пауза',
|
|
206
|
-
'dockResume': 'Продолжить чтение',
|
|
207
|
-
'dockStop': 'Прекратить чтение',
|
|
208
|
-
|
|
209
|
-
'general': 'Общее',
|
|
210
|
-
'loading': 'Загрузка…',
|
|
211
|
-
'chainHintCard': 'Выберите провайдера, вставьте ключ и нажмите «Сохранить» (или просто уйдите из поля). Ключ сразу попадает в хранилище учётных данных харнесса; браузер его обратно не читает.',
|
|
212
|
-
'speakRepliesHint': 'Каждый законченный ответ озвучивается на стороне харнесса и играет в этом браузере. Звук модели не отправляется.',
|
|
213
|
-
'chainHint': 'Порядок сверху вниз — порядок отката. Ключи облачных провайдеров попадают в хранилище учётных данных харнесса, а не в настройки плагина.',
|
|
214
|
-
'title': 'Озвучка',
|
|
215
|
-
'speakReplies': 'Читать ответы агента',
|
|
216
|
-
'speakAsItGoes': 'Читать по ходу',
|
|
217
|
-
'speakAsItGoesHint': 'Читать каждый ответ, как только он пришёл, предложениями, не дожидаясь конца хода.',
|
|
218
|
-
'rate': 'Скорость чтения',
|
|
219
|
-
'rateHint': 'От 0.5 до 2. Синтез не меняется — быстрее или медленнее играет браузер.',
|
|
220
|
-
'bargeIn': 'Замолкать, когда вы заговорили',
|
|
221
|
-
'bargeInHint': 'Прекращать чтение, как только открылся микрофон.',
|
|
222
|
-
'announce': 'Объявлять запрос подтверждения',
|
|
223
|
-
'announceHint': 'Говорить вслух и подавать сигнал, когда агент остановился ради подтверждения или задал вопрос.',
|
|
224
|
-
'approvalText': 'Что говорить о подтверждении',
|
|
225
|
-
'approvalHint': 'Произносится при запросе подтверждения; имя инструмента добавляется следом.',
|
|
226
|
-
'questionText': 'Что говорить о вопросе',
|
|
227
|
-
'questionHint': 'Произносится, когда агент задал вопрос. Пусто — не произносить.',
|
|
228
|
-
'chime': 'Сигнал',
|
|
229
|
-
'chimeHint': 'ding, beep или none.',
|
|
230
|
-
'language': 'Язык',
|
|
231
|
-
'languageHint': 'Подсказка для голосов, которым нужен язык (eSpeak, Azure SSML).',
|
|
232
|
-
'piperModel': 'Piper: путь к модели',
|
|
233
|
-
'piperModelHint': 'Абсолютный путь к модели ONNX. Пусто — Piper пропускается.',
|
|
234
|
-
'piperBin': 'Piper: бинарь',
|
|
235
|
-
'binHint': 'Ищется в PATH, если путь не абсолютный.',
|
|
236
|
-
'espeakBin': 'eSpeak: бинарь',
|
|
237
|
-
'espeakHint': 'Обычно espeak-ng.',
|
|
238
|
-
'edgeBin': 'edge-tts: бинарь',
|
|
239
|
-
'edgeHint': 'Консольная утилита из пакета edge-tts.',
|
|
240
|
-
'azureRegion': 'Azure: регион',
|
|
241
|
-
'azureHint': 'Нужен провайдеру azure, например eastus.',
|
|
242
|
-
'openaiBaseUrl': 'OpenAI base URL',
|
|
243
|
-
'openaiBaseUrlHint': 'OpenAI-совместимый origin для провайдера openai.',
|
|
244
|
-
'mimoBaseUrl': 'MiMo base URL',
|
|
245
|
-
'mimoBaseUrlHint': 'Корень API MiMo; синтез идёт через chat endpoint.',
|
|
246
|
-
'mimoFormat': 'Формат MiMo',
|
|
247
|
-
'mimoFormatHint': 'mp3 или wav.',
|
|
248
|
-
'minimaxBin': 'MiniMax CLI',
|
|
249
|
-
'minimaxBinHint': 'Ищется в PATH, если путь не абсолютный; вход в CLI — отдельно.',
|
|
250
|
-
'advancedTitle': 'Дополнительно',
|
|
251
|
-
'advancedHint': 'Лимиты и override endpoint. API-ключи — в редакторе цепочки; имена credential-слотов (*KeyEnv) — только в конфиге.',
|
|
252
|
-
'maxChars': 'Максимум символов',
|
|
253
|
-
'maxCharsHint': 'Жёсткий лимит до обработки длинных ответов; 0 — без лимита.',
|
|
254
|
-
'sentenceChars': 'Размер куска (символы)',
|
|
255
|
-
'sentenceCharsHint': 'Максимум символов в streaming-куске предложения.',
|
|
256
|
-
'timeoutMs': 'Таймаут провайдера (мс)',
|
|
257
|
-
'timeoutMsHint': 'Прервать попытку провайдера через столько миллисекунд.',
|
|
258
|
-
'maxQueue': 'Длина очереди',
|
|
259
|
-
'maxQueueHint': 'Отбрасывать самые старые куски сверх этого числа.',
|
|
260
|
-
'chainTitle': 'Цепочка провайдеров',
|
|
261
|
-
'addProvider': 'Добавить провайдера',
|
|
262
|
-
'localProvider': 'Локальный провайдер — ключ не нужен',
|
|
263
|
-
'configured': 'Ключ задан',
|
|
264
|
-
'notSet': 'Ключа нет',
|
|
265
|
-
'fromEnv': 'Задан в окружении',
|
|
266
|
-
'clear': 'Убрать',
|
|
267
|
-
'remove': 'Удалить',
|
|
268
|
-
'up': 'Выше',
|
|
269
|
-
'down': 'Ниже',
|
|
270
|
-
'save': 'Сохранить',
|
|
271
|
-
'saved': 'Сохранено',
|
|
272
|
-
'cardHint': 'Цепочка провайдеров, голоса и параметры чтения.',
|
|
273
|
-
'skipCode': 'Пропускать код',
|
|
274
|
-
'skipCodeHint': 'Вместо чтения кода вслух — короткая пометка.',
|
|
275
|
-
'cache': 'Кэш синтеза',
|
|
276
|
-
'cacheHint': 'Повторяющиеся фразы не синтезируются заново.',
|
|
277
|
-
'cacheMaxMb': 'Лимит кэша, МБ',
|
|
278
|
-
'cacheMaxMbHint': 'Сначала вытесняется самое давнее по использованию.',
|
|
279
|
-
'clearCache': 'Очистить кэш',
|
|
280
|
-
'pronTitle': 'Произношение',
|
|
281
|
-
'pronHint': 'Правила работают сверху вниз; /…/ в левой части — регулярка; Язк ограничивает правило.',
|
|
282
|
-
'pronFrom': 'Заменить',
|
|
283
|
-
'pronTo': 'На',
|
|
284
|
-
'pronWhole': 'Целое слово',
|
|
285
|
-
'pronLang': 'Язк',
|
|
286
|
-
'addRule': 'Добавить правило',
|
|
287
|
-
'rolesTitle': 'Голоса ролей',
|
|
288
|
-
'rolesHint': 'Пустые поля наследуют основную цепочку.',
|
|
289
|
-
'roleReply': 'Ответ',
|
|
290
|
-
'roleApproval': 'Подтверждение',
|
|
291
|
-
'roleError': 'Ошибка',
|
|
292
|
-
'longReply': 'Длинные ответы',
|
|
293
|
-
'longReplyHint': 'truncate: обрезать; summarize: короткий пересказ; full: читать целиком.',
|
|
294
|
-
'summaryModel': 'Модель пересказа',
|
|
295
|
-
'summaryModelHint': 'provider/model, пусто — как у разговора.',
|
|
296
|
-
'summarySentences': 'Длина пересказа',
|
|
297
|
-
'summarySentencesHint': 'Предложений в озвучиваемом пересказе.',
|
|
298
|
-
'preview': 'Проба',
|
|
299
|
-
'statsTitle': 'Статистика',
|
|
300
|
-
'statTotal': 'Всего',
|
|
301
|
-
'statHits': 'Кэш-хиты',
|
|
302
|
-
'statErrors': 'Ошибки',
|
|
303
|
-
'statReset': 'Сбросить',
|
|
304
|
-
|
|
305
|
-
'autoDetect': 'Автоязык по куску',
|
|
306
|
-
'autoDetectHint': 'Подсказывать провайдеру ru/en по каждому куску вместо общей настройки.',
|
|
307
|
-
'narrateQuotesOnly': 'Только цитаты',
|
|
308
|
-
'narrateQuotesHint': 'Читать только фрагменты в кавычках.',
|
|
309
|
-
'skipActions': 'Пропускать *действия*',
|
|
310
|
-
'skipActionsHint': 'Вырезать блоки в звёздочках.',
|
|
311
|
-
'removeRegex': 'Регулярка вырезания',
|
|
312
|
-
'removeRegexHint': 'Глобальная регулярка: совпадения удаляются перед синтезом.',
|
|
313
|
-
'customBaseUrl': 'Свой TTS endpoint',
|
|
314
|
-
'customBaseUrlHint': 'OpenAI-compatible base URL, e.g. http://localhost:8880/v1; empty disables it.',
|
|
315
|
-
'customKeyEnv': 'Имя ключа для endpoint',
|
|
316
|
-
'customKeyEnvHint': 'Имя credential для Bearer-ключа своего endpointа.',
|
|
317
186
|
|
|
318
|
-
|
|
319
|
-
'
|
|
320
|
-
'
|
|
321
|
-
'
|
|
322
|
-
'
|
|
323
|
-
'
|
|
324
|
-
'
|
|
325
|
-
'
|
|
326
|
-
'
|
|
327
|
-
'
|
|
328
|
-
'
|
|
329
|
-
'
|
|
330
|
-
'
|
|
331
|
-
'
|
|
332
|
-
'
|
|
333
|
-
'
|
|
334
|
-
|
|
335
|
-
'
|
|
336
|
-
'
|
|
337
|
-
'
|
|
338
|
-
'
|
|
339
|
-
'
|
|
340
|
-
'
|
|
341
|
-
'
|
|
342
|
-
'
|
|
343
|
-
'
|
|
344
|
-
'
|
|
345
|
-
'
|
|
346
|
-
'
|
|
347
|
-
'
|
|
348
|
-
|
|
349
|
-
'
|
|
350
|
-
'
|
|
351
|
-
'
|
|
352
|
-
'
|
|
353
|
-
|
|
354
|
-
'
|
|
355
|
-
'
|
|
356
|
-
'
|
|
357
|
-
'
|
|
358
|
-
'
|
|
359
|
-
'
|
|
187
|
+
const zh = {
|
|
188
|
+
'dockLabel': '语音朗读',
|
|
189
|
+
'dockPause': '暂停朗读',
|
|
190
|
+
'dockResume': '继续朗读',
|
|
191
|
+
'dockStop': '停止朗读',
|
|
192
|
+
'dockCurrent': '当前朗读',
|
|
193
|
+
'dockNone': '等待语音',
|
|
194
|
+
'dockQueue': '队列',
|
|
195
|
+
'dockClearQueue': '清空队列',
|
|
196
|
+
'dockReplay': '重新朗读',
|
|
197
|
+
'dockFavorite': '收藏',
|
|
198
|
+
'dockUnfavorite': '取消收藏',
|
|
199
|
+
'dockFavorites': '收藏语音',
|
|
200
|
+
'exportAudio': '导出语音片段',
|
|
201
|
+
'exportSuccess': '音频已导出',
|
|
202
|
+
'statusOnline': '服务在线',
|
|
203
|
+
'recent': '最近朗读',
|
|
204
|
+
'recentEmpty': '暂无朗读记录',
|
|
205
|
+
'retry': '重试',
|
|
206
|
+
'advancedCollapsed': '高级设置',
|
|
207
|
+
'confirmClearCache': '确定清除语音合成缓存吗?此操作无法撤销。',
|
|
208
|
+
'confirmDeleteModel': '确定删除该引擎已下载的模型权重吗?',
|
|
209
|
+
'autoplayBlocked': '声音已受系统限制,请点击任意位置以激活音频',
|
|
210
|
+
'enableSound': '开启声音',
|
|
211
|
+
'runtimeNotBundled': '未捆绑神经推理运行时',
|
|
212
|
+
'runtimeNotBundledHint': '仅下载权重无法直接合成。建议使用 Edge、Piper 或 eSpeak 进行离线语音合成。',
|
|
213
|
+
'testProvider': '测试服务商',
|
|
214
|
+
'breakerOpen': '熔断中',
|
|
215
|
+
'lastError': '最近错误',
|
|
216
|
+
'activeProvider': '当前服务商',
|
|
217
|
+
'queueLen': '队列长度',
|
|
218
|
+
'sseState': 'SSE状态',
|
|
219
|
+
'currentVoice': '发音人',
|
|
220
|
+
'statusOffline': '服务不可达',
|
|
221
|
+
'cacheStats': '缓存统计',
|
|
222
|
+
'general': '常规设置',
|
|
223
|
+
'loading': '加载中…',
|
|
224
|
+
'chainHintCard': '选择服务商,粘贴 API Key 并保存。密钥将直接写入服务端安全凭证库,前端不会回显。',
|
|
225
|
+
'speakRepliesHint': '启用后,智能体回复将在服务端合成语音并在当前浏览器中播放。音频不会回传至模型。',
|
|
226
|
+
'chainHint': '从上到下为降级候选顺序。云端密钥直接保存在服务端凭证中心,绝不存入插件公开设置中。',
|
|
227
|
+
'title': '语音朗读 (TTS)',
|
|
228
|
+
'speakReplies': '朗读智能体回复',
|
|
229
|
+
'speakAsItGoes': '流式逐句朗读',
|
|
230
|
+
'speakAsItGoesHint': '模型输出时按句子即时流式朗读,无需等待整段内容生成完毕。',
|
|
231
|
+
'rate': '播放语速',
|
|
232
|
+
'rateHint': '0.5 到 2.0 倍速。服务端按标准速度合成,由浏览器调整播放速率。',
|
|
233
|
+
'bargeIn': '打断静音 (Barge-in)',
|
|
234
|
+
'bargeInHint': '当麦克风开启或检测到说话时立即中断朗读。',
|
|
235
|
+
'announce': '重要事件播报',
|
|
236
|
+
'announceHint': '当智能体等待用户审批或提出问题时,播放提示音并语音播报。',
|
|
237
|
+
'approvalText': '审批提示语',
|
|
238
|
+
'approvalHint': '请求审批时播报的短语,会自动附带工具名称。',
|
|
239
|
+
'questionText': '提问提示语',
|
|
240
|
+
'questionHint': '智能体提问时播报的前导短语。留空则直接读出问题。',
|
|
241
|
+
'chime': '提示音效',
|
|
242
|
+
'chimeHint': '可选 ding、beep 或无。',
|
|
243
|
+
'language': '语言选择',
|
|
244
|
+
'languageHint': '针对需要指定语言的服务商(如 eSpeak 或 Azure SSML)。',
|
|
245
|
+
'piperModel': 'Piper 模型路径',
|
|
246
|
+
'piperModelHint': 'ONNX 模型的绝对路径。留空则跳过 Piper。',
|
|
247
|
+
'piperBin': 'Piper 可执行文件',
|
|
248
|
+
'binHint': '默认从 PATH 查找,除非指定绝对路径。',
|
|
249
|
+
'espeakBin': 'eSpeak 可执行文件',
|
|
250
|
+
'espeakHint': '通常为 espeak-ng。',
|
|
251
|
+
'edgeBin': 'edge-tts 可执行文件',
|
|
252
|
+
'edgeHint': 'Python edge-tts 命令行工具。',
|
|
253
|
+
'azureRegion': 'Azure 区域',
|
|
254
|
+
'azureHint': 'Azure 语音服务所在区域,例如 eastus。',
|
|
255
|
+
'openaiBaseUrl': 'OpenAI API 地址',
|
|
256
|
+
'openaiBaseUrlHint': '兼容 OpenAI 的 TTS 接口地址。',
|
|
257
|
+
'mimoBaseUrl': 'MiMo API 地址',
|
|
258
|
+
'mimoBaseUrlHint': 'MiMo 接口根地址,通过其对话接口提取语音。',
|
|
259
|
+
'mimoFormat': 'MiMo 音频格式',
|
|
260
|
+
'mimoFormatHint': 'mp3 或 wav。',
|
|
261
|
+
'minimaxBin': 'MiniMax CLI 路径',
|
|
262
|
+
'minimaxBinHint': '默认从 PATH 查找,需提前安装并登录。',
|
|
263
|
+
'advancedTitle': '高级参数',
|
|
264
|
+
'advancedHint': '字符上限与接口重定向。API 密钥在候选链中维护,凭证环境变量仅在后台生效。',
|
|
265
|
+
'maxChars': '最大字符上限',
|
|
266
|
+
'maxCharsHint': '触发长文本截断或摘要的硬性上限;0 表示无限制。',
|
|
267
|
+
'sentenceChars': '单句切片大小',
|
|
268
|
+
'sentenceCharsHint': '流式语音合成中单句的最大字符长度。',
|
|
269
|
+
'timeoutMs': '服务商超时 (毫秒)',
|
|
270
|
+
'timeoutMsHint': '单次语音合成请求超时时间,超时后自动切换下一服务商。',
|
|
271
|
+
'maxQueue': '最大队列长度',
|
|
272
|
+
'maxQueueHint': '超出该长度时丢弃最旧的未播放语音片段。',
|
|
273
|
+
'chainTitle': '服务商降级链 (Fallback Chain)',
|
|
274
|
+
'addProvider': '添加服务商',
|
|
275
|
+
'localProvider': '本地 / CLI 服务商(无需 API 密钥)',
|
|
276
|
+
'configured': '已配置',
|
|
277
|
+
'notSet': '未设置',
|
|
278
|
+
'fromEnv': '来自环境变量',
|
|
279
|
+
'clear': '清空',
|
|
280
|
+
'remove': '移除',
|
|
281
|
+
'up': '上移',
|
|
282
|
+
'down': '下移',
|
|
283
|
+
'save': '保存设置',
|
|
284
|
+
'saved': '已保存',
|
|
285
|
+
'cardHint': '配置服务商降级顺序、发音人与朗读参数。',
|
|
286
|
+
'skipCode': '跳过代码块',
|
|
287
|
+
'skipCodeHint': '遇到代码块时朗读简短提示,而不逐行朗读代码字符。',
|
|
288
|
+
'cache': '合成音频缓存',
|
|
289
|
+
'cacheHint': '重复短语直接从本地缓存读取,无需重复合成。',
|
|
290
|
+
'cacheMaxMb': '缓存上限 (MB)',
|
|
291
|
+
'cacheMaxMbHint': '超出容量时优先淘汰最近最少使用 (LRU) 的音频。',
|
|
292
|
+
'clearCache': '清空缓存',
|
|
293
|
+
'pronTitle': '发音纠正字典',
|
|
294
|
+
'pronHint': '规则自上而下匹配。左侧支持 /…/ 正则表达式;支持按语言过滤。',
|
|
295
|
+
'pronFrom': '原词',
|
|
296
|
+
'pronTo': '替换为',
|
|
297
|
+
'pronWhole': '全词匹配',
|
|
298
|
+
'pronLang': '语言',
|
|
299
|
+
'addRule': '添加规则',
|
|
300
|
+
'rolesTitle': '角色专属音色',
|
|
301
|
+
'rolesHint': '留空项将自动继承主服务商配置。',
|
|
302
|
+
'roleReply': '普通回复',
|
|
303
|
+
'roleApproval': '操作审批',
|
|
304
|
+
'roleError': '系统错误',
|
|
305
|
+
'ssmlStyle': '情感语调 (SSML)',
|
|
306
|
+
'ssmlStyleHint': '情绪语调风格(如 cheerful、serious、empathetic 等)。',
|
|
307
|
+
'longReply': '长回复处理',
|
|
308
|
+
'longReplyHint': 'truncate: 截断超长部分; summarize: 智能生成简述; full: 完整朗读全文。',
|
|
309
|
+
'summaryModel': '摘要生成模型',
|
|
310
|
+
'summaryModelHint': 'provider/model 格式,留空则跟随当前对话模型。',
|
|
311
|
+
'summarySentences': '摘要句数',
|
|
312
|
+
'summarySentencesHint': '语音朗读所采用的摘要句子数量。',
|
|
313
|
+
'preview': '试听',
|
|
314
|
+
'statsTitle': '实时统计',
|
|
315
|
+
'statTotal': '总请求数',
|
|
316
|
+
'statHits': '缓存命中',
|
|
317
|
+
'statErrors': '合成失败',
|
|
318
|
+
'statReset': '重置统计',
|
|
319
|
+
'autoDetect': '按句自动识别语种',
|
|
320
|
+
'autoDetectHint': '每句根据字符特征自动推测语言,而非强制使用全局设定。',
|
|
321
|
+
'narrateQuotesOnly': '仅朗读引用内容',
|
|
322
|
+
'narrateQuotesHint': '仅朗读正文中用引号括起来的引用短语。',
|
|
323
|
+
'skipActions': '跳过 *星号动作*',
|
|
324
|
+
'skipActionsHint': '自动剔除 *动作说明* 文本。',
|
|
325
|
+
'removeRegex': '自定义正则过滤',
|
|
326
|
+
'removeRegexHint': '在合成前由全局正则表达式匹配并剔除匹配文本。',
|
|
327
|
+
'customBaseUrl': '自定义 TTS API 地址',
|
|
328
|
+
'customBaseUrlHint': '兼容 OpenAI 的接口地址,如 http://localhost:8880/v1;留空禁用。',
|
|
329
|
+
'customKeyEnv': '自定义接口凭据名',
|
|
330
|
+
'customKeyEnvHint': '用于存储自定义接口 Bearer Token 的凭证槽位名称。',
|
|
331
|
+
'localEnginesTitle': '本地离线引擎 (Offline)',
|
|
332
|
+
'localEnginesHint': '在本地离线运行语音合成,无需 API 密钥与互联网。',
|
|
333
|
+
'enableLocalEngines': '启用本地引擎',
|
|
334
|
+
'enableLocalEnginesHint': '检测并调用本地 Kokoro 或 F5-TTS 模型。',
|
|
335
|
+
'streamingEnabled': '毫秒级低延迟流式 (< 300 ms)',
|
|
336
|
+
'streamingEnabledHint': '利用 AudioWorklet 和 SSE 实时输出流式音频。',
|
|
337
|
+
'useKokoro': '启用 Kokoro-82M (CPU)',
|
|
338
|
+
'useKokoroHint': '基于 CPU 的轻量级快速神经语音合成。',
|
|
339
|
+
'useF5': '启用 F5-TTS (GPU)',
|
|
340
|
+
'useF5Hint': '基于 NVIDIA GPU 的高保真零样本克隆语音合成。',
|
|
341
|
+
'installed': '已就绪',
|
|
342
|
+
'notInstalled': '未安装',
|
|
343
|
+
'downloading': '下载中',
|
|
344
|
+
'installKokoro': '安装权重 (350 MB)',
|
|
345
|
+
'installF5': '安装权重 (2.1 GB)',
|
|
346
|
+
'deleteModel': '删除模型',
|
|
347
|
+
'voiceDuplexTitle': '双工语音对话 (Voice-to-Voice)',
|
|
348
|
+
'voiceDuplexHint': '与 dsh-voice 协同提供全双工交互式对话。',
|
|
349
|
+
'voiceNotInstalledTitle': '依赖插件 @goodandready/dsh-voice 尚未安装',
|
|
350
|
+
'voiceInstallHint': '如需启用语音对话,请通过以下命令安装该插件:',
|
|
351
|
+
'voiceDuplex': '语音双工模式',
|
|
352
|
+
'voiceDuplexHintToggle': '用户语音输入结束后,自动使用语音朗读回复。',
|
|
353
|
+
'vadBargeIn': 'VAD 语音打断',
|
|
354
|
+
'vadBargeInHint': '检测到人类语音时立即终止智能体当前语音输出。',
|
|
355
|
+
'newSubagentRole': '子智能体标识 (例如 coder, reviewer)',
|
|
356
|
+
'addSubagentRole': '添加子智能体',
|
|
357
|
+
'autoDetectSubagent': '自动感应会话中的子智能体角色',
|
|
358
|
+
'role_coder': '编码助手 (Coder)',
|
|
359
|
+
'role_reviewer': '代码评审 (Reviewer)',
|
|
360
|
+
'role_planner': '架构规划 (Planner)',
|
|
361
|
+
'enableItDictionary': '内置 IT 术语发音校正',
|
|
362
|
+
'enableItDictionaryHint': '自动纠正 SQL、Nginx、K8s、Docker、API、JSON、YAML 等专有名词发音。',
|
|
363
|
+
'loadItDictionary': '填入常用 IT 术语',
|
|
364
|
+
'previewRule': '试听发音',
|
|
365
|
+
'messengerIntegrationTitle': '即时通讯联动 (Telegram, Discord)',
|
|
366
|
+
'messengerIntegrationHint': '通过 @goodandready/dsh-messenger-gateway 向聊天群组发送语音回复。',
|
|
367
|
+
'messengerNotInstalledTitle': '依赖插件 @goodandready/dsh-messenger-gateway 尚未安装',
|
|
368
|
+
'messengerInstallHint': '如需向聊天软件发送语音回复,请运行:',
|
|
369
|
+
'messengerTtsEnabled': '群组语音回复',
|
|
370
|
+
'messengerTtsEnabledHint': '允许网关将回复以语音便签 (Voice Note) 形式发送。',
|
|
360
371
|
}
|
|
361
|
-
|