使用.Net Core开发WPF App系列教程( 其他、实现多语言切换的几种方式)
在WPF中有多种方式可以实现多语言,这里提供几种常用的方式。
一、使用XML实现多语言切换
使用XML实现多语言的思路就是使用XML作为绑定的数据源。主要用到XmlDataProvider类.
使用XmlDataProvider.Source属性指定XML文件的路径或通过XmlDataProvider.Document指定XML文档对象,XmlDataProvider.XPath属性指定绑定的路径。
新建一个WPF工程,在debug目录下创建两个StrResource.xml文件,分别置于en-US和zh-CN文件夹
debug\en-US\StrResource.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>
debug\zh-CN\StrResource.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目录。再分别在目录下创建资源字典文件,内容如下:
language\en-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>
language\zh-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