亚洲必赢bwin696.com[WPF]本地化入门

1. 前言

WPF的本地化是个非常广阔的功用,我举行了之WPF程序大部分都落实了本地化(不管最后闹无发以)。通常本地化有以下几点需求:

  • 在次启动时冲CultureInfo.CurrentUICulture或者安排起显示对承诺语言的UI。
  • 于程序运行时可动态切换UI语言(无需另行开程序)。
  • 制作对应不同语言的安装包。
  • 由此下载语言包实现多种语言的本地化。

中间只有首先沾是不可或缺的。
第二触及最为好呢得以兑现,很多时段切换语言就为看有专业术语在英语中之初稿是啊,或者临时打印个英文报表,平时采用要用汉语,用户不思量以这点更开程序。
其三碰与季沾虽然好广阔,但自己常有没有兑现了,毕竟文字资源(有时还发少量图)占用的半空中不会见太多,大部分WPF程序还未曾大到需要考虑设置包大小,所有语言的资源全从包上一个安包就好了。

WPF本地化技术非常熟,也出几种方案,微软在MSDN给有了详实的牵线WPF
全球化及本地化概述.aspx),还有平等份古老的文档WPF
Localization
Guidance,整整66页,里面详细介绍了各种WPF本地化的机制。

本文只介绍两种植实现上述第1、2点需求的方案。

2. 动资源词典

2.1 基本原理

对WPF开发者来说,资源词典肯定不会见生。不过以资源词典里使用string可能较少。

<Window x:Class="LocalizationDemoWpf.Window1"
        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:LocalizationDemoWpf"
        mc:Ignorable="d" 
        xmlns:system="clr-namespace:System;assembly=mscorlib"
        Title="Window1" Height="300" Width="300">
    <Window.Resources>
        <system:String x:Key="Chinese">中文</system:String>
    </Window.Resources>
    <Grid>
        <TextBlock Text="{DynamicResource Chinese}"/>
    </Grid>
</Window>

假定以上代码所示,在XAML中定义string资源需要事先引入xmlns:system="clr-namespace:System;assembly=mscorlib"取名空间,之后再也用DynamicResource引用这资源。不要使用StaticResource,这样没法做到动态切换语言。

要动用资源词典实现本地化,需要事先创造所急需语言的xaml,我当DEMO中开创了en-us.xaml和zh-cn.xaml两个资源词典,里面的蕴藏的资源结构同样(指数量和Key一样):

<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
                    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" 
                    xmlns:system="clr-namespace:System;assembly=mscorlib"
                    xmlns:local="clr-namespace:LocalizationDemoWpf">
    <system:String x:Key="SwitchLanguage">切换语言</system:String>
    <system:String x:Key="Chinese">中文</system:String>
    <system:String x:Key="English">英文</system:String>
    <system:String x:Key="Username">用户名</system:String>
    <system:String x:Key="Sex">性别</system:String>
    <system:String x:Key="Address">地址</system:String>
    <SolidColorBrush x:Key="Background" Color="#88FF0000"/>
</ResourceDictionary>

于次启动时冲CultureInfo.CurrentUICulture或者部署起选相应之资源词典,使用MergedDictionaries的法门加载到程序的资源聚合中:

var culture = ReadCultureFromConfig();
var cultureInfo = new System.Globalization.CultureInfo(culture);
Thread.CurrentThread.CurrentUICulture = cultureInfo;
Thread.CurrentThread.CurrentCulture = cultureInfo;


ResourceDictionary dictionary = new ResourceDictionary { Source = new Uri($@"Resources\{culture}.xaml", UriKind.RelativeOrAbsolute) };
Application.Current.Resources.MergedDictionaries[0] = dictionary;

如此本地化的力量就成功了。

2.2 动态切换语言

事实上上述方案都兑现了动态切换语言。
XAML资源的援原则是内外原则,这个附近不仅指VisualTree上之前后,还因日达的跟前。后补偿加进资源词典的资源以替换之前的同名资源。使用DynamicResource而休是StaticResource,就是为在资源为轮换时亦可实时变更UI的来得。

