C#实现多语言界面程序

一直想做一个多语言的程序,研究了一下.net的本地化方法,觉得做起来比较麻烦,而且不能快速切换,就自己琢磨着写一个。

以我做的一个C# winform 项目为例。

在建立C#实现多语言界面程序之前,首先设计多语言文件,这里我用XML来保存,基本结构如下。 

  1. < ?xml version = "1.0" encoding = "GB2312"?> 
  2.  < AirControl language="简体中文"> 
  3.      < Menu> 
  4.          < Project> 
  5.              < Item id="0" key="MenuProject" value="项目(&P)" /> 
  6.              < Item id="1" key="MenuProjectItem1" value="新建(&N)" /> 
  7.              < Item id="2" key="MenuProjectItem2" value="打开(&O)" /> 
  8.              < Item id="3" key="MenuProjectItem3" value="保存(&S)" /> 
  9.              < Item id="5" key="MenuProjectItem5" value="退出(&X)" /> 
  10.         < /Project> 
  11.         < Manage> 
  12.             < Item id="0" key="MenuManage" value="管理(&M)" /> 
  13.             < Item id="1" key="MenuManageItem1" value="登录(&I)" /> 
  14.             < Item id="2" key="MenuManageItem2" value="注销(&O)" /> 
  15.             < Item id="3" key="MenuManageItem3" value="修改密码(&C)" /> 
  16.             < Item id="4" key="MenuManageItem4" value="用户管理(&U)" /> 
  17.         < /Manage> 
  18.         < Help> 
  19.             < Item id="0" key="MenuHelp" value="帮助(&H)" /> 
  20.             < Item id="1" key="MenuHelpItem1" value="帮助内容(&H)" /> 
  21.             < Item id="2" key="MenuHelpItem2" value="关于(&A)" /> 
  22.         < /Help> 
  23.     < /Menu> 
  24.     < Toolbar> 
  25.         < Statusbar> 
  26.             < Item id="1" key="StatusItem1" value="用户名: " /> 
  27.             < Item id="2" key="StatusItem2" value="用户组: " /> 
  28.             < Item id="3" key="StatusItem3" value="上次登录时间: " /> 
  29.             < Item id="4" key="StatusItem4" value="本次登录时间:" /> 
  30.         < /Statusbar> 
  31.     < /Toolbar> 
  32.     < Form> 
  33.         < MainForm> 
  34.             < Item id="0" key="MainForm" value="xx" /> 
  35.             < Item id="1" key="buttonGo" value="开始" /> 
  36.             < Item id="2" key="buttonStop" value="停止" />          
  37.             < Item id="3" key="groupBox1" value="用户信息" /> 
  38.             < Item id="4" key="groupBox2" value="常规数据" />      
  39.         < /MainForm> 
  40.         < UserLoginForm> 
  41.             < Item id="0" key="UserLoginForm" value="用户登录" /> 
  42.             < Item id="1" key="labelTitle"  value="xx" /> 
  43.             < Item id="2" key="labelUsername" value="用户名" /> 
  44.             < Item id="3" key="labelPassword" value="密码" /> 
  45.             < Item id="4" key="buttonLogin" value="登录" /> 
  46.         < /UserLoginForm> 
  47.         < ChangePasswordForm> 
  48.             < Item id="0" key="ChangePasswordForm" value="修改密码" /> 
  49.             < Item id="1" key="label1" value="原密码" /> 
  50.             < Item id="2" key="label2" value="新密码" /> 
  51.             < Item id="3" key="label3" value="再输入" /> 
  52.             < Item id="4" key="buttonConfirm" value="确认" /> 
  53.             < Item id="5" key="buttonCancel" value="取消" /> 
  54.         < /ChangePasswordForm> 
  55.     < /Form> 
  56.     < Dialog> 
  57.         < Title> 
  58.             < Item id="0" key="0001" value="xx" /> 
  59.             < Item id="1" key="0002" value="添加测试" /> 
  60.             < Item id="2" key="0003" value="添加用户" /> 
  61.             < Item id="3" key="0004" value="修改密码" /> 
  62.         < /Title> 
  63.         < Message> 
  64.             < Item id="0" key="0000" value="一切正常" /> 
  65.             < Item id="1" key="2001" value="用户名或密码错误" /> 
  66.             < Item id="5" key="2002" value="密码不一致" /> 
  67.             < Item id="6" key="2003" value="用户名已存在" /> 
  68.             < Item id="7" key="2004" value="添加用户成功" />              
  69.         < /Message>              
  70.     < /Dialog> 
  71. < /AirControl> 

这里是语言文件的局部,主体分为四个部分,Menu, Toolbar, Form 和 Dialog,分别对应菜单,工具栏,窗体和对话框的显示字符串。

