zl程序教程

您现在的位置是:首页 >  后端

当前栏目

C#的Winform多语言实现(resx文件)

2023-09-11 14:16:44 时间

 使用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

  1. class ResourceCluture  
  2. {  
  3.     /// <summary>  
  4.     /// 设定语言环境  
  5.     /// </summary>  
  6.     /// <param name="strClutrue"></param>  
  7.     public static void SetLocalClutrue(string strClutrue)  
  8.     {  
  9.         if(string.IsNullOrEmpty(strClutrue))  
  10.         {  
  11.             strClutrue = "zh-CN";  
  12.         }  
  13.         CultureInfo currentClutrue = new CultureInfo(strClutrue);  
  14.         Thread.CurrentThread.CurrentCulture = currentClutrue;  
  15.     }  
  16.   
  17.     /// <summary>  
  18.     /// 取值  
  19.     /// </summary>  
  20.     /// <param name="id"></param>  
  21.     /// <returns></returns>  
  22.     public static string GetString(string id)  
  23.     {  
  24.         string strValue = string.Empty;  
  25.         try  
  26.         {  
  27.             ResourceManager resManager = new ResourceManager("ApplicationActive.Properties.Resource", Assembly.GetExecutingAssembly());  
  28.             strValue = resManager.GetString(id, Thread.CurrentThread.CurrentCulture);  
  29.         }  
  30.         catch  
  31.         {  
  32.             strValue = "No id:" + id + "please add";  
  33.         }  
  34.         return strValue;          
  35.     }  
  36. }  

//***********************异常

今天在测试一个工程的时候,突然遇到了这样一个问题:

 

错误信息: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来描述位置

而资源文件中并没有这行代码,只能靠真正的位置,这才是关键