2.3 设计时支持

VisualStudio的XAML设计时支持对出WPF程序要,对本地化来说,设计时支持至关重要含有3有些:

  • 每当编写XAML时好博资源的智能感知
  • 起整机的宏图视图
  • 当不同语言里切换

应用资源词典实现本地化,只需要以App.xaml中集合对应之资源词典即可获取完全的设计时支持。

<Application x:Class="LocalizationDemoWpf.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:LocalizationDemoWpf"
             xmlns:resource="clr-namespace:LocalizationDemoWpf.Resource;assembly=LocalizationDemoWpf.Resource"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <ResourceDictionary>
            <ResourceDictionary.MergedDictionaries>
                <ResourceDictionary Source="/LocalizationDemoWpf;component/Resources/zh-cn.xaml"/>
                <!--<ResourceDictionary Source="/LocalizationDemoWpf;component/Resources/en-us.xaml"/>-->
            </ResourceDictionary.MergedDictionaries>
        </ResourceDictionary>
    </Application.Resources>
</Application>

亚洲必赢bwin696.com 1

当下段XAML只是为了增强规划时体验,没有吗能经过编译。

2.4 在代码里拜访资源

以代码中做客资源比较累,需要了解资源的名称,而且从不智能感知,如果资源词典由第三在类库提供就会重复麻烦。

var message = TryFindResource("SwitchLanguage") as string;
if (string.IsNullOrWhiteSpace(message) == false)
    MessageBox.Show(message);

2.5 在代码里替换资源

private void OnReplaceString(object sender, RoutedEventArgs e)
{
    _totalReplace++;
    string content = "Replace " + _totalReplace;
    Resources["StringToReplace"] = content;
}

苟齐所示,在代码中替换资源异常简单,不过这种简易也带了资源不可控的问题。

2.6 在程序集以内共享资源

地方有取过,在获得第三正类库中有资源充分辛苦,不仅如此,连得第三方类库中的资源词典名称都不行劳动。我提议在类库中定义如下的切近,可以为开发者提供有造福:

public static class Resources
{
    public static Uri EnglishResourceUri { get; } =
        new Uri("/LocalizationDemoWpf.Resource;component/Resource.en-us.xaml", UriKind.RelativeOrAbsolute);

    public static Uri ChineseResourceUri { get; } =
        new Uri("/LocalizationDemoWpf.Resource;component/Resource.zh-cn.xaml", UriKind.RelativeOrAbsolute);
}

2.7 总结

资源词典是兑现本地化的一律种植很宽泛的方,它发生如下优点:

  • 简单易行容易用,而且爱掌握。
  • XAML语法简单。
  • 资源可以是除string以外的路,如SolidColorBrush。

只是这种方式的老毛病也不在少数:

  • 麻烦管理,一旦资源过多,重名、互相覆盖、智能感知列表过长齐题材拿极大地影响开发,就连管不同语言里资源词典里的资源数量一样都不行烦。
  • 每当程序集里难以共享,引用很简短,但出于没有智能感知将生不便用,而且不同程序集以内的资源同名更难跟踪。

除却本条之外,在动态切换语言上还在有的问题。下面这段XAML就无奈完成动态切换语言:

<DataGrid Grid.Row="1" Margin="5">
    <DataGrid.Columns>
        <DataGridTextColumn Header="{DynamicResource Username}"/>
        <DataGridTextColumn Header="{DynamicResource Sex}"/>
        <DataGridTextColumn Header="{DynamicResource Address}" Width="*"/>
    </DataGrid.Columns>
</DataGrid>

每当DataGridColumn的Header上举行动态切换语言,需要写成DataTemplate的章程:

