package comm import "strings" // 语言码归一:客户端与各服务商用的码不是一套。 // // 客户端一律传 BCP-47(zh-CN / en-US / ja-JP…),而阿里云机器翻译要的是短 ISO 码 // (zh / en / ja…)。传错格式阿里云不会报错,只会返回空结果或原文, // 排查起来非常费劲,所以这里统一收口。 // // 这份表原先在 modules/echomeet 里私有,实时翻译接口也要用,提到 comm 共用。 var bcp47ToShort = map[string]string{ "zh-CN": "zh", "zh-TW": "zh", "zh-HK": "zh", "en-US": "en", "en-GB": "en", "ja-JP": "ja", "ko-KR": "ko", "id-ID": "id", "es-MX": "es", "es-ES": "es", "pt-BR": "pt", "pt-PT": "pt", "de-DE": "de", "fr-FR": "fr", "fil-PH": "fil", "ms-MY": "ms", "th-TH": "th", "ar-SA": "ar", "ru-RU": "ru", "ru-UA": "ru", "vi-VN": "vi", "tr-TR": "tr", "pl-PL": "pl", "nl-NL": "nl", "it-IT": "it", "uk-UA": "uk", // ⚠️ 挪威语:客户端按 Azure 的语音码传 nb-NO,取主标签会得到 nb, // 而阿里云机器翻译认的是 **no**(2026-09-15 真机实测:zh→no / no→zh / en→no 全通, // 传 nb 一律回 `10033 语种拼写错误`)。这类"两家用不同 ISO 变体"的语种必须显式列表, // 靠取主标签的兜底救不了。 "nb-NO": "no", "nn-NO": "no", "no-NO": "no", } // BCP47ToShortLang 把 BCP-47 语言码转成短 ISO 码(阿里云机器翻译用)。 // // 表里没有的原样返回:一是客户端可能已经传的就是短码,二是新语种在补表之前 // 至少还能透传给服务商试一把,比硬变成空串强。 func BCP47ToShortLang(code string) string { c := strings.TrimSpace(code) if c == "" { return "" } if v, ok := bcp47ToShort[c]; ok { return v } // 已经是短码(en / zh)或没收录的地区变体(xx-YY):取主语言子标签 if i := strings.IndexByte(c, '-'); i > 0 { return strings.ToLower(c[:i]) } return strings.ToLower(c) } // IsLangUnsupportedErr 判断服务商返回的错误是不是「这个语种/语向不支持」。 // // 阿里云机器翻译的两个码(2026-09-15 实测确认语义): // // 10005 translate from source to target not support —— 码认识,但这条语向不通(如乌克兰语) // 10033 语种拼写错误 —— 这个语种码根本不在它的支持表里(如克罗地亚语,hr/hrv/cro 都不认) // // 这类错误**重试无用**,调用方应当直接告诉用户换一个语言,而不是「请稍后再试」。 func IsLangUnsupportedErr(err error) bool { if err == nil { return false } s := err.Error() return strings.Contains(s, "code=10005") || strings.Contains(s, "code=10033") || strings.Contains(s, "not support") || strings.Contains(s, "语种拼写错误") }