WinForm应用实战开发指南 - 如何快速实现多语言处理?

📅 2026/8/22 12:00:30
WinForm应用实战开发指南 - 如何快速实现多语言处理?
在国际化环境下越来越多的程序需要做多语言版本以适应各种业务需求的变化。在Winform应用程序中实现多语言也有常规的处理方式处理不过需要针对每个语言版本重新修改​​​​​​​WinForms界面的显示对一些常规的辅助类也需要引入一个统一的资源管理类来处理多语言的问题相对比较繁琐。本篇随笔针对多语言的需求希望尽量避免繁琐的操作既能符合本地语种开发人员的开发习惯又能快速实现WinForms​​​​​​​程序的多语言场景处理。DevExpress新旧版本帮助文档获取可点击这篇文章查看——《界面控件DevExpress v26.1帮助文档大全CHM版本》多语言开发的困惑和思路在常规的多语言版本程序中开发总是伴随着很多不愉快的事情大概列举一些仅供参考对窗体的多语言处理时维护多个语言版本的界面非常繁琐多语言处理的时候以资源参照的时候默认键值为一些英文字符串或者单词不太符合如中文语境的开发调整代码则需要很多工作量对于已开发好的程序全面引入多语言的处理代码需要大量修改对于大量中文的多语言处理工作量望而却步对于常规Resx文件的处理觉得繁琐缺乏一个统一处理多语言需求的方案。在多语言的处理上我一直希望找出一种高效的处理方式由于我的​​​​​​​WinForms开发框架中很多模块是现成的希望能够使用继承处理的方式实现最简化的处理同时大量中文的英文针对英文版本翻译也是一个头痛的事情突然想到百度的翻译API接口可以利用那么我们可以利用翻译接口实现开始的翻译然后对资源进行一定的调整则可以提高效率和准确率。对于编辑和承载多语言的信息我一直觉得JSON格式挺好的可以利用它序列化为字典集合通过字典获取对应键值的多语言版本字符串也是很高效的一种方式那么就决定用JSON来存储多语言信息了易读好用。对于多余的处理逻辑尽量封装为独立的模块可以在多个模块中进行调用处理。多语言的处理实现1. 多语言的信息存储和加载首先我们来看看多语言处理的目录和格式问题目录大概是根据多语言的简称进行放置如下所示。这个目录就是会输出到debug或者Release的运行目录中我们就是根据相对于运行目录进行资源读取即可所有模块共用同一的多语言文件我们可以把各个模块基础通用的多语言文件放在Basic.json文件中也可以根据模块独立起名主程序如TestMultiLanguage的多语言文件我则放在TestMultiLanguage.json文件中。实际上目录名称是为了区分而已程序加载的时候会把目录下面所有的JSON文件进行加载读取里面的键值作为资源的字典参照。多语言的JSON文件是标准的Json格式只是我们只用键值的字典参考即可不需要使用复杂的JSON对象格式如下是basic.json文件的部分内容。这些资源文件采用中文-英文的参照方式我们以我们常规的母语开发即使我们不做多语言也不影响代码的正常处理我们只需要把窗体上和代码里面的中文提取出来然后进行多语言处理如变为英文即可。由于我们使用键值字典对象的JSON内容那么我们就可以把这些内容序列号为字典集合如下代码我们可以通过 JSON.NET 组件把它们序列化为字典集合这些字典集合就是我们用来做多语言的关键。var content File.ReadAllText(file, Encoding.UTF8); if (!string.IsNullOrEmpty(content)) { var dict JsonConvert.DeserializeObjectDictionarystring, string(content); foreach (string key in dict.Keys) { //遍历集合如果语言资源键值不存在则创建否则更新 if (!resources.ContainsKey(key)) { resources.Add(key, dict[key]); } else { resources[key] dict[key]; } } }加载多语言处理的时候我们遍历相对目录下的lang/***里面的文件即可实现多语言信息的加载如下代码所示。/// summary /// 根据语言初始化信息。 /// 加载对应语言的JSON信息把翻译信息存储在全属性resources里面。 /// /summary /// param namelanguage默认的语言类型如zh-Hansen-US等/param private void LoadLanguage(string language ) { if (string.IsNullOrEmpty(language)) { language System.Threading.Thread.CurrentThread.CurrentUICulture.Name; } this.resources new Dictionarystring, string(); string dir Path.Combine(AppDomain.CurrentDomain.BaseDirectory, string.Format(lang/{0}, language)); if (Directory.Exists(dir)) { var jsonFiles Directory.GetFiles(dir, *.json, SearchOption.AllDirectories); foreach (string file in jsonFiles) { LoadFile(file); } } }我们把多语言的加载和翻译处理放在一个独立的项目上如我定义为框架的一个模块WHC.Framework.Language这样我们在各个模块中使用多语言处理过程的时候包含这个模块就可以了。2. 多语言信息的翻译做多语言的版本程序翻译工作也是一个繁琐的工作如果你是非常精通各种语言如中文、英文、日文等等那当然不在话下不过我们做开发的多少也是会一些的如英语吧即时不能非常准确那么也可以做到差不多但是做这个还是累还容易敲打错别字那么用第三方提供的翻译API来预处理后调整结果就简化很多了可以极大提高效率的。这里以我们经常使用的百度翻译来实现用Google翻译也可以增加接口实现即可。百度翻译接口的使用你先注册一个开发账户获得相应的秘钥信息就可以使用免费的翻译接口了百度翻译开放平台。有了这些准备后就可以利用C#代码进行翻译处理了。百度翻译的接口处理代码如下所示。/// summary /// 百度接口翻译 /// /summary /// param nameinputString输入字符串/param /// param namefrom源内容语言/param /// param nameto目标语言/param /// returns/returns private static string BaiduTranslate(string inputString, string from zh, string to en) { string content ; string appId 你的APPID; string securityId 你的秘钥; int salt 0; StringBuilder signString new StringBuilder(); string md5Result string.Empty; //1.拼接字符,为了生成sign signString.Append(appId); signString.Append(inputString); signString.Append(salt); signString.Append(securityId); //2.通过md5获取sign byte[] sourceMd5Byte Encoding.UTF8.GetBytes(signString.ToString()); MD5 md5 new MD5CryptoServiceProvider(); byte[] destMd5Byte md5.ComputeHash(sourceMd5Byte); md5Result BitConverter.ToString(destMd5Byte).Replace(-, ); md5Result md5Result.ToLower(); try { //3.获取web翻译的json结果 WebClient client new WebClient(); string url string.Format(http://api.fanyi.baidu.com/api/trans/vip/translate?q{0}fromzhtoenappid{1}salt{2}sign{3}, inputString, appId, salt, md5Result); byte[] buffer client.DownloadData(url); string result Encoding.UTF8.GetString(buffer); var trans JsonConvert.DeserializeObjectTranslationJson(result); if (trans ! null) { content trans.trans_result[0].dst; content StringUtil.ToProperCase(content); } } catch(Exception ex) { Debug.WriteLine(ex); } return content; }其中把JSON转换为类对象需要两个类对翻译结果进行转换如下代码所示。internal class TranslationJson { public string from { get; set; } public string to { get; set; } public ListTranslationResult trans_result { get; set; } } internal class TranslationResult { public string src { get; set; } public string dst { get; set; } }这样我们在多语言处理的时候可以对默认输入为空的键值进行翻译即可如英文翻译。//遍历集合进行翻译 var value dict[key]; if (string.IsNullOrWhiteSpace(value)) { //如果值为空那么调用翻译接口处理 var newValue TranslationHelper.Translate(key, from, to); if (!string.IsNullOrWhiteSpace(newValue)) { dict[key] newValue; } }然后重新更新我们的资源文件就可以了。//不排序 var newContent JsonConvert.SerializeObject(dict, Formatting.Indented); File.WriteAllText(file, newContent, Encoding.UTF8);如果需要对键值进行排序那么使用SortDictionary进行包装下即可。//进行排序 SortedDictionarystring, string sortedDict new SortedDictionarystring, string(dict); var newContent JsonConvert.SerializeObject(sortedDict, Formatting.Indented);在多语言处理的时候我们一般不必要一次填写完毕中英文对照的资源我们可以先把字典键值的键写出来值保留为空如下文件所示。运行程序的时候让翻译的接口先行翻译然后我们再对翻译的资源进行调整适应我们程序的语境即可翻译后的内容后如下所示。好了弹药都准备好了就看我们如何使用 下一步介绍如何使用这些资源。3. 多语言在界面中的应用前面介绍都是为程序界面准备好对应的多语言资源内容我们在程序启动的时候可以通过常规的方式设置界面的CurrentUICulture区域信息如下代码所示。//界面多语言 //System.Threading.Thread.CurrentThread.CurrentUICulture new System.Globalization.CultureInfo(zh-Hans);//中文界面 System.Threading.Thread.CurrentThread.CurrentUICulture new System.Globalization.CultureInfo(en-US);//英文界面然后我们在​​​​​​​WinForms程序中开发设计我们的界面内容例如设计一个普通的界面如下所示。这个窗体我们添加了几个按钮并设置它的中文显示内容它的基类默认还是保持它的DevExpress基类 XtraForm如下所示。/// summary /// 测试多语言的窗体界面 /// /summary public partial class Form1 : XtraForm那么我们如果要自动实现多语言的处理那么还需要在窗体的Load或者Shown事件里面实现处理如下代码所示。private void Form1_Shown(object sender, EventArgs e) { //窗体加载并显示后对窗体实现多语言处理 if (!this.DesignMode) { LanguageHelper.InitLanguage(this); } }如果我们为每个窗体都需要添加这些代码也是繁琐的事情那么我们可以把这个处理逻辑放到我们常规自定义的窗体基类里面如BaseForm那么我们就不需要任何额外的代码了。所需的就是集成窗体基类即可这也是我们一般开发都做的事情通过继承使得我们的代码又省去了。/// summary /// 测试多语言的窗体界面 /// /summary public partial class Form1 : BaseForm那么我们真正关注的就是我们前面介绍的逻辑代码实现了。LanguageHelper.InitLanguage(this);这个辅助类主要就是在窗体初始化后遍历界面的所有类型控件对控件进行相应的多语言处理。/// summary /// 对界面控件进行多语言的处理辅助类 /// /summary public class LanguageHelper { /// summary /// 初始化语言 /// /summary public static void InitLanguage(Control control) { //如果没有资源那么不必遍历控件提高速度 if (!JsonLanguage.Default.HasResource) return; //使用递归的方式对控件及其子控件进行处理 SetControlLanguage(control); foreach (Control ctrl in control.Controls) { InitLanguage(ctrl); } //工具栏或者菜单动态构建窗体或者控件的时候重新对子控件进行处理 control.ControlAdded (sender, e) { InitLanguage(e.Control); }; }通过递归的方式我们可以对常规的如GridControl工具栏、NavBar导航栏、菜单、按钮等资源进行统一的多语言处理而这里面对于我们开发应用程序界面都不需要额外的担心极大的提高了效率。下面是几个常规的界面我们来体验下英文版本的界面效果。这些英文界面我们只需要把界面的中文提取出来放到JSON文件中自动翻译再调整即可然后界面继承保持BaseForm或者BaseDock这些窗体基类不变只是调整了这些基类的加载增加一行代码就可以顺利实现了多语言的效果了。这样我们就把核心的工作放在提取界面中的中文资源并进行整理即可这是核心的工作但翻译也基本不用自己从头做窗体代码几乎不需要做其他修改就实现了我们所需要的多语言效果了这样做极大提高了开发效率对于我们已经开发好的模块更是四两拨千斤了。本文转载自博客园 - 伍华聪