在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
<?xml version="1.0" encoding="utf-8"?>
<Language>
<Main_Title>Login Form</Main_Title>
<Main_UserName>UserName</Main_UserName>
<Main_Password>Password</Main_Password>
<Main_Button>Login</Main_Button>
<Window1_Title>Main Form</Window1_Title>
<Window1_Label>Welcome</Window1_Label>
</Language>debug\zh-CN\StrResource.xml
<?xml version="1.0" encoding="utf-8"?>
<Language>
<Main_Title>登陆窗体</Main_Title>
<Main_UserName>用户名</Main_UserName>
<Main_Password>密码</Main_Password>
<Main_Button>登陆</Main_Button>
<Window1_Title>主界面</Window1_Title>
<Window1_Label>欢迎</Window1_Label>
</Language>主窗体XAML
<StackPanel>
<Label Content="{Binding XPath=Main_UserName}"></Label>
<TextBox></TextBox>
<Label Name="Password" Content="{Binding XPath=Main_Password}"></Label>
<TextBox></TextBox>
<Button Height="20" Margin="10,5" Background="LightSkyBlue" Name="Login" Content="{Binding XPath=Main_Button}" Click="Login_Click"></Button>
<ComboBox Name="combox" SelectedIndex="0" SelectionChanged="combox_SelectionChanged">
<ComboBoxItem>中文</ComboBoxItem>
<ComboBoxItem>English</ComboBoxItem>
</ComboBox>
</StackPanel>在后台代码中,将XmlDataProvider对象绑定到界面即可
XmlDocument doc = new XmlDocument();
XmlDataProvider xdp = new XmlDataProvider();
doc.Load("./zh-CN/language.xml"); //在切换语言时,重新加载xml文档,并重新绑定到界面即可
xdp.Document = doc;
xdp.XPath = @"/Language";
this.DataContext = xdp;运行效果如下:

二、使用资源字典实现多语言切换
资源字典的实现方式也比较简单,这是最常用的一种方式。
主要实现步骤是:将要显示的字符绑定到资源文件,然后在切换语言时用代码更改当前使用的资源文件即可。
创建一个WPF工程,添加一个language目录,再添加en-US和zh-CN目录。再分别在目录下创建资源字典文件,内容如下:
language\en-US.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<s:String x:Key="Main.Title">Main Form</s:String>
<s:String x:Key="Main.RibbonTab.Setting">Setting</s:String>
<s:String x:Key="Main.RibbonGroup.Setting">All Setting</s:String>
<s:String x:Key="Main.RibbonButton.Setting">Setting</s:String>
<s:String x:Key="Main.RibbonButton.Setting.Title">Setting</s:String>
<s:String x:Key="Main.RibbonButton.Setting.Description">All Setting Include Language</s:String>
<s:String x:Key="Setting.Title">Setting</s:String>
<s:String x:Key="Setting.Tab.Language">Language Setting</s:String>
<s:String x:Key="Setting.Tab.Label.ChooseLanguage">Please choose a language</s:String>
</ResourceDictionary>language\zh-CN.xaml
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:System;assembly=mscorlib">
<s:String x:Key="Main.Title">主界面</s:String>
<s:String x:Key="Main.RibbonTab.Setting">设置</s:String>
<s:String x:Key="Main.RibbonGroup.Setting">全部设置</s:String>
<s:String x:Key="Main.RibbonButton.Setting">设置</s:String>
<s:String x:Key="Main.RibbonButton.Setting.Title">设置</s:String>
<s:String x:Key="Main.RibbonButton.Setting.Description">包括语言在内的全部设置</s:String>
<s:String x:Key="Setting.Title">设置</s:String>
<s:String x:Key="Setting.Tab.Language">语言设置</s:String>
<s:String x:Key="Setting.Tab.Label.ChooseLanguage">请选择一种语言</s:String>
</ResourceDictionary>主窗体XAML
<TabControl>
<TabItem Header="{DynamicResource Setting.Tab.Language}">
<StackPanel>
<TextBlock VerticalAlignment="Top" Margin="5,5,5,0" HorizontalAlignment="Left" Text="{DynamicResource Setting.Tab.Label.ChooseLanguage}">
</TextBlock>
<ComboBox Height="20" VerticalAlignment="Top" Margin="5,10" Width="200" HorizontalAlignment="Left" Name="combox_Language" SelectionChanged="combox_Language_SelectionChanged">
<ComboBoxItem>中文</ComboBoxItem>
<ComboBoxItem>English</ComboBoxItem>
</ComboBox>
</StackPanel>
</TabItem>
</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
<Grid>
<Label HorizontalAlignment="Left" VerticalAlignment="Top" Content="{x:Static local:StrResources.ChangeLanguage}"></Label>
<ComboBox HorizontalAlignment="Left" VerticalAlignment="Top" Margin="120,5,0,0" Width="200" Name="combox_Culture">
<ComboBoxItem Content="{x:Static local:StrResources.zh_CN}"></ComboBoxItem>
<ComboBoxItem Content="{x:Static local:StrResources.en_US}"></ComboBoxItem>
</ComboBox>
<Button Content="{x:Static local:StrResources.OK}" Width="88" Height="28" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,120,0"/>
<Button Content="{x:Static local:StrResources.Cancel}" Width="88" Height="28" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,10,0"/>
</Grid>主界面后台逻辑
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
LoadCulture();
}
public void LoadCulture()
{
if(CultureInfo.CurrentCulture.Name== "zh-CN")
{
combox_Culture.SelectedIndex = 0;
}
else
{
combox_Culture.SelectedIndex = 1;
}
}
}在Application类的Startup事件中可以切换语言,但在程序运行后无法再切换
public partial class App : Application
{
private void Application_Startup(object sender, StartupEventArgs e)
{
//在这里可以更改语言
ChangeCulture(0);
}
public void ChangeCulture(int index)
{
string cultureName = "";
switch (index)
{
case 0:
cultureName = "zh-CN";
break;
case 1:
cultureName = "en-US";
break;
default:
cultureName = "en-US";
break;
}
Thread.CurrentThread.CurrentCulture = new CultureInfo(cultureName);
Thread.CurrentThread.CurrentUICulture = new CultureInfo(cultureName);
}
}运行效果:

