/// 同声翻译选哪条讯飞微软会话。 /// /// 两端都在同传表里:一条 AST,发音人用短名。 /// 源或目标是中文、粤语、爱尔兰语:不启 AST,识别走微软实时转写,合成用区域码音色。 /// 两边都不在这两类里:不启动。 class SimulRoute { const SimulRoute.ast({ required this.lang, required this.targetLang, required this.speaker, }) : ast = true, languageName = ''; const SimulRoute.splice({ required this.languageName, required this.speaker, }) : ast = false, lang = '', targetLang = ''; final bool ast; /// AST 的源语种短码,例如 ja。 final String lang; /// AST 的目标语种短码,例如 en。 final String targetLang; /// AST 用短名(JennyNeural)。拼接用区域码音色(en-US-JennyNeural)。 final String speaker; /// 拼接识别的 language_name,例如 zh-CN。 final String languageName; } class SimulMicrosoft { static SimulRoute? plan(String source, String target) { final from = _norm(source); final to = _norm(target); if (_ast.containsKey(from) && _ast.containsKey(to)) { return SimulRoute.ast( lang: from, targetLang: to, speaker: _ast[to]!.speaker, ); } if (_splice.containsKey(from) || _splice.containsKey(to)) { final locale = _listenLocale(from); final voice = _speakVoice(to); if (locale == null || voice == null) return null; return SimulRoute.splice(languageName: locale, speaker: voice); } return null; } /// 同传能力 ast_msft 没授权时,用同一对语种走微软转写加合成。 /// 识别用区域码,合成用完整神经音色,不再用 AST 的短名。 static SimulRoute spliceInstead(SimulRoute route) { final locale = _listenLocale(route.lang); final voice = _speakVoice(route.targetLang); if (locale == null || voice == null) return route; return SimulRoute.splice(languageName: locale, speaker: voice); } /// 界面短码收成表里的键。香港繁体要在「凡是 zh- 都当普通话」之前认出来。 static String _norm(String code) { final raw = code.trim().toLowerCase().replaceAll('_', '-'); if (raw == 'yue' || raw.startsWith('yue-')) return 'yue'; if (raw == 'ga' || raw.startsWith('ga-')) return 'ga'; if (raw == 'zh-hant-hk' || raw == 'zh-hk') return 'zh-HK'; if (raw == 'zh-hant' || raw == 'zh-tw' || raw.startsWith('zh-hant')) { return 'zh-Hant'; } if (raw == 'zh' || raw.startsWith('zh-')) return 'zh'; final dash = raw.indexOf('-'); return dash < 0 ? raw : raw.substring(0, dash); } /// 讯飞 SDK 文本翻译(gtrans)的语种码。中文用 zh-CN,不要收成 zh。 static String translateCode(String code) { switch (_norm(code)) { case 'zh': return 'zh-CN'; case 'zh-Hant': return 'zh-TW'; case 'zh-HK': return 'zh-HK'; case 'yue': return 'yue'; case 'ga': return 'ga'; default: return _norm(code); } } static String? _listenLocale(String key) { final splice = _splice[key]; if (splice != null) return splice.locale; return _ast[key]?.locale; } static String? _speakVoice(String key) { final splice = _splice[key]; if (splice != null) return splice.voice; final ast = _ast[key]; if (ast == null) return null; return '${ast.locale}-${ast.speaker}'; } /// 同传表。speaker 是 AST 的短名,locale 是拼接到 OnlineTTS 时用的区域码。 static const _ast = { 'ar': _AstLang('ar-EG', 'AbdullahNeural'), 'bg': _AstLang('bg-BG', 'BorislavNeural'), 'bn': _AstLang('bn-IN', 'BashkarNeural'), 'ca': _AstLang('ca-ES', 'AlbaNeural'), 'cs': _AstLang('cs-CZ', 'AntoninNeural'), 'da': _AstLang('da-DK', 'ChristelNeural'), 'de': _AstLang('de-DE', 'AmalaNeural'), 'el': _AstLang('el-GR', 'AthinaNeural'), 'en': _AstLang('en-US', 'JennyNeural'), 'es': _AstLang('es-ES', 'ElviraNeural'), 'et': _AstLang('et-EE', 'AnuNeural'), 'fa': _AstLang('fa-IR', 'DilaraNeural'), 'fi': _AstLang('fi-FI', 'HarriNeural'), 'fr': _AstLang('fr-FR', 'DeniseNeural'), 'hi': _AstLang('hi-IN', 'MadhurNeural'), 'hu': _AstLang('hu-HU', 'NoemiNeural'), 'id': _AstLang('id-ID', 'ArdiNeural'), 'it': _AstLang('it-IT', 'ElsaNeural'), 'ja': _AstLang('ja-JP', 'NanamiNeural'), 'ko': _AstLang('ko-KR', 'SunHiNeural'), 'lt': _AstLang('lt-LT', 'LeonasNeural'), 'lv': _AstLang('lv-LV', 'EveritaNeural'), 'ms': _AstLang('ms-MY', 'OsmanNeural'), 'nl': _AstLang('nl-NL', 'ArnaudNeural'), 'pl': _AstLang('pl-PL', 'AgnieszkaNeural'), 'pt': _AstLang('pt-BR', 'AntonioNeural'), 'ro': _AstLang('ro-RO', 'AlinaNeural'), 'ru': _AstLang('ru-RU', 'DariyaNeural'), 'sk': _AstLang('sk-SK', 'LukasNeural'), 'sl': _AstLang('sl-SI', 'PetraNeural'), 'sv': _AstLang('sv-SE', 'HilleviNeural'), 'sw': _AstLang('sw-KE', 'DaudiNeural'), 'ta': _AstLang('ta-IN', 'AnbuNeural'), 'th': _AstLang('th-TH', 'AcharaNeural'), 'tr': _AstLang('tr-TR', 'AhmetNeural'), 'uk': _AstLang('uk-UA', 'OstapNeural'), 'ur': _AstLang('ur-IN', 'AsadNeural'), 'vi': _AstLang('vi-VN', 'HoaiMyNeural'), }; /// 微软同传表没有的三语。繁体中文按普通话识别,合成用繁体音色。 static const _splice = { 'zh': _SpliceLang('zh-CN', 'zh-CN-XiaoxiaoNeural'), 'zh-Hant': _SpliceLang('zh-CN', 'zh-TW-HsiaoChenNeural'), 'yue': _SpliceLang('yue-CN', 'yue-CN-XiaoMinNeural'), 'zh-HK': _SpliceLang('zh-HK', 'zh-HK-HiuMaanNeural'), 'ga': _SpliceLang('ga-IE', 'ga-IE-OrlaNeural'), }; } class _AstLang { const _AstLang(this.locale, this.speaker); final String locale; final String speaker; } class _SpliceLang { const _SpliceLang(this.locale, this.voice); final String locale; final String voice; }