/// 一次识别结果该按哪个方向翻译。 /// /// 抽成纯函数是为了能脱离设备/网络单测:方向判错不会报任何错, /// 只表现为「识别出字了、译文不出来或者是原文」,靠真机盲测极难发现。 class TranslationDirection { const TranslationDirection(this.source, this.target); final String source; final String target; @override bool operator ==(Object other) => other is TranslationDirection && other.source == source && other.target == target; @override int get hashCode => Object.hash(source, target); @override String toString() => '$source -> $target'; } /// 单向翻译的模式:同声翻译、多媒体(音视频)翻译。 /// /// 这两个模式的语义是「把听到的内容翻成目标语言播出来」,方向固定。 /// 面对面要判断谁在说(双向),通话翻译两条腿各自定死方向,都不在此列。 bool isOneWayTranslationMode(String mode) => mode == 'simultaneous' || mode == 'audioVideo'; /// 计算这一句的翻译方向。 /// /// [speaker] 仅面对面模式有意义:1 = 源语言那一方,2 = 目标语言那一方,0 = 未知。 /// [detectedLanguageCode] 是 ASR 上报的语种,**只在面对面的未知说话人分支才采信**。 TranslationDirection resolveTranslationDirection({ required String mode, required String sourceCode, required String targetCode, String detectedLanguageCode = '', int speaker = 0, }) { // 单向:永远 源 → 目标,不看 ASR 报的语种。 // // ⚠️ 这两个模式给 ASR 的本来就只有一种语言,固定语种下原生上报的 // detectedLanguage 形式并不保证与 sourceCode 逐字相等(空串 / 大小写 / // 带不带地区都出现过)。老实现拿它去比,一旦不相等就判成反向, // 把源语言的句子当成目标语言送去翻译 —— 方向整个颠倒。 if (isOneWayTranslationMode(mode)) { return TranslationDirection(sourceCode, targetCode); } if (mode == 'faceToFace') { if (speaker == 1) return TranslationDirection(sourceCode, targetCode); if (speaker == 2) return TranslationDirection(targetCode, sourceCode); } // 面对面且说不清是谁在说(或通话模式):按 ASR 检测结果定方向。 final fromSource = detectedLanguageCode == sourceCode; return fromSource ? TranslationDirection(sourceCode, targetCode) : TranslationDirection(targetCode, sourceCode); }