diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 9508968..0b16d8e 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -7083,9 +7083,9 @@ dependencies = [ [[package]] name = "transcribe-rs" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2039bf80d6497655ecdef8ebfa10583862fad3cc017077e105bc1c18b7502ff8" +checksum = "23e3bf6e1dd4909268796bdf3794fb6fda70f4a0e5a80e2e56cd3fc213a11586" dependencies = [ "base64 0.22.1", "derive_builder", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index b27cfd2..04addb5 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -68,7 +68,7 @@ chrono = "0.4" rusqlite = { version = "0.37", features = ["bundled"] } tar = "0.4.44" flate2 = "1.0" -transcribe-rs = { version = "0.2.3", features = ["whisper", "parakeet", "moonshine", "sense_voice"] } +transcribe-rs = { version = "0.2.5", features = ["whisper", "parakeet", "moonshine", "sense_voice"] } handy-keys = "0.2.0" ferrous-opencc = "0.2.3" specta = "=2.0.0-rc.22" diff --git a/src-tauri/src/managers/model.rs b/src-tauri/src/managers/model.rs index 73fbaee..9a211d9 100644 --- a/src-tauri/src/managers/model.rs +++ b/src-tauri/src/managers/model.rs @@ -21,6 +21,7 @@ pub enum EngineType { Whisper, Parakeet, Moonshine, + MoonshineStreaming, SenseVoice, } @@ -290,6 +291,81 @@ impl ModelManager { }, ); + available_models.insert( + "moonshine-tiny-streaming-en".to_string(), + ModelInfo { + id: "moonshine-tiny-streaming-en".to_string(), + name: "Moonshine V2 Tiny".to_string(), + description: "Ultra-fast, English only".to_string(), + filename: "moonshine-tiny-streaming-en".to_string(), + url: Some( + "https://blob.handy.computer/moonshine-tiny-streaming-en.tar.gz".to_string(), + ), + size_mb: 31, + is_downloaded: false, + is_downloading: false, + partial_size: 0, + is_directory: true, + engine_type: EngineType::MoonshineStreaming, + accuracy_score: 0.55, + speed_score: 0.95, + supports_translation: false, + is_recommended: false, + supported_languages: vec!["en".to_string()], + is_custom: false, + }, + ); + + available_models.insert( + "moonshine-small-streaming-en".to_string(), + ModelInfo { + id: "moonshine-small-streaming-en".to_string(), + name: "Moonshine V2 Small".to_string(), + description: "Fast, English only. Good balance of speed and accuracy.".to_string(), + filename: "moonshine-small-streaming-en".to_string(), + url: Some( + "https://blob.handy.computer/moonshine-small-streaming-en.tar.gz".to_string(), + ), + size_mb: 100, + is_downloaded: false, + is_downloading: false, + partial_size: 0, + is_directory: true, + engine_type: EngineType::MoonshineStreaming, + accuracy_score: 0.65, + speed_score: 0.90, + supports_translation: false, + is_recommended: false, + supported_languages: vec!["en".to_string()], + is_custom: false, + }, + ); + + available_models.insert( + "moonshine-medium-streaming-en".to_string(), + ModelInfo { + id: "moonshine-medium-streaming-en".to_string(), + name: "Moonshine V2 Medium".to_string(), + description: "English only. High quality.".to_string(), + filename: "moonshine-medium-streaming-en".to_string(), + url: Some( + "https://blob.handy.computer/moonshine-medium-streaming-en.tar.gz".to_string(), + ), + size_mb: 192, + is_downloaded: false, + is_downloading: false, + partial_size: 0, + is_directory: true, + engine_type: EngineType::MoonshineStreaming, + accuracy_score: 0.75, + speed_score: 0.80, + supports_translation: false, + is_recommended: false, + supported_languages: vec!["en".to_string()], + is_custom: false, + }, + ); + // SenseVoice supported languages let sense_voice_languages: Vec = vec!["zh", "zh-Hans", "zh-Hant", "en", "yue", "ja", "ko"] diff --git a/src-tauri/src/managers/transcription.rs b/src-tauri/src/managers/transcription.rs index 0703e1a..e1b514e 100644 --- a/src-tauri/src/managers/transcription.rs +++ b/src-tauri/src/managers/transcription.rs @@ -4,14 +4,18 @@ use crate::settings::{get_settings, ModelUnloadTimeout}; use anyhow::Result; use log::{debug, error, info, warn}; use serde::Serialize; +use std::panic::{catch_unwind, AssertUnwindSafe}; use std::sync::atomic::{AtomicBool, AtomicU64, Ordering}; -use std::sync::{Arc, Condvar, Mutex}; +use std::sync::{Arc, Condvar, Mutex, MutexGuard}; use std::thread; use std::time::{Duration, SystemTime}; use tauri::{AppHandle, Emitter}; use transcribe_rs::{ engines::{ - moonshine::{ModelVariant, MoonshineEngine, MoonshineModelParams}, + moonshine::{ + ModelVariant, MoonshineEngine, MoonshineModelParams, MoonshineStreamingEngine, + StreamingModelParams, + }, parakeet::{ ParakeetEngine, ParakeetInferenceParams, ParakeetModelParams, TimestampGranularity, }, @@ -36,6 +40,7 @@ enum LoadedEngine { Whisper(WhisperEngine), Parakeet(ParakeetEngine), Moonshine(MoonshineEngine), + MoonshineStreaming(MoonshineStreamingEngine), SenseVoice(SenseVoiceEngine), } @@ -134,8 +139,16 @@ impl TranscriptionManager { Ok(manager) } + /// Lock the engine mutex, recovering from poison if a previous transcription panicked. + fn lock_engine(&self) -> MutexGuard<'_, Option> { + self.engine.lock().unwrap_or_else(|poisoned| { + warn!("Engine mutex was poisoned by a previous panic, recovering"); + poisoned.into_inner() + }) + } + pub fn is_model_loaded(&self) -> bool { - let engine = self.engine.lock().unwrap(); + let engine = self.lock_engine(); engine.is_some() } @@ -144,12 +157,13 @@ impl TranscriptionManager { debug!("Starting to unload model"); { - let mut engine = self.engine.lock().unwrap(); + let mut engine = self.lock_engine(); if let Some(ref mut loaded_engine) = *engine { match loaded_engine { LoadedEngine::Whisper(ref mut e) => e.unload_model(), LoadedEngine::Parakeet(ref mut e) => e.unload_model(), LoadedEngine::Moonshine(ref mut e) => e.unload_model(), + LoadedEngine::MoonshineStreaming(ref mut e) => e.unload_model(), LoadedEngine::SenseVoice(ref mut e) => e.unload_model(), } } @@ -290,6 +304,28 @@ impl TranscriptionManager { })?; LoadedEngine::Moonshine(engine) } + EngineType::MoonshineStreaming => { + let mut engine = MoonshineStreamingEngine::new(); + engine + .load_model_with_params(&model_path, StreamingModelParams::default()) + .map_err(|e| { + let error_msg = format!( + "Failed to load moonshine streaming model {}: {}", + model_id, e + ); + let _ = self.app_handle.emit( + "model-state-changed", + ModelStateEvent { + event_type: "loading_failed".to_string(), + model_id: Some(model_id.to_string()), + model_name: Some(model_info.name.clone()), + error: Some(error_msg.clone()), + }, + ); + anyhow::anyhow!(error_msg) + })?; + LoadedEngine::MoonshineStreaming(engine) + } EngineType::SenseVoice => { let mut engine = SenseVoiceEngine::new(); engine @@ -314,7 +350,7 @@ impl TranscriptionManager { // Update the current engine and model ID { - let mut engine = self.engine.lock().unwrap(); + let mut engine = self.lock_engine(); *engine = Some(loaded_engine); } { @@ -395,7 +431,7 @@ impl TranscriptionManager { is_loading = self.loading_condvar.wait(is_loading).unwrap(); } - let engine_guard = self.engine.lock().unwrap(); + let engine_guard = self.lock_engine(); if engine_guard.is_none() { return Err(anyhow::anyhow!("Model is not loaded for transcription.")); } @@ -404,70 +440,141 @@ impl TranscriptionManager { // Get current settings for configuration let settings = get_settings(&self.app_handle); - // Perform transcription with the appropriate engine + // Perform transcription with the appropriate engine. + // We use catch_unwind to prevent engine panics from poisoning the mutex, + // which would make the app hang indefinitely on subsequent operations. let result = { - let mut engine_guard = self.engine.lock().unwrap(); - let engine = engine_guard.as_mut().ok_or_else(|| { - anyhow::anyhow!( - "Model failed to load after auto-load attempt. Please check your model settings." - ) - })?; + let mut engine_guard = self.lock_engine(); - match engine { - LoadedEngine::Whisper(whisper_engine) => { - // Normalize language code for Whisper - // Convert zh-Hans and zh-Hant to zh since Whisper uses ISO 639-1 codes - let whisper_language = if settings.selected_language == "auto" { - None + // Take the engine out so we own it during transcription. + // If the engine panics, we simply don't put it back (effectively unloading it) + // instead of poisoning the mutex. + let mut engine = match engine_guard.take() { + Some(e) => e, + None => { + return Err(anyhow::anyhow!( + "Model failed to load after auto-load attempt. Please check your model settings." + )); + } + }; + + // Release the lock before transcribing — no mutex held during the engine call + drop(engine_guard); + + let transcribe_result = catch_unwind(AssertUnwindSafe( + || -> Result { + match &mut engine { + LoadedEngine::Whisper(whisper_engine) => { + let whisper_language = if settings.selected_language == "auto" { + None + } else { + let normalized = if settings.selected_language == "zh-Hans" + || settings.selected_language == "zh-Hant" + { + "zh".to_string() + } else { + settings.selected_language.clone() + }; + Some(normalized) + }; + + let params = WhisperInferenceParams { + language: whisper_language, + translate: settings.translate_to_english, + ..Default::default() + }; + + whisper_engine + .transcribe_samples(audio, Some(params)) + .map_err(|e| anyhow::anyhow!("Whisper transcription failed: {}", e)) + } + LoadedEngine::Parakeet(parakeet_engine) => { + let params = ParakeetInferenceParams { + timestamp_granularity: TimestampGranularity::Segment, + ..Default::default() + }; + parakeet_engine + .transcribe_samples(audio, Some(params)) + .map_err(|e| { + anyhow::anyhow!("Parakeet transcription failed: {}", e) + }) + } + LoadedEngine::Moonshine(moonshine_engine) => moonshine_engine + .transcribe_samples(audio, None) + .map_err(|e| anyhow::anyhow!("Moonshine transcription failed: {}", e)), + LoadedEngine::MoonshineStreaming(streaming_engine) => streaming_engine + .transcribe_samples(audio, None) + .map_err(|e| { + anyhow::anyhow!("Moonshine streaming transcription failed: {}", e) + }), + LoadedEngine::SenseVoice(sense_voice_engine) => { + let language = match settings.selected_language.as_str() { + "zh" | "zh-Hans" | "zh-Hant" => SenseVoiceLanguage::Chinese, + "en" => SenseVoiceLanguage::English, + "ja" => SenseVoiceLanguage::Japanese, + "ko" => SenseVoiceLanguage::Korean, + "yue" => SenseVoiceLanguage::Cantonese, + _ => SenseVoiceLanguage::Auto, + }; + let params = SenseVoiceInferenceParams { + language, + use_itn: true, + }; + sense_voice_engine + .transcribe_samples(audio, Some(params)) + .map_err(|e| { + anyhow::anyhow!("SenseVoice transcription failed: {}", e) + }) + } + } + }, + )); + + match transcribe_result { + Ok(inner_result) => { + // Success or normal error — put the engine back + let mut engine_guard = self.lock_engine(); + *engine_guard = Some(engine); + inner_result? + } + Err(panic_payload) => { + // Engine panicked — do NOT put it back (it's in an unknown state). + // The engine is dropped here, effectively unloading it. + let panic_msg = if let Some(s) = panic_payload.downcast_ref::<&str>() { + s.to_string() + } else if let Some(s) = panic_payload.downcast_ref::() { + s.clone() } else { - let normalized = if settings.selected_language == "zh-Hans" - || settings.selected_language == "zh-Hant" - { - "zh".to_string() - } else { - settings.selected_language.clone() - }; - Some(normalized) + "unknown panic".to_string() }; + error!( + "Transcription engine panicked: {}. Model has been unloaded.", + panic_msg + ); - let params = WhisperInferenceParams { - language: whisper_language, - translate: settings.translate_to_english, - ..Default::default() - }; + // Clear the model ID so it will be reloaded on next attempt + { + let mut current_model = self + .current_model_id + .lock() + .unwrap_or_else(|e| e.into_inner()); + *current_model = None; + } - whisper_engine - .transcribe_samples(audio, Some(params)) - .map_err(|e| anyhow::anyhow!("Whisper transcription failed: {}", e))? - } - LoadedEngine::Parakeet(parakeet_engine) => { - let params = ParakeetInferenceParams { - timestamp_granularity: TimestampGranularity::Segment, - ..Default::default() - }; - parakeet_engine - .transcribe_samples(audio, Some(params)) - .map_err(|e| anyhow::anyhow!("Parakeet transcription failed: {}", e))? - } - LoadedEngine::Moonshine(moonshine_engine) => moonshine_engine - .transcribe_samples(audio, None) - .map_err(|e| anyhow::anyhow!("Moonshine transcription failed: {}", e))?, - LoadedEngine::SenseVoice(sense_voice_engine) => { - let language = match settings.selected_language.as_str() { - "zh" | "zh-Hans" | "zh-Hant" => SenseVoiceLanguage::Chinese, - "en" => SenseVoiceLanguage::English, - "ja" => SenseVoiceLanguage::Japanese, - "ko" => SenseVoiceLanguage::Korean, - "yue" => SenseVoiceLanguage::Cantonese, - _ => SenseVoiceLanguage::Auto, - }; - let params = SenseVoiceInferenceParams { - language, - use_itn: true, - }; - sense_voice_engine - .transcribe_samples(audio, Some(params)) - .map_err(|e| anyhow::anyhow!("SenseVoice transcription failed: {}", e))? + let _ = self.app_handle.emit( + "model-state-changed", + ModelStateEvent { + event_type: "unloaded".to_string(), + model_id: None, + model_name: None, + error: Some(format!("Engine panicked: {}", panic_msg)), + }, + ); + + return Err(anyhow::anyhow!( + "Transcription engine panicked: {}. The model has been unloaded and will reload on next attempt.", + panic_msg + )); } } }; diff --git a/src/bindings.ts b/src/bindings.ts index 80c9ca3..d85b915 100644 --- a/src/bindings.ts +++ b/src/bindings.ts @@ -736,7 +736,7 @@ export type AutoSubmitKey = "enter" | "ctrl_enter" | "cmd_enter" export type BindingResponse = { success: boolean; binding: ShortcutBinding | null; error: string | null } export type ClipboardHandling = "dont_modify" | "copy_to_clipboard" export type CustomSounds = { start: boolean; stop: boolean } -export type EngineType = "Whisper" | "Parakeet" | "Moonshine" | "SenseVoice" +export type EngineType = "Whisper" | "Parakeet" | "Moonshine" | "MoonshineStreaming" | "SenseVoice" export type HistoryEntry = { id: number; file_name: string; timestamp: number; saved: boolean; title: string; transcription_text: string; post_processed_text: string | null; post_process_prompt: string | null } /** * Result of changing keyboard implementation diff --git a/src/i18n/locales/ar/translation.json b/src/i18n/locales/ar/translation.json index c51c8cc..4fce560 100644 --- a/src/i18n/locales/ar/translation.json +++ b/src/i18n/locales/ar/translation.json @@ -55,6 +55,26 @@ "moonshine-base": { "name": "Moonshine Base", "description": ".سريع جداً، الإنجليزية فقط. يتعامل جيداً مع اللهجات" + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "سريع للغاية، الإنجليزية فقط" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "سريع، الإنجليزية فقط. توازن جيد بين السرعة والدقة." + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "الإنجليزية فقط. جودة عالية." + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "محسّن للماندرين التايوانية. دعم التبديل بين اللغات." + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "سريع جداً. الصينية، الإنجليزية، اليابانية، الكورية، الكانتونية." } }, "errors": { diff --git a/src/i18n/locales/cs/translation.json b/src/i18n/locales/cs/translation.json index 56e3420..8cf45f6 100644 --- a/src/i18n/locales/cs/translation.json +++ b/src/i18n/locales/cs/translation.json @@ -55,6 +55,26 @@ "moonshine-base": { "name": "Moonshine Base", "description": "Velmi rychlý, pouze angličtina. Dobře zvládá přízvuky." + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "Ultrarychlý, pouze angličtina" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "Rychlý, pouze angličtina. Dobrý poměr rychlosti a přesnosti." + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "Pouze angličtina. Vysoká kvalita." + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "Optimalizováno pro tchajwanskou mandarínštinu. Podpora přepínání jazyků." + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "Velmi rychlý. Čínština, angličtina, japonština, korejština, kantonština." } }, "errors": { diff --git a/src/i18n/locales/de/translation.json b/src/i18n/locales/de/translation.json index 4526edf..6a6e1f8 100644 --- a/src/i18n/locales/de/translation.json +++ b/src/i18n/locales/de/translation.json @@ -54,7 +54,27 @@ }, "moonshine-base": { "name": "Moonshine Base", - "description": "Very fast, English only. Handles accents well." + "description": "Sehr schnell, nur Englisch. Verarbeitet Akzente gut." + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "Ultraschnell, nur Englisch" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "Schnell, nur Englisch. Gute Balance zwischen Geschwindigkeit und Genauigkeit." + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "Nur Englisch. Hohe Qualität." + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "Optimiert für taiwanesisches Mandarin. Unterstützung für Code-Switching." + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "Sehr schnell. Chinesisch, Englisch, Japanisch, Koreanisch, Kantonesisch." } }, "errors": { diff --git a/src/i18n/locales/en/translation.json b/src/i18n/locales/en/translation.json index 2baff18..d07b537 100644 --- a/src/i18n/locales/en/translation.json +++ b/src/i18n/locales/en/translation.json @@ -55,6 +55,26 @@ "moonshine-base": { "name": "Moonshine Base", "description": "Very fast, English only. Handles accents well." + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "Ultra-fast, English only" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "Fast, English only. Good balance of speed and accuracy." + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "English only. High quality." + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "Optimized for Taiwanese Mandarin. Code-switching support." + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "Very fast. Chinese, English, Japanese, Korean, Cantonese." } }, "errors": { diff --git a/src/i18n/locales/es/translation.json b/src/i18n/locales/es/translation.json index 4cd055e..e4c6050 100644 --- a/src/i18n/locales/es/translation.json +++ b/src/i18n/locales/es/translation.json @@ -54,7 +54,27 @@ }, "moonshine-base": { "name": "Moonshine Base", - "description": "Very fast, English only. Handles accents well." + "description": "Muy rápido, solo inglés. Maneja bien los acentos." + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "Ultrarrápido, solo inglés" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "Rápido, solo inglés. Buen equilibrio entre velocidad y precisión." + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "Solo inglés. Alta calidad." + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "Optimizado para mandarín taiwanés. Soporte para cambio de código." + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "Muy rápido. Chino, inglés, japonés, coreano, cantonés." } }, "errors": { diff --git a/src/i18n/locales/fr/translation.json b/src/i18n/locales/fr/translation.json index 9ac29c4..5aa9bd7 100644 --- a/src/i18n/locales/fr/translation.json +++ b/src/i18n/locales/fr/translation.json @@ -54,7 +54,27 @@ }, "moonshine-base": { "name": "Moonshine Base", - "description": "Very fast, English only. Handles accents well." + "description": "Très rapide, anglais uniquement. Gère bien les accents." + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "Ultra-rapide, anglais uniquement" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "Rapide, anglais uniquement. Bon équilibre entre vitesse et précision." + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "Anglais uniquement. Haute qualité." + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "Optimisé pour le mandarin taïwanais. Prise en charge de l'alternance codique." + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "Très rapide. Chinois, anglais, japonais, coréen, cantonais." } }, "errors": { diff --git a/src/i18n/locales/it/translation.json b/src/i18n/locales/it/translation.json index f85904b..f7ddcf7 100644 --- a/src/i18n/locales/it/translation.json +++ b/src/i18n/locales/it/translation.json @@ -54,7 +54,27 @@ }, "moonshine-base": { "name": "Moonshine Base", - "description": "Very fast, English only. Handles accents well." + "description": "Molto veloce, solo inglese. Gestisce bene gli accenti." + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "Ultra-veloce, solo inglese" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "Veloce, solo inglese. Buon equilibrio tra velocità e precisione." + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "Solo inglese. Alta qualità." + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "Ottimizzato per il mandarino taiwanese. Supporto per il code-switching." + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "Molto veloce. Cinese, inglese, giapponese, coreano, cantonese." } }, "errors": { diff --git a/src/i18n/locales/ja/translation.json b/src/i18n/locales/ja/translation.json index 9f620b3..25ecea0 100644 --- a/src/i18n/locales/ja/translation.json +++ b/src/i18n/locales/ja/translation.json @@ -54,7 +54,27 @@ }, "moonshine-base": { "name": "Moonshine Base", - "description": "Very fast, English only. Handles accents well." + "description": "非常に高速、英語のみ。アクセントにも対応。" + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "超高速、英語のみ" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "高速、英語のみ。速度と精度のバランスが良い。" + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "英語のみ。高品質。" + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "台湾華語に最適化。コードスイッチングに対応。" + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "非常に高速。中国語、英語、日本語、韓国語、広東語。" } }, "errors": { diff --git a/src/i18n/locales/ko/translation.json b/src/i18n/locales/ko/translation.json index 7c166f7..cd3f0ef 100644 --- a/src/i18n/locales/ko/translation.json +++ b/src/i18n/locales/ko/translation.json @@ -55,6 +55,26 @@ "moonshine-base": { "name": "Moonshine Base", "description": "매우 빠름, 영어 전용. 억양을 잘 처리합니다." + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "초고속, 영어 전용" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "빠름, 영어 전용. 속도와 정확도의 균형이 좋음." + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "영어 전용. 높은 품질." + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "대만 만다린에 최적화. 코드 스위칭 지원." + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "매우 빠름. 중국어, 영어, 일본어, 한국어, 광둥어." } }, "errors": { diff --git a/src/i18n/locales/pl/translation.json b/src/i18n/locales/pl/translation.json index d907791..d68e8bf 100644 --- a/src/i18n/locales/pl/translation.json +++ b/src/i18n/locales/pl/translation.json @@ -54,7 +54,27 @@ }, "moonshine-base": { "name": "Moonshine Base", - "description": "Very fast, English only. Handles accents well." + "description": "Bardzo szybki, tylko angielski. Dobrze radzi sobie z akcentami." + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "Ultraszybki, tylko angielski" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "Szybki, tylko angielski. Dobra równowaga między szybkością a dokładnością." + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "Tylko angielski. Wysoka jakość." + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "Zoptymalizowany dla tajwańskiego mandaryńskiego. Obsługa przełączania języków." + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "Bardzo szybki. Chiński, angielski, japoński, koreański, kantoński." } }, "errors": { diff --git a/src/i18n/locales/pt/translation.json b/src/i18n/locales/pt/translation.json index 6c4cb60..bbb2a05 100644 --- a/src/i18n/locales/pt/translation.json +++ b/src/i18n/locales/pt/translation.json @@ -54,7 +54,27 @@ }, "moonshine-base": { "name": "Moonshine Base", - "description": "Very fast, English only. Handles accents well." + "description": "Muito rápido, apenas inglês. Lida bem com sotaques." + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "Ultrarrápido, apenas inglês" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "Rápido, apenas inglês. Bom equilíbrio entre velocidade e precisão." + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "Apenas inglês. Alta qualidade." + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "Otimizado para mandarim taiwanês. Suporte para alternância de código." + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "Muito rápido. Chinês, inglês, japonês, coreano, cantonês." } }, "errors": { diff --git a/src/i18n/locales/ru/translation.json b/src/i18n/locales/ru/translation.json index 81fcfb5..770d884 100644 --- a/src/i18n/locales/ru/translation.json +++ b/src/i18n/locales/ru/translation.json @@ -54,7 +54,27 @@ }, "moonshine-base": { "name": "Moonshine Base", - "description": "Very fast, English only. Handles accents well." + "description": "Очень быстрый, только английский. Хорошо справляется с акцентами." + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "Сверхбыстрый, только английский" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "Быстрый, только английский. Хороший баланс скорости и точности." + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "Только английский. Высокое качество." + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "Оптимизирован для тайваньского мандаринского. Поддержка переключения языков." + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "Очень быстрый. Китайский, английский, японский, корейский, кантонский." } }, "errors": { diff --git a/src/i18n/locales/tr/translation.json b/src/i18n/locales/tr/translation.json index 40a0e12..099fff3 100644 --- a/src/i18n/locales/tr/translation.json +++ b/src/i18n/locales/tr/translation.json @@ -55,6 +55,26 @@ "moonshine-base": { "name": "Moonshine Base", "description": "Çok hızlı, yalnızca İngilizce. Aksanları iyi işler." + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "Ultra hızlı, yalnızca İngilizce" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "Hızlı, yalnızca İngilizce. Hız ve doğruluk arasında iyi denge." + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "Yalnızca İngilizce. Yüksek kalite." + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "Tayvan Mandarin Çincesi için optimize edilmiş. Dil değiştirme desteği." + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "Çok hızlı. Çince, İngilizce, Japonca, Korece, Kantonca." } }, "errors": { diff --git a/src/i18n/locales/uk/translation.json b/src/i18n/locales/uk/translation.json index a3c43d3..9db5891 100644 --- a/src/i18n/locales/uk/translation.json +++ b/src/i18n/locales/uk/translation.json @@ -54,7 +54,27 @@ }, "moonshine-base": { "name": "Moonshine Base", - "description": "Very fast, English only. Handles accents well." + "description": "Дуже швидкий, лише англійська. Добре справляється з акцентами." + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "Надшвидкий, лише англійська" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "Швидкий, лише англійська. Добрий баланс швидкості та точності." + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "Лише англійська. Висока якість." + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "Оптимізовано для тайванської мандаринської. Підтримка перемикання мов." + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "Дуже швидкий. Китайська, англійська, японська, корейська, кантонська." } }, "errors": { diff --git a/src/i18n/locales/vi/translation.json b/src/i18n/locales/vi/translation.json index ec64103..677ae91 100644 --- a/src/i18n/locales/vi/translation.json +++ b/src/i18n/locales/vi/translation.json @@ -54,7 +54,27 @@ }, "moonshine-base": { "name": "Moonshine Base", - "description": "Very fast, English only. Handles accents well." + "description": "Rất nhanh, chỉ tiếng Anh. Xử lý tốt các giọng nói." + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "Cực nhanh, chỉ tiếng Anh" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "Nhanh, chỉ tiếng Anh. Cân bằng tốt giữa tốc độ và độ chính xác." + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "Chỉ tiếng Anh. Chất lượng cao." + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "Tối ưu hóa cho tiếng Quan Thoại Đài Loan. Hỗ trợ chuyển đổi ngôn ngữ." + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "Rất nhanh. Tiếng Trung, tiếng Anh, tiếng Nhật, tiếng Hàn, tiếng Quảng Đông." } }, "errors": { diff --git a/src/i18n/locales/zh-TW/translation.json b/src/i18n/locales/zh-TW/translation.json index 3f3f51e..473fa49 100644 --- a/src/i18n/locales/zh-TW/translation.json +++ b/src/i18n/locales/zh-TW/translation.json @@ -55,6 +55,26 @@ "moonshine-base": { "name": "Moonshine Base", "description": "速度極快,僅支援英語,擅長處理各種口音" + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "超快速,僅支援英語" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "快速,僅支援英語。速度與準確度的良好平衡。" + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "僅支援英語。高品質。" + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "針對臺灣華語最佳化。支援語碼轉換。" + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "速度極快。支援中文、英語、日語、韓語、粵語。" } }, "errors": { diff --git a/src/i18n/locales/zh/translation.json b/src/i18n/locales/zh/translation.json index af4ea20..6713833 100644 --- a/src/i18n/locales/zh/translation.json +++ b/src/i18n/locales/zh/translation.json @@ -54,7 +54,27 @@ }, "moonshine-base": { "name": "Moonshine Base", - "description": "Very fast, English only. Handles accents well." + "description": "非常快速,仅支持英语。口音处理良好。" + }, + "moonshine-tiny-streaming-en": { + "name": "Moonshine V2 Tiny", + "description": "超快速,仅支持英语" + }, + "moonshine-small-streaming-en": { + "name": "Moonshine V2 Small", + "description": "快速,仅支持英语。速度与准确度的良好平衡。" + }, + "moonshine-medium-streaming-en": { + "name": "Moonshine V2 Medium", + "description": "仅支持英语。高质量。" + }, + "breeze-asr": { + "name": "Breeze ASR", + "description": "针对台湾国语优化。支持语码转换。" + }, + "sense-voice-int8": { + "name": "SenseVoice", + "description": "非常快速。支持中文、英语、日语、韩语、粤语。" } }, "errors": {