• 使用.Net Core开发WPF App系列教程( 其他、实现多语言切换的几种方式)


    使用.Net Core开发WPF App系列教程

    一、.Net Core和WPF介绍

    二、在Visual Studio 2019中创建.Net Core WPF工程

    三、与.Net Framework的区别

    四、WPF中的XAML

    五、WPF中的布局

    六、WPF中的常用控件(上)

    其它、实现多语言切换的几种方式


    在WPF中有多种方式可以实现多语言,这里提供几种常用的方式。

    一、使用XML实现多语言切换

    使用XML实现多语言的思路就是使用XML作为绑定的数据源。主要用到XmlDataProvider类.

    使用XmlDataProvider.Source属性指定XML文件的路径或通过XmlDataProvider.Document指定XML文档对象,XmlDataProvider.XPath属性指定绑定的路径。

    新建一个WPF工程,在debug目录下创建两个StrResource.xml文件,分别置于en-US和zh-CN文件夹

    debugen-USStrResource.xml

    1 <?xml version="1.0" encoding="utf-8"?>
    2 <Language>
    3     <Main_Title>Login Form</Main_Title>
    4     <Main_UserName>UserName</Main_UserName>
    5     <Main_Password>Password</Main_Password>
    6     <Main_Button>Login</Main_Button>
    7     <Window1_Title>Main Form</Window1_Title>
    8     <Window1_Label>Welcome</Window1_Label>
    9 </Language>

    debugzh-CNStrResource.xml

    1 <?xml version="1.0" encoding="utf-8"?>
    2 <Language>
    3     <Main_Title>登陆窗体</Main_Title>
    4     <Main_UserName>用户名</Main_UserName>
    5     <Main_Password>密码</Main_Password>
    6     <Main_Button>登陆</Main_Button>
    7     <Window1_Title>主界面</Window1_Title>
    8     <Window1_Label>欢迎</Window1_Label>
    9 </Language>

    主窗体XAML

     1  <StackPanel>
     2         <Label Content="{Binding XPath=Main_UserName}"></Label>
     3         <TextBox></TextBox>
     4         <Label Name="Password" Content="{Binding XPath=Main_Password}"></Label>
     5         <TextBox></TextBox>
     6         <Button Height="20" Margin="10,5" Background="LightSkyBlue" Name="Login" Content="{Binding XPath=Main_Button}" Click="Login_Click"></Button>
     7         <ComboBox Name="combox" SelectedIndex="0" SelectionChanged="combox_SelectionChanged">
     8             <ComboBoxItem>中文</ComboBoxItem>
     9             <ComboBoxItem>English</ComboBoxItem>
    10         </ComboBox>
    11     </StackPanel>

    后台代码中,将XmlDataProvider对象绑定到界面即可

    1 XmlDocument doc = new XmlDocument();
    2 XmlDataProvider xdp = new XmlDataProvider();
    3 doc.Load("./zh-CN/language.xml");  //在切换语言时,重新加载xml文档,并重新绑定到界面即可
    4 xdp.Document = doc;
    5 xdp.XPath = @"/Language";
    6 this.DataContext = xdp;

    运行效果如下:

    二、使用资源字典实现多语言切换

    资源字典的实现方式也比较简单,这是最常用的一种方式。

    主要实现步骤是:将要显示的字符绑定到资源文件,然后在切换语言时用代码更改当前使用的资源文件即可。

    创建一个WPF工程,添加一个language目录,再添加en-US和zh-CN目录。再分别在目录下创建资源字典文件,内容如下:

    languageen-US.xaml

     1 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     2                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     3                     xmlns:s="clr-namespace:System;assembly=mscorlib">
     4     <s:String x:Key="Main.Title">Main Form</s:String>
     5     <s:String x:Key="Main.RibbonTab.Setting">Setting</s:String>
     6     <s:String x:Key="Main.RibbonGroup.Setting">All Setting</s:String>
     7     <s:String x:Key="Main.RibbonButton.Setting">Setting</s:String>
     8     <s:String x:Key="Main.RibbonButton.Setting.Title">Setting</s:String>
     9     <s:String x:Key="Main.RibbonButton.Setting.Description">All Setting Include Language</s:String>
    10     <s:String x:Key="Setting.Title">Setting</s:String>
    11     <s:String x:Key="Setting.Tab.Language">Language Setting</s:String>
    12     <s:String x:Key="Setting.Tab.Label.ChooseLanguage">Please choose a language</s:String>
    13 </ResourceDictionary>

    languagezh-CN.xaml

     1 <ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
     2                     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
     3                     xmlns:s="clr-namespace:System;assembly=mscorlib">
     4     <s:String x:Key="Main.Title">主界面</s:String>
     5     <s:String x:Key="Main.RibbonTab.Setting">设置</s:String>
     6     <s:String x:Key="Main.RibbonGroup.Setting">全部设置</s:String>
     7     <s:String x:Key="Main.RibbonButton.Setting">设置</s:String>
     8     <s:String x:Key="Main.RibbonButton.Setting.Title">设置</s:String>
     9     <s:String x:Key="Main.RibbonButton.Setting.Description">包括语言在内的全部设置</s:String>
    10     <s:String x:Key="Setting.Title">设置</s:String>
    11     <s:String x:Key="Setting.Tab.Language">语言设置</s:String>
    12     <s:String x:Key="Setting.Tab.Label.ChooseLanguage">请选择一种语言</s:String>
    13 </ResourceDictionary>

    主窗体XAML

     1 <TabControl>
     2         <TabItem Header="{DynamicResource Setting.Tab.Language}">
     3             <StackPanel>
     4                 <TextBlock VerticalAlignment="Top" Margin="5,5,5,0" HorizontalAlignment="Left" Text="{DynamicResource Setting.Tab.Label.ChooseLanguage}">
     5                 </TextBlock>
     6                 <ComboBox Height="20"  VerticalAlignment="Top" Margin="5,10" Width="200" HorizontalAlignment="Left" Name="combox_Language" SelectionChanged="combox_Language_SelectionChanged">
     7                     <ComboBoxItem>中文</ComboBoxItem>
     8                     <ComboBoxItem>English</ComboBoxItem>
     9                 </ComboBox>
    10             </StackPanel>
    11         </TabItem>
    12     </TabControl>

    后台代码

     private void combox_Language_SelectionChanged(object sender, SelectionChangedEventArgs e)
            {
                ChangeLanguage(this.combox_Language.SelectedIndex);
            }
    
    
            /// <summary>
            /// 切换 语言
            /// </summary>
            /// <param name="index"></param>
            public void ChangeLanguage(int index)
            {
                ResourceDictionary rd = new ResourceDictionary();
                switch(index)
                {
                    case 0:
                        rd.Source = new Uri("Language/zh-CN.xaml", UriKind.Relative);
                        break;
                    case 1:
                        rd.Source = new Uri("Language/en-US.xaml", UriKind.Relative);
                        break;
                    default:
                        break;
                }            
                Application.Current.Resources.MergedDictionaries[0] = rd;
            }

    运行效果如下:

    三、使用资源文件实现多语言切换

    这种方式的实现也比较简单,也是将字符绑定到资源文件(.resx)

    但需要注意的是,这种方式是静态的,不能实现动态切换。只能在启动时更改。

    创建一个WPF工程,添加一个字符资源文件StrResources.resx作为默认的字符资源文件,再添加一个StrResources.zh-CN.resx做为中文字符资源(因为我用于演示的这台电脑系统是英文的)

    注意:需要将访问修饰符改为public,否则运行会报错

    主界面XAML

     1  <Grid>
     2         <Label HorizontalAlignment="Left" VerticalAlignment="Top" Content="{x:Static local:StrResources.ChangeLanguage}"></Label>
     3         <ComboBox HorizontalAlignment="Left" VerticalAlignment="Top" Margin="120,5,0,0" Width="200" Name="combox_Culture">
     4             <ComboBoxItem Content="{x:Static local:StrResources.zh_CN}"></ComboBoxItem>
     5             <ComboBoxItem Content="{x:Static local:StrResources.en_US}"></ComboBoxItem>
     6         </ComboBox>
     7 
     8         <Button Content="{x:Static local:StrResources.OK}" Width="88" Height="28" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,120,0"/>
     9         <Button Content="{x:Static local:StrResources.Cancel}" Width="88" Height="28" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,10,0"/>
    10     </Grid>

    主界面后台逻辑

     1  public partial class MainWindow : Window
     2     {
     3         public MainWindow()
     4         {
     5             InitializeComponent();
     6 
     7             LoadCulture();
     8         }
     9 
    10         public void LoadCulture()
    11         {
    12             if(CultureInfo.CurrentCulture.Name== "zh-CN")
    13             {
    14                 combox_Culture.SelectedIndex = 0;
    15             }
    16             else
    17             {
    18                 combox_Culture.SelectedIndex = 1;
    19             }
    20         }   
    21     }

    在Application类的Startup事件中可以切换语言,但在程序运行后无法再切换

     1    public partial class App : Application
     2     {
     3         private void Application_Startup(object sender, StartupEventArgs e)
     4         {
     5             //在这里可以更改语言
     6             ChangeCulture(0);
     7         }
     8 
     9         public void ChangeCulture(int index)
    10         {
    11             string cultureName = "";
    12 
    13             switch (index)
    14             {
    15                 case 0:
    16                     cultureName = "zh-CN";
    17                     break;
    18                 case 1:
    19                     cultureName = "en-US";
    20                     break;
    21                 default:
    22                     cultureName = "en-US";
    23                     break;
    24             }
    25 
    26             Thread.CurrentThread.CurrentCulture = new CultureInfo(cultureName);
    27             Thread.CurrentThread.CurrentUICulture = new CultureInfo(cultureName);
    28         }
    29     }

    运行效果:

    四、使用json文件实现多语言切换

    这种方式实现多语言切换有点麻烦,但可以使用json作为语言文件(其它格式文件其实也可以.txt .xml .csv)。

    这种方式的实现原理是使用索引器方法查找每个字段值,然后绑定到界面上。支持动态切换

    debug目录下创建

    zh-CN.json

    1 {
    2   "OK": "确定",
    3   "Cancel": "取消",
    4   "ChangeLanguage": "更改语言",
    5   "zh_CN": "中文",
    6   "en_US": "English"
    7 }

    en-US.json

    1 {
    2   "OK": "OK",
    3   "Cancel": "Cancel",
    4   "ChangeLanguage": "Change language",
    5   "zh_CN": "中文",
    6   "en_US": "English"
    7 }

    封装一个绑定通知类,这个类用于切换语言时,绑定的通知更新。

     1 /// <summary>
     2     /// 绑定通知类
     3     /// </summary>
     4     public class NotifyPropertyChanged : INotifyPropertyChanged
     5     {
     6         public event PropertyChangedEventHandler PropertyChanged;
     7 
     8         protected void RaisePropertyChanged(string PropertyName)
     9         {
    10             PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
    11         }
    12 
    13         protected void OnPropertyChanged([CallerMemberName] string PropertyName = null)
    14         {
    15             RaisePropertyChanged(PropertyName);
    16         }
    17 
    18         protected void RaiseAllChanged()
    19         {
    20             RaisePropertyChanged("");
    21         }
    22     }

    创建一个语言字段类,这个类用于封装所有的语言字段。这一步确实就比较麻烦了,每个字段都得封装一个属性。

     1  /// <summary>
     2     /// 语言字段类
     3     /// </summary>
     4     public class LanguageFields : NotifyPropertyChanged
     5     {
     6         /// <summary>
     7         /// 需要被重写的方法 用于获取语言字段值
     8         /// </summary>
     9         /// <param name="key"></param>
    10         /// <returns></returns>
    11         protected virtual string GetValue(string key) => "";
    12 
    13         protected virtual void SetValue(string Key, string value) { }
    14 
    15         /// <summary>
    16         /// 使用CallerMemberName特性传递当前属性名
    17         /// </summary>
    18         /// <param name="propertyName"></param>
    19         /// <returns></returns>
    20         string Get([CallerMemberName] string propertyName = null)
    21         {
    22             return GetValue(propertyName);
    23         }
    24 
    25         void Set(string value, [CallerMemberName] string propertyName = null)
    26         {
    27             SetValue(propertyName, value);
    28         }
    29 
    30         public string OK { get => Get(); set => Set(value); }
    31         public string Cancel { get => Get(); set => Set(value); }
    32         public string ChangeLanguage { get => Get(); set => Set(value); }
    33         public string zh_CN { get => Get(); set => Set(value); }
    34         public string en_US { get => Get(); set => Set(value); }
    35     }

    创建一个语言切换帮助类,这个类可以对当前使用的语言以及字段值进行操作

     1  public class LanguageHelper : LanguageFields
     2     {   
     3         private JObject currentLanguage;           //当前语言的JObject对象 
     4         private static readonly string dir = Environment.CurrentDirectory;  //语言文件夹
     5         private CultureInfo currentCulture;   //当前语言
     6 
     7         public static LanguageHelper Instance { get; } = new LanguageHelper();
     8 
     9         LanguageHelper()
    10         {
    11             CurrentCulture = CultureInfo.CurrentCulture;
    12         }
    13 
    14         /// <summary>
    15         /// 当前语言属性 当值更新时,加载语言并更新绑定
    16         /// </summary>
    17         public CultureInfo CurrentCulture
    18         {
    19             get => currentCulture;
    20             set
    21             {
    22                 currentCulture = value;
    23                 CultureInfo.CurrentUICulture = value;
    24                 currentLanguage = LoadLang(value.Name); 
    25                 LanguageChanged?.Invoke(value);
    26                 RaiseAllChanged();
    27             }
    28         }
    29 
    30         /// <summary>
    31         /// 加载语言文件 
    32         /// </summary>
    33         /// <param name="LanguageId"></param>
    34         /// <returns></returns>
    35         JObject LoadLang(string LanguageId)
    36         {
    37             try
    38             {
    39                 var filePath = System.IO.Path.Combine(dir, $"{LanguageId}.json");
    40                 return JObject.Parse(File.ReadAllText(filePath));
    41             }
    42             catch
    43             {
    44                 return new JObject();
    45             }
    46         }
    47 
    48         /// <summary>
    49         /// 索引器方法 用于查找语言字段值
    50         /// </summary>
    51         /// <param name="Key"></param>
    52         /// <returns></returns>
    53         public string this[string Key]
    54         {
    55             get
    56             {
    57                 if (Key == null)
    58                     return "";
    59 
    60                 if (currentLanguage != null && currentLanguage.TryGetValue(Key, out var value) && value.ToString() is string s && !string.IsNullOrWhiteSpace(s))
    61                     return s;
    62 
    63                 return Key;
    64             }
    65         }
    66 
    67         /// <summary>
    68         /// 重写 GetValue方法,调用索引器方法 
    69         /// </summary>
    70         /// <param name="PropertyName"></param>
    71         /// <returns></returns>
    72         protected override string GetValue(string PropertyName) => this[PropertyName];
    73 
    74         /// <summary>
    75         /// 语言更改事件
    76         /// </summary>
    77         public event Action<CultureInfo> LanguageChanged;
    78     }

    主窗体XAML

     1  <Grid>
     2         <Label HorizontalAlignment="Left" VerticalAlignment="Top" Content="{Binding ChangeLanguage, Source={StaticResource LangManger}, Mode=OneWay}"></Label>
     3         <ComboBox HorizontalAlignment="Left" VerticalAlignment="Top" Margin="120,5,0,0" Width="200" Name="combox_Culture" SelectionChanged="combox_Culture_SelectionChanged">
     4             <ComboBoxItem Content="{Binding zh_CN, Source={StaticResource LangManger}, Mode=OneWay}"></ComboBoxItem>
     5             <ComboBoxItem Content="{Binding en_US, Source={StaticResource LangManger}, Mode=OneWay}"></ComboBoxItem>
     6         </ComboBox>
     7 
     8         <Button Content="{Binding OK, Source={StaticResource LangManger}, Mode=OneWay}" Width="88" Height="28" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,120,0"/>
     9         <Button Content="{Binding Cancel, Source={StaticResource LangManger}, Mode=OneWay}" Width="88" Height="28" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,10,0"/>
    10     </Grid>

    主窗体后台逻辑

    软件启动时,加载当前语言。当下位框切换时,切换语言。

     1 public partial class MainWindow : Window
     2     {
     3         public MainWindow()
     4         {
     5             InitializeComponent();
     6 
     7             LanguageHelper.Instance.LanguageChanged += Instance_LanguageChanged;
     8             LoadCulture(LanguageHelper.Instance.CurrentCulture);
     9         }
    10 
    11         private void Instance_LanguageChanged(System.Globalization.CultureInfo obj)
    12         {
    13             //这里可以对语言更改进行处理
    14             switch(obj.Name)
    15             {
    16                 case "zh-CN":
    17                     break;
    18                 case "en-US":
    19                     break;
    20             }
    21         }
    22 
    23         private void LoadCulture(System.Globalization.CultureInfo culture)
    24         {
    25             switch(culture.Name)
    26             {
    27                 case "zh-CN":
    28                     combox_Culture.SelectedIndex = 0;
    29                     break;
    30                 case "en-US":
    31                     combox_Culture.SelectedIndex = 1;
    32                     break;
    33             }
    34         }
    35 
    36         private void combox_Culture_SelectionChanged(object sender, SelectionChangedEventArgs e)
    37         {
    38             var culture = "zh-CN";
    39 
    40             switch(combox_Culture.SelectedIndex)
    41             {
    42                 case 0:
    43                     culture = "zh-CN";
    44                     break;
    45                 case 1:
    46                     culture = "en-US";
    47                     break;
    48             }
    49 
    50             if (culture == null)
    51                 return;
    52 
    53             LanguageHelper.Instance.CurrentCulture = new System.Globalization.CultureInfo(culture.ToString().Replace("_", "-"));   //变量命名不支持 '-' ,所以这里需要替换一下
    54         }
    55     }

    示例代码

    https://github.com/zhaotianff/DotNetCoreWPF/tree/master/其它、实现多语言切换的几种方式/MultiLanguageDemo

  • 相关阅读:
    SSRS Fields cannot be used in page headers or footers
    mac os x 触摸板点击无效
    Android内核sysfs中switch类使用实例
    hdu1827之强联通
    解决Gradle执行命令时报Could not determine the dependencies of task &#39;:compileReleaseJava&#39;.
    我对Lamport Logical Clock的理解
    側滑删除进阶(七、八)
    管理之路(成长之路--五)
    Qt跨平台的一个例程
    IOS 开发推荐经常使用lib
  • 原文地址:https://www.cnblogs.com/zhaotianff/p/14042803.html
Copyright © 2020-2023  润新知