diff --git a/packages/client/src/components/hermes/chat/MessageItem.vue b/packages/client/src/components/hermes/chat/MessageItem.vue
index 157ea80..c0582e9 100644
--- a/packages/client/src/components/hermes/chat/MessageItem.vue
+++ b/packages/client/src/components/hermes/chat/MessageItem.vue
@@ -371,22 +371,22 @@ const canPlaySpeech = computed(() => {
// 只有 assistant 消息可以播放
if (props.message.role !== 'assistant') return false
if (!copyableContent.value) return false
- // OpenAI / Custom / Edge 不依赖浏览器 Web Speech API
- if (voiceSettings.provider.value === 'openai' || voiceSettings.provider.value === 'custom' || voiceSettings.provider.value === 'edge') return true
+ // OpenAI / Custom / Edge / MiMo 不依赖浏览器 Web Speech API
+ if (voiceSettings.provider.value === 'openai' || voiceSettings.provider.value === 'custom' || voiceSettings.provider.value === 'edge' || voiceSettings.provider.value === 'mimo') return true
return speech.isSupported
})
const isPlayingThisMessage = computed(() => {
- // OpenAI / Custom / Edge 模式
- if (voiceSettings.provider.value === 'openai' || voiceSettings.provider.value === 'custom' || voiceSettings.provider.value === 'edge') {
+ // OpenAI / Custom / Edge / MiMo 模式
+ if (voiceSettings.provider.value === 'openai' || voiceSettings.provider.value === 'custom' || voiceSettings.provider.value === 'edge' || voiceSettings.provider.value === 'mimo') {
return speech.currentCustomMessageId.value === props.message.id && speech.isCustomPlaying.value
}
return speech.currentMessageId.value === props.message.id && speech.isPlaying.value
})
const isPausedThisMessage = computed(() => {
- // OpenAI / Custom / Edge 模式
- if (voiceSettings.provider.value === 'openai' || voiceSettings.provider.value === 'custom' || voiceSettings.provider.value === 'edge') {
+ // OpenAI / Custom / Edge / MiMo 模式
+ if (voiceSettings.provider.value === 'openai' || voiceSettings.provider.value === 'custom' || voiceSettings.provider.value === 'edge' || voiceSettings.provider.value === 'mimo') {
return speech.currentCustomMessageId.value === props.message.id && speech.isCustomPaused.value
}
return speech.currentMessageId.value === props.message.id && speech.isPaused.value
@@ -441,6 +441,24 @@ function handleSpeechToggle() {
return
}
+ // MiMo TTS 模式
+ if (voiceSettings.provider.value === 'mimo') {
+ const apiKey = voiceSettings.mimoApiKey.value
+ if (!apiKey) {
+ console.warn('[MessageItem] MiMo TTS API Key 为空')
+ return
+ }
+ speech.mimoToggle(props.message.id, content, {
+ baseUrl: voiceSettings.mimoBaseUrl.value,
+ apiKey,
+ model: voiceSettings.mimoModel.value,
+ voice: voiceSettings.mimoVoice.value,
+ voiceDesignDesc: voiceSettings.mimoVoiceDesignDesc.value || undefined,
+ stylePrompt: voiceSettings.mimoStylePrompt.value || undefined,
+ })
+ return
+ }
+
// Web Speech API 模式
if (voiceSettings.provider.value === 'webspeech') {
const text = speech.extractReadableText(content)
@@ -486,6 +504,18 @@ onMounted(() => {
rate: speedToEdgeRate(voiceSettings.edgeRate.value),
pitch: hzToEdgePitch(voiceSettings.edgePitchHz.value),
})
+ } else if (voiceSettings.provider.value === 'mimo') {
+ const apiKey = voiceSettings.mimoApiKey.value
+ if (apiKey) {
+ speech.mimoPlay(props.message.id, content, {
+ baseUrl: voiceSettings.mimoBaseUrl.value,
+ apiKey,
+ model: voiceSettings.mimoModel.value,
+ voice: voiceSettings.mimoVoice.value,
+ voiceDesignDesc: voiceSettings.mimoVoiceDesignDesc.value || undefined,
+ stylePrompt: voiceSettings.mimoStylePrompt.value || undefined,
+ })
+ }
} else if (voiceSettings.provider.value === 'webspeech') {
const text = speech.extractReadableText(content)
if (text) {
diff --git a/packages/client/src/components/hermes/settings/VoiceSettings.vue b/packages/client/src/components/hermes/settings/VoiceSettings.vue
index 215bde3..36c2a74 100644
--- a/packages/client/src/components/hermes/settings/VoiceSettings.vue
+++ b/packages/client/src/components/hermes/settings/VoiceSettings.vue
@@ -19,6 +19,7 @@ const providerOptions = [
{ label: t('settings.voice.providerOpenai'), value: 'openai' },
{ label: t('settings.voice.providerCustom'), value: 'custom' },
{ label: t('settings.voice.providerEdge'), value: 'edge' },
+ { label: t('settings.voice.providerMimo'), value: 'mimo' },
]
const openaiModelOptions = [
@@ -76,6 +77,28 @@ onMounted(() => {
}
})
+// ── MiMo TTS options ──
+const mimoBaseUrlOptions = [
+ { label: 'https://api.xiaomimimo.com/v1', value: 'https://api.xiaomimimo.com/v1' },
+ { label: 'https://token-plan-cn.xiaomimimo.com/v1', value: 'https://token-plan-cn.xiaomimimo.com/v1' },
+]
+
+const mimoModelOptions = [
+ { label: t('settings.voice.mimoModelPreset'), value: 'mimo-v2.5-tts' },
+ { label: t('settings.voice.mimoModelVoiceDesign'), value: 'mimo-v2.5-tts-voicedesign' },
+]
+
+const mimoVoiceOptions = [
+ { label: '冰糖 (中文·女)', value: '冰糖' },
+ { label: '茉莉 (中文·女)', value: '茉莉' },
+ { label: '苏打 (中文·男)', value: '苏打' },
+ { label: '白桦 (中文·男)', value: '白桦' },
+ { label: 'Mia (English·Female)', value: 'Mia' },
+ { label: 'Chloe (English·Female)', value: 'Chloe' },
+ { label: 'Milo (English·Male)', value: 'Milo' },
+ { label: 'Dean (English·Male)', value: 'Dean' },
+]
+
async function handleTest() {
const text = testText.value.trim()
if (!text) return
@@ -113,6 +136,19 @@ async function handleTest() {
rate: speedToEdgeRate(vs.edgeRate.value),
pitch: hzToEdgePitch(vs.edgePitchHz.value),
})
+ } else if (vs.provider.value === 'mimo') {
+ if (!vs.mimoApiKey.value) {
+ console.warn('[VoiceSettings] MiMo API Key empty')
+ return
+ }
+ await speech.mimoPlay('__test__', text, {
+ baseUrl: vs.mimoBaseUrl.value,
+ apiKey: vs.mimoApiKey.value,
+ model: vs.mimoModel.value,
+ voice: vs.mimoVoice.value,
+ voiceDesignDesc: vs.mimoVoiceDesignDesc.value || undefined,
+ stylePrompt: vs.mimoStylePrompt.value || undefined,
+ })
}
} catch (err) {
console.error('[VoiceSettings] Test failed:', err)
@@ -312,6 +348,104 @@ async function handleTest() {
+
+
+
+ {{ t('settings.voice.mimoHint') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ t('settings.voice.testTitle') }}
diff --git a/packages/client/src/composables/useSpeech.ts b/packages/client/src/composables/useSpeech.ts
index 6bfbb56..7f20826 100644
--- a/packages/client/src/composables/useSpeech.ts
+++ b/packages/client/src/composables/useSpeech.ts
@@ -15,6 +15,15 @@ export interface OpenaiTtsOptions {
pitch?: string // Edge TTS pitch format, e.g. "-8Hz"
}
+export interface MimoTtsOptions {
+ baseUrl: string
+ apiKey: string
+ model: string
+ voice: string // preset voice ID (preset mode) or data URI (clone mode)
+ voiceDesignDesc?: string // voice design description text (voice design mode)
+ stylePrompt?: string // natural language style instruction
+}
+
export interface SpeechState {
isPlaying: boolean
isPaused: boolean
@@ -333,20 +342,17 @@ export function useSpeech() {
function openaiToggle(messageId: string, content: string, opts: OpenaiTtsOptions) {
if (currentCustomMessageId.value === messageId && isCustomPlaying.value) {
if (isCustomPaused.value) {
- // Resume
if (customAudio) {
customAudio.play()
}
isCustomPaused.value = false
} else {
- // Pause
if (customAudio) {
customAudio.pause()
}
isCustomPaused.value = true
}
} else {
- // Stop other speech and start new
stop(false)
if (customAudio) {
customAudio.pause()
@@ -356,6 +362,148 @@ export function useSpeech() {
}
}
+ // ─── MiMo TTS Engine ──────────────────────────────────────────
+
+ async function mimoPlay(
+ messageId: string,
+ content: string,
+ opts: MimoTtsOptions,
+ ) {
+ const text = extractReadableText(content)
+ if (!text) return
+
+ const token = ++playbackToken
+
+ isCustomPlaying.value = true
+ isCustomPaused.value = false
+ currentCustomMessageId.value = messageId
+
+ // Build messages based on model type
+ const messages: Array<{ role: string; content: string }> = []
+
+ if (opts.model === 'mimo-v2.5-tts-voicedesign') {
+ // Voice design: user message = voice description (+ appended style prompt)
+ const desc = opts.voiceDesignDesc || ''
+ const userContent = opts.stylePrompt
+ ? `${desc}\n风格指令:${opts.stylePrompt}`
+ : desc
+ messages.push({ role: 'user', content: userContent || '默认音色' })
+ } else {
+ // Preset voices: user message = style prompt or empty
+ messages.push({ role: 'user', content: opts.stylePrompt || '' })
+ }
+
+ // assistant message = synthesis text
+ messages.push({ role: 'assistant', content: text })
+
+ const audio: Record = { format: 'wav' }
+ // Voice design model does not accept audio.voice
+ if (opts.model !== 'mimo-v2.5-tts-voicedesign') {
+ audio.voice = opts.voice
+ }
+
+ const body: Record = {
+ model: opts.model,
+ messages,
+ audio,
+ }
+
+ const url = `${opts.baseUrl.replace(/\/+$/, '')}/chat/completions`
+
+ const headers: Record = {
+ 'Content-Type': 'application/json',
+ 'api-key': opts.apiKey,
+ }
+
+ try {
+ const res = await fetch(url, {
+ method: 'POST',
+ headers,
+ body: JSON.stringify(body),
+ })
+
+ if (token !== playbackToken) return
+
+ if (!res.ok) {
+ const errText = await res.text().catch(() => '')
+ throw new Error(`MiMo TTS 返回 ${res.status}: ${errText || res.statusText}`)
+ }
+
+ const json = await res.json()
+ if (token !== playbackToken) return
+
+ const audioBase64 = json?.choices?.[0]?.message?.audio?.data
+ if (!audioBase64) {
+ throw new Error('MiMo TTS 响应中未找到音频数据')
+ }
+
+ // base64 → binary → Blob
+ const binaryStr = atob(audioBase64)
+ const bytes = new Uint8Array(binaryStr.length)
+ for (let i = 0; i < binaryStr.length; i++) {
+ bytes[i] = binaryStr.charCodeAt(i)
+ }
+ const audioBlob = new Blob([bytes], { type: 'audio/wav' })
+
+ if (token !== playbackToken) return
+
+ const audioUrl = URL.createObjectURL(audioBlob)
+ const audio = new Audio(audioUrl)
+ customAudio = audio
+
+ audio.onended = () => {
+ if (token !== playbackToken) return
+ URL.revokeObjectURL(audioUrl)
+ isCustomPlaying.value = false
+ isCustomPaused.value = false
+ currentCustomMessageId.value = null
+ customAudio = null
+ }
+
+ audio.onerror = () => {
+ if (token !== playbackToken) return
+ URL.revokeObjectURL(audioUrl)
+ console.warn('[useSpeech] MiMo TTS audio playback error')
+ isCustomPlaying.value = false
+ isCustomPaused.value = false
+ currentCustomMessageId.value = null
+ customAudio = null
+ }
+
+ await audio.play()
+ } catch (err) {
+ if (token !== playbackToken) return
+ console.error('[useSpeech] MiMo TTS 请求失败:', err)
+ isCustomPlaying.value = false
+ isCustomPaused.value = false
+ currentCustomMessageId.value = null
+ throw err
+ }
+ }
+
+ function mimoToggle(messageId: string, content: string, opts: MimoTtsOptions) {
+ if (currentCustomMessageId.value === messageId && isCustomPlaying.value) {
+ if (isCustomPaused.value) {
+ if (customAudio) {
+ customAudio.play()
+ }
+ isCustomPaused.value = false
+ } else {
+ if (customAudio) {
+ customAudio.pause()
+ }
+ isCustomPaused.value = true
+ }
+ } else {
+ stop(false)
+ if (customAudio) {
+ customAudio.pause()
+ customAudio = null
+ }
+ mimoPlay(messageId, content, opts)
+ }
+ }
+
// ─── Unified speak ──────────────────────────────────────────
function speak(messageId: string, text: string, options: SpeechOptions = {}) {
@@ -473,6 +621,10 @@ export function useSpeech() {
openaiPlay,
openaiToggle,
+ // MiMo TTS
+ mimoPlay,
+ mimoToggle,
+
// Browser WebSpeech (直接调用避免 Rolldown 树摇)
speakViaBrowser,
}
diff --git a/packages/client/src/composables/useVoiceSettings.ts b/packages/client/src/composables/useVoiceSettings.ts
index 7c77f64..30aed0f 100644
--- a/packages/client/src/composables/useVoiceSettings.ts
+++ b/packages/client/src/composables/useVoiceSettings.ts
@@ -1,6 +1,6 @@
import { ref, watch } from 'vue'
-export type TtsProvider = 'webspeech' | 'openai' | 'custom' | 'edge'
+export type TtsProvider = 'webspeech' | 'openai' | 'custom' | 'edge' | 'mimo'
export interface VoiceSettingsData {
provider: TtsProvider
@@ -23,6 +23,14 @@ export interface VoiceSettingsData {
edgeVoice: string
edgeRate: number // 语速倍率 0.5~2.0,1.0 = 正常
edgePitchHz: number // 音调偏移 Hz,-20~20,0 = 正常
+
+ // MiMo TTS
+ mimoApiKey: string
+ mimoBaseUrl: string
+ mimoModel: string // 'mimo-v2.5-tts' | 'mimo-v2.5-tts-voicedesign'
+ mimoVoice: string // 预置音色 ID
+ mimoVoiceDesignDesc: string // 音色设计描述文本
+ mimoStylePrompt: string // 风格指令
}
const STORAGE_KEY = 'hermes-tts-settings-v2'
@@ -67,6 +75,13 @@ const DEFAULT: VoiceSettingsData = {
edgeVoice: 'zh-CN-XiaoxiaoNeural',
edgeRate: 1.0,
edgePitchHz: 0,
+
+ mimoApiKey: '',
+ mimoBaseUrl: 'https://api.xiaomimimo.com/v1',
+ mimoModel: 'mimo-v2.5-tts',
+ mimoVoice: '冰糖',
+ mimoVoiceDesignDesc: '',
+ mimoStylePrompt: '',
}
function sanitize(data: VoiceSettingsData): VoiceSettingsData {
@@ -110,10 +125,19 @@ const edgeVoice = ref(load().edgeVoice)
const edgeRate = ref(load().edgeRate)
const edgePitchHz = ref(load().edgePitchHz)
+// MiMo TTS
+const mimoApiKey = ref(load().mimoApiKey)
+const mimoBaseUrl = ref(load().mimoBaseUrl)
+const mimoModel = ref(load().mimoModel)
+const mimoVoice = ref(load().mimoVoice)
+const mimoVoiceDesignDesc = ref(load().mimoVoiceDesignDesc)
+const mimoStylePrompt = ref(load().mimoStylePrompt)
+
// Auto-persist on change
watch(
[provider, webspeechVoice, openaiApiKey, openaiBaseUrl, openaiModel, openaiVoice,
- customUrl, customApiKey, edgeUrl, edgeVoice, edgeRate, edgePitchHz],
+ customUrl, customApiKey, edgeUrl, edgeVoice, edgeRate, edgePitchHz,
+ mimoApiKey, mimoBaseUrl, mimoModel, mimoVoice, mimoVoiceDesignDesc, mimoStylePrompt],
() => {
localStorage.setItem(STORAGE_KEY, JSON.stringify({
provider: provider.value,
@@ -128,6 +152,12 @@ watch(
edgeVoice: edgeVoice.value,
edgeRate: edgeRate.value,
edgePitchHz: edgePitchHz.value,
+ mimoApiKey: mimoApiKey.value,
+ mimoBaseUrl: mimoBaseUrl.value,
+ mimoModel: mimoModel.value,
+ mimoVoice: mimoVoice.value,
+ mimoVoiceDesignDesc: mimoVoiceDesignDesc.value,
+ mimoStylePrompt: mimoStylePrompt.value,
}))
},
)
@@ -146,6 +176,12 @@ export function useVoiceSettings() {
edgeVoice,
edgeRate,
edgePitchHz,
+ mimoApiKey,
+ mimoBaseUrl,
+ mimoModel,
+ mimoVoice,
+ mimoVoiceDesignDesc,
+ mimoStylePrompt,
setProvider(v: TtsProvider) { provider.value = v },
setWebSpeechVoice(v: string) { webspeechVoice.value = v },
@@ -159,6 +195,12 @@ export function useVoiceSettings() {
setEdgeVoice(v: string) { edgeVoice.value = v },
setEdgeRate(v: number) { edgeRate.value = v },
setEdgePitchHz(v: number) { edgePitchHz.value = v },
+ setMimoApiKey(v: string) { mimoApiKey.value = v },
+ setMimoBaseUrl(v: string) { mimoBaseUrl.value = v },
+ setMimoModel(v: string) { mimoModel.value = v },
+ setMimoVoice(v: string) { mimoVoice.value = v },
+ setMimoVoiceDesignDesc(v: string) { mimoVoiceDesignDesc.value = v },
+ setMimoStylePrompt(v: string) { mimoStylePrompt.value = v },
reset() {
provider.value = DEFAULT.provider
@@ -173,6 +215,12 @@ export function useVoiceSettings() {
edgeVoice.value = DEFAULT.edgeVoice
edgeRate.value = DEFAULT.edgeRate
edgePitchHz.value = DEFAULT.edgePitchHz
+ mimoApiKey.value = DEFAULT.mimoApiKey
+ mimoBaseUrl.value = DEFAULT.mimoBaseUrl
+ mimoModel.value = DEFAULT.mimoModel
+ mimoVoice.value = DEFAULT.mimoVoice
+ mimoVoiceDesignDesc.value = DEFAULT.mimoVoiceDesignDesc
+ mimoStylePrompt.value = DEFAULT.mimoStylePrompt
},
}
}
diff --git a/packages/client/src/i18n/locales/de.ts b/packages/client/src/i18n/locales/de.ts
index fdd490d..0a486a9 100644
--- a/packages/client/src/i18n/locales/de.ts
+++ b/packages/client/src/i18n/locales/de.ts
@@ -670,6 +670,32 @@ jobTriggered: 'Job ausgelost',
testButton: 'Testen',
testButtonPlaying: 'Wiedergabe...',
testFailed: 'Test fehlgeschlagen: {error}',
+
+ // MiMo TTS
+ providerMimo: 'MiMo TTS',
+ mimoHint: 'Xiaomi MiMo TTS — unterstützt Voreingestellte Stimmen, Stimmdesign und Stimmklonung',
+ mimoApiKey: 'API-Schluessel',
+ mimoApiKeyHint: 'Holen Sie sich Ihren Schluessel auf platform.xiaomimimo.com',
+ mimoApiKeyPlaceholder: 'MiMo API-Schluessel',
+ mimoBaseUrl: 'Basis-URL',
+ mimoBaseUrlHint: 'MiMo API-Endpunkt-URL',
+ mimoModel: 'Modell',
+ mimoModelHint: 'Sprachsynthesemodell auswählen',
+ mimoModelPreset: 'Voreingestellte Stimmen',
+ mimoModelVoiceDesign: 'Stimmdesign',
+ mimoModelVoiceClone: 'Stimmklonung',
+ mimoVoice: 'Stimme',
+ mimoVoiceHint: 'Voreingestellte Stimme auswählen',
+ mimoVoiceDesignPrompt: 'Stimmbeschreibung',
+ mimoVoiceDesignPromptHint: 'Beschreiben Sie die gewünschten Stimmmerkmale',
+ mimoVoiceDesignPromptPlaceholder: 'Z.B.: Eine warme junge Frauenstimme, etwas langsam, mit magnetischem Ton',
+ mimoCloneAudio: 'Audio hochladen',
+ mimoCloneAudioHint: 'Audio-Beispiel für Stimmklonung hochladen (mp3/wav, max. 10 MB)',
+ mimoCloneAudioUpload: 'Datei auswählen',
+ mimoCloneAudioClear: 'Löschen',
+ mimoStylePrompt: 'Stil-Eingabe',
+ mimoStylePromptHint: 'Optional — beschreiben Sie den Sprechstil in natürlicher Sprache',
+ mimoStylePromptPlaceholder: 'Z.B.: Heller, lebhafter Ton, schnelles Tempo',
},
lockedIps: {
title: 'Gesperrte IPs',
diff --git a/packages/client/src/i18n/locales/en.ts b/packages/client/src/i18n/locales/en.ts
index af32025..063c096 100644
--- a/packages/client/src/i18n/locales/en.ts
+++ b/packages/client/src/i18n/locales/en.ts
@@ -847,6 +847,32 @@ export default {
testButton: 'Test',
testButtonPlaying: 'Playing...',
testFailed: 'Test failed: {error}',
+
+ // MiMo TTS
+ providerMimo: 'MiMo TTS',
+ mimoHint: 'Xiaomi MiMo TTS — supports preset voices, voice design, and voice clone modes',
+ mimoApiKey: 'API Key',
+ mimoApiKeyHint: 'Get your key at platform.xiaomimimo.com',
+ mimoApiKeyPlaceholder: 'MiMo API Key',
+ mimoBaseUrl: 'Base URL',
+ mimoBaseUrlHint: 'MiMo API endpoint URL',
+ mimoModel: 'Model',
+ mimoModelHint: 'Select speech synthesis model',
+ mimoModelPreset: 'Preset Voices',
+ mimoModelVoiceDesign: 'Voice Design',
+ mimoModelVoiceClone: 'Voice Clone',
+ mimoVoice: 'Voice',
+ mimoVoiceHint: 'Select a preset voice',
+ mimoVoiceDesignPrompt: 'Voice Description',
+ mimoVoiceDesignPromptHint: 'Describe the voice characteristics you want',
+ mimoVoiceDesignPromptPlaceholder: 'e.g., A warm young female voice, slightly slow, with a magnetic tone',
+ mimoCloneAudio: 'Upload Audio',
+ mimoCloneAudioHint: 'Upload an audio sample for voice cloning (mp3/wav, max 10MB)',
+ mimoCloneAudioUpload: 'Choose File',
+ mimoCloneAudioClear: 'Clear',
+ mimoStylePrompt: 'Style Prompt',
+ mimoStylePromptHint: 'Optional — describe the speaking style in natural language',
+ mimoStylePromptPlaceholder: 'e.g., Bright and bouncy tone, fast pace',
},
},
diff --git a/packages/client/src/i18n/locales/es.ts b/packages/client/src/i18n/locales/es.ts
index e829f4a..04d4504 100644
--- a/packages/client/src/i18n/locales/es.ts
+++ b/packages/client/src/i18n/locales/es.ts
@@ -670,6 +670,32 @@ jobTriggered: 'Job ejecutado',
testButton: 'Probar',
testButtonPlaying: 'Reproduciendo...',
testFailed: 'Prueba fallida: {error}',
+
+ // MiMo TTS
+ providerMimo: 'MiMo TTS',
+ mimoHint: 'Xiaomi MiMo TTS — voces predefinidas, diseño de voz y clonación de voz',
+ mimoApiKey: 'Clave API',
+ mimoApiKeyHint: 'Obtenga su clave en platform.xiaomimimo.com',
+ mimoApiKeyPlaceholder: 'Clave API MiMo',
+ mimoBaseUrl: 'URL base',
+ mimoBaseUrlHint: 'URL del endpoint de la API MiMo',
+ mimoModel: 'Modelo',
+ mimoModelHint: 'Seleccione el modelo de síntesis de voz',
+ mimoModelPreset: 'Voces predefinidas',
+ mimoModelVoiceDesign: 'Diseño de voz',
+ mimoModelVoiceClone: 'Clonación de voz',
+ mimoVoice: 'Voz',
+ mimoVoiceHint: 'Seleccione una voz predefinida',
+ mimoVoiceDesignPrompt: 'Descripción de voz',
+ mimoVoiceDesignPromptHint: 'Describa las características de voz deseadas',
+ mimoVoiceDesignPromptPlaceholder: 'Ej: Una voz femenina cálida y joven, algo lenta, con tono magnético',
+ mimoCloneAudio: 'Subir audio',
+ mimoCloneAudioHint: 'Suba una muestra de audio para clonación (mp3/wav, máx. 10 MB)',
+ mimoCloneAudioUpload: 'Elegir archivo',
+ mimoCloneAudioClear: 'Borrar',
+ mimoStylePrompt: 'Indicador de estilo',
+ mimoStylePromptHint: 'Opcional — describa el estilo de habla en lenguaje natural',
+ mimoStylePromptPlaceholder: 'Ej: Tono brillante y animado, ritmo rápido',
},
lockedIps: {
title: 'IPs bloqueadas',
diff --git a/packages/client/src/i18n/locales/fr.ts b/packages/client/src/i18n/locales/fr.ts
index 1c25d6b..b7d7621 100644
--- a/packages/client/src/i18n/locales/fr.ts
+++ b/packages/client/src/i18n/locales/fr.ts
@@ -670,6 +670,32 @@ jobTriggered: 'Job declenche',
testButton: 'Tester',
testButtonPlaying: 'Lecture...',
testFailed: 'Echec du test : {error}',
+
+ // MiMo TTS
+ providerMimo: 'MiMo TTS',
+ mimoHint: 'Xiaomi MiMo TTS — voices predefinies, conception vocale et clonage vocal',
+ mimoApiKey: 'Cle API',
+ mimoApiKeyHint: 'Obtenez votre cle sur platform.xiaomimimo.com',
+ mimoApiKeyPlaceholder: 'Cle API MiMo',
+ mimoBaseUrl: 'URL de base',
+ mimoBaseUrlHint: 'URL de l\'endpoint API MiMo',
+ mimoModel: 'Modele',
+ mimoModelHint: 'Selectionnez le modele de synthese vocale',
+ mimoModelPreset: 'Voix predefinies',
+ mimoModelVoiceDesign: 'Conception vocale',
+ mimoModelVoiceClone: 'Clonage vocal',
+ mimoVoice: 'Voix',
+ mimoVoiceHint: 'Selectionnez une voix predefinie',
+ mimoVoiceDesignPrompt: 'Description vocale',
+ mimoVoiceDesignPromptHint: 'Decrivez les caracteristiques vocales souhaitees',
+ mimoVoiceDesignPromptPlaceholder: 'Ex : Une voix feminine chaude et jeune, legerement lente, avec un ton magnetique',
+ mimoCloneAudio: 'Televerser un audio',
+ mimoCloneAudioHint: 'Televersez un echantillon audio pour le clonage (mp3/wav, max 10 Mo)',
+ mimoCloneAudioUpload: 'Choisir un fichier',
+ mimoCloneAudioClear: 'Effacer',
+ mimoStylePrompt: 'Invite de style',
+ mimoStylePromptHint: 'Optionnel — decrivez le style de parole en langage naturel',
+ mimoStylePromptPlaceholder: 'Ex : Ton vif et entrain, rythme rapide',
},
lockedIps: {
title: 'IPs bloquees',
diff --git a/packages/client/src/i18n/locales/ja.ts b/packages/client/src/i18n/locales/ja.ts
index f87f975..ed8b4b7 100644
--- a/packages/client/src/i18n/locales/ja.ts
+++ b/packages/client/src/i18n/locales/ja.ts
@@ -670,6 +670,32 @@ export default {
testButton: 'テスト',
testButtonPlaying: '再生中...',
testFailed: 'テスト失敗:{error}',
+
+ // MiMo TTS
+ providerMimo: 'MiMo TTS',
+ mimoHint: 'Xiaomi MiMo TTS — プリセット音声、音声デザイン、音声クローンの3つのモードをサポート',
+ mimoApiKey: 'API Key',
+ mimoApiKeyHint: 'platform.xiaomimimo.com で取得',
+ mimoApiKeyPlaceholder: 'MiMo API Key',
+ mimoBaseUrl: 'Base URL',
+ mimoBaseUrlHint: 'MiMo API エンドポイントURL',
+ mimoModel: 'モデル',
+ mimoModelHint: '音声合成モデルを選択',
+ mimoModelPreset: 'プリセット音声',
+ mimoModelVoiceDesign: '音声デザイン',
+ mimoModelVoiceClone: '音声クローン',
+ mimoVoice: '音声',
+ mimoVoiceHint: 'プリセット音声を選択',
+ mimoVoiceDesignPrompt: '音声の説明',
+ mimoVoiceDesignPromptHint: '希望する音声の特徴を説明してください',
+ mimoVoiceDesignPromptPlaceholder: '例:温かみのある若い女性の声、少しゆっくり、磁力的なトーン',
+ mimoCloneAudio: '音声アップロード',
+ mimoCloneAudioHint: '音声クローン用の音声サンプルをアップロード(mp3/wav、最大10MB)',
+ mimoCloneAudioUpload: 'ファイルを選択',
+ mimoCloneAudioClear: 'クリア',
+ mimoStylePrompt: 'スタイルプロンプト',
+ mimoStylePromptHint: 'オプション — 自然言語で話すスタイルを説明',
+ mimoStylePromptPlaceholder: '例:明るく弾むようなトーン、速めのテンポ',
},
lockedIps: {
title: 'ロック済みIP管理',
diff --git a/packages/client/src/i18n/locales/ko.ts b/packages/client/src/i18n/locales/ko.ts
index ac48888..54ad186 100644
--- a/packages/client/src/i18n/locales/ko.ts
+++ b/packages/client/src/i18n/locales/ko.ts
@@ -670,6 +670,32 @@ export default {
testButton: '테스트',
testButtonPlaying: '재생 중...',
testFailed: '테스트 실패: {error}',
+
+ // MiMo TTS
+ providerMimo: 'MiMo TTS',
+ mimoHint: '샤오미 MiMo TTS — 프리셋 음성, 음성 디자인, 음성 클론 세 가지 모드 지원',
+ mimoApiKey: 'API Key',
+ mimoApiKeyHint: 'platform.xiaomimimo.com에서 발급',
+ mimoApiKeyPlaceholder: 'MiMo API Key',
+ mimoBaseUrl: 'Base URL',
+ mimoBaseUrlHint: 'MiMo API 엔드포인트 URL',
+ mimoModel: '모델',
+ mimoModelHint: '음성 합성 모델 선택',
+ mimoModelPreset: '프리셋 음성',
+ mimoModelVoiceDesign: '음성 디자인',
+ mimoModelVoiceClone: '음성 클론',
+ mimoVoice: '음성',
+ mimoVoiceHint: '프리셋 음성 선택',
+ mimoVoiceDesignPrompt: '음성 설명',
+ mimoVoiceDesignPromptHint: '원하는 음성 특징을 설명하세요',
+ mimoVoiceDesignPromptPlaceholder: '예: 따뜻한 젊은 여성 목소리, 약간 느린 속도, 마그네틱한 톤',
+ mimoCloneAudio: '오디오 업로드',
+ mimoCloneAudioHint: '음성 클론용 오디오 샘플 업로드 (mp3/wav, 최대 10MB)',
+ mimoCloneAudioUpload: '파일 선택',
+ mimoCloneAudioClear: '지우기',
+ mimoStylePrompt: '스타일 프롬프트',
+ mimoStylePromptHint: '선택사항 — 자연어로 말하기 스타일 설명',
+ mimoStylePromptPlaceholder: '예: 밝고 경쾌한 톤, 빠른 속도',
},
lockedIps: {
title: '잠긴 IP 관리',
diff --git a/packages/client/src/i18n/locales/pt.ts b/packages/client/src/i18n/locales/pt.ts
index 515d0d8..5fced91 100644
--- a/packages/client/src/i18n/locales/pt.ts
+++ b/packages/client/src/i18n/locales/pt.ts
@@ -670,6 +670,32 @@ jobTriggered: 'Job acionado',
testButton: 'Testar',
testButtonPlaying: 'Reproduzindo...',
testFailed: 'Teste falhou: {error}',
+
+ // MiMo TTS
+ providerMimo: 'MiMo TTS',
+ mimoHint: 'Xiaomi MiMo TTS — vozes predefinidas, design de voz e clonagem de voz',
+ mimoApiKey: 'Chave API',
+ mimoApiKeyHint: 'Obtenha sua chave em platform.xiaomimimo.com',
+ mimoApiKeyPlaceholder: 'Chave API MiMo',
+ mimoBaseUrl: 'URL base',
+ mimoBaseUrlHint: 'URL do endpoint da API MiMo',
+ mimoModel: 'Modelo',
+ mimoModelHint: 'Selecione o modelo de síntese de voz',
+ mimoModelPreset: 'Vozes predefinidas',
+ mimoModelVoiceDesign: 'Design de voz',
+ mimoModelVoiceClone: 'Clonagem de voz',
+ mimoVoice: 'Voz',
+ mimoVoiceHint: 'Selecione uma voz predefinida',
+ mimoVoiceDesignPrompt: 'Descrição da voz',
+ mimoVoiceDesignPromptHint: 'Descreva as características de voz desejadas',
+ mimoVoiceDesignPromptPlaceholder: 'Ex: Uma voz feminina quente e jovem, ligeiramente lenta, com tom magnético',
+ mimoCloneAudio: 'Enviar áudio',
+ mimoCloneAudioHint: 'Envie uma amostra de áudio para clonagem (mp3/wav, máx. 10 MB)',
+ mimoCloneAudioUpload: 'Escolher arquivo',
+ mimoCloneAudioClear: 'Limpar',
+ mimoStylePrompt: 'Prompt de estilo',
+ mimoStylePromptHint: 'Opcional — descreva o estilo de fala em linguagem natural',
+ mimoStylePromptPlaceholder: 'Ex: Tom brilhante e animado, ritmo rápido',
},
lockedIps: {
title: 'IPs bloqueadas',
diff --git a/packages/client/src/i18n/locales/zh-TW.ts b/packages/client/src/i18n/locales/zh-TW.ts
index 0e2d77e..f3b918d 100644
--- a/packages/client/src/i18n/locales/zh-TW.ts
+++ b/packages/client/src/i18n/locales/zh-TW.ts
@@ -836,6 +836,32 @@ export default {
testButton: '試聽',
testButtonPlaying: '播放中...',
testFailed: '測試失敗:{error}',
+
+ // MiMo TTS
+ providerMimo: 'MiMo TTS',
+ mimoHint: '小米 MiMo TTS,支援預設音色、音色設計、音色複製三種模式',
+ mimoApiKey: 'API Key',
+ mimoApiKeyHint: '在 platform.xiaomimimo.com 取得',
+ mimoApiKeyPlaceholder: 'MiMo API Key',
+ mimoBaseUrl: 'Base URL',
+ mimoBaseUrlHint: 'MiMo API 端點位址',
+ mimoModel: '模型',
+ mimoModelHint: '選擇語音合成模型',
+ mimoModelPreset: '預設音色',
+ mimoModelVoiceDesign: '音色設計',
+ mimoModelVoiceClone: '音色複製',
+ mimoVoice: '音色',
+ mimoVoiceHint: '選擇預設音色',
+ mimoVoiceDesignPrompt: '音色描述',
+ mimoVoiceDesignPromptHint: '描述你想要的音色特徵',
+ mimoVoiceDesignPromptPlaceholder: '例如:溫柔的年輕女聲,語速稍慢,帶著磁性',
+ mimoCloneAudio: '上傳音訊',
+ mimoCloneAudioHint: '上傳音訊樣本用於音色複製,支援 mp3/wav,最大 10MB',
+ mimoCloneAudioUpload: '選擇檔案',
+ mimoCloneAudioClear: '清除音訊',
+ mimoStylePrompt: '風格指令',
+ mimoStylePromptHint: '可選,用自然語言描述語音風格',
+ mimoStylePromptPlaceholder: '例如:用輕快上揚的語調,語速稍快',
},
},
diff --git a/packages/client/src/i18n/locales/zh.ts b/packages/client/src/i18n/locales/zh.ts
index 57c3de0..25c8396 100644
--- a/packages/client/src/i18n/locales/zh.ts
+++ b/packages/client/src/i18n/locales/zh.ts
@@ -839,6 +839,32 @@ export default {
testButton: '试听',
testButtonPlaying: '播放中...',
testFailed: '测试失败:{error}',
+
+ // MiMo TTS
+ providerMimo: 'MiMo TTS',
+ mimoHint: '小米 MiMo TTS,支持预置音色、音色设计、音色复刻三种模式',
+ mimoApiKey: 'API Key',
+ mimoApiKeyHint: '在 platform.xiaomimimo.com 获取',
+ mimoApiKeyPlaceholder: 'MiMo API Key',
+ mimoBaseUrl: 'Base URL',
+ mimoBaseUrlHint: 'MiMo API 端点地址',
+ mimoModel: '模型',
+ mimoModelHint: '选择语音合成模型',
+ mimoModelPreset: '预置音色',
+ mimoModelVoiceDesign: '音色设计',
+ mimoModelVoiceClone: '音色复刻',
+ mimoVoice: '音色',
+ mimoVoiceHint: '选择预置音色',
+ mimoVoiceDesignPrompt: '音色描述',
+ mimoVoiceDesignPromptHint: '描述你想要的音色特征',
+ mimoVoiceDesignPromptPlaceholder: '例如:温柔的年轻女声,语速稍慢,带着磁性',
+ mimoCloneAudio: '上传音频',
+ mimoCloneAudioHint: '上传音频样本用于音色复刻,支持 mp3/wav,最大 10MB',
+ mimoCloneAudioUpload: '选择文件',
+ mimoCloneAudioClear: '清除音频',
+ mimoStylePrompt: '风格指令',
+ mimoStylePromptHint: '可选,用自然语言描述语音风格',
+ mimoStylePromptPlaceholder: '例如:用轻快上扬的语调,语速稍快',
},
},