使用resx文件实现Winform多语言切换,本博文提供的代码可以实现简体中文、繁体中文、英文的切换。如果需要增加其它语言的切换,只需要编写相应的语言的resx文件即可。 并且,当修改了语言之后,会更新所有打开的窗口。先贴几张图展示一下效果。
程序下载:点击打开链接
http://download.csdn.net/detail/softimite_zifeng/9731575
1. 简体中文
2. 繁体中文
3. 英文
下面子丰介绍一下实现的过程:
1. 为每个窗口创建相应语言的resx文件。子丰以英文为例,右键->添加->新建项->资源文件,文件名为窗口名.en-US,如上面的两个窗口,分别为LoginForm.en-US.resx和PasswordForm.en-US.resx。简体中文为LoginForm.zh-CN.resx和PasswordForm.zh-CN.resx,繁体中文为LoginForm.zh-CHT.resx和PasswordForm.zh-CHT.resx。下面给出LoginForm.en-US.resx文件的截图。
2. 在项目的Properties的Settings.settings中添加变量DefaultLanguage,用于保存当前设置的默认语言。当下次启动程序时,会读取该变量,从而将程序的语言设置为上次程序关闭时的语言。
3. 创建一个静态类(MultiLanguage.cs)用于编写与切换语言相关的变量和代码。
(1)变量DefaultLanguage,用于保存当前默认语言
//当前默认语言
public static string DefaultLanguage = "zh-CN";
(2)函数SetDefaultLanguage修改当前默认语言
/// <summary>
/// 修改默认语言
/// </summary>
/// <param name="lang">待设置默认语言</param>
public static void SetDefaultLanguage(string lang)
{
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(lang);
DefaultLanguage = lang;
Properties.Settings.Default.DefaultLanguage = lang;
Properties.Settings.Default.Save();
}
(3)函数LoadLanguage用于加载语言或切换语言
/// <summary>
/// 加载语言
/// </summary>
/// <param name="form">加载语言的窗口</param>
/// <param name="formType">窗口的类型</param>
public static void LoadLanguage(Form form, Type formType)
{
if (form != null)
{
System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(formType);
resources.ApplyResources(form, "$this");
Loading(form, resources);
}
}
/// <summary>
/// 加载语言
/// </summary>
/// <param name="control">控件</param>
/// <param name="resources">语言资源</param>
private static void Loading(Control control, System.ComponentModel.ComponentResourceManager resources)
{
if (control is MenuStrip)
{
//将资源与控件对应
resources.ApplyResources(control, control.Name);
MenuStrip ms = (MenuStrip)control;
if (ms.Items.Count > 0)
{
foreach (ToolStripMenuItem c in ms.Items)
{
//遍历菜单
Loading(c, resources);
}
}
}
foreach (Control c in control.Controls)
{
resources.ApplyResources(c, c.Name);
Loading(c, resources);
}
}
/// <summary>
/// 遍历菜单
/// </summary>
/// <param name="item">菜单项</param>
/// <param name="resources">语言资源</param>
private static void Loading(ToolStripMenuItem item, System.ComponentModel.ComponentResourceManager resources)
{
if (item is ToolStripMenuItem)
{
resources.ApplyResources(item, item.Name);
ToolStripMenuItem tsmi = (ToolStripMenuItem)item;
if (tsmi.DropDownItems.Count > 0)
{
foreach (ToolStripMenuItem c in tsmi.DropDownItems)
{
Loading(c, resources);
}
}
}
}
4. 在主窗口的Load事件中读取Properties.Settings.Default.DefaultLanguage,并将ComboBox赋值为当前默认语言,即简体中文、繁体中文或英文。
private void LoginForm_Load(object sender, EventArgs e)
{
//设置combobox的值
string language = Properties.Settings.Default.DefaultLanguage;
if (language == "zh-CN")
{
languageTxt.Text = "简体中文(默认)";
}
else if (language == "zh-CHT")
{
languageTxt.Text = "繁體中文";
}
else if (language == "en-US")
{
languageTxt.Text = "English";
}
}
5. 在每个窗口的Load事件中调用函数MultiLanguage.LoadLanguage,使窗口在出现时即显示为当前默认语言。
private void PasswordForm_Load(object sender, EventArgs e)
{
//加载语言
MultiLanguage.LoadLanguage(this, typeof(PasswordForm));
}
6. 编写用于切换语言的ComboBox的SelectedIndexChanged事件,使得当用户选择对应的语言时,程序会切换到该语言。
//切换语言
private void languageTxt_SelectedIndexChanged(object sender, EventArgs e)
{
languageTxt.Enabled = false;
if (languageTxt.Text == "简体中文(默认)")
{
//修改默认语言
MultiLanguage.SetDefaultLanguage("zh-CN");
//对所有打开的窗口重新加载语言
foreach (Form form in Application.OpenForms)
{
LoadAll(form);
}
}
else if (languageTxt.Text == "繁體中文")
{
//修改默认语言
MultiLanguage.SetDefaultLanguage("zh-CHT");
//对所有打开的窗口重新加载语言
foreach (Form form in Application.OpenForms)
{
LoadAll(form);
}
}
else if (languageTxt.Text == "English")
{
//修改默认语言
MultiLanguage.SetDefaultLanguage("en-US");
//对所有打开的窗口重新加载语言
foreach (Form form in Application.OpenForms)
{
LoadAll(form);
}
}
languageTxt.Enabled = true;
}
private void LoadAll(Form form)
{
if (form.Name == "LoginForm")
{
MultiLanguage.LoadLanguage(form, typeof(LoginForm));
}
else if (form.Name == "PasswordForm")
{
MultiLanguage.LoadLanguage(form, typeof(PasswordForm));
}
}
//***********
国际化就是要实现多语种的界面切换。首先,我们不可能用if else等语句来根据选择的不同语种对所有的控件一个一个的设值,这样太麻烦了。.Net提供了国际化相关的支持,主要放在System.Globalization命名空间下。下面是一个简单的实现,麻雀虽小,五脏俱全。
这个程序是最近写的一个程序,就在这基础上进行改造,关键是弄懂原理。
1> 首先要加入Resource文件
在VS工程-->添加新项目里加入三个resource文件,Resource文件的作用就是存放我们的界面控件的显示字符串。这里我们要实现3种语言的切换,所有加入了3个,分别命名为:Resource.en-US.resx,Resource.zh-CN.resx,Resource.ja-JP.resx。注意命名规则第一部分要相同,第二部分是不同语言的culture name,我定义的就是英语,中文,日文。
2> 在这3个文件中定义界面相关的字符串信息。
注意定义的名字在3个文件中要一致,否则找不到。
Resource.en-US.resx
tsmi_language_Name Language
tsmi_japanese_Name Japanese
tsmi_english_Name English
tsmi_close_Name Close
tsmi_chiniese_Name Chinese
Resource.zh-CN.resx
tsmi_language_Name 语言
tsmi_chiniese_Name 中文
tsmi_english_Name 英文
tsmi_japanese_Name 日文
tsmi_close_Name 退出
Resource.ja-JP.resx
tsmi_language_Name 言語
tsmi_chiniese_Name 中国語
tsmi_english_Name 英語
tsmi_japanese_Name 日本語
tsmi_close_Name 閉じる
3> 利用ResourceManager类进行读取。
ResourceManager会根据不同的cultrue来读不同的resource文件,所以,实现的关键就是根据不同的语言来改变当前程序线程的cultrue就可以达到目地。而对于界面控件Text的赋值代码将是不变的,当新增加一种语言是,也就是再加一个resource文件,对代码的改动几乎没有影响。
基本代码:
Resource的读取类
[csharp] view plaincopy
- class ResourceCluture
- {
- /// <summary>
- /// 设定语言环境
- /// </summary>
- /// <param name="strClutrue"></param>
- public static void SetLocalClutrue(string strClutrue)
- {
- if(string.IsNullOrEmpty(strClutrue))
- {
- strClutrue = "zh-CN";
- }
- CultureInfo currentClutrue = new CultureInfo(strClutrue);
- Thread.CurrentThread.CurrentCulture = currentClutrue;
- }
- /// <summary>
- /// 取值
- /// </summary>
- /// <param name="id"></param>
- /// <returns></returns>
- public static string GetString(string id)
- {
- string strValue = string.Empty;
- try
- {
- ResourceManager resManager = new ResourceManager("ApplicationActive.Properties.Resource", Assembly.GetExecutingAssembly());
- strValue = resManager.GetString(id, Thread.CurrentThread.CurrentCulture);
- }
- catch
- {
- strValue = "No id:" + id + "please add";
- }
- return strValue;
- }
- }
//***********************异常
今天在测试一个工程的时候,突然遇到了这样一个问题:
错误信息:System.Resources.MissingManifestResourceException: 未能找到任何适合于指定的区域或非特定区域性的资源。请确保在编译时已将“****.****.Resource.resources”正确嵌入或链接到程序集"****",或者确保所有需要的附属程序集都可加载并已进行了完全签名。
在网上搜索了N久都没看到几篇解决的文章,最后在不懈的努力下终于解决了,所以决定写下解决方法方便以后遇到同样问题的朋友:
其实这个错误的主要问题就是没有找到需要的资源文件(该文件为Resources.resx),
引用该文件的地方就是
System.Resources.ResourceManager manager = new System.Resources.ResourceManager("×××.Resources", typeof(Resources).Assembly);
而导致错误的原因就是"×××.Resources"的配置错误
解决方法:首先查看工程中时候存在Resources.resx相关的文件,
找到它在工程中的位置(不如说一般都是在:工程名.Properties 命名空间下),
最后更改配置为new System.Resources.ResourceManager("工程名.Properties.Resources", typeof(Resources).Assembly);
续:
我这里在举个详细的例子,以便能够更明白哈
首先,一个容器(不管是Form,或是Panel也好),如果与其配套一个资源文件(以后缀名为.resx),则这2个文件应是在同一个命名空间下,才能相互使用
举个最特殊的例子
比如说一个工程名叫Test.Tname
其下有一个文件夹叫aaa(文件夹名字随便,没有关系)
文件夹aaa下有一个类文件叫Form1.cs,其命名空间为:Test.Tname(注意:命名空间才是重要的位置信息)
现在也有一个资源文件叫Form1.resx
(默认此资源文件是没有问题的,但是在特别的情况下,此文件就不会再正确的位置,导致出现如题的异常信息,比如说我反编译一个工程,自动生成的资源文件的位置就有问题)。
在Form1.cs中有句代码是需要资源文件的
ResourceManager manager = new ResourceManager(typeof(Test.Tname.Form1));
这里就注意了,如果Form1.cs类文件与Form1.resx资源文件是分开的话,
那么Form1.resx资源文件应放在哪个地方,程序才能找到他呢?
首先我们看哈Form1.cs类文件的命名空间为Test.Tname。
而资源文件Form1.resx中并没有命名空间的描述,所以只有靠位置来表示
再看下我们的工程名称:Test.Tname很好,它跟Form1.cs类文件的命名空间相同
所以我们就把Form1.resx资源文件放在工程的根目录下面
问题解决了,呵呵
其实我觉得最重要的问题就是类文件中可以有namespace来描述位置
而资源文件中并没有这行代码,只能靠真正的位置,这才是关键