四、使用json文件实现多语言切换
这种方式实现多语言切换有点麻烦,但可以使用json作为语言文件(其它格式文件其实也可以.txt .xml .csv)。
这种方式的实现原理是使用索引器方法查找每个字段值,然后绑定到界面上。支持动态切换
在debug目录下创建
zh-CN.json
{
"OK": "确定",
"Cancel": "取消",
"ChangeLanguage": "更改语言",
"zh_CN": "中文",
"en_US": "English"
}en-US.json
{
"OK": "OK",
"Cancel": "Cancel",
"ChangeLanguage": "Change language",
"zh_CN": "中文",
"en_US": "English"
}封装一个绑定通知类,这个类用于切换语言时,绑定的通知更新。
/// <summary>
/// 绑定通知类
/// </summary>
public class NotifyPropertyChanged : INotifyPropertyChanged
{
public event PropertyChangedEventHandler PropertyChanged;
protected void RaisePropertyChanged(string PropertyName)
{
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(PropertyName));
}
protected void OnPropertyChanged([CallerMemberName] string PropertyName = null)
{
RaisePropertyChanged(PropertyName);
}
protected void RaiseAllChanged()
{
RaisePropertyChanged("");
}
}创建一个语言字段类,这个类用于封装所有的语言字段。这一步确实就比较麻烦了,每个字段都得封装一个属性。
/// <summary>
/// 语言字段类
/// </summary>
public class LanguageFields : NotifyPropertyChanged
{
/// <summary>
/// 需要被重写的方法 用于获取语言字段值
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
protected virtual string GetValue(string key) => "";
protected virtual void SetValue(string Key, string value) { }
/// <summary>
/// 使用CallerMemberName特性传递当前属性名
/// </summary>
/// <param name="propertyName"></param>
/// <returns></returns>
string Get([CallerMemberName] string propertyName = null)
{
return GetValue(propertyName);
}
void Set(string value, [CallerMemberName] string propertyName = null)
{
SetValue(propertyName, value);
}
public string OK { get => Get(); set => Set(value); }
public string Cancel { get => Get(); set => Set(value); }
public string ChangeLanguage { get => Get(); set => Set(value); }
public string zh_CN { get => Get(); set => Set(value); }
public string en_US { get => Get(); set => Set(value); }
}创建一个语言切换帮助类,这个类可以对当前使用的语言以及字段值进行操作
public class LanguageHelper : LanguageFields
{
private JObject currentLanguage; //当前语言的JObject对象
private static readonly string dir = Environment.CurrentDirectory; //语言文件夹
private CultureInfo currentCulture; //当前语言
public static LanguageHelper Instance { get; } = new LanguageHelper();
LanguageHelper()
{
CurrentCulture = CultureInfo.CurrentCulture;
}
/// <summary>
/// 当前语言属性 当值更新时,加载语言并更新绑定
/// </summary>
public CultureInfo CurrentCulture
{
get => currentCulture;
set
{
currentCulture = value;
CultureInfo.CurrentUICulture = value;
currentLanguage = LoadLang(value.Name);
LanguageChanged?.Invoke(value);
RaiseAllChanged();
}
}
/// <summary>
/// 加载语言文件
/// </summary>
/// <param name="LanguageId"></param>
/// <returns></returns>
JObject LoadLang(string LanguageId)
{
try
{
var filePath = System.IO.Path.Combine(dir, $"{LanguageId}.json");
return JObject.Parse(File.ReadAllText(filePath));
}
catch
{
return new JObject();
}
}
/// <summary>
/// 索引器方法 用于查找语言字段值
/// </summary>
/// <param name="Key"></param>
/// <returns></returns>
public string this[string Key]
{
get
{
if (Key == null)
return "";
if (currentLanguage != null && currentLanguage.TryGetValue(Key, out var value) && value.ToString() is string s && !string.IsNullOrWhiteSpace(s))
return s;
return Key;
}
}
/// <summary>
/// 重写 GetValue方法,调用索引器方法
/// </summary>
/// <param name="PropertyName"></param>
/// <returns></returns>
protected override string GetValue(string PropertyName) => this[PropertyName];
/// <summary>
/// 语言更改事件
/// </summary>
public event Action<CultureInfo> LanguageChanged;
}主窗体XAML
<Grid>
<Label HorizontalAlignment="Left" VerticalAlignment="Top" Content="{Binding ChangeLanguage, Source={StaticResource LangManger}, Mode=OneWay}"></Label>
<ComboBox HorizontalAlignment="Left" VerticalAlignment="Top" Margin="120,5,0,0" Width="200" Name="combox_Culture" SelectionChanged="combox_Culture_SelectionChanged">
<ComboBoxItem Content="{Binding zh_CN, Source={StaticResource LangManger}, Mode=OneWay}"></ComboBoxItem>
<ComboBoxItem Content="{Binding en_US, Source={StaticResource LangManger}, Mode=OneWay}"></ComboBoxItem>
</ComboBox>
<Button Content="{Binding OK, Source={StaticResource LangManger}, Mode=OneWay}" Width="88" Height="28" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,120,0"/>
<Button Content="{Binding Cancel, Source={StaticResource LangManger}, Mode=OneWay}" Width="88" Height="28" HorizontalAlignment="Right" VerticalAlignment="Bottom" Margin="0,0,10,0"/>
</Grid>主窗体后台逻辑
软件启动时,加载当前语言。当下位框切换时,切换语言。
public partial class MainWindow : Window
{
public MainWindow()
{
InitializeComponent();
LanguageHelper.Instance.LanguageChanged += Instance_LanguageChanged;
LoadCulture(LanguageHelper.Instance.CurrentCulture);
}
private void Instance_LanguageChanged(System.Globalization.CultureInfo obj)
{
//这里可以对语言更改进行处理
switch(obj.Name)
{
case "zh-CN":
break;
case "en-US":
break;
}
}
private void LoadCulture(System.Globalization.CultureInfo culture)
{
switch(culture.Name)
{
case "zh-CN":
combox_Culture.SelectedIndex = 0;
break;
case "en-US":
combox_Culture.SelectedIndex = 1;
break;
}
}
private void combox_Culture_SelectionChanged(object sender, SelectionChangedEventArgs e)
{
var culture = "zh-CN";
switch(combox_Culture.SelectedIndex)
{
case 0:
culture = "zh-CN";
break;
case 1:
culture = "en-US";
break;
}
if (culture == null)
return;
LanguageHelper.Instance.CurrentCulture = new System.Globalization.CultureInfo(culture.ToString().Replace("_", "-")); //变量命名不支持 '-' ,所以这里需要替换一下
}
}示例代码
https://github.com/zhaotianff/DotNetCoreWPF/tree/master/其它、实现多语言切换的几种方式/MultiLanguageDemo