在WinForm應(yīng)用程序中快速實(shí)現(xiàn)多語(yǔ)言的處理的方法
在國(guó)際化環(huán)境下,越來(lái)越多的程序需要做多語(yǔ)言版本,以適應(yīng)各種業(yè)務(wù)需求的變化。在Winform應(yīng)用程序中實(shí)現(xiàn)多語(yǔ)言也有常規(guī)的處理方式處理,不過(guò)需要針對(duì)每個(gè)語(yǔ)言版本,重新修改Winform界面的顯示,對(duì)一些常規(guī)的輔助類,也需要引入一個(gè)統(tǒng)一的資源管理類來(lái)處理多語(yǔ)言的問(wèn)題,相對(duì)比較繁瑣。本篇隨筆針對(duì)多語(yǔ)言的需求,希望盡量避免繁瑣的操作,既能符合本地語(yǔ)種開(kāi)發(fā)人員的開(kāi)發(fā)習(xí)慣,又能快速實(shí)現(xiàn)Winform程序的多語(yǔ)言場(chǎng)景處理。
1、多語(yǔ)言開(kāi)發(fā)的困惑和思路
在常規(guī)的多語(yǔ)言版本程序中,開(kāi)發(fā)總是伴隨著很多不愉快的事情,大概列舉一些僅供參考:
1)對(duì)窗體的多語(yǔ)言處理時(shí),維護(hù)多個(gè)語(yǔ)言版本的界面非常繁瑣;
2)多語(yǔ)言處理的時(shí)候,以資源參照的時(shí)候,默認(rèn)鍵值為一些英文字符串或者單詞,不太符合如中文語(yǔ)境的開(kāi)發(fā),調(diào)整代碼則需要很多工作量;
3)對(duì)于已開(kāi)發(fā)好的程序,全面引入多語(yǔ)言的處理代碼,需要大量修改;
4)對(duì)于大量中文的多語(yǔ)言處理,工作量望而卻步;
5)對(duì)于常規(guī)Resx文件的處理覺(jué)得繁瑣
6)缺乏一個(gè)統(tǒng)一處理多語(yǔ)言需求的方案
在多語(yǔ)言的處理上,我一直希望找出一種高效的處理方式,由于我的Winform開(kāi)發(fā)框架中很多模塊是現(xiàn)成的,希望能夠使用繼承處理的方式,實(shí)現(xiàn)最簡(jiǎn)化的處理;
同時(shí)大量中文的英文(針對(duì)英文版本)翻譯也是一個(gè)頭痛的事情,突然想到百度的翻譯API接口可以利用,那么我們可以利用翻譯接口實(shí)現(xiàn)開(kāi)始的翻譯,然后對(duì)資源進(jìn)行一定的調(diào)整則可以提高效率和準(zhǔn)確率。
對(duì)于編輯和承載多語(yǔ)言的信息,我一直覺(jué)得JSON格式挺好的,可以利用它序列化為字典集合,通過(guò)字典獲取對(duì)應(yīng)鍵值的多語(yǔ)言版本字符串也是很高效的一種方式,那么就決定用JSON來(lái)存儲(chǔ)多語(yǔ)言信息了,易讀好用。
對(duì)于多余的處理邏輯,盡量封裝為獨(dú)立的模塊,可以在多個(gè)模塊中進(jìn)行調(diào)用處理。
2、多語(yǔ)言的處理實(shí)現(xiàn)
在思考多語(yǔ)言的合理處理方案過(guò)程中,參考了另一位博友的文章《分享兩種實(shí)現(xiàn)Winform程序的多語(yǔ)言支持的解決方案》,思路有點(diǎn)符合我的期望,因此吸收了一些處理思想進(jìn)行處理,目的就是提高開(kāi)發(fā)效率。
1)多語(yǔ)言的信息存儲(chǔ)和加載
首先,我們來(lái)看看多語(yǔ)言處理的目錄和格式問(wèn)題,目錄大概是根據(jù)多語(yǔ)言的簡(jiǎn)稱進(jìn)行放置,如下所示。
這個(gè)目錄就是會(huì)輸出到debug或者Release的運(yùn)行目錄中,我們就是根據(jù)相對(duì)于運(yùn)行目錄進(jìn)行資源讀取即可,所有模塊共用同一的多語(yǔ)言文件,我們可以把各個(gè)模塊基礎(chǔ)通用的多語(yǔ)言文件放在Basic.json文件中,也可以根據(jù)模塊獨(dú)立起名,主程序如TestMultiLanguage的多語(yǔ)言文件我則放在TestMultiLanguage.json文件中。實(shí)際上目錄名稱是為了區(qū)分而已,程序加載的時(shí)候,會(huì)把目錄下面所有的JSON文件進(jìn)行加載,讀取里面的鍵值作為資源的字典參照。
多語(yǔ)言的JSON文件是標(biāo)準(zhǔn)的Json格式,只是我們只用鍵值的字典參考即可,不需要使用復(fù)雜的JSON對(duì)象格式,如下是basic.json文件的部分內(nèi)容。
這些資源文件采用中文-英文的參照方式,我們以我們常規(guī)的母語(yǔ)開(kāi)發(fā),即使我們不做多語(yǔ)言,也不影響代碼的正常處理,我們只需要把窗體上和代碼里面的中文提取出來(lái),然后進(jìn)行多語(yǔ)言處理(如變?yōu)橛⑽模┘纯伞?/p>
由于我們使用鍵值字典對(duì)象的JSON內(nèi)容,那么我們就可以把這些內(nèi)容序列號(hào)為字典集合,如下代碼我們可以通過(guò) JSON.NET 組件把它們序列化為字典集合,這些字典集合就是我們用來(lái)做多語(yǔ)言的關(guān)鍵。
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) { //遍歷集合如果語(yǔ)言資源鍵值不存在,則創(chuàng)建,否則更新 if (!resources.ContainsKey(key)) { resources.Add(key, dict[key]); } else { resources[key] = dict[key]; } } }
加載多語(yǔ)言處理的時(shí)候,我們遍歷相對(duì)目錄下的lang/***里面的文件即可實(shí)現(xiàn)多語(yǔ)言信息的加載,如下代碼所示。
/// <summary> /// 根據(jù)語(yǔ)言初始化信息。 /// 加載對(duì)應(yīng)語(yǔ)言的JSON信息,把翻譯信息存儲(chǔ)在全屬性resources里面。 /// </summary> /// <param name="language">默認(rèn)的語(yǔ)言類型,如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); } } }
我們把多語(yǔ)言的加載和翻譯處理,放在一個(gè)獨(dú)立的項(xiàng)目上,如我定義為框架的一個(gè)模塊:WHC.Framework.Language
這樣我們?cè)诟鱾€(gè)模塊中使用多語(yǔ)言處理過(guò)程的時(shí)候,包含這個(gè)模塊就可以了。
2)多語(yǔ)言信息的翻譯
做多語(yǔ)言的版本程序,翻譯工作也是一個(gè)繁瑣的工作,如果你是非常精通各種語(yǔ)言(如中文、英文、日文等等),那當(dāng)然不在話下,不過(guò)我們做開(kāi)發(fā)的多少也是會(huì)一些的,如英語(yǔ)吧,即時(shí)不能非常準(zhǔn)確,那么也可以做到差不多,但是做這個(gè)還是累,還容易敲打錯(cuò)別字,那么用第三方提供的翻譯API來(lái)預(yù)處理后調(diào)整,結(jié)果就簡(jiǎn)化很多了,可以極大提高效率的。
這里以我們經(jīng)常使用的百度翻譯來(lái)實(shí)現(xiàn)(用Google翻譯也可以,增加接口實(shí)現(xiàn)即可)
百度翻譯接口的使用,你先注冊(cè)一個(gè)開(kāi)發(fā)賬戶,獲得相應(yīng)的秘鑰信息就可以使用免費(fèi)的翻譯接口了(http://api.fanyi.baidu.com/api/trans/product/index)。
有了這些準(zhǔn)備后,就可以利用C#代碼進(jìn)行翻譯處理了。
百度翻譯的接口處理代碼如下所示。
/// <summary> /// 百度接口翻譯 /// </summary> /// <param name="inputString">輸入字符串</param> /// <param name="from">源內(nèi)容語(yǔ)言</param> /// <param name="to">目標(biāo)語(yǔ)言</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.通過(guò)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結(jié)果 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[0].dst; content = StringUtil.ToProperCase(content); } } catch(Exception ex) { Debug.WriteLine(ex); } return content; }
其中把JSON轉(zhuǎn)換為類對(duì)象需要兩個(gè)類,對(duì)翻譯結(jié)果進(jìn)行轉(zhuǎn)換,如下代碼所示。
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; } }
這樣我們?cè)诙嗾Z(yǔ)言處理的時(shí)候,可以對(duì)默認(rèn)輸入為空的鍵值進(jìn)行翻譯即可(如英文翻譯)。
//遍歷集合進(jìn)行翻譯 var value = dict[key]; if (string.IsNullOrWhiteSpace(value)) { //如果值為空,那么調(diào)用翻譯接口處理 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);
如果需要對(duì)鍵值進(jìn)行排序,那么使用SortDictionary進(jìn)行包裝下即可
//進(jìn)行排序 SortedDictionary<string, string> sortedDict = new SortedDictionary<string, string>(dict); var newContent = JsonConvert.SerializeObject(sortedDict, Formatting.Indented);
在多語(yǔ)言處理的時(shí)候,我們一般不必要一次填寫(xiě)完畢中英文對(duì)照的資源,我們可以先把字典鍵值的鍵寫(xiě)出來(lái),值保留為空,如下文件所示。
運(yùn)行程序的時(shí)候,讓翻譯的接口先行翻譯,然后我們?cè)賹?duì)翻譯的資源進(jìn)行調(diào)整,適應(yīng)我們程序的語(yǔ)境即可,翻譯后的內(nèi)容后如下所示。
好了,彈藥都準(zhǔn)備好了,就看我們?nèi)绾问褂茫?下一步介紹如何使用這些資源。
3、多語(yǔ)言在界面中的應(yīng)用
前面介紹都是為程序界面準(zhǔn)備好對(duì)應(yīng)的多語(yǔ)言資源內(nèi)容,我們?cè)诔绦騿?dòng)的時(shí)候,可以通過(guò)常規(guī)的方式,設(shè)置界面的CurrentUICulture區(qū)域信息,如下代碼所示。
//界面多語(yǔ)言 //System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("zh-Hans");//中文界面 System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo("en-US");//英文界面
然后我們?cè)赪inform程序中開(kāi)發(fā)設(shè)計(jì)我們的界面內(nèi)容,例如設(shè)計(jì)一個(gè)普通的界面如下所示。
這個(gè)窗體我們添加了幾個(gè)按鈕,并設(shè)置它的中文顯示內(nèi)容,它的基類默認(rèn)還是保持它的DevExpress基類XtraForm,如下所示。
/// <summary> /// 測(cè)試多語(yǔ)言的窗體界面 /// </summary> public partial class Form1 : XtraForm
那么我們?nèi)绻詣?dòng)實(shí)現(xiàn)多語(yǔ)言的處理,那么還需要在窗體的Load或者Shown事件里面實(shí)現(xiàn)處理,如下代碼所示。
private void Form1_Shown(object sender, EventArgs e) { //窗體加載并顯示后,對(duì)窗體實(shí)現(xiàn)多語(yǔ)言處理 if (!this.DesignMode) { LanguageHelper.InitLanguage(this); } }
如果我們?yōu)槊總€(gè)窗體都需要添加這些代碼,也是繁瑣的事情,那么我們可以把這個(gè)處理邏輯,放到我們常規(guī)自定義的窗體基類里面(如BaseForm),那么我們就不需要任何額外的代碼了。
所需的就是集成窗體基類即可,這也是我們一般開(kāi)發(fā)都做的事情,通過(guò)繼承使得我們的代碼又省去了。
/// <summary> /// 測(cè)試多語(yǔ)言的窗體界面 /// </summary> public partial class Form1 : BaseForm
那么我們真正關(guān)注的就是我們前面介紹的邏輯代碼實(shí)現(xiàn)了
LanguageHelper.InitLanguage(this);
這個(gè)輔助類,主要就是在窗體初始化后,遍歷界面的所有類型控件,對(duì)控件進(jìn)行相應(yīng)的多語(yǔ)言處理。
/// <summary> /// 對(duì)界面控件進(jìn)行多語(yǔ)言的處理輔助類 /// </summary> public class LanguageHelper { /// <summary> /// 初始化語(yǔ)言 /// </summary> public static void InitLanguage(Control control) { //如果沒(méi)有資源,那么不必遍歷控件,提高速度 if (!JsonLanguage.Default.HasResource) return; //使用遞歸的方式對(duì)控件及其子控件進(jìn)行處理 SetControlLanguage(control); foreach (Control ctrl in control.Controls) { InitLanguage(ctrl); } //工具欄或者菜單動(dòng)態(tài)構(gòu)建窗體或者控件的時(shí)候,重新對(duì)子控件進(jìn)行處理 control.ControlAdded += (sender, e) => { InitLanguage(e.Control); }; }
通過(guò)遞歸的方式,我們可以對(duì)常規(guī)的如GridControl,工具欄、NavBar導(dǎo)航欄、菜單、按鈕等資源進(jìn)行統(tǒng)一的多語(yǔ)言處理,而這里面對(duì)于我們開(kāi)發(fā)應(yīng)用程序界面,都不需要額外的擔(dān)心,極大的提高了效率。
下面是幾個(gè)常規(guī)的界面,我們來(lái)體驗(yàn)下英文版本的界面效果。
這些英文界面我們只需要把界面的中文提取出來(lái)放到JSON文件中,自動(dòng)翻譯再調(diào)整即可,然后界面繼承保持BaseForm或者BaseDock這些窗體基類不變,只是調(diào)整了這些基類的加載,增加一行代碼就可以順利實(shí)現(xiàn)了多語(yǔ)言的效果了。
這樣我們就把核心的工作放在提取界面中的中文資源并進(jìn)行整理即可,這是核心的工作但翻譯也基本不用自己從頭做,窗體代碼幾乎不需要做其他修改就實(shí)現(xiàn)了我們所需要的多語(yǔ)言效果了,這樣做極大提高了開(kāi)發(fā)效率,對(duì)于我們已經(jīng)開(kāi)發(fā)好的模塊,更是四兩撥千斤了。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- 分享兩種實(shí)現(xiàn)Winform程序的多語(yǔ)言支持的多種解決方案
- VisualStudio2019中為.NET Core WinForm App啟用窗體設(shè)計(jì)器
- visual studio 2019使用net core3.0創(chuàng)建winform無(wú)法使用窗體設(shè)計(jì)器
- WINFORM 窗體間的傳值實(shí)現(xiàn)解析
- c# WinForm 窗體之間傳值的幾種方式(小結(jié))
- C# Winform選項(xiàng)卡集成窗體詳解
- C# WinForm實(shí)現(xiàn)窗體上控件自由拖動(dòng)功能示例
- C# WinForm制作異形窗體與控件的方法
- winform c#中子窗體關(guān)閉刷新父窗體的實(shí)例
- Winform窗體如何改變語(yǔ)言類型
相關(guān)文章
淺談C#各種數(shù)組直接的數(shù)據(jù)復(fù)制/轉(zhuǎn)換
下面小編就為大家?guī)?lái)一篇淺談C#各種數(shù)組直接的數(shù)據(jù)復(fù)制/轉(zhuǎn)換。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2016-08-08c# 使用Entity Framework操作Access數(shù)據(jù)庫(kù)的示例
本篇文章主要介紹了c# 使用Entity Framework操作Access數(shù)據(jù)庫(kù)的示例,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11C#基于面向過(guò)程計(jì)算加權(quán)平均分的方法
這篇文章主要介紹了C#基于面向過(guò)程計(jì)算加權(quán)平均分的方法,涉及C#數(shù)學(xué)運(yùn)算的相關(guān)技巧,具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-07-07C#實(shí)現(xiàn)一個(gè)簡(jiǎn)單實(shí)用的TXT文本操作及日志框架詳解
這篇文章主要給大家介紹了關(guān)于利用C#如何實(shí)現(xiàn)一個(gè)簡(jiǎn)單實(shí)用的TXT文本操作及日志框架的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們一起來(lái)看看吧2018-07-07c#泛型序列化對(duì)象為字節(jié)數(shù)組的示例
這篇文章主要介紹了c#泛型序列化對(duì)象為字節(jié)數(shù)組的示例,需要的朋友可以參考下2014-04-04WCF如何使用動(dòng)態(tài)代理精簡(jiǎn)代碼架構(gòu)
這篇文章主要介紹了WCF如何使用動(dòng)態(tài)代理精簡(jiǎn)代碼架構(gòu),幫助大家更好的理解和學(xué)習(xí)使用c#,感興趣的朋友可以了解下2021-03-03