C#实现动态多语言界面程序
一直想做一个多语言的程序,研究了一下.net的本地化方法,觉得做起来比较麻烦,而且不能快速切换,就自己琢磨着写一个。
以我做的一个C# winform 项目为例。
首先设计多语言文件,这里我用XML来保存,基本结构如下。
在Form里面,其每个子树分别对应一个窗体。XML每项有三个域,id 这个只是用来标号,程序中为用,key,value形成一个字典,key是控件
的名称,value是控件的text。在Dialog中key用数字编号。
做其他语言文件时,只用将value里面的值改成对应的语言即可。
当然,我们也不一定用XML来写语言文件,简单的ini文件也行。我现在觉得在一般的中小型程序中用XML,出于一个最基本的目的就是装B。
下面设计读取这个XML的类,
Forms, Menu, Toolbar, Dialog几个属性分别对应XML中的子树,使用.net中的Dictionary范型,Forms嵌套了一层Dictionary。
Load方法是加载语言文件,readLanguage 和paraseXML 函数对XML进行解析,并保存字符串到对应的属性中。
AddForm这个方法是将每个窗体的动态的添加到forms 里面。
在程序开始main 函数中,首先调用AddForm方法,添加所有窗体。
在每个Form的Load事件中初始化每个控件的Text。
递归更新每个控件Text
另外主窗体的Menu和Toolbar,我采用以下的方法更新。
需要转变为不同语言时只需要再调用一次Localization.Load方法。
小结:
这种实现多语言界面的方式我思考了很久,也在网上查了一些资料,最后设计了这样一种方式,XML中利用字典来记录控件的语言
在添加,读取时非常方便,Localization类做成静态类,在运行时就相当于一个常量,没有构造函数这样的开销,整个界面也可以再运行时
直接改变界面语言。
当然这种方法不一定是最好的, 如果有更好的方法欢迎指点。
以我做的一个C# winform 项目为例。
首先设计多语言文件,这里我用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,分别对应菜单,工具栏,窗体和对话框的显示字符串。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>
在Form里面,其每个子树分别对应一个窗体。XML每项有三个域,id 这个只是用来标号,程序中为用,key,value形成一个字典,key是控件
的名称,value是控件的text。在Dialog中key用数字编号。
做其他语言文件时,只用将value里面的值改成对应的语言即可。
当然,我们也不一定用XML来写语言文件,简单的ini文件也行。我现在觉得在一般的中小型程序中用XML,出于一个最基本的目的就是装B。
下面设计读取这个XML的类,
1using System;
2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using System.Xml;
6
7namespace AirLibrary
8{
9 /// <summary>
10 /// 本地化类
11 /// </summary>
12 public static class Localization
13 {
14 Property //Property
18
19 Attribute //Attribute
25
26 Method //Method
66
67 Function //Function
116
117 Property //Property
158 }
159}
160
这里我使用静态类来读取和保存,这样效率相对会高一些。读取XML时,我使用的是XmlReader,它使用流式读取,速度也比较快。2using System.Collections.Generic;
3using System.Linq;
4using System.Text;
5using System.Xml;
6
7namespace AirLibrary
8{
9 /// <summary>
10 /// 本地化类
11 /// </summary>
12 public static class Localization
13 {
14 Property //Property
18
19 Attribute //Attribute
25
26 Method //Method
66
67 Function //Function
116
117 Property //Property
158 }
159}
160
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");
8 }
然后加载语言文件。2 private static void AddForm()
3 {
4 Localization.AddForm("MainForm");
5 Localization.AddForm("UserLoginForm");
6 Localization.AddForm("UserManageForm");
7 Localization.AddForm("ChangePasswordForm");
8 }
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;
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();
2 RefreshLanguage();
// 更新窗体语言
public static void RefreshLanguage(Form form)
{
form.Text = Localization.Forms[form.Name][form.Name];
SetControlsLanguage(form, Localization.Forms[form.Name]);
}
public static void RefreshLanguage(Form form)
{
form.Text = Localization.Forms[form.Name][form.Name];
SetControlsLanguage(form, Localization.Forms[form.Name]);
}
递归更新每个控件Text
1 /// <summary>
2 /// 设置control子控件语言
3 /// </summary>
4 /// <param name="control">父控件</param>
5 /// <param name="obj">语言字典</param>
6 public static void SetControlsLanguage(Control control, Dictionary<string, string> obj)
7 {
8 foreach (Control ctrl in control.Controls)
9 {
10 // set the control which one's key in the dictionary
11 string text = "";
12 if (obj.TryGetValue(ctrl.Name, out text))
13 ctrl.Text = text;
14
15 if (ctrl.HasChildren)
16 SetControlsLanguage(ctrl, obj);
17 }
18 }
2 /// 设置control子控件语言
3 /// </summary>
4 /// <param name="control">父控件</param>
5 /// <param name="obj">语言字典</param>
6 public static void SetControlsLanguage(Control control, Dictionary<string, string> obj)
7 {
8 foreach (Control ctrl in control.Controls)
9 {
10 // set the control which one's key in the dictionary
11 string text = "";
12 if (obj.TryGetValue(ctrl.Name, out text))
13 ctrl.Text = text;
14
15 if (ctrl.HasChildren)
16 SetControlsLanguage(ctrl, obj);
17 }
18 }
另外主窗体的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属性即可。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 }
需要转变为不同语言时只需要再调用一次Localization.Load方法。
小结:
这种实现多语言界面的方式我思考了很久,也在网上查了一些资料,最后设计了这样一种方式,XML中利用字典来记录控件的语言
在添加,读取时非常方便,Localization类做成静态类,在运行时就相当于一个常量,没有构造函数这样的开销,整个界面也可以再运行时
直接改变界面语言。
当然这种方法不一定是最好的, 如果有更好的方法欢迎指点。