在WinForm应用程序中快速实现多语言的处理
在国际化环境下,越来越多的程序需要做多语言版本,以适应各种业务需求的变化。在Winform应用程序中实现多语言也有常规的处理方式处理,不过需要针对每个语言版本,重新修改Winform界面的显示,对一些常规的辅助类,也需要引入一个统一的资源管理类来处理多语言的问题,相对比较繁琐。本篇随笔针对多语言的需求,希望尽量避免繁琐的操作,既能符合本地语种开发人员的开发习惯,又能快速实现Winform程序的多语言场景处理。
1、多语言开发的困惑和思路
在常规的多语言版本程序中,开发总是伴随着很多不愉快的事情,大概列举一些仅供参考:
1)对窗体的多语言处理时,维护多个语言版本的界面非常繁琐;
2)多语言处理的时候,以资源参照的时候,默认键值为一些英文字符串或者单词,不太符合如中文语境的开发,调整代码则需要很多工作量;
3)对于已开发好的程序,全面引入多语言的处理代码,需要大量修改;
4)对于大量中文的多语言处理,工作量望而却步;
5)对于常规Resx文件的处理觉得繁琐
6)缺乏一个统一处理多语言需求的方案
在多语言的处理上,我一直希望找出一种高效的处理方式,由于我的Winform开发框架中很多模块是现成的,希望能够使用继承处理的方式,实现最简化的处理;
同时大量中文的英文(针对英文版本)翻译也是一个头痛的事情,突然想到百度的翻译API接口可以利用,那么我们可以利用翻译接口实现开始的翻译,然后对资源进行一定的调整则可以提高效率和准确率。
对于编辑和承载多语言的信息,我一直觉得JSON格式挺好的,可以利用它序列化为字典集合,通过字典获取对应键值的多语言版本字符串也是很高效的一种方式,那么就决定用JSON来存储多语言信息了,易读好用。
对于多余的处理逻辑,尽量封装为独立的模块,可以在多个模块中进行调用处理。
2、多语言的处理实现
在思考多语言的合理处理方案过程中,参考了另一位博友的文章《分享两种实现Winform程序的多语言支持的解决方案》,思路有点符合我的期望,因此吸收了一些处理思想进行处理,目的就是提高开发效率。
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.DeserializeObject<Dictionary<string, 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 name="language">默认的语言类型,如zh-Hans,en-US等</param>
private void LoadLanguage(string language = "")
{
if (string.IsNullOrEmpty(language))
{
language = System.Threading.Thread.CurrentThread.CurrentUICulture.Name;
} this.resources = new Dictionary<string, 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翻译也可以,增加接口实现即可)
百度翻译接口的使用,你先注册一个开发账户,获得相应的秘钥信息就可以使用免费的翻译接口了(http://api.fanyi.baidu.com/api/trans/product/index)。
有了这些准备后,就可以利用C#代码进行翻译处理了。
百度翻译的接口处理代码如下所示。
/// <summary>
/// 百度接口翻译
/// </summary>
/// <param name="inputString">输入字符串</param>
/// <param name="from">源内容语言</param>
/// <param name="to">目标语言</param>
/// <returns></returns>
private static string BaiduTranslate(string inputString, string from = "zh", string to = "en")
{
string content = ""; string appId = "你的APPID";
string securityId = "你的秘钥";
int salt = ; 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}&from=zh&to=en&appid={1}&salt={2}&sign={3}", inputString, appId, salt, md5Result);
byte[] buffer = client.DownloadData(url);
string result = Encoding.UTF8.GetString(buffer); var trans = JsonConvert.DeserializeObject<TranslationJson>(result);
if (trans != null)
{
content = trans.trans_result[].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 List<TranslationResult> 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进行包装下即可
//进行排序
SortedDictionary<string, string> sortedDict = new SortedDictionary<string, 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");//英文界面
然后我们在Winform程序中开发设计我们的界面内容,例如设计一个普通的界面如下所示。
这个窗体我们添加了几个按钮,并设置它的中文显示内容,它的基类默认还是保持它的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这些窗体基类不变,只是调整了这些基类的加载,增加一行代码就可以顺利实现了多语言的效果了。
这样我们就把核心的工作放在提取界面中的中文资源并进行整理即可,这是核心的工作但翻译也基本不用自己从头做,窗体代码几乎不需要做其他修改就实现了我们所需要的多语言效果了,这样做极大提高了开发效率,对于我们已经开发好的模块,更是四两拨千斤了。
在WinForm应用程序中快速实现多语言的处理的更多相关文章
- 在WinForm应用程序中快速实现多语言的处理(2)--开发框架模块的整合
我在上篇随笔<在WinForm应用程序中快速实现多语言的处理>里面介绍了Winform开发中多语言的处理解决方案,整个多语言解决方案是以实际需求为驱动,以减少代码改动,高效处理为目的,通过 ...
- C#WinForm应用程序中嵌入ECharts图表
C#WinForm应用程序中嵌入ECharts图表 程序运行效果: 下载ECharts: 官网下载ECharts :http://echarts.baidu.com/download.html 或者直 ...
- 在WinForm应用程序中嵌入WPF控件
我们知道,在WPF界面上添加WinForm的控件需要使用WindowsFormHost类.而在WinForm界面上添加WPF控件该如何做呢?有没有类似的类呢?明显是有的,ElementHost就是为了 ...
- WinForm应用程序中实现自动更新功能
WinForm应用程序中实现自动更新功能 编写人:左丘文 2015-4-20 近来在给一客户实施ECM系统,但他们使用功能并不是我们ECM制造版提供的标准功能,他们要求对系统作一些定制功能,为了避免因 ...
- Java程序员快速入门Go语言
这篇文章帮助Java程序员快速入门Go语言. 转载至 开源中国社区. http://www.oschina.net 本文将以一个有代表性的例子为开始,以此让Java程序员对Go语言有个初步认识,随后将 ...
- 解决Winform应用程序中窗体背景闪烁的问题
本文转载:https://my.oschina.net/Tsybius2014/blog/659742 我的操作系统是Win7,使用的VS版本是VS2012,文中的代码都是C#代码. 这几天遇到一个问 ...
- 在WinForm应用程序中,使用选项卡控件来加载不同的Form界面!
TabPage tp=new TabPage(); your选项卡控件.Controls.Add(tp); From1 frm=new Form1(); frm.TopLevel = false; f ...
- (转)C#.NET WINFORM应用程序中控制应用程序只启动一次
原文地址 :http://www.cnblogs.com/emanlee/archive/2009/08/31/1557379.html using System; using System.Thre ...
- 在ASP.NET MVC5应用程序中快速接入QQ和新浪微博OAuth
http://www.cnblogs.com/xiaoyaojian/p/4611660.html
随机推荐
- 在CSDN开通博客专栏后如何发布文章(图文)
今天打开电脑登上CSDN发现自己授予了专栏勋章,有必要了解如何在专栏发布文章. 很感谢已经有前辈给出了图文教程,此文章转载自博客:http://blog.csdn.net/upi2u/article/ ...
- java反射机制--reflection
反射,reflection,听其名就像照镜子一样,可以看见自己也可以看见别人的每一部分.在java语言中这是一个很重要的特性.下面是来自sun公司官网关于反射的介绍: Reflection is ...
- sed在行首或者行尾添加内容
原文地址:http://www.cnblogs.com/ITEagle/archive/2013/06/20/3145546.html 用sed命令在行首或行尾添加字符的命令有以下几种: 假设处理的文 ...
- JAVA之旅(二十二)——Map概述,子类对象特点,共性方法,keySet,entrySet,Map小练习
JAVA之旅(二十二)--Map概述,子类对象特点,共性方法,keySet,entrySet,Map小练习 继续坚持下去吧,各位骚年们! 事实上,我们的数据结构,只剩下这个Map的知识点了,平时开发中 ...
- 【翻译】Ext JS 6 Beta发布
原文:Ext JS 6 Beta is Now Available 概述 Ext JS 6的好处 新的Ext JS功能和工具 需要你的反馈意见 概述 很高兴,Ext JS 6 beta版本现在发布了. ...
- C++ Primer 有感(重载操作符)
1.用于内置类型的操作符,其含义不能改变.也不能为任何内置类型定义额外的新的操作符.(重载操作符必须具有至少一个类类型或枚举类型的操作数.这条规则强制重载操作符不能重新定义用于内置类型对象的操作符的含 ...
- Iterm2安装Zsh + Oh My Zsh+Solarized
安装Oh My Zsh curl -L https://raw.github.com/robbyrussell/oh-my-zsh/master/tools/install.sh | sh 安装Zsh ...
- 【嵌入式开发】C语言 结构体相关 的 函数 指针 数组
. 作者 : 万境绝尘 转载请注明出处 : http://www.hanshuliang.com/?post=30 . 结构体概述 : 结构体是 多个 变量的集合, 变量的类型可以不同; -- 可进行 ...
- Cocos2d中update与fixedUpdate的区别(六)
它如何工作呢? update:和fixedUpdate:方法实际这样工作. Cocos2D将从iOS接口中取得时间间隔(delta)在你的游戏代码执行期间,并且检查fixedUpdate:方法在间隔期 ...
- Linux特殊权限分析(第二版)
SetUID[权限值=4] 问题:为什么普通用户可以修改自己的密码? ll $(which passwd) 1.SetUID:当一个可执行程序/命令具有SetUID 权限,用户执行这个程序时,将以这个 ...