You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

831 lines
25 KiB

import 'package:get/get.dart';
import '../../core/utils/logger.dart';
import '../models/user_Info.dart';
import '../services/speech_factory.dart';
import 'language_configs/azure_language_config.dart';
import 'language_configs/google_language_config.dart';
import 'language_configs/iflytek_language_config.dart';
import 'language_configs/volcano_language_config.dart';
import 'language_configs/alibaba_language_config.dart';
import 'language_configs/ast_config.dart';
/// 语言信息类,包含语言的各种表示形式
class LanguageInfo {
/// 短代码,如 'zh', 'en'
final String shortCode;
/// 中文名称
final String chineseName;
/// 英文名称
final String englishName;
/// Azure ASR 语言代码,如 'zh-CN', 'en-US'
final String asrCode;
/// Azure TTS 语音名称(女声)
final String ttsVoiceName;
/// Azure TTS 语音名称(男声)
final String ttsVoiceNameMale;
/// 备注信息
final String? note;
LanguageInfo({
required this.shortCode,
required this.chineseName,
required this.englishName,
required this.asrCode,
required this.ttsVoiceName,
required this.ttsVoiceNameMale,
this.note,
});
}
/// 语言类型管理类,提供语言信息的查询和转换功能
class LanguageManager extends GetxService {
// 单例实例
static LanguageManager? _instance;
// 单例工厂方法
factory LanguageManager() {
_instance ??= LanguageManager._internal();
return _instance!;
}
// 私有构造函数
LanguageManager._internal() {
_initLanguageMap();
}
// 语言信息映射表,以 ASR 代码为键
final Map<String, LanguageInfo> _languageMap = {};
// 中文名称到 ASR 代码的映射
final Map<String, String> _chineseNameToAsrCodeMap = {};
// 英文名称到 ASR 代码的映射
final Map<String, String> _englishNameToAsrCodeMap = {};
// 短代码到 ASR 代码的映射
final Map<String, String> _shortCodeToAsrCodeMap = {};
/// 初始化语言映射表
void _initLanguageMap() {
// 中文相关
_addLanguage(
shortCode: 'zh',
chineseName: '中文(简体)',
englishName: 'Chinese (simplified)',
asrCode: 'zh-CN',
ttsVoiceName: 'zh-CN-XiaoxiaoNeural',
ttsVoiceNameMale: 'zh-CN-YunxiNeural',
);
_addLanguage(
shortCode: 'zh-Hant',
chineseName: '中文(繁体)',
englishName: 'Chinese (traditional)',
asrCode: 'zh-TW',
ttsVoiceName: 'zh-TW-HsiaoChenNeural',
ttsVoiceNameMale: 'zh-TW-YunJheNeural',
);
_addLanguage(
shortCode: 'zh-Hant-hk',
chineseName: '中文(香港繁体)',
englishName: 'Chinese (Hongkong traditional)',
asrCode: 'zh-HK',
ttsVoiceName: 'zh-HK-HiuMaanNeural',
ttsVoiceNameMale: 'zh-HK-WanLungNeural',
);
// 英语相关
_addLanguage(
shortCode: 'en',
chineseName: '英语',
englishName: 'English',
asrCode: 'en-US',
ttsVoiceName: 'en-US-AriaNeural',
ttsVoiceNameMale: 'en-US-GuyNeural',
);
// 日语相关
_addLanguage(
shortCode: 'ja',
chineseName: '日语',
englishName: 'Japanese',
asrCode: 'ja-JP',
ttsVoiceName: 'ja-JP-NanamiNeural',
ttsVoiceNameMale: 'ja-JP-KeitaNeural',
);
// 韩语相关
_addLanguage(
shortCode: 'ko',
chineseName: '韩语',
englishName: 'Korean',
asrCode: 'ko-KR',
ttsVoiceName: 'ko-KR-SunHiNeural',
ttsVoiceNameMale: 'ko-KR-InJoonNeural',
);
// 欧洲语言
_addLanguage(
shortCode: 'fr',
chineseName: '法语',
englishName: 'French',
asrCode: 'fr-FR',
ttsVoiceName: 'fr-FR-DeniseNeural',
ttsVoiceNameMale: 'fr-FR-HenriNeural',
);
_addLanguage(
shortCode: 'es',
chineseName: '西班牙语',
englishName: 'Spanish',
asrCode: 'es-ES',
ttsVoiceName: 'es-ES-ElviraNeural',
ttsVoiceNameMale: 'es-ES-AlvaroNeural',
);
_addLanguage(
shortCode: 'pt',
chineseName: '葡萄牙语',
englishName: 'Portuguese',
asrCode: 'pt-BR',
ttsVoiceName: 'pt-BR-FranciscaNeural',
ttsVoiceNameMale: 'pt-BR-AntonioNeural',
);
_addLanguage(
shortCode: 'it',
chineseName: '意大利语',
englishName: 'Italian',
asrCode: 'it-IT',
ttsVoiceName: 'it-IT-ElsaNeural',
ttsVoiceNameMale: 'it-IT-DiegoNeural',
);
_addLanguage(
shortCode: 'de',
chineseName: '德语',
englishName: 'German',
asrCode: 'de-DE',
ttsVoiceName: 'de-DE-KatjaNeural',
ttsVoiceNameMale: 'de-DE-ConradNeural',
);
_addLanguage(
shortCode: 'ru',
chineseName: '俄语',
englishName: 'Russian',
asrCode: 'ru-RU',
ttsVoiceName: 'ru-RU-SvetlanaNeural',
ttsVoiceNameMale: 'ru-RU-DmitryNeural',
);
_addLanguage(
shortCode: 'pl',
chineseName: '波兰语',
englishName: 'Polish',
asrCode: 'pl-PL',
ttsVoiceName: 'pl-PL-AgnieszkaNeural',
ttsVoiceNameMale: 'pl-PL-MarekNeural',
);
_addLanguage(
shortCode: 'nl',
chineseName: '荷兰语',
englishName: 'Dutch',
asrCode: 'nl-NL',
ttsVoiceName: 'nl-NL-ColetteNeural',
ttsVoiceNameMale: 'nl-NL-MaartenNeural',
);
_addLanguage(
shortCode: 'sv',
chineseName: '瑞典语',
englishName: 'Swedish',
asrCode: 'sv-SE',
ttsVoiceName: 'sv-SE-SofieNeural',
ttsVoiceNameMale: 'sv-SE-MattiasNeural',
);
_addLanguage(
shortCode: 'cs',
chineseName: '捷克语',
englishName: 'Czech',
asrCode: 'cs-CZ',
ttsVoiceName: 'cs-CZ-VlastaNeural',
ttsVoiceNameMale: 'cs-CZ-AntoninNeural',
);
_addLanguage(
shortCode: 'el',
chineseName: '现代希腊语',
englishName: 'Modern Greek',
asrCode: 'el-GR',
ttsVoiceName: 'el-GR-AthinaNeural',
ttsVoiceNameMale: 'el-GR-NestorasNeural',
);
_addLanguage(
shortCode: 'ro',
chineseName: '罗马尼亚语',
englishName: 'Romanian',
asrCode: 'ro-RO',
ttsVoiceName: 'ro-RO-AlinaNeural',
ttsVoiceNameMale: 'ro-RO-EmilNeural',
);
_addLanguage(
shortCode: 'hu',
chineseName: '匈牙利语',
englishName: 'Hungarian',
asrCode: 'hu-HU',
ttsVoiceName: 'hu-HU-NoemiNeural',
ttsVoiceNameMale: 'hu-HU-TamasNeural',
);
_addLanguage(
shortCode: 'uk',
chineseName: '乌克兰语',
englishName: 'Ukrainian',
asrCode: 'uk-UA',
ttsVoiceName: 'uk-UA-PolinaNeural',
ttsVoiceNameMale: 'uk-UA-OstapNeural',
);
_addLanguage(
shortCode: 'da',
chineseName: '丹麦语',
englishName: 'Danish',
asrCode: 'da-DK',
ttsVoiceName: 'da-DK-ChristelNeural',
ttsVoiceNameMale: 'da-DK-JeppeNeural',
);
_addLanguage(
shortCode: 'fi',
chineseName: '芬兰语',
englishName: 'Finnish',
asrCode: 'fi-FI',
ttsVoiceName: 'fi-FI-NooraNeural',
ttsVoiceNameMale: 'fi-FI-HarriNeural',
);
_addLanguage(
shortCode: 'no',
chineseName: '挪威语',
englishName: 'Norwegian',
asrCode: 'nb-NO',
ttsVoiceName: 'nb-NO-IselinNeural',
ttsVoiceNameMale: 'nb-NO-FinnNeural',
);
_addLanguage(
shortCode: 'hr',
chineseName: '克罗地亚语',
englishName: 'Croatian',
asrCode: 'hr-HR',
ttsVoiceName: 'hr-HR-GabrijelaNeural',
ttsVoiceNameMale: 'hr-HR-SreckoNeural',
);
_addLanguage(
shortCode: 'ca',
chineseName: '加泰隆语',
englishName: 'Catalan',
asrCode: 'ca-ES',
ttsVoiceName: 'ca-ES-JoanaNeural',
ttsVoiceNameMale: 'ca-ES-EnricNeural',
);
// 亚洲语言
_addLanguage(
shortCode: 'ar',
chineseName: '阿拉伯语',
englishName: 'Arabic',
asrCode: 'ar-EG',
ttsVoiceName: 'ar-EG-SalmaNeural',
ttsVoiceNameMale: 'ar-EG-ShakirNeural',
);
_addLanguage(
shortCode: 'th',
chineseName: '泰语',
englishName: 'Thai',
asrCode: 'th-TH',
ttsVoiceName: 'th-TH-PremwadeeNeural',
ttsVoiceNameMale: 'th-TH-NiwatNeural',
);
_addLanguage(
shortCode: 'vi',
chineseName: '越南语',
englishName: 'Vietnamese',
asrCode: 'vi-VN',
ttsVoiceName: 'vi-VN-HoaiMyNeural',
ttsVoiceNameMale: 'vi-VN-NamMinhNeural',
);
_addLanguage(
shortCode: 'id',
chineseName: '印尼语',
englishName: 'Indonesian',
asrCode: 'id-ID',
ttsVoiceName: 'id-ID-GadisNeural',
ttsVoiceNameMale: 'id-ID-ArdiNeural',
);
_addLanguage(
shortCode: 'hi',
chineseName: '印地语',
englishName: 'Hindi',
asrCode: 'hi-IN',
ttsVoiceName: 'hi-IN-SwaraNeural',
ttsVoiceNameMale: 'hi-IN-MadhurNeural',
);
_addLanguage(
shortCode: 'tr',
chineseName: '土耳其语',
englishName: 'Turkish',
asrCode: 'tr-TR',
ttsVoiceName: 'tr-TR-EmelNeural',
ttsVoiceNameMale: 'tr-TR-AhmetNeural',
);
_addLanguage(
shortCode: 'ms',
chineseName: '马来语',
englishName: 'Malay',
asrCode: 'ms-MY',
ttsVoiceName: 'ms-MY-YasminNeural',
ttsVoiceNameMale: 'ms-MY-OsmanNeural',
);
_addLanguage(
shortCode: 'bn',
chineseName: '孟加拉语',
englishName: 'Bengali',
asrCode: 'bn-IN',
ttsVoiceName: 'bn-IN-TanishaaNeural',
ttsVoiceNameMale: 'bn-IN-BashkarNeural',
);
_addLanguage(
shortCode: 'ta',
chineseName: '泰米尔语',
englishName: 'Tamil',
asrCode: 'ta-IN',
ttsVoiceName: 'ta-IN-PallaviNeural',
ttsVoiceNameMale: 'ta-IN-ValluvarNeural',
);
_addLanguage(
shortCode: 'te',
chineseName: '泰卢固语',
englishName: 'Telugu',
asrCode: 'te-IN',
ttsVoiceName: 'te-IN-ShrutiNeural',
ttsVoiceNameMale: 'te-IN-MohanNeural',
);
_addLanguage(
shortCode: 'mr',
chineseName: '马拉提语',
englishName: 'Marathi',
asrCode: 'mr-IN',
ttsVoiceName: 'mr-IN-AarohiNeural',
ttsVoiceNameMale: 'mr-IN-ManoharNeural',
);
_addLanguage(
shortCode: 'ur',
chineseName: '乌尔都语',
englishName: 'Urdu',
asrCode: 'ur-IN',
ttsVoiceName: 'ur-IN-GulNeural',
ttsVoiceNameMale: 'ur-IN-SalmanNeural',
);
_addLanguage(
shortCode: 'fa',
chineseName: '波斯语',
englishName: 'Persian',
asrCode: 'fa-IR',
ttsVoiceName: 'fa-IR-DilaraNeural',
ttsVoiceNameMale: 'fa-IR-FaridNeural',
);
// 非洲语言
_addLanguage(
shortCode: 'sw',
chineseName: '斯瓦希里语',
englishName: 'Swahili',
asrCode: 'sw-KE',
ttsVoiceName: 'sw-KE-ZuriNeural',
ttsVoiceNameMale: 'sw-KE-RafikiNeural',
);
Logger.info('语言管理器初始化完成,共加载 ${_languageMap.length} 种语言');
}
/// 添加语言信息到映射表
void _addLanguage({
required String shortCode,
required String chineseName,
required String englishName,
required String asrCode,
required String ttsVoiceName,
required String ttsVoiceNameMale,
String? note,
}) {
final languageInfo = LanguageInfo(
shortCode: shortCode,
chineseName: chineseName,
englishName: englishName,
asrCode: asrCode,
ttsVoiceName: ttsVoiceName,
ttsVoiceNameMale: ttsVoiceNameMale,
note: note,
);
_languageMap[asrCode] = languageInfo;
_chineseNameToAsrCodeMap[chineseName] = asrCode;
_englishNameToAsrCodeMap[englishName] = asrCode;
_shortCodeToAsrCodeMap[shortCode] = asrCode;
}
/// 获取所有支持的语言信息
Map<String, LanguageInfo> getAllLanguages() {
return Map.from(_languageMap);
}
/// 获取所有支持的语言的中文名称
List<String> getAllChineseNames() {
return _chineseNameToAsrCodeMap.keys.toList();
}
/// 获取所有支持的语言的英文名称
List<String> getAllEnglishNames() {
return _englishNameToAsrCodeMap.keys.toList();
}
/// 根据 ASR 代码获取语言信息
LanguageInfo? getLanguageByAsrCode(String asrCode) {
return _languageMap[asrCode];
}
/// 根据中文名称获取语言信息
LanguageInfo? getLanguageByChineseName(String chineseName) {
final asrCode = _chineseNameToAsrCodeMap[chineseName];
if (asrCode == null) return null;
return _languageMap[asrCode];
}
/// 根据英文名称获取语言信息
LanguageInfo? getLanguageByEnglishName(String englishName) {
final asrCode = _englishNameToAsrCodeMap[englishName];
if (asrCode == null) return null;
return _languageMap[asrCode];
}
/// 根据短代码获取语言信息
LanguageInfo? getLanguageByShortCode(String shortCode) {
final asrCode = _shortCodeToAsrCodeMap[shortCode];
if (asrCode == null) return null;
return _languageMap[asrCode];
}
/// 根据中文名称获取 ASR 代码
String? getAsrCodeByChineseName(String chineseName) {
return _chineseNameToAsrCodeMap[chineseName];
}
/// 根据英文名称获取 ASR 代码
String? getAsrCodeByEnglishName(String englishName) {
return _englishNameToAsrCodeMap[englishName];
}
/// 根据短代码获取 ASR 代码
String? getAsrCodeByShortCode(String shortCode) {
return _shortCodeToAsrCodeMap[shortCode];
}
/// 根据 ASR 代码获取中文名称
String? getChineseNameByAsrCode(String asrCode) {
final languageInfo = _languageMap[asrCode];
return languageInfo?.chineseName;
}
/// 根据 ASR 代码获取英文名称
String? getEnglishNameByAsrCode(String asrCode) {
final languageInfo = _languageMap[asrCode];
return languageInfo?.englishName;
}
/// 根据 ASR 代码获取短代码
String? getShortCodeByAsrCode(String asrCode) {
final languageInfo = _languageMap[asrCode];
return languageInfo?.shortCode;
}
/// 根据 shortCode 获取性别适配的 TTS 音色(用于 findBestMatchingProvider)
String? _getGenderAwareTtsCode(String shortCode) {
final asrCode = _shortCodeToAsrCodeMap[shortCode];
if (asrCode == null) return null;
return _getVoiceByGender(_languageMap[asrCode]);
}
/// 根据用户性别获取对应的 TTS 语音名称
/// gender: 0=未设置(默认女声), 1=男, 2=女
String? _getVoiceByGender(LanguageInfo? info) {
if (info == null) return null;
try {
final gender = User.instance.gender;
final voice = gender == 1 ? info.ttsVoiceNameMale : info.ttsVoiceName;
Logger.debug('TTS音色选择: gender=$gender, voice=$voice');
return voice;
} catch (_) {
Logger.debug('TTS音色选择: 用户未初始化,使用默认女声');
return info.ttsVoiceName;
}
}
/// 根据 ASR 代码获取 TTS 语音名称(根据用户性别自动选择)
String? getTtsVoiceNameByAsrCode(String asrCode) {
return _getVoiceByGender(_languageMap[asrCode]);
}
/// 根据中文名称获取 TTS 语音名称(根据用户性别自动选择)
String? getTtsVoiceNameByChineseName(String chineseName) {
final asrCode = _chineseNameToAsrCodeMap[chineseName];
if (asrCode == null) return null;
return _getVoiceByGender(_languageMap[asrCode]);
}
/// 根据短代码获取中文名称
String? getChineseNameByShortCode(String shortCode) {
final asrCode = _shortCodeToAsrCodeMap[shortCode];
if (asrCode == null) return null;
return _languageMap[asrCode]?.chineseName;
}
/// 根据短代码获取英文名称
String? getEnglishNameByShortCode(String shortCode) {
final asrCode = _shortCodeToAsrCodeMap[shortCode];
if (asrCode == null) return null;
return _languageMap[asrCode]?.englishName;
}
/// 根据短代码获取 TTS 语音名称(根据用户性别自动选择)
String? getTtsVoiceNameByShortCode(String shortCode) {
final asrCode = _shortCodeToAsrCodeMap[shortCode];
if (asrCode == null) return null;
return _getVoiceByGender(_languageMap[asrCode]);
}
/// 获取中文名称到 ASR 代码的映射
Map<String, String> getChineseNameToAsrCodeMap() {
return Map.from(_chineseNameToAsrCodeMap);
}
/// 获取英文名称到 ASR 代码的映射
Map<String, String> getEnglishNameToAsrCodeMap() {
return Map.from(_englishNameToAsrCodeMap);
}
/// 获取短代码到 ASR 代码的映射
Map<String, String> getShortCodeToAsrCodeMap() {
return Map.from(_shortCodeToAsrCodeMap);
}
/// 返回当前所有的AST语言配置(中文名称)
List<String> getAllAstLanguageNames() {
return getAstLanguageConfigs()
.map((e) => e['chineseName'] ?? '')
.where((e) => e.isNotEmpty)
.toList();
}
/// 豆包端到端 STS 实际优化良好的语言白名单
/// 只有源和目标都在白名单内,且至少一个是 zh/en 时,才走豆包;
/// 其余情况退到阿里或微软,避免法/德/西/葡/俄等语种走豆包端到端时
/// 出现词被拆碎、ASR 误识别、WebSocket 频繁断开等问题
static const Set<String> _volcanoE2EWhitelist = {'zh', 'en', 'ja', 'ko'};
/// 查找同时支持源语言和目标语言的最佳提供商(通过 shortCode 匹配)
/// 路由策略:
/// 1. 豆包:源/目标都在白名单(zh/en/ja/ko)且至少一个是 zh/en
/// 2. 阿里:源/目标都在阿里翻译表内(zh/en/ja/ko/fr/de/es/it/pt/ru)
/// 3. 微软:其他所有组合(阿里不覆盖的稀有语种,或两端有任一不在阿里白名单)
/// 4. 讯飞:兜底
BestProviderResult? findBestMatchingProvider(
String sourceShortCode, String targetShortCode) {
final providers = [
SpeechServiceType.volcano,
SpeechServiceType.alibaba,
SpeechServiceType.azure,
SpeechServiceType.iflytek,
];
Logger.info(
'[TRANS-INIT] 开始匹配提供商: $sourceShortCode -> $targetShortCode, 策略=豆包(zh/en/ja/ko白名单且含zh|en)>阿里(含zh)>微软>讯飞');
for (final provider in providers) {
final providerName = _providerStringFromType(provider);
final transSpecs = _getTranslationSpecs(provider);
Map<String, String>? sourceTransSpec;
Map<String, String>? targetTransSpec;
for (final spec in transSpecs) {
if (spec['shortCode'] == sourceShortCode) {
sourceTransSpec ??= {};
sourceTransSpec["translationCode"] = spec["translationCode"]!;
}
if (spec['shortCode'] == targetShortCode) {
targetTransSpec ??= {};
targetTransSpec["translationCode"] = spec["translationCode"]!;
}
}
if (sourceTransSpec == null || targetTransSpec == null) {
Logger.info(
'[TRANS-INIT] 跳过 ${providerDisplayName(providerName)}: 翻译表缺少${sourceTransSpec == null ? " 源=$sourceShortCode" : ""}${targetTransSpec == null ? " 目标=$targetShortCode" : ""}');
continue;
}
// 豆包端到端限制:
// (a) 源和目标都必须在白名单 {zh, en, ja, ko}
// (b) 且至少一个是 zh/en(豆包 STS 以中英为 pivot)
if (provider == SpeechServiceType.volcano) {
final bothInWhitelist =
_volcanoE2EWhitelist.contains(sourceShortCode) &&
_volcanoE2EWhitelist.contains(targetShortCode);
final hasZhOrEn = sourceShortCode == 'zh' ||
sourceShortCode == 'en' ||
targetShortCode == 'zh' ||
targetShortCode == 'en';
if (!bothInWhitelist) {
Logger.info(
'[TRANS-INIT] 跳过 ${providerDisplayName('volcano')}: 端到端仅对 {zh,en,ja,ko} 稳定,当前 $sourceShortCode/$targetShortCode 不全在白名单');
continue;
}
if (!hasZhOrEn) {
Logger.info(
'[TRANS-INIT] 跳过 ${providerDisplayName('volcano')}: 需源或目标至少一个为 zh/en(当前 $sourceShortCode/$targetShortCode)');
continue;
}
}
// 阿里端到端 (qwen3-livetranslate): 覆盖主流 10 语(zh/en/ja/ko/fr/de/es/it/pt/ru),
// 两端都在阿里翻译表内即走阿里;稀有语种交给微软兜底。
// 这里不加额外白名单——上面 transSpecs 的匹配检查已经做了筛选。
// 对于非端到端服务(Azure, Iflytek),还需要检查 ASR 和 TTS 支持
if (provider != SpeechServiceType.volcano &&
provider != SpeechServiceType.alibaba) {
final asrSpecs = _getAsrSpecs(provider);
final ttsSpecs = _getTtsSpecs(provider);
for (final spec in asrSpecs) {
if (spec['shortCode'] == sourceShortCode) {
sourceTransSpec["asrCode"] = spec["asrCode"]!;
}
if (spec['shortCode'] == targetShortCode) {
targetTransSpec["asrCode"] = spec["asrCode"]!;
}
}
for (final spec in ttsSpecs) {
if (spec['shortCode'] == sourceShortCode) {
sourceTransSpec["ttsCode"] =
_getGenderAwareTtsCode(spec['shortCode']!) ?? spec["ttsVoiceName"]!;
}
if (spec['shortCode'] == targetShortCode) {
targetTransSpec["ttsCode"] =
_getGenderAwareTtsCode(spec['shortCode']!) ?? spec["ttsVoiceName"]!;
}
}
} else if (provider == SpeechServiceType.alibaba) {
final ttsSpecs = _getTtsSpecs(provider);
for (final spec in ttsSpecs) {
if (spec['shortCode'] == sourceShortCode) {
sourceTransSpec["ttsCode"] = spec["ttsVoiceName"]!;
}
if (spec['shortCode'] == targetShortCode) {
targetTransSpec["ttsCode"] = spec["ttsVoiceName"]!;
}
}
}
final providerString = _providerStringFromType(provider);
final isEndToEnd = provider == SpeechServiceType.volcano ||
provider == SpeechServiceType.alibaba;
Logger.info(
'[TRANS-INIT] 命中 ${providerDisplayName(providerString)}[$providerString] for $sourceShortCode -> $targetShortCode (端到端=$isEndToEnd), transSrc=${sourceTransSpec["translationCode"]}, transTgt=${targetTransSpec["translationCode"]}, asrSrc=${sourceTransSpec["asrCode"]}, asrTgt=${targetTransSpec["asrCode"]}, ttsSrc=${sourceTransSpec["ttsCode"]}, ttsTgt=${targetTransSpec["ttsCode"]}');
return BestProviderResult(
providerString, sourceTransSpec, targetTransSpec);
}
Logger.warning(
'[TRANS-INIT] 所有提供商都不匹配 $sourceShortCode -> $targetShortCode,返回 null');
return null;
}
String _providerStringFromType(SpeechServiceType type) {
switch (type) {
case SpeechServiceType.iflytek:
return 'iflytek';
case SpeechServiceType.azure:
return 'azure';
case SpeechServiceType.google:
return 'google';
case SpeechServiceType.alibaba:
return 'alibaba';
case SpeechServiceType.volcano:
return 'volcano';
case SpeechServiceType.flutter:
return 'flutter';
}
}
/// 将 provider 字符串映射为中文厂商名,用于日志可读性
static String providerDisplayName(String provider) {
switch (provider) {
case 'volcano':
return '豆包(火山)';
case 'alibaba':
return '阿里(通义)';
case 'azure':
return '微软(Azure)';
case 'iflytek':
return '讯飞';
case 'google':
return '谷歌';
case 'flutter':
return '本地(Flutter)';
default:
return provider;
}
}
// ===== 私有工具方法:加载不同提供商的语言规格 =====
List<Map<String, String>> _getAsrSpecs(SpeechServiceType provider) {
switch (provider) {
case SpeechServiceType.azure:
return getAzureASRLanguageSpecs();
case SpeechServiceType.google:
return getGoogleASRLanguageSpecs();
case SpeechServiceType.iflytek:
return getIflytekASRLanguageSpecs();
default:
return getAzureASRLanguageSpecs();
}
}
List<Map<String, String>> _getTranslationSpecs(SpeechServiceType provider) {
switch (provider) {
case SpeechServiceType.azure:
return getAzureTranslationLanguageSpecs();
case SpeechServiceType.google:
return getGoogleTranslationLanguageSpecs();
case SpeechServiceType.iflytek:
return getIflytekTranslationLanguageSpecs();
case SpeechServiceType.volcano:
return getVolcanoTranslationLanguageSpecs();
case SpeechServiceType.alibaba:
return getAlibabaTranslationLanguageSpecs();
default:
return getAzureTranslationLanguageSpecs();
}
}
List<Map<String, String>> _getTtsSpecs(SpeechServiceType provider) {
switch (provider) {
case SpeechServiceType.azure:
return getAzureTTSLanguageSpecs();
case SpeechServiceType.google:
return getGoogleTTSLanguageSpecs();
case SpeechServiceType.iflytek:
return getIflytekTTSLanguageSpecs();
case SpeechServiceType.alibaba:
return getAlibabaTranslationLanguageSpecs();
default:
return getAzureTTSLanguageSpecs();
}
}
}
class BestProviderResult {
final String provider;
final Map<String, String> sourceSpec;
final Map<String, String> targetSpec;
BestProviderResult(this.provider, this.sourceSpec, this.targetSpec);
}