C#實現多語言界面程序的方法介紹
一直想做一個多語言的程序,研究了一下.net的本地化方法,覺得做起來比較麻煩,而且不能快速切換,就自己琢磨著寫一個。
以我做的一個C# winform 項目為例。
在建立C#實現多語言界面程序之前,首先設計多語言文件,這里我用XML來保存,基本結構如下。
- < ?xml version = "1.0" encoding = "GB2312"?>
- < AirControl language="簡體中文">
- < Menu>
- < Project>
- < Item id="0" key="MenuProject" value="項目(&P)" />
- < Item id="1" key="MenuProjectItem1" value="新建(&N)" />
- < Item id="2" key="MenuProjectItem2" value="打開(&O)" />
- < Item id="3" key="MenuProjectItem3" value="保存(&S)" />
- < Item id="5" key="MenuProjectItem5" value="退出(&X)" />
- < /Project>
- < Manage>
- < Item id="0" key="MenuManage" value="管理(&M)" />
- < Item id="1" key="MenuManageItem1" value="登錄(&I)" />
- < Item id="2" key="MenuManageItem2" value="注銷(&O)" />
- < Item id="3" key="MenuManageItem3" value="修改密碼(&C)" />
- < Item id="4" key="MenuManageItem4" value="用戶管理(&U)" />
- < /Manage>
- < Help>
- < Item id="0" key="MenuHelp" value="幫助(&H)" />
- < Item id="1" key="MenuHelpItem1" value="幫助內容(&H)" />
- < Item id="2" key="MenuHelpItem2" value="關于(&A)" />
- < /Help>
- < /Menu>
- < Toolbar>
- < Statusbar>
- < Item id="1" key="StatusItem1" value="用戶名: " />
- < Item id="2" key="StatusItem2" value="用戶組: " />
- < Item id="3" key="StatusItem3" value="上次登錄時間: " />
- < Item id="4" key="StatusItem4" value="本次登錄時間:" />
- < /Statusbar>
- < /Toolbar>
- < Form>
- < MainForm>
- < Item id="0" key="MainForm" value="xx" />
- < Item id="1" key="buttonGo" value="開始" />
- < Item id="2" key="buttonStop" value="停止" />
- < Item id="3" key="groupBox1" value="用戶信息" />
- < Item id="4" key="groupBox2" value="常規數據" />
- < /MainForm>
- < UserLoginForm>
- < Item id="0" key="UserLoginForm" value="用戶登錄" />
- < Item id="1" key="labelTitle" value="xx" />
- < Item id="2" key="labelUsername" value="用戶名" />
- < Item id="3" key="labelPassword" value="密碼" />
- < Item id="4" key="buttonLogin" value="登錄" />
- < /UserLoginForm>
- < ChangePasswordForm>
- < Item id="0" key="ChangePasswordForm" value="修改密碼" />
- < Item id="1" key="label1" value="原密碼" />
- < Item id="2" key="label2" value="新密碼" />
- < Item id="3" key="label3" value="再輸入" />
- < Item id="4" key="buttonConfirm" value="確認" />
- < Item id="5" key="buttonCancel" value="取消" />
- < /ChangePasswordForm>
- < /Form>
- < Dialog>
- < Title>
- < Item id="0" key="0001" value="xx" />
- < Item id="1" key="0002" value="添加測試" />
- < Item id="2" key="0003" value="添加用戶" />
- < Item id="3" key="0004" value="修改密碼" />
- < /Title>
- < Message>
- < Item id="0" key="0000" value="一切正常" />
- < Item id="1" key="2001" value="用戶名或密碼錯誤" />
- < Item id="5" key="2002" value="密碼不一致" />
- < Item id="6" key="2003" value="用戶名已存在" />
- < Item id="7" key="2004" value="添加用戶成功" />
- < /Message>
- < /Dialog>
- < /AirControl>
這里是語言文件的局部,主體分為四個部分,Menu, Toolbar, Form 和 Dialog,分別對應菜單,工具欄,窗體和對話框的顯示字符串。
在Form里面,其每個子樹分別對應一個窗體。XML每項有三個域,id 這個只是用來標號,程序中為用,key,value形成一個字典,key是控件的名稱,value是控件的text。在Dialog中key用數字編號。
做其他語言文件時,只用將value里面的值改成對應的語言即可。
當然,我們也不一定用XML來寫語言文件,簡單的ini文件也行。下面設計讀取這個XML的類,
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.Xml;
- namespace AirLibrary
- {
- /**//// < summary>
- /// 本地化類
- /// < /summary>
- public static class Localization
- {
- Property#region Property
- public static string Lang { get; private set; }
- public static bool HasLang { get; set; }
- #endregion //Property
- Attribute#region Attribute
- private static Dictionary< string, Dictionary< string, string>> forms = new Dictionary< string, Dictionary< string, string>>();
- private static Dictionary< string, string> menu = new Dictionary< string, string>();
- private static Dictionary< string, string> toolbar = new Dictionary< string, string>();
- private static Dictionary< string, string> dialog = new Dictionary< string, string>();
- #endregion //Attribute
- Method#region Method
- public static void AddForm(string formName)
- {
- forms.Add(formName, new Dictionary< string, string>());
- //formMap.Add(formName, count++);
- }
- /**//// < summary>
- /// 加載語言文件
- /// < /summary>
- /// < param name="lang">語言< /param>
- /// < returns>< /returns>
- public static bool Load(string lang)
- {
- string path = "";
- Localization.Lang = "English";
- menu.Clear();
- toolbar.Clear();
- dialog.Clear();
- exception.Clear();
- foreach (Dictionary< string, string> form in forms.Values)
- form.Clear();
- switch (lang)
- {
- case "zh":
- path = @"resources/lang-zh.xml";
- break;
- case "en":
- path = @"resources/lang-en.xml";
- break;
- default:
- path = @"resources/lang-zh.xml";
- break;
- }
- return readLanguage(path);
- }
- #endregion //Method
- Function#region Function
- private static bool readLanguage(string path)
- {
- // Read the language file
- XmlReader reader;
- try
- {
- reader = XmlReader.Create(path);
- }
- catch (Exception)
- {
- return false;
- }
- // Begin to parase
- try
- {
- reader.ReadToFollowing("AirControl");
- Localization.Lang = reader.GetAttribute("language");
- paraseXml(reader, "Menu", menu);
- paraseXml(reader, "Toolbar", toolbar);
- foreach (string formName in forms.Keys)
- {
- paraseXml(reader, formName, forms[formName]);
- }
- paraseXml(reader, "Dialog", dialog);
- }
- catch (Exception)
- {
- return false;
- }
- return true;
- }
- private static void paraseXml(XmlReader reader, string item, Dictionary< string, string> obj)
- {
- // Get the attribute key & value
- reader.ReadToFollowing(item);
- XmlReader subreader = reader.ReadSubtree();
- while (subreader.Read())
- {
- if (subreader.NodeType == XmlNodeType.Element && subreader.Name == "Item")
- obj.Add(subreader.GetAttribute("key"), subreader.GetAttribute("value"));
- }
- }
- #endregion //Function
- Property#region Property
- public static Dictionary< string, string> Menu
- {
- get
- {
- return menu;
- }
- private set
- { }
- }
- public static Dictionary< string, string> Toolbar
- {
- get
- {
- return toolbar;
- }
- private set
- { }
- }
- public static Dictionary< string, Dictionary< string, string>> Forms
- {
- get
- {
- return forms;
- }
- private set
- { }
- }
- public static Dictionary< string, string> Dialog
- {
- get
- {
- return dialog;
- }
- private set
- { }
- }
- #endregion //Property
- }
- }
這里我使用靜態類來讀取和保存,這樣效率相對會高一些。讀取XML時,我使用的是XmlReader,它使用流式讀取,速度也比較快。
Forms, Menu, Toolbar, Dialog幾個屬性分別對應XML中的子樹,使用.net中的Dictionary范型,Forms嵌套了一層Dictionary。
Load方法是加載語言文件,readLanguage 和paraseXML 函數對XML進行解析,并保存字符串到對應的屬性中。
AddForm這個方法是將每個窗體的動態的添加到forms 里面。
在程序開始main 函數中,首先調用AddForm方法,添加所有窗體。
- // 添加所有窗體用于本地化(按XML中順序)
- private static void AddForm()
- {
- Localization.AddForm("MainForm");
- Localization.AddForm("UserLoginForm");
- Localization.AddForm("UserManageForm");
- Localization.AddForm("ChangePasswordForm");
- }
然后加載語言文件。
- if (!Localization.Load("zh"))
- {
- MessageBox.Show("無法加載語言配置文件, 將顯示英文.", "錯誤", MessageBoxButtons.OK,
- MessageBoxIcon.Exclamation);
- Localization.HasLang = false;
- }
- else
- Localization.HasLang = true;
在每個Form的Load事件中初始化每個控件的Text。
- if (Localization.HasLang)
- RefreshLanguage();
- // 更新窗體語言
- public static void RefreshLanguage(Form form)
- {
- form.Text = Localization.Forms[form.Name][form.Name];
- SetControlsLanguage(form, Localization.Forms[form.Name]);
- }
- 遞歸更新每個控件Text
- /// < summary>
- /// 設置control子控件語言
- /// < /summary>
- /// < param name="control">父控件< /param>
- /// < param name="obj">語言字典< /param>
- public static void SetControlsLanguage(Control control, Dictionary< string, string> obj)
- {
- foreach (Control ctrl in control.Controls)
- {
- // set the control which one's key in the dictionary
- string text = "";
- if (obj.TryGetValue(ctrl.Name, out text))
- ctrl.Text = text;
- if (ctrl.HasChildren)
- SetControlsLanguage(ctrl, obj);
- }
- }
另外主窗體的Menu和Toolbar,我采用以下的方法更新。
- // Refresh the menu language
- foreach (ToolStripMenuItem topItem in MainMenuStrip.Items)
- {
- topItem.Text = Localization.Menu[topItem.Name];
- foreach (ToolStripItem item in topItem.DropDownItems)
- {
- if (item is ToolStripMenuItem)
- {
- string text = "";
- if (Localization.Menu.TryGetValue(item.Name, out text))
- item.Text = text;
- }
- }
- }
- // Refresh the statusbar language
- foreach (ToolStripItem item in mainStatus.Items)
- {
- string text = "";
- if (Localization.Toolbar.TryGetValue(item.Name, out text))
- item.Text = text;
- }
Dialog就直接調用Localization中的Dialog屬性即可。
需要轉變為不同語言時只需要再調用一次Localization.Load方法。
這樣,就完成了C#實現多語言界面程序。
小結:
這種C#實現多語言界面程序的方式我思考了很久,也在網上查了一些資料,最后設計了這樣一種方式,XML中利用字典來記錄控件的語言在添加,讀取時非常方便,Localization類做成靜態類,在運行時就相當于一個常量,沒有構造函數這樣的開銷,整個界面也可以再運行時直接改變界面語言。當然這種方法不一定是最好的, 如果有更好的方法歡迎指點。
【編輯推薦】