<DataGrid Grid.Row="2" Margin="5">
    <DataGrid.Columns>
        <DataGridTextColumn >
            <DataGridTextColumn.HeaderTemplate>
                <DataTemplate >
                    <TextBlock Text="{DynamicResource Username}"/
                </DataTemplate>
            </DataGridTextColumn.HeaderTemplate>
        </DataGridTextColumn>
        <DataGridTextColumn >
            <DataGridTextColumn.HeaderTemplate>
                <DataTemplate >
                    <TextBlock Text="{DynamicResource Sex}"/>
                </DataTemplate>
            </DataGridTextColumn.HeaderTemplate>
        </DataGridTextColumn>
        <DataGridTextColumn Width="*">
            <DataGridTextColumn.HeaderTemplate>
                <DataTemplate >
                    <TextBlock Text="{DynamicResource Address}"/>
                </DataTemplate>
            </DataGridTextColumn.HeaderTemplate>
        </DataGridTextColumn>
    </DataGrid.Columns>
</DataGrid>

3. 行使Resx资源文件

3.1 基本原理

于从资源词典,我还欣赏用Resx资源文件,不过这种办法语法复杂一些,而且也出为数不少略题目。
每当VisualStudio中开创后缀名为resx的资源文件并打开,可每当偏下UI编辑资源文件的值(将拜访修饰符改为public用起方便些):
亚洲必赢bwin696.com 2

在窜资源文件之值后PublicResXFileCodeGenerator将电动创建对应的类并为各一个键值添加如下代码:

/// <summary>
///   查找类似 Address 的本地化字符串。
/// </summary>
public static string Address {
    get {
        return ResourceManager.GetString("Address", resourceCulture);
    }
}

下一场将是资源文件复制粘贴一份,将称变更吗“原名+.+对应之言语+.resx”的格式,并且以其中的价值翻译成对诺语言如下:
亚洲必赢bwin696.com 3

每当UI上使用x:Static绑定到对应之资源:

<DataGridTextColumn Header="{x:Static local:Labels.Username}"/>

诸如此类中心的本地化就形成了。很多控件库都是利用这种方法做本地化。除了字符串,resx资源文件还支持除字符串以外的资源,如图、音频等。
亚洲必赢bwin696.com 4

可是这方案只有兑现了无以复加基本的本地化,而且最特别之题材是单纯支持直接以字符串,不支持TypeConverter,甚至也未支持除字符串以外的其他XAML内置类型.aspx)(即Boolea,Char,Decimal,Single,Double,Int16,Int32,Int64,TimeSpan,Uri,Byte,Array等品类)。例如利用Label.resx中谓吧Background值为
#880000FF 的字符串为Grid.Background实现本地化:

Labels.designer.resx

/// <summary>
///   查找类似 #880000FF 的本地化字符串。
/// </summary>
public static string Background {
    get {
        return ResourceManager.GetString("Background", resourceCulture);
    }
}

MainWindow.xaml

<Grid  Background="{x:Static local:Labels.Background}"/>

运作时报错:ArgumentException:
“#88FF0000”不是性质“Background”的有效值。

这么资源文件的实用性大打折扣。当然,这个方案吗非支持动态切换语言。

3.2 动态切换语言

在Silverlight.aspx)中都没有了x:Static的绑定方式,改呢利用Binding实现本地化,这样虽然语法复杂一些,但越是实用。WPF当然为足以以这种方法。

第一, 创建一个接近包装资源文件生成的好像(在斯Demo中是Labels):

public class ApplicationResources
{
    public ApplicationResources()
    {
        Labels = new Labels();
    }

    public Labels Labels { get; set; }
}

接下来于App.xaml中以此类似作为资源丰富到资源集聚中,为了以后用的语法简单些,我日常将Key取得很粗略:

<Application x:Class="LocalizationDemoWpfUsingResource.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             xmlns:local="clr-namespace:LocalizationDemoWpfUsingResource"
             StartupUri="MainWindow.xaml">
    <Application.Resources>
        <local:ApplicationResources x:Key="R"  />
    </Application.Resources>
</Application>

