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 _languageMap = {}; // 中文名称到 ASR 代码的映射 final Map _chineseNameToAsrCodeMap = {}; // 英文名称到 ASR 代码的映射 final Map _englishNameToAsrCodeMap = {}; // 短代码到 ASR 代码的映射 final Map _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 getAllLanguages() { return Map.from(_languageMap); } /// 获取所有支持的语言的中文名称 List getAllChineseNames() { return _chineseNameToAsrCodeMap.keys.toList(); } /// 获取所有支持的语言的英文名称 List 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 getChineseNameToAsrCodeMap() { return Map.from(_chineseNameToAsrCodeMap); } /// 获取英文名称到 ASR 代码的映射 Map getEnglishNameToAsrCodeMap() { return Map.from(_englishNameToAsrCodeMap); } /// 获取短代码到 ASR 代码的映射 Map getShortCodeToAsrCodeMap() { return Map.from(_shortCodeToAsrCodeMap); } /// 返回当前所有的AST语言配置(中文名称) List getAllAstLanguageNames() { return getAstLanguageConfigs() .map((e) => e['chineseName'] ?? '') .where((e) => e.isNotEmpty) .toList(); } /// 查找同时支持源语言和目标语言的最佳提供商(通过 shortCode 匹配) /// 优先级:豆包(Volcano) > 阿里(Alibaba) > 微软(Azure) > 讯飞(Iflytek) BestProviderResult? findBestMatchingProvider( String sourceShortCode, String targetShortCode) { final providers = [ SpeechServiceType.volcano, SpeechServiceType.alibaba, SpeechServiceType.azure, SpeechServiceType.iflytek, ]; for (final provider in providers) { final transSpecs = _getTranslationSpecs(provider); Map? sourceTransSpec; Map? 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) { continue; } // 豆包特殊限制:源语言或目标语言必须有一个是中文或英语 if (provider == SpeechServiceType.volcano) { final isSourceZhOrEn = sourceShortCode == 'zh' || sourceShortCode == 'en'; final isTargetZhOrEn = targetShortCode == 'zh' || targetShortCode == 'en'; if (!isSourceZhOrEn && !isTargetZhOrEn) { continue; } } // 对于非端到端服务(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"] = _getGenderAwareTtsCode(spec['shortCode']!) ?? spec["ttsVoiceName"]!; } if (spec['shortCode'] == targetShortCode) { targetTransSpec["ttsCode"] = _getGenderAwareTtsCode(spec['shortCode']!) ?? spec["ttsVoiceName"]!; } } } final providerString = _providerStringFromType(provider); Logger.info( 'Found best provider: $providerString for $sourceShortCode -> $targetShortCode, sourceTts=${sourceTransSpec["ttsCode"]}, targetTts=${targetTransSpec["ttsCode"]}'); return BestProviderResult( providerString, sourceTransSpec, targetTransSpec); } 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'; } } // ===== 私有工具方法:加载不同提供商的语言规格 ===== List> _getAsrSpecs(SpeechServiceType provider) { switch (provider) { case SpeechServiceType.azure: return getAzureASRLanguageSpecs(); case SpeechServiceType.google: return getGoogleASRLanguageSpecs(); case SpeechServiceType.iflytek: return getIflytekASRLanguageSpecs(); default: return getAzureASRLanguageSpecs(); } } List> _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> _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 sourceSpec; final Map targetSpec; BestProviderResult(this.provider, this.sourceSpec, this.targetSpec); }