WPF - Localization on-the-fly

Posted by William Basics on Thursday, June 3, 2021

WPF的本地化或者说对多国语言的支持,微软官方给出的方案是借助Uid加上WPF Samples中的小工具locBaml来实现。这种方案的一个缺点就是界面语言的切换需要重新打开Window甚至重启程序。文章最后编辑日期是在2017年,都2021年了,WPF竟然还没有给出一个优雅的方式来支持on-the-fly的界面语言切换。说WPF已死还太早,但WPF的开发生命应该已经到头了。

寻找WPF的相关文章,仿佛是在考古。解决方案也有一些,但最终在CodeProject上找到一篇Andrew Wood文章。里边介绍的解决方案是通过resource(也就是 *.resx文件)+ ObjectDataProvider的方式来实现on-the-fly localization。通过resx文件来实现多语言支持,早在WinForm上就应用过。并且有Visual Studio插件ResXManager的支持,十分便利。ObjectDataProvider,简单来说,就是让你可以在XAML中创建自定义的对象,并且使其能够作为binding source。我们需要通过它来绑定我们多国语言资源,是这些资源能够在通过单击按钮类似操作下,切换界面语言。

原文及其代码有点令人抓不住重点,我重新提炼下,并且采用Visual Studio 2019的样式重新实现了一个自己的版本。下面逐一介绍下实现的步骤。创建WPF project以及使用ResXManager插件产生多国语言resource就省略了。总之,我们有了两个语言资源文件,一个默认英语,一个简体中文(zh-CN)。

创建一个类名为CultureResources,用来封装ObjectDataProvider对象,并且后面会绑定到XAML资源文件里。

CultureResources.cs

using System.Globalization;
using System.Windows;
using System.Windows.Data;

namespace WPF_Localization_on_the_fly
{
    public class CultureResources
    {
        private static ObjectDataProvider _provider;

        public static ObjectDataProvider ResourceProvider
        {
            get {
                return _provider ?? 
                    (_provider = ObjectDataProvider)Application.Current.FindResource("CultureRes"));
                // 注意"CultureRes"必须和后面加到Application resources中的ObjectDataProvider的key名一致。
            }
        }

        public static void ChangeCulture(CultureInfo culture)
        {
            Properties.Resources.Culture = culture;
            ResourceProvider.Refresh();  // <- refresh the localization content with the new culture
        }

        public Properties.Resources GetResourceInstance()
        {
            return new Properties.Resources(); // Properties.Resources对象里包含着多语言字符串资源
        }
    }
}

在Application的resources中加入CultureResources对象。

<Application x:Class="WPF_Localization_on_the_fly.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:WPF_Localization_on_the_fly"
             StartupUri="MainWindow.xaml">
  <Application.Resources>
    <ObjectDataProvider x:Key="CultureRes" ObjectType="{x:Type local:CultureResources}" MethodName="GetResourceInstance"></ObjectDataProvider>
  </Application.Resources>
</Application>

在MainWindow.xaml中绑定CultureRes

<Window x:Class="WPF_Localization_on_the_fly.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        xmlns:local="clr-namespace:WPF_Localization_on_the_fly"
        mc:Ignorable="d"
        Title="{Binding Path=Title, Source={StaticResource CultureRes}}" Height="450" Width="800">
    <Grid>
        <StackPanel HorizontalAlignment="Center" VerticalAlignment="Center">
      <TextBlock Text="{Binding Path=Hello, Source={StaticResource CultureRes}}" FontSize="48"></TextBlock>
      <Button Content="{Binding Path=Switch, Source={StaticResource CultureRes}}" FontSize="24" Height="50" Width="100" Name="SwitchButton" Click="SwitchButton_OnClick"></Button>
        </StackPanel>
    </Grid>
</Window>

Build & F5

单击按钮,界面上的语言会在中英文之间切换。

Source Code: github

(end)