末在XAML中这样绑定:

<DataGridTextColumn Header="{Binding Labels.Username, Source={StaticResource R}}"/>

这样语法复杂一些,但为来很多利益:

  • 支持TypeConverter,这样便可下除String以外的另外种类。
  • 支持Binding的外力量,如IValueConverter。

劳的是,WPF似乎不是好喜欢这种方法,VisualStudio会提示这种似是而非,毕竟资源文件被的性质都是static属性,不是实例成员。幸运的凡编译一差这种似是而非提示就见面磨灭。
亚洲必赢bwin696.com 5

将调用方式转也Binding以后就是好实现动态切换语言了。由于UI通过Binding获取资源文件之情,可以通过INotifyPropertyChanged通知UI更新。将ApplicationResources
改造一下:

public class ApplicationResources : INotifyPropertyChanged
{
    public static ApplicationResources Current { get; private set; }

    public ApplicationResources()
    {
        Current = this;
        Labels = new Labels();
    }

    public Labels Labels { get; set; }



    public event PropertyChangedEventHandler PropertyChanged;

    public  void ChangeCulture(System.Globalization.CultureInfo cultureInfo)
    {
        Thread.CurrentThread.CurrentUICulture = cultureInfo;
        Thread.CurrentThread.CurrentCulture = cultureInfo;
        Labels.Culture = cultureInfo;

        if (Current != null)
            Current.RaiseProoertyChanged();
    }

    public void RaiseProoertyChanged()
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(""));
    }
}

今昔好略地切换语言了。

var culture = ReadCultureFromConfig();
var cultureInfo = new System.Globalization.CultureInfo(culture);
ApplicationResources.Current.ChangeCulture(cultureInfo);

3.3 设计时支持

实现本地化的一个万分辛苦的事体是安在规划视图看到各种语言下的效力。在运资源词典的方案受到凡是通过在App.xaml中集合对应之资源词典:

<ResourceDictionary.MergedDictionaries>
    <ResourceDictionary Source="/LocalizationDemoWpf;component/Resources/zh-cn.xaml"/>
    <!--<ResourceDictionary Source="/LocalizationDemoWpf;component/Resources/en-us.xaml"/>-->
</ResourceDictionary.MergedDictionaries>

当资源文件之方案中,需要以ApplicationResources中补充加一个性:

private string _language;

/// <summary>
/// 获取或设置 Language 的值
/// </summary>
public string Language
{
    get { return _language; }
    set
    {
        if (_language == value)
            return;

        _language = value;
        var cultureInfo = new CultureInfo(value);
        Thread.CurrentThread.CurrentUICulture = cultureInfo;
        Thread.CurrentThread.CurrentCulture = cultureInfo;
        Labels.Culture = cultureInfo;

        RaiseProoertyChanged();
    }
}

自此在App.xaml中虽得经变更是特性来转计划时的UI的言语,在VS2017负并编译都无欲就可以改规划视图的言语。

<local:ApplicationResources x:Key="R"  Language="zh-CN"/>

亚洲必赢bwin696.com 6

3.4 在代码里拜访资源

以代码里拜访资源文件的资源大简易:

MessageBox.Show(Labels.SwitchLanguage);

3.5 在代码里替换资源

资源文件要促成者需求就一些都不好玩了,至少自己没以实质上工作吃召开了。最要命的难题是资源文件生成的好像中之性是静态属性,而且只有getter方法:

public static string StringToReplace {
    get {
        return ResourceManager.GetString("StringToReplace", resourceCulture);
    }
}

我们吧足以创建一个差生类,强行替换对应之属性:

public class ExtendLabels : Labels
{
    /// <summary>
    /// 获取或设置 StringToReplace 的值
    /// </summary>
    public new string StringToReplace { get; set; }
}

然后替换ApplicationResources中的Labels,并且触发PropertyChanged。不过这样见面刷新所有UI上之字符串等资源,只为替换一个字符资源代价来接触十分,幸好一般的话并无见面极其耗费性能。

