LanguageCatalog.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. namespace LocalizationTool.Services
  5. {
  6. /// <summary>语言条目:系统代码 / 显示名 / 中文名 / 百度翻译代码 的映射。</summary>
  7. public class LanguageEntry
  8. {
  9. /// <summary>系统语言代码(= .dat 文件名 / 语言包 LanguageCode)。主流语言为 BCP 47(如 ja-JP),小众语言直接使用百度代码。</summary>
  10. public string Code { get; set; }
  11. /// <summary>显示名(主流语言为本地名,小众语言为中文名)。</summary>
  12. public string Name { get; set; }
  13. /// <summary>中文名,如 "日语"。</summary>
  14. public string ChineseName { get; set; }
  15. /// <summary>百度翻译 API 的语言代码,如 "jp"。</summary>
  16. public string BaiduCode { get; set; }
  17. /// <summary>百度是否支持该语言的自动语种检测。</summary>
  18. public bool SupportsDetect { get; set; }
  19. /// <summary>下拉框显示文本(显示名与中文名相同时不重复)。</summary>
  20. public string Display =>
  21. string.Equals(Name, ChineseName, StringComparison.Ordinal)
  22. ? $"{Name} ({Code})"
  23. : $"{Name} ({Code}) —— {ChineseName}";
  24. }
  25. /// <summary>
  26. /// 语言映射目录:系统语言代码 ↔ 百度翻译代码 的全量对照表。
  27. /// 数据来源:百度翻译开放平台官方语言列表(https://fanyi-api.baidu.com/doc/21 附录)。
  28. /// 添加语言时从这里展开选择;翻译时由 GetBaiduCode 自动映射为百度的字段。
  29. /// </summary>
  30. public static class LanguageCatalog
  31. {
  32. /// <summary>
  33. /// 百度全量语言原始数据:百度码 | 中文名 | 支持语种检测。
  34. /// ("auto 自动检测"不属于可新增的翻译目标语言,未列入。)
  35. /// </summary>
  36. private static readonly string[] BaiduRaw = new[]
  37. {
  38. // A
  39. "ara|阿拉伯语|是", "gle|爱尔兰语|是", "oci|奥克语|是",
  40. "alb|阿尔巴尼亚语|是","arq|阿尔及利亚阿拉伯语|否","aka|阿肯语|否",
  41. "arg|阿拉贡语|否", "amh|阿姆哈拉语|是", "asm|阿萨姆语|是",
  42. "aym|艾马拉语|否", "aze|阿塞拜疆语|是", "ast|阿斯图里亚斯语|是",
  43. "oss|奥塞梯语|否", "est|爱沙尼亚语|是", "oji|奥杰布瓦语|否",
  44. "ori|奥里亚语|是", "orm|奥罗莫语|否",
  45. // B
  46. "pl|波兰语|是", "per|波斯语|是", "bre|布列塔尼语|是",
  47. "bak|巴什基尔语|否", "baq|巴斯克语|是", "pot|巴西葡萄牙语|否",
  48. "bel|白俄罗斯语|是", "ber|柏柏尔语|是", "pam|邦板牙语|否",
  49. "bul|保加利亚语|是", "sme|北方萨米语|否", "ped|北索托语|否",
  50. "bem|本巴语|否", "bli|比林语|否", "bis|比斯拉马语|否",
  51. "bal|俾路支语|否", "ice|冰岛语|是", "bos|波斯尼亚语|是",
  52. "bho|博杰普尔语|否",
  53. // C
  54. "chv|楚瓦什语|否", "tso|聪加语|否",
  55. // D
  56. "dan|丹麦语|是", "de|德语|是", "tat|鞑靼语|是",
  57. "sha|掸语|否", "tet|德顿语|否", "div|迪维希语|否",
  58. "log|低地德语|是",
  59. // E
  60. "ru|俄语|是",
  61. // F
  62. "fra|法语|是", "fil|菲律宾语|是", "fin|芬兰语|是",
  63. "san|梵语|否", "fri|弗留利语|否", "ful|富拉尼语|否",
  64. "fao|法罗语|否",
  65. // G
  66. "gla|盖尔语|否", "kon|刚果语|否", "ups|高地索布语|否",
  67. "hkm|高棉语|是", "kal|格陵兰语|否", "geo|格鲁吉亚语|是",
  68. "guj|古吉拉特语|是", "gra|古希腊语|否", "eno|古英语|否",
  69. "grn|瓜拉尼语|否",
  70. // H
  71. "kor|韩语|是", "nl|荷兰语|是", "hup|胡帕语|否",
  72. "hak|哈卡钦语|否", "ht|海地语|否", "mot|黑山语|否",
  73. "hau|豪萨语|否",
  74. // J
  75. "kir|吉尔吉斯语|否", "glg|加利西亚语|是", "frn|加拿大法语|否",
  76. "cat|加泰罗尼亚语|是","cs|捷克语|是",
  77. // K
  78. "kab|卡拜尔语|是", "kan|卡纳达语|是", "kau|卡努里语|否",
  79. "kah|卡舒比语|否", "cor|康瓦尔语|否", "xho|科萨语|是",
  80. "cos|科西嘉语|否", "cre|克里克语|否", "cri|克里米亚鞑靼语|否",
  81. "kli|克林贡语|否", "hrv|克罗地亚语|是", "que|克丘亚语|否",
  82. "kas|克什米尔语|否", "kok|孔卡尼语|否", "kur|库尔德语|是",
  83. // L
  84. "lat|拉丁语|是", "lao|老挝语|否", "rom|罗马尼亚语|是",
  85. "lag|拉特加莱语|否", "lav|拉脱维亚语|是", "lim|林堡语|否",
  86. "lin|林加拉语|否", "lug|卢干达语|否", "ltz|卢森堡语|否",
  87. "ruy|卢森尼亚语|否", "kin|卢旺达语|是", "lit|立陶宛语|是",
  88. "roh|罗曼什语|否", "ro|罗姆语|否", "loj|逻辑语|否",
  89. // M
  90. "may|马来语|是", "bur|缅甸语|是", "mar|马拉地语|否",
  91. "mg|马拉加斯语|是", "mal|马拉雅拉姆语|是", "mac|马其顿语|是",
  92. "mah|马绍尔语|否", "mai|迈蒂利语|是", "glv|曼克斯语|否",
  93. "mau|毛里求斯克里奥尔语|否","mao|毛利语|否", "ben|孟加拉语|是",
  94. "mlt|马耳他语|是", "hmn|苗语|否",
  95. // N
  96. "nor|挪威语|是", "nea|那不勒斯语|否", "nbl|南恩德贝莱语|否",
  97. "afr|南非荷兰语|是", "sot|南索托语|否", "nep|尼泊尔语|是",
  98. // P
  99. "pt|葡萄牙语|是", "pan|旁遮普语|是", "pap|帕皮阿门托语|否",
  100. "pus|普什图语|否",
  101. // Q
  102. "nya|齐切瓦语|否", "twi|契维语|否", "chr|切罗基语|否",
  103. // R
  104. "jp|日语|是", "swe|瑞典语|是",
  105. // S
  106. "srd|萨丁尼亚语|否", "sm|萨摩亚语|否", "sec|塞尔维亚-克罗地亚语|否",
  107. "srp|塞尔维亚语|是", "sol|桑海语|否", "sin|僧伽罗语|是",
  108. "epo|世界语|是", "nob|书面挪威语|是", "sk|斯洛伐克语|是",
  109. "slo|斯洛文尼亚语|是","swa|斯瓦希里语|是", "src|塞尔维亚语(西里尔)|否",
  110. "som|索马里语|是", "sco|苏格兰语|否",
  111. // T
  112. "th|泰语|是", "tr|土耳其语|是", "tgk|塔吉克语|是",
  113. "tam|泰米尔语|是", "tgl|他加禄语|是", "tir|提格利尼亚语|否",
  114. "tel|泰卢固语|是", "tua|突尼斯阿拉伯语|否","tuk|土库曼语|否",
  115. // W
  116. "ukr|乌克兰语|是", "wln|瓦隆语|是", "wel|威尔士语|是",
  117. "ven|文达语|否", "wol|沃洛夫语|否", "urd|乌尔都语|是",
  118. // X
  119. "spa|西班牙语|是", "heb|希伯来语|是", "el|希腊语|是",
  120. "hu|匈牙利语|是", "fry|西弗里斯语|是", "sil|西里西亚语|否",
  121. "hil|希利盖农语|否", "los|下索布语|否", "haw|夏威夷语|否",
  122. "nno|新挪威语|是", "nqo|西非书面语|否", "snd|信德语|否",
  123. "sna|修纳语|否", "ceb|宿务语|否", "syr|叙利亚语|否",
  124. "sun|巽他语|否",
  125. // Y
  126. "en|英语|是", "hi|印地语|是", "id|印尼语|是",
  127. "it|意大利语|是", "vie|越南语|是", "yid|意第绪语|否",
  128. "ina|因特语|否", "ach|亚齐语|否", "ing|印古什语|否",
  129. "ibo|伊博语|否", "ido|伊多语|否", "yor|约鲁巴语|否",
  130. "arm|亚美尼亚语|是", "iku|伊努克提图特语|否",
  131. // Z
  132. "zh|中文(简体)|是", "cht|中文(繁体)|是", "wyw|中文(文言文)|是",
  133. "yue|中文(粤语)|是", "zaz|扎扎其语|否", "frm|中古法语|否",
  134. "zul|祖鲁语|否", "jav|爪哇语|否",
  135. };
  136. /// <summary>
  137. /// 主流语言的系统代码与本地显示名(百度码 → 系统码 + 本地名)。
  138. /// 未在此表中的语言:系统码直接用百度码,显示名用中文名。
  139. /// </summary>
  140. private static readonly Dictionary<string, (string Code, string Name)> Preferred =
  141. new Dictionary<string, (string, string)>(StringComparer.OrdinalIgnoreCase)
  142. {
  143. ["zh"] = ("zh-CN", "简体中文"),
  144. ["cht"] = ("zh-TW", "繁體中文"),
  145. ["yue"] = ("yue", "粵語"),
  146. ["en"] = ("en-US", "English"),
  147. ["jp"] = ("ja-JP", "日本語"),
  148. ["kor"] = ("ko-KR", "한국어"),
  149. ["fra"] = ("fr-FR", "Français"),
  150. ["spa"] = ("es-ES", "Español"),
  151. ["de"] = ("de-DE", "Deutsch"),
  152. ["it"] = ("it-IT", "Italiano"),
  153. ["pt"] = ("pt-BR", "Português"),
  154. ["ru"] = ("ru-RU", "Русский"),
  155. ["ara"] = ("ar-SA", "العربية"),
  156. ["th"] = ("th-TH", "ไทย"),
  157. ["nl"] = ("nl-NL", "Nederlands"),
  158. ["el"] = ("el-GR", "Ελληνικά"),
  159. ["pl"] = ("pl-PL", "Polski"),
  160. ["bul"] = ("bg-BG", "Български"),
  161. ["est"] = ("et-EE", "Eesti"),
  162. ["dan"] = ("da-DK", "Dansk"),
  163. ["fin"] = ("fi-FI", "Suomi"),
  164. ["cs"] = ("cs-CZ", "Čeština"),
  165. ["rom"] = ("ro-RO", "Română"),
  166. ["slo"] = ("sl-SI", "Slovenščina"),
  167. ["swe"] = ("sv-SE", "Svenska"),
  168. ["hu"] = ("hu-HU", "Magyar"),
  169. ["vie"] = ("vi-VN", "Tiếng Việt"),
  170. ["tr"] = ("tr-TR", "Türkçe"),
  171. ["id"] = ("id-ID", "Bahasa Indonesia"),
  172. ["may"] = ("ms-MY", "Bahasa Melayu"),
  173. ["ukr"] = ("uk-UA", "Українська"),
  174. ["heb"] = ("he-IL", "עברית"),
  175. ["ice"] = ("is-IS", "Íslenska"),
  176. ["lav"] = ("lv-LV", "Latviešu"),
  177. ["lit"] = ("lt-LT", "Lietuvių"),
  178. ["hrv"] = ("hr-HR", "Hrvatski"),
  179. ["srp"] = ("sr-RS", "Srpski"),
  180. ["sk"] = ("sk-SK", "Slovenčina"),
  181. ["alb"] = ("sq-AL", "Shqip"),
  182. ["aze"] = ("az-AZ", "Azərbaycanca"),
  183. ["geo"] = ("ka-GE", "ქართული"),
  184. ["cat"] = ("ca-ES", "Català"),
  185. ["glg"] = ("gl-ES", "Galego"),
  186. ["baq"] = ("eu-ES", "Euskara"),
  187. ["mlt"] = ("mt-MT", "Malti"),
  188. ["afr"] = ("af-ZA", "Afrikaans"),
  189. ["epo"] = ("eo", "Esperanto"),
  190. ["wel"] = ("cy-GB", "Cymraeg"),
  191. ["fil"] = ("fil-PH","Filipino"),
  192. ["ben"] = ("bn-IN", "বাংলা"),
  193. ["tam"] = ("ta-IN", "தமிழ்"),
  194. ["tel"] = ("te-IN", "తెలుగు"),
  195. ["pan"] = ("pa-IN", "ਪੰਜਾਬੀ"),
  196. ["mar"] = ("mr-IN", "मराठी"),
  197. ["mal"] = ("ml-IN", "മലയാളം"),
  198. ["kan"] = ("kn-IN", "ಕನ್ನಡ"),
  199. ["guj"] = ("gu-IN", "ગુજરાતી"),
  200. ["hi"] = ("hi-IN", "हिन्दी"),
  201. ["urd"] = ("ur-PK", "اردو"),
  202. ["per"] = ("fa-IR", "فارسی"),
  203. ["bur"] = ("my-MM", "မြန်မာ"),
  204. ["nep"] = ("ne-NP", "नेपाली"),
  205. ["sin"] = ("si-LK", "සිංහල"),
  206. ["swa"] = ("sw-KE", "Kiswahili"),
  207. ["amh"] = ("am-ET", "አማርኛ"),
  208. ["kur"] = ("ku", "Kurdî"),
  209. ["lat"] = ("la", "Latine"),
  210. ["san"] = ("sa", "संस्कृतम्"),
  211. };
  212. /// <summary>全量语言目录(已按系统码排序,主流语言在前风格由排序自然决定)。</summary>
  213. public static readonly List<LanguageEntry> All = Build();
  214. private static List<LanguageEntry> Build()
  215. {
  216. var list = new List<LanguageEntry>();
  217. var seen = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
  218. foreach (var line in BaiduRaw)
  219. {
  220. var parts = line.Split('|');
  221. if (parts.Length < 3) continue;
  222. var baidu = parts[0].Trim();
  223. var cn = parts[1].Trim();
  224. var detect = string.Equals(parts[2].Trim(), "是", StringComparison.Ordinal);
  225. // 去重(官方表偶有重复行,如斯洛文尼亚语出现两次)
  226. if (!seen.Add(baidu)) continue;
  227. var hasPreferred = Preferred.TryGetValue(baidu, out var pref);
  228. var code = hasPreferred ? pref.Code : baidu;
  229. var name = hasPreferred ? pref.Name : cn;
  230. list.Add(new LanguageEntry
  231. {
  232. Code = code,
  233. Name = name,
  234. ChineseName = cn,
  235. BaiduCode = baidu,
  236. SupportsDetect = detect,
  237. });
  238. }
  239. // 主流语言排前面(系统码精确排序:zh 系 → 常见 → 其余按码)
  240. list.Sort((a, b) =>
  241. {
  242. int Rank(LanguageEntry e)
  243. {
  244. switch (e.BaiduCode)
  245. {
  246. case "zh": return 0;
  247. case "cht": return 1;
  248. case "en": return 2;
  249. case "jp": return 3;
  250. case "kor": return 4;
  251. default: return 10;
  252. }
  253. }
  254. var r = Rank(a).CompareTo(Rank(b));
  255. return r != 0 ? r : string.CompareOrdinal(a.Code, b.Code);
  256. });
  257. return list;
  258. }
  259. /// <summary>
  260. /// 系统语言代码 → 百度翻译代码。
  261. /// 优先精确匹配 Code;其次匹配主代码(如 "ja-XX" → ja-JP → jp);都没有则返回主代码本身兜底。
  262. /// </summary>
  263. public static string GetBaiduCode(string langCode)
  264. {
  265. if (string.IsNullOrWhiteSpace(langCode))
  266. return "zh";
  267. var code = langCode.Trim();
  268. // 1) 精确匹配
  269. var exact = All.FirstOrDefault(e => string.Equals(e.Code, code, StringComparison.OrdinalIgnoreCase));
  270. if (exact != null) return exact.BaiduCode;
  271. // 2) 主代码匹配("ja-XX" 之类非标准区域码也能命中)
  272. var main = code.Split('-')[0].ToLowerInvariant();
  273. var byMain = All.FirstOrDefault(e =>
  274. string.Equals(e.Code.Split('-')[0], main, StringComparison.OrdinalIgnoreCase));
  275. if (byMain != null) return byMain.BaiduCode;
  276. // 3) 兜底:直接给主代码(也许百度认识)
  277. return main;
  278. }
  279. /// <summary>按系统代码查找条目。</summary>
  280. public static LanguageEntry Find(string langCode) =>
  281. All.FirstOrDefault(e => string.Equals(e.Code, langCode, StringComparison.OrdinalIgnoreCase));
  282. }
  283. }