|
@@ -3,7 +3,9 @@ using System.Collections.Generic;
|
|
|
using System.Collections.Specialized;
|
|
using System.Collections.Specialized;
|
|
|
using System.Globalization;
|
|
using System.Globalization;
|
|
|
using System.IO;
|
|
using System.IO;
|
|
|
|
|
+using System.Linq;
|
|
|
using System.Reflection;
|
|
using System.Reflection;
|
|
|
|
|
+using System.Text.RegularExpressions;
|
|
|
using System.Threading;
|
|
using System.Threading;
|
|
|
using System.Windows.Forms;
|
|
using System.Windows.Forms;
|
|
|
using System.Xml;
|
|
using System.Xml;
|
|
@@ -24,10 +26,18 @@ namespace UAS_MES_NEW.I18n
|
|
|
private static readonly string ResourceName =
|
|
private static readonly string ResourceName =
|
|
|
typeof(LanguageManager).Namespace + ".Lang.xml";
|
|
typeof(LanguageManager).Namespace + ".Lang.xml";
|
|
|
|
|
|
|
|
|
|
+ // 嵌入资源逻辑名:根命名空间.I18n.Templates.xml
|
|
|
|
|
+ private static readonly string TemplatesResourceName =
|
|
|
|
|
+ typeof(LanguageManager).Namespace + ".Templates.xml";
|
|
|
|
|
+
|
|
|
// code -> (中文源文 -> 译文)
|
|
// code -> (中文源文 -> 译文)
|
|
|
private static readonly Dictionary<string, Dictionary<string, string>> _dict =
|
|
private static readonly Dictionary<string, Dictionary<string, string>> _dict =
|
|
|
new Dictionary<string, Dictionary<string, string>>();
|
|
new Dictionary<string, Dictionary<string, string>>();
|
|
|
|
|
|
|
|
|
|
+ // 动态错误模板列表(中文正则 -> 各语言模板)
|
|
|
|
|
+ private static readonly List<MessageTemplate> _templates =
|
|
|
|
|
+ new List<MessageTemplate>();
|
|
|
|
|
+
|
|
|
// code -> 显示名(如 中文 / English)
|
|
// code -> 显示名(如 中文 / English)
|
|
|
private static readonly Dictionary<string, string> _langNames =
|
|
private static readonly Dictionary<string, string> _langNames =
|
|
|
new Dictionary<string, string>();
|
|
new Dictionary<string, string>();
|
|
@@ -51,6 +61,7 @@ namespace UAS_MES_NEW.I18n
|
|
|
static LanguageManager()
|
|
static LanguageManager()
|
|
|
{
|
|
{
|
|
|
LoadResources();
|
|
LoadResources();
|
|
|
|
|
+ LoadTemplates();
|
|
|
SupportedLanguages = new Dictionary<string, string>(_langNames);
|
|
SupportedLanguages = new Dictionary<string, string>(_langNames);
|
|
|
if (!_dict.ContainsKey(_currentCulture))
|
|
if (!_dict.ContainsKey(_currentCulture))
|
|
|
_currentCulture = _defaultCulture;
|
|
_currentCulture = _defaultCulture;
|
|
@@ -143,6 +154,99 @@ namespace UAS_MES_NEW.I18n
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 加载动态错误模板(I18n/Templates.xml 嵌入资源)。
|
|
|
|
|
+ /// 模板用于把数据库返回的“中文正则模式”替换为带变量的多语言文本。
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ private static void LoadTemplates()
|
|
|
|
|
+ {
|
|
|
|
|
+ try
|
|
|
|
|
+ {
|
|
|
|
|
+ Assembly asm = Assembly.GetExecutingAssembly();
|
|
|
|
|
+ Stream s = asm.GetManifestResourceStream(TemplatesResourceName);
|
|
|
|
|
+ if (s == null)
|
|
|
|
|
+ {
|
|
|
|
|
+ foreach (string rn in asm.GetManifestResourceNames())
|
|
|
|
|
+ {
|
|
|
|
|
+ if (rn.EndsWith("Templates.xml", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
+ {
|
|
|
|
|
+ s = asm.GetManifestResourceStream(rn);
|
|
|
|
|
+ break;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (s == null) return;
|
|
|
|
|
+
|
|
|
|
|
+ XmlDocument doc = new XmlDocument();
|
|
|
|
|
+ doc.Load(s);
|
|
|
|
|
+ XmlElement root = doc.DocumentElement;
|
|
|
|
|
+ if (root == null) return;
|
|
|
|
|
+
|
|
|
|
|
+ foreach (XmlNode node in root.SelectNodes("Template"))
|
|
|
|
|
+ {
|
|
|
|
|
+ XmlElement te = node as XmlElement;
|
|
|
|
|
+ if (te == null) continue;
|
|
|
|
|
+ string zhPattern = te.GetAttribute("zh");
|
|
|
|
|
+ if (string.IsNullOrEmpty(zhPattern)) continue;
|
|
|
|
|
+
|
|
|
|
|
+ var translations = new Dictionary<string, string>(StringComparer.Ordinal);
|
|
|
|
|
+ foreach (string code in new[] { "en", "vi", "id", "zh" })
|
|
|
|
|
+ {
|
|
|
|
|
+ string val = te.GetAttribute(code);
|
|
|
|
|
+ if (!string.IsNullOrEmpty(val))
|
|
|
|
|
+ translations[code] = val;
|
|
|
|
|
+ }
|
|
|
|
|
+ _templates.Add(new MessageTemplate(zhPattern, translations));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ catch
|
|
|
|
|
+ {
|
|
|
|
|
+ // 模板加载失败不影响主程序运行
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 动态模板翻译:把数据库返回的带变量中文提示翻译成当前语言。
|
|
|
|
|
+ /// 匹配成功后,用正则捕获组填充模板中的 {0}、{1}... 占位符。
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public static string TranslateTemplate(string text)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (string.IsNullOrEmpty(text) || _currentCulture == _defaultCulture) return text;
|
|
|
|
|
+
|
|
|
|
|
+ foreach (var tmpl in _templates)
|
|
|
|
|
+ {
|
|
|
|
|
+ Match m = tmpl.Regex.Match(text);
|
|
|
|
|
+ if (!m.Success) continue;
|
|
|
|
|
+
|
|
|
|
|
+ if (!tmpl.Translations.TryGetValue(_currentCulture, out string format))
|
|
|
|
|
+ {
|
|
|
|
|
+ if (!tmpl.Translations.TryGetValue(_defaultCulture, out format))
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 用捕获组填充 {0}、{1}...
|
|
|
|
|
+ string[] groups = new string[m.Groups.Count - 1];
|
|
|
|
|
+ for (int i = 1; i < m.Groups.Count; i++)
|
|
|
|
|
+ groups[i - 1] = m.Groups[i].Value;
|
|
|
|
|
+
|
|
|
|
|
+ try { return string.Format(format, groups); }
|
|
|
|
|
+ catch { return format; }
|
|
|
|
|
+ }
|
|
|
|
|
+ return text;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private class MessageTemplate
|
|
|
|
|
+ {
|
|
|
|
|
+ public Regex Regex { get; }
|
|
|
|
|
+ public Dictionary<string, string> Translations { get; }
|
|
|
|
|
+
|
|
|
|
|
+ public MessageTemplate(string zhPattern, Dictionary<string, string> translations)
|
|
|
|
|
+ {
|
|
|
|
|
+ Regex = new Regex(zhPattern, RegexOptions.Compiled);
|
|
|
|
|
+ Translations = translations;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/// <summary>当前语言代码(如 zh / en / vi / hi)。</summary>
|
|
/// <summary>当前语言代码(如 zh / en / vi / hi)。</summary>
|
|
|
public static string CurrentCulture
|
|
public static string CurrentCulture
|
|
|
{
|
|
{
|
|
@@ -225,6 +329,47 @@ namespace UAS_MES_NEW.I18n
|
|
|
return fallback ?? key;
|
|
return fallback ?? key;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
|
+ /// 对一段混合文本进行翻译:扫描其中包含的所有中文 key,并替换为当前语言译文。
|
|
|
|
|
+ /// 用于处理数据库返回的带变量前缀/后缀的中文提示(如 "SN:xxx 无工单归属记录,必须先归属工单")。
|
|
|
|
|
+ /// 按 key 长度降序匹配,避免短 key 截断长 key。
|
|
|
|
|
+ /// </summary>
|
|
|
|
|
+ public static string TranslateContainedKeys(string text)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (string.IsNullOrEmpty(text)) return text;
|
|
|
|
|
+ if (_currentCulture == _defaultCulture) return text;
|
|
|
|
|
+
|
|
|
|
|
+ if (!_dict.TryGetValue(_currentCulture, out var map)) return text;
|
|
|
|
|
+
|
|
|
|
|
+ // 取出所有非空且翻译不同于原文的中文 key,按长度降序排列,
|
|
|
|
|
+ // 这样长句子优先匹配,短词不会先把长句子的组成部分替换掉。
|
|
|
|
|
+ var keys = map
|
|
|
|
|
+ .Where(kv => !string.IsNullOrEmpty(kv.Value)
|
|
|
|
|
+ && kv.Value != kv.Key
|
|
|
|
|
+ && ContainsChinese(kv.Key))
|
|
|
|
|
+ .OrderByDescending(kv => kv.Key.Length)
|
|
|
|
|
+ .ToList();
|
|
|
|
|
+
|
|
|
|
|
+ string result = text;
|
|
|
|
|
+ foreach (var kv in keys)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (result.Contains(kv.Key))
|
|
|
|
|
+ result = result.Replace(kv.Key, kv.Value);
|
|
|
|
|
+ }
|
|
|
|
|
+ return result;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /// <summary>判断字符串中是否包含中文字符。</summary>
|
|
|
|
|
+ private static bool ContainsChinese(string text)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (string.IsNullOrEmpty(text)) return false;
|
|
|
|
|
+ foreach (char c in text)
|
|
|
|
|
+ {
|
|
|
|
|
+ if (c >= 0x4E00 && c <= 0x9FFF) return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
/// <summary>
|
|
/// <summary>
|
|
|
/// 本地化整个窗体:遍历控件树翻译静态文本,并订阅 LanguageChanged
|
|
/// 本地化整个窗体:遍历控件树翻译静态文本,并订阅 LanguageChanged
|
|
|
/// 以便运行时切换语言时自动刷新。可在窗体构造函数 InitializeComponent() 之后调用。
|
|
/// 以便运行时切换语言时自动刷新。可在窗体构造函数 InitializeComponent() 之后调用。
|