private void OnReplaceString(object sender, RoutedEventArgs e)
{
    _totalReplace++;
    string content = Labels.StringToReplace + " " + _totalReplace;
    if (_extendLabels == null)
        _extendLabels = new ExtendLabels();

    _extendLabels.StringToReplace = content;
    ApplicationResources.Current.Labels = _extendLabels;
    ApplicationResources.Current.RaiseProoertyChanged();
}

3.6 在先后集以内共享资源

不过待用资源文件的看修饰符改为public,无需外操作就可一本万利地在次集里共享资源。
亚洲必赢bwin696.com 7

3.7 管理资源文件

比从资源词典,资源文件还有一个死要命之优势就是便于管理。Demo中只出一个名字Labels的资源文件,实际项目被可按照效益要模块分别立相应之资源文件,解决了资源词典重名、互相覆盖、智能感知列表过长相当于问题。另外自推荐下VS的恢宏程序ResXManager管理所有资源文件。
亚洲必赢bwin696.com 8

它可在一个UI里管理有语言的资源文件,极大地好了资源文件之动。
亚洲必赢bwin696.com 9

3.8 ReSharper支持

针对Resx资源文件,ReSharper也供了漂亮的支撑。

当用吗某资源修改Key时,可以随“资源文件名称”+”.”+”Key”来全局替换,通常这样都足足放心。ReSharper更进一步,它提供了重命名功能。假设要用Labels的资源English重名为呢Englishs,可以预先在Labels.Designer.cs重命名,然后下“Apply
rename refactoring”选项:
亚洲必赢bwin696.com 10

此时有援,包括XAML都曾经运用新的名:
亚洲必赢bwin696.com 11

而是最终准欲协调下手在资源文件编辑器中修改Key。

除此之外,如果以XAML中使了左的Key,ReSharper也起错提示:
亚洲必赢bwin696.com 12

在少数场合,ReShaper还只是应用“Move To Resource”功能:
亚洲必赢bwin696.com 13
亚洲必赢bwin696.com 14

3.9 总结

动用Resx资源文件落实地方化有如下优点:

  • 资源管理好。
  • 易当代码中行使。
  • 善在先后集里共享。
  • 支撑TypeConverter,这样即使好运用除String以外的任何项目。
  • 支持Binding的其他功能,如IValueConverter。
  • 兼容性好,Silverlight及之后的XAML技术都得使。
  • 其三着工具支持。
  • 支撑图片亚洲必赢bwin696.com、音频等资源。

缺点如下:

  • XAML语法相对复杂。
  • 未可知直接动用于TypeConverter不支持之种,例如LinearGradientBrush。

尽管未可知一直支持LinearGradientBrush,但为无是一点一滴没办法,只是复杂了成百上千,如分别指向LinearGradientBrush的GradientStop做本地化:

<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
    <GradientStop Color="Black" Offset="0"/>
    <GradientStop Color="{Binding Source={StaticResource R},Path=Labels.Background}" Offset="1"/>
</LinearGradientBrush>

4. 结语

即首文章就介绍了本地化的入门知识,其它还有很多本地化的要义,如验证信息遭到的本地化没有关系。另外,本地化还足以使用x:Uid方式要WPFLocalizeExtension对等措施实现,这里虽非详细介绍。
WPF
全球化及本地化概述.aspx)里有介绍一些本地化的极品做法,如UI上应该下相对布局要休绝对布局、字体选择当,这里不再累赘。

要小心的是上述两种植方案还不适用于CLR属性,这为是为什么我一直强调UIElement的性最好是据属性的因由有。

假如发错漏请指出。

5. 参考

WPF
全球化与本地化概述.aspx)
Silverlight
部署与本地化.aspx)
WPFLocalizationExtension
WPF Localization Guidance
XAML
Resources
CultureInfo
类.aspx)
Supported
languages

6. 源码

LocalizationDemo

相关文章