在Form里面,其每个子树分别对应一个窗体。XML每项有三个域,id 这个只是用来标号,程序中为用,key,value形成一个字典,key是控件的名称,value是控件的text。在Dialog中key用数字编号。

做其他语言文件时,只用将value里面的值改成对应的语言即可。

当然,我们也不一定用XML来写语言文件,简单的ini文件也行。下面设计读取这个XML的类,

  1. using System;  
  2. using System.Collections.Generic;  
  3. using System.Linq;  
  4. using System.Text;  
  5. using System.Xml;  
  6.  
  7. namespace AirLibrary  
  8. {  
  9.     /**//// < summary>  
  10.     /// 本地化类  
  11.     /// < /summary>  
  12.     public static class Localization  
  13.     {  
  14.         Property#region Property  
  15.         public static string Lang { getprivate set; }  
  16.         public static bool HasLang { getset; }          
  17.         #endregion //Property  
  18.  
  19.         Attribute#region Attribute  
  20.         private static Dictionary< string, Dictionary< stringstring>> forms = new Dictionary< string, Dictionary< stringstring>>();  
  21.         private static Dictionary< stringstring> menu = new Dictionary< stringstring>();  
  22.         private static Dictionary< stringstring> toolbar = new Dictionary< stringstring>();  
  23.         private static Dictionary< stringstring> dialog = new Dictionary< stringstring>();  
  24.         #endregion //Attribute  
  25.  
  26.         Method#region Method  
  27.          public static void AddForm(string formName)  
  28.          {  
  29.              forms.Add(formName, new Dictionary< stringstring>());  
  30.              //formMap.Add(formName, count++);  
  31.          }  
  32.    
  33.         /**//// < summary>  
  34.          /// 加载语言文件  
  35.          /// < /summary>  
  36.          /// < param name="lang">语言< /param>  
  37.          /// < returns>< /returns>  
  38.          public static bool Load(string lang)  
  39.          {  
  40.              string path = "";  
  41.              Localization.Lang = "English";  
  42.    
  43.              menu.Clear();  
  44.              toolbar.Clear();  
  45.              dialog.Clear();  
  46.              exception.Clear();  
  47.              foreach (Dictionary< stringstring> form in forms.Values)  
  48.                  form.Clear();  
  49.                
  50.              switch (lang)  
  51.              {  
  52.                  case "zh":                     
  53.                      path = @"resources/lang-zh.xml";  
  54.                      break;  
  55.                  case "en":                    
  56.                      path = @"resources/lang-en.xml";  
  57.                      break;  
  58.                  default:                      
  59.                      path = @"resources/lang-zh.xml";  
  60.                      break;  
  61.              }  
  62.    
  63.              return readLanguage(path);  
  64.          }  
  65.          #endregion //Method  
  66.    
  67.          Function#region Function  
  68.          private static bool readLanguage(string path)  
  69.          {  
  70.              // Read the language file  
  71.              XmlReader reader;  
  72.              try 
  73.              {  
  74.                  reader = XmlReader.Create(path);  
  75.              }  
  76.              catch (Exception)  
  77.              {  
  78.                  return false;  
  79.              }  
  80.    
  81.              // Begin to parase  
  82.             try 
  83.             {  
  84.                 reader.ReadToFollowing("AirControl");  
  85.                 Localization.Lang = reader.GetAttribute("language");  
  86.  
  87.                 paraseXml(reader, "Menu", menu);  
  88.                 paraseXml(reader, "Toolbar", toolbar);  
  89.     
  90.                 foreach (string formName in forms.Keys)  
  91.                 {  
  92.                    paraseXml(reader, formName, forms[formName]);  
  93.                 }  
  94.                 paraseXml(reader, "Dialog", dialog);                 
  95.             }  
  96.             catch (Exception)  
  97.             {  
  98.                 return false;  
  99.             }  
  100.             return true;  
  101.         }  
  102.  
  103.         private static void paraseXml(XmlReader reader, string item, Dictionary< stringstring> obj)  
  104.         {  
  105.             // Get the attribute key & value   
  106.             reader.ReadToFollowing(item);  
  107.  
  108.             XmlReader subreader = reader.ReadSubtree();  
  109.             while (subreader.Read())  
  110.             {  
  111.                 if (subreader.NodeType == XmlNodeType.Element && subreader.Name == "Item")  
  112.                     obj.Add(subreader.GetAttribute("key"), subreader.GetAttribute("value"));  
  113.             }  
  114.         }  
  115.         #endregion //Function  
  116.  
  117.         Property#region Property  
  118.         public static Dictionary< stringstring> Menu  
  119.         {  
  120.             get 
  121.             {  
  122.                 return menu;  
  123.             }  
  124.             private set 
  125.             { }  
  126.         }  
  127.  
  128.         public static Dictionary< stringstring> Toolbar  
  129.         {  
  130.             get 
  131.             {  
  132.                 return toolbar;  
  133.             }  
  134.             private set 
  135.             { }  
  136.         }  
  137.  
  138.         public static Dictionary< string, Dictionary< stringstring>> Forms  
  139.         {  
  140.             get 
  141.             {  
  142.                 return forms;  
  143.             }  
  144.             private set 
  145.             { }  
  146.         }  
  147.    
  148.         public static Dictionary< stringstring> Dialog  
  149.         {  
  150.             get 
  151.             {  
  152.                return dialog;  
  153.             }  
  154.             private set 
  155.             { }  
  156.         }  
  157.         #endregion //Property  
  158.     }  

这里我使用静态类来读取和保存,这样效率相对会高一些。读取XML时,我使用的是XmlReader,它使用流式读取,速度也比较快。

Forms, Menu, Toolbar, Dialog几个属性分别对应XML中的子树,使用.net中的Dictionary范型,Forms嵌套了一层Dictionary。

Load方法是加载语言文件,readLanguage 和paraseXML 函数对XML进行解析,并保存字符串到对应的属性中。

AddForm这个方法是将每个窗体的动态的添加到forms 里面。

在程序开始main 函数中,首先调用AddForm方法,添加所有窗体。

  1. // 添加所有窗体用于本地化(按XML中顺序)  
  2. private static void AddForm()  
  3. {  
  4.     Localization.AddForm("MainForm");  
  5.     Localization.AddForm("UserLoginForm");  
  6.     Localization.AddForm("UserManageForm");  
  7.     Localization.AddForm("ChangePasswordForm");  

然后加载语言文件。 

  1. if (!Localization.Load("zh"))  
  2.             {  
  3.                 MessageBox.Show("无法加载语言配置文件, 将显示英文.""错误", MessageBoxButtons.OK,  
  4.                     MessageBoxIcon.Exclamation);  
  5.                 Localization.HasLang = false;  
  6.             }  
  7.             else 
  8.                 Localization.HasLang = true

在每个Form的Load事件中初始化每个控件的Text。 

  1. if (Localization.HasLang)  
  2.          RefreshLanguage();  
  3.  
  4.  
  5. // 更新窗体语言  
  6. public static void RefreshLanguage(Form form)  
  7. {            
  8.      form.Text = Localization.Forms[form.Name][form.Name];  
  9.      SetControlsLanguage(form, Localization.Forms[form.Name]);  
  10. }  
  11.  
  12.          递归更新每个控件Text  
  13.  
  14.  /// < summary>  
  15.  /// 设置control子控件语言  
  16.  /// < /summary>  
  17.  /// < param name="control">父控件< /param>  
  18.  /// < param name="obj">语言字典< /param>  
  19.  public static void SetControlsLanguage(Control control, Dictionary< stringstring> obj)  
  20.  {  
  21.       foreach (Control ctrl in control.Controls)  
  22.       {  
  23.          // set the control which one's key in the dictionary  
  24.          string text = "";  
  25.          if (obj.TryGetValue(ctrl.Name, out text))  
  26.                ctrl.Text = text;  
  27.    
  28.          if (ctrl.HasChildren)  
  29.                SetControlsLanguage(ctrl, obj);  
  30.        }  
  31.  } 

另外主窗体的Menu和Toolbar,我采用以下的方法更新。 

  1. // Refresh the menu language  
  2.  foreach (ToolStripMenuItem topItem in MainMenuStrip.Items)  
  3.  {  
  4.        topItem.Text = Localization.Menu[topItem.Name];  
  5.        foreach (ToolStripItem item in topItem.DropDownItems)  
  6.        {  
  7.              if (item is ToolStripMenuItem)  
  8.              {  
  9.                   string text = "";  
  10.                   if (Localization.Menu.TryGetValue(item.Name, out text))  
  11.                        item.Text = text;  
  12.              }  
  13.        }  
  14.  }  
  15.    
  16.  // Refresh the statusbar language  
  17.  foreach (ToolStripItem item in mainStatus.Items)  
  18.  {  
  19.       string text = "";  
  20.       if (Localization.Toolbar.TryGetValue(item.Name, out text))  
  21.               item.Text = text;  
  22.  } 

Dialog就直接调用Localization中的Dialog属性即可。

需要转变为不同语言时只需要再调用一次Localization.Load方法。

这样,就完成了C#实现多语言界面程序。  

小结:

这种C#实现多语言界面程序的方式我思考了很久,也在网上查了一些资料,最后设计了这样一种方式,XML中利用字典来记录控件的语言在添加,读取时非常方便,Localization类做成静态类,在运行时就相当于一个常量,没有构造函数这样的开销,整个界面也可以再运行时直接改变界面语言。当然这种方法不一定是最好的, 如果有更好的方法欢迎指点。

posted @ 2012-03-22 20:06  欢喜王子  阅读(2128)  评论(0编辑  收藏  举报