本地化入门,本地化入门

1. 前言

WPF的本地化是个很普遍的成效,我做过的WPF程序超过半数都得以已毕了本地化(不管最终有没有利用)。日常本地化有以下几点须要:

  • 在程序启动时根据CultureInfo.CurrentUICulture或陈设项呈现对应语言的UI。
  • 在程序运行时方可动态切换UI语言(无需重启程序)。
  • 制作对应分化语言的安装包。
  • 通过下载语言包完成多种语言的本地化。

个中唯有首先点是必不可少的。
第二点最好也可以兑现,很多时候切换语言只为了看看某个专业术语在泰语中的原文是什么样,或者暂时打印个英文报表,日常采用如故用普通话,用户不想为了那点重启程序。
其三点和第四点尽管很广泛,但自己一贯没完成过,毕竟文字资源(有时还有少量图片)占用的上空不会太多,一大半WPF程序都尚未大到要求考虑安装包大小,所有语言的资源总体打包进一个安装包就足以了。

WPF本地化技术很干练,也有二种方案,微软在MSDN给出了详实的牵线WPF
满世界化和本地化概述
.aspx),还有一份古老的文档WPF
Localization
Guidance
,整整66页,里面详细介绍了各个WPF本地化的编制。

本文只介绍三种完成上述第1、2点需要的方案。

1. 前言

上一篇小说介绍了种种WPF本地化的入门知识,那篇小说介绍UWP本地化的入门知识。

2. 选用资源词典

2. 施用resw资源文件得以完毕本地化

在从前的XAML平台,resx资源文件是一种很便宜的本地化方案,但在UWP中微软又重新推荐x:Uid方案,默认的资源文件也成为resw资源文件。即使后缀名只差了一个假名,但利用办法完全差距。最关键的分别是resw资源文件不会成立对应的Designer.cs类,那就导致本地化的完结方案完全不一样。

lovebet爱博体育 1

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.1 在XAML中贯彻本地化

在XAML中贯彻本地化的长河很粗略。首先在档次中新建”strings”文件夹,在”strings”文夹下开创”en-US”和”zh-CN”文件夹,并在两个文本夹中分头增进”Resources.resw”资源文件。最后目录结构如下:
lovebet爱博体育 2

在zh-CN\Resources.resw和en-US\Resources.resw添加八个新资源,分别是UsernameTextBox.Width和UsernameTextBox.Header:
lovebet爱博体育 3

在XAML中添加一个TextBox,设置x:Uid为UsernameTextBox,x:Uid将XAML元素和资源文件中的资源拓展关联:

<TextBox x:Uid="UsernameTextBox"/>

运作后即可看到UsernameTextBox的Header设置为”用户名”,Width为100。

在“设置\区域和言语”大校”English”设置为默许语言,再一次运行应用可观望运行在爱尔兰语环境下的功用。
lovebet爱博体育 4

诸如此类焦点的本地化成效就落到实处了。那种本地化情势有如下优点:

  • 简短高效,简单上手
  • 语法简单,不需求Binding等学问
  • 可以指定任意属性进行本地化
  • 支持CLR属性

除了,上一篇文章提到的ResXManager也帮忙Resw资源文件,还能动用多语言应用工具包对资源文件进行保管,今日头条的这篇文章页对那个工具进行了详尽介绍:
Win10 UWP
开发系列:使用多语言工具包让应用支撑多语言

要么参考那个视频:
Windows 10 Apps Designing for Global
Customers

2.2 动态切换语言

实在上述方案已得以完毕了动态切换语言。
XAML资源的引用原则是左右原则,这些附近不仅指VisualTree上的跟前,还指时间上的跟前。后添加进资源词典的资源将替换此前的同名资源。使用DynamicResource而不是StaticResource,就是为了在资源被替换时能实时变更UI的彰显。

2.2 关联到此外资源文件

UI元素默许与Resources.resw举行关联,如若须要和其余资源文件涉及,可以增长资源文件的门路。如须要与/OtherResources.resw中的资源事关,x:Uid的语法如下:

x:Uid="/OtherResources/AddressTextBox"

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>

lovebet爱博体育 5

那段XAML只是为了增加设计时体验,没有也能通过编译。

2.3 附加属性的本地化

对系统提供的附加属性,资源的称呼语法如下:

UsernameTextBox.Grid.Row

对自定义附加属性,语法稍微复杂一些:

ShowMessageButton.[using:LocalizationDemoUwp]ButtonEx.Content

奇怪的是,就这么平昔运行应用会报错。只有采用那一个资源的UI元素已经有其一附加属性的值才能正常运转,不难的话就是急需随便为那个附加属性设置一个值:

<Button Margin="5" x:Uid="ShowMessageButton"  local:ButtonEx.Content="ssssss"/>

2.4 在代码里拜访资源

在代码中走访资源比较辛劳,必要通晓资源的名号,而且尚未智能感知,假如资源词典由第三方类库提供就会更麻烦。

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

2.4 其余资源的本地化

除了字符串资源,其余资源的本地化形式不要求设置x:Uid,只必要建立对应语言的目录结构及命名就足以在XAML中一贯引用。如项目中有如下两张图片:
lovebet爱博体育 6

在XAML中可以平昔通过Images/Flag.png引用。路径中的”zh-CN”、”en-US”称为资源限定符,用于援救多种来得比例、UI
语言、高相比度设置等,具体可参考Load images and assets tailored for
scale, theme, high contrast, and
others

2.5 在代码里替换资源

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

如上所示,在代码中替换资源分外简短,可是那种简易也拉动了资源不可控的题目。

2.5 在代码里拜访资源

在代码中做客资源的代码如下:

var resourceLoader = ResourceLoader.GetForCurrentView();
var currentLanguage = resourceLoader.GetString("CurrentLanguage");
resourceLoader = ResourceLoader.GetForCurrentView("OtherResources");
var message = resourceLoader.GetString("Message");

上边的代码中,currentLanguage从默许的资源文件Resources.resw中获取,resourceLoader
无需指定资源文件的称呼;而message
则从OtherResources.resw获取,resourceLoader 须要指定资源文件的称谓。

如需求选用其余类库中的资源,代码如下:

resourceLoader = ResourceLoader.GetForCurrentView("LocalizationDemoUwp.ResourceLibrary/Resources");
currentLanguage = resourceLoader.GetString("CurrentLanguage");

尽管如此语法简单,但足以见见最大的题材是资源的名称没有智能感知和不当提醒,这样使用资源很简单失误。

lovebet爱博体育 7

如上图所示,对不当的资源名称,Re夏普(Sharp)er会有荒唐提醒,不过那种结构ResourceLoader的点子已经被标记为Deprecated并提醒使用GetForCurrentView获取ResourceLoader,而使用GetForCurrentView的情状下Re夏普er又从未错误提醒。不了解Re夏普(Sharp)er几时才能支撑在GetForCurrentView的不二法门下显得错误提醒(我设置的Re夏普(Sharp)er已是最新的2017.2)。

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.6 存在的题目

其一本地化方案即使简易,但自我以为很难使用,因为那几个方案存在许多问题。

率先是统筹时协理,对本地化来说,设计时支持重点涵盖3片段:

  • 在编写XAML时得以获取资源的智能感知
  • 有全部的布署视图
  • 在分化语言之间切换

先是点,没有,而且写错属性名称还不会在编译时报错,而是用最严寒的艺术表现:运行时崩溃。

第二点,在Fall Creators Update
(16299)往日,没有,设计视图一片空白。也足以任由写一些内容(如TextBox x:Uid="UsernameTextBox" Header="(here is header)")以援救设计。但在XAML中写的其余内容都可能被资源文件覆盖,无论是公事仍旧大小、对齐格局或任何具有属性对XAML的编辑来说都是不可控的,不到实在运作时一直不清楚UI的最终效果,那就很考验本地化人士和测试人士。在Fall
Creators
Update未来终于得以在筹划视图看到本地化的法力,那只好说是巨大的上进。

其三点,近期来看做不到。

此外,资源管理也是个很麻烦的题材。同一个字符串,如若要对应TextBlock.Text、ContentControl.Content、TextBox.Header,那样就须求多少个资源,造成了冗余,而恢宏的冗余最后会招致错误。

总的来说,这几个本地化方案有无数题目,纵然这几个方案是微软援引的。既然是微软引进的,应该是永葆最好的,也许是自己的用法不对?

接下去在那些方案的根底上做些改动,希望可以让本地化更好用。

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. 动态切换语言

不是我太执着动态切换语言,是测试员真的喜欢这几个成效,因为不用重启应用就能够测试到具有语言的UI。

UWP提供了ApplicationLanguages.PrimaryLanguageOverride性能用于转移语言首选项,即可以变更使用的语言,用法如下:

Windows.Globalization.ApplicationLanguages.PrimaryLanguageOverride = "zh-CN";

本条变更是永久的,但不会对当下UI及片段系统组件生效,只会影响之后创建的UI元素。更改ApplicationLanguages.PrimaryLanguageOverride,会异步地接触ResourceContext.QualifierValues的MapChanged事件,可以监听那么些事件并更新UI。那样就足以兑现简单的动态切换语言功效。

DynamicResources.cs

public class DynamicResources : INotifyPropertyChanged
{
    public DynamicResources()
    {
        _defaultContextForCurrentView = ResourceContext.GetForCurrentView();

        _defaultContextForCurrentView.QualifierValues.MapChanged += async (s, m) =>
        {
            await MainPage.Current.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal, () =>
            {
                OnPropertyChanged("");
            });
        };
    }

    private ResourceContext _defaultContextForCurrentView;

    public string Main
    {
        get { return ResourceManager.Current.MainResourceMap.GetValue("DynamicResources/Main", _defaultContextForCurrentView).ValueAsString; }
    }

    public string Settings
    {

        get { return ResourceManager.Current.MainResourceMap.GetValue("DynamicResources/Settings", _defaultContextForCurrentView).ValueAsString; }
    }

    public string RestartNote
    {
        get { return ResourceManager.Current.MainResourceMap.GetValue("DynamicResources/RestartNote", _defaultContextForCurrentView).ValueAsString; }
    }

    public event PropertyChangedEventHandler PropertyChanged;

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

SettingView.xaml

<Page.Resources>
    <local:DynamicResources x:Key="DynamicResources"/>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
    <StackPanel>
        <ListView x:Name="LanguageListView" Margin="10">
            <ListViewItem Tag="zh-Hans-CN" Content="中文"/>
            <ListViewItem Tag="en-US" Content="English"/>
        </ListView>
        <TextBlock x:Name="NoteElement" Foreground="#FFF99F00" Margin="20,10" Visibility="Collapsed"
                   Text="{Binding RestartNote,Source={StaticResource DynamicResources}}"
                   />
    </StackPanel>
</Grid>

SettingView.xaml.cs

private async void OnLanguageListViewSelectionChanged(object sender, SelectionChangedEventArgs e)
{
    var item = LanguageListView.SelectedItem as ListViewItem;
    if (item == null)
        return;

    ApplicationLanguages.PrimaryLanguageOverride = item.Tag as string;
    _hasChangedLanguage = true;
    await Dispatcher.RunAsync(CoreDispatcherPriority.Normal, ShowNoteElement);
}

private void ShowNoteElement()
{
    NoteElement.Visibility = Visibility.Visible;
    var appView = Windows.UI.ViewManagement.ApplicationView.GetForCurrentView();
    appView.Title = (LanguageListView.SelectedItem as ListViewItem)?.Content as string;
}

lovebet爱博体育 8

只在设置页面及菜单这几个在切换语言时不会重复加载的UI上选择Binding,其余地点不变,那样简单的动态切换语言就兑现了。运行结果如上,可以看出TextBox右键菜单仍未切换语言,须要再一次起动。

UWP默认只安装总括机对应的言语,那样可以省去安装空间,但影响到动态切换语言的出力,要解决那么些题目得以参照以下内容(我未曾表明过):[localization

3. 施用Resx资源文件

4. 收获完整的安排视图

在Fall Creators
Update以前为了得到设计时视图可以应用索引器。很少有机会在C#中用到索引器,XAML中也很少用到Binding到字符串索引的语法,就是这八个功效在本地化中帮了大忙。

public class ResourcesStrings
{
    public string this[string key]
    {
        get
        {
            return ResourceLoader.GetForViewIndependentUse().GetString(key);
        }
    }
}

<Page.Resources>
    <local:ResourcesStrings x:Key="S"/>
</Page.Resources>
<Grid Background="{ThemeResource ApplicationPageBackgroundThemeBrush}">
        <TextBlock Text="{Binding Source={StaticResource S},Path=[MainTitle]}" />
</Grid>

lovebet爱博体育 9

只需求如此写就能够得到完全的设计时准备,可是依旧没有解决智能感知和不当提醒那七个问题。

在这么些方案上也可概括地贯彻动态切换语言。

public class ApplicationResources : INotifyPropertyChanged
{
    public ApplicationResources()
    {
        DynamicResources = new DynamicResourcesStrings();
        Resources = new ResourcesStrings();
        Current = this;
    }

    public static ApplicationResources Current { get; private set; }

    public event PropertyChangedEventHandler PropertyChanged;

    public DynamicResourcesStrings DynamicResources { get; }

    public ResourcesStrings Resources { get; }

    public string Language
    {
        get
        {
            return ApplicationLanguages.PrimaryLanguageOverride;
        }
        set
        {

            if (ApplicationLanguages.PrimaryLanguageOverride == value)
                return;

            ApplicationLanguages.PrimaryLanguageOverride = value;
            if (MainPage.Current != null )
                MainPage.Current.Dispatcher.RunAsync(CoreDispatcherPriority.Normal, () => { OnPropertyChanged(""); });
        }
    }

    [NotifyPropertyChangedInvocator]
    protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
    }
}

<ListViewItem Content="{Binding Source={StaticResource R},Path=DynamicResources[Main]}"/>

不驾驭为何,在VisualStudio上偶然无法得到设计时视图,所有文字都来得为”Item”。

3.1 基本原理

比起资源词典,我更欣赏使用Resx资源文件,但是那种措施语法复杂一些,而且也有不少小题目。
在VisualStudio中创建后缀名为resx的资源文件并开拓,可在以下UI编辑资源文件的值(将做客修饰符改为public用起来方便些):
lovebet爱博体育 10

在修改资源文件的值后PublicResXFileCodeGenerator将活动创制对应的类并为每一个键值添加如下代码:

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

下一场将以此资源文件复制粘贴一份,将名称改为“原名+.+对应的言语+.resx”的格式,并且将中间的值翻译成对应语言如下:
lovebet爱博体育 11

在UI上使用x:Static绑定到相应的资源:

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

那般中央的本地化就大功告成了。很多控件库都是选拔那种措施做本地化。除了字符串,resx资源文件还协理除字符串以外的资源,如图片、音频等。
lovebet爱博体育 12

但是那个方案只兑现了最基本的本地化,而且最大的问题是只帮衬间接运用字符串,不协理TypeConverter,甚至也不帮助除字符串以外的任何XAML内置类型.aspx)(即Boolea,Char,Decimal,Single,Double,Int16,Int32,Int64,提姆(Tim)eSpan,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”的有效值。

诸如此类资源文件的实用性大优惠扣。当然,那个方案也不援助动态切换语言。

5. 利用resx资源文件

既然UWP是XAML大家族的一份子,那么相应也足以接纳resx资源文件贯彻本地化,毕竟生成resx对应代码的是PublicResXFileCodeGenerator,而不是UWP本身。

  1. 开拓“添加新项”对话框,选中“资源文件(.resw)”,在“名称”文本框中校文件名称改为“Labels.resx”,点击“添加”。
  2. 在“解决方案资源管理器”选中“Labels.resx”,邮件打开“属性”视图,“生成操作”拔取“嵌入的资源”。
  3. 将“Labels.resx”复制为“Labels.zh-CN.resx”,打开“Labels.zh-CN.resx”,“访问修饰符”改为“无代码生成”。
  4. 在“AssemblyInfo.cs”添加如下代码:

    [assembly: NeutralResourcesLanguage("en-US")]
    

如此这般就可以在UWP中动用resx资源文件了。已毕本地化的代码和上一篇小说中牵线的WPF本地化方案差不离。

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

    public ApplicationResources()
    {
        Labels = new Labels();
        if (string.IsNullOrWhiteSpace(ApplicationLanguages.PrimaryLanguageOverride) == false)
            Language = ApplicationLanguages.PrimaryLanguageOverride;
        else
            Language = Windows.System.UserProfile.GlobalizationPreferences.Languages.FirstOrDefault();

        Current = this;
    }

    public Labels Labels { get; set; }

    public event PropertyChangedEventHandler PropertyChanged;

    public virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
    {
        PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));

    }

    private string _language;

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

            _language = value;
            Labels.Culture = new System.Globalization.CultureInfo(_language);
            ApplicationLanguages.PrimaryLanguageOverride = _language;
            OnPropertyChanged("");
        }
    }
}

行使体验和WPF中的resx本地化方案差不离,设计时帮忙大致应有尽有,包蕴智能感知和谬误提醒,可是依然无法解决系统组件中的本地化问题(如TextBox右键菜单)。其它,编译时会报错:带有输出类型“appcontainerexe”的项目不援救生成操作“EmbeddedResource”。解决方案是不在UWP应用项目中添加resx资源文件,而在类库中添加resx资源文件,那样连错误都不报了。

不知情Xamarin.Forms是不是也得以这么已毕,毕竟它也是XAML大家族的一员。

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属性,不是实例成员。幸运的是编译一遍那种张冠李戴提醒就会化为乌有。
lovebet爱博体育 13

将调用格局改为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);

6. 结语

商量了那般多resw资源文件的方案,结果或者resx资源文件用得最顺手,毕竟这一个方案我一度用了过多年(在silverlight中只好用这几个方案)。具体应用哪个方案差异。

内需强调的是resx并无法一心代表resw方案,很多时候要求混合使用,例如使用的Display
Name可以应用resw轻松完成本地化:
lovebet爱博体育 14

本地化的主旨仍有诸多情节,那篇小说只打算介绍入门知识,更长远的学识可以参考上边给出的链接。

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"/>

lovebet爱博体育 15

7. 参考

Guidelines for globalization – UWP app developer Microsoft
Docs

Localize strings in your UI and app package manifest – UWP app
developer Microsoft
Docs

Load images and assets tailored for scale, theme, high contrast, and
others – UWP app developer Microsoft
Docs

迅猛入门:翻译 UI 资源
(XAML)

c# – UWP Resource file for languages is not deployed correctly – Stack
Overflow

localization – How to always install all localized resources in Windows
Store UWP app – Stack
Overflow

Win10 UWP 开发体系:使用多语言工具包让应用支撑多语言 – yan_xiaodi –
博客园

Windows 10 Apps Designing for Global
Customers

3.4 在代码里拜访资源

在代码里拜访资源文件的资源丰富简短:

MessageBox.Show(Labels.SwitchLanguage);

8. 源码

GitHub –
LocalizationDemo

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,无需任何操作就可以一本万利地在先后集以内共享资源。
lovebet爱博体育 16

3.7 管理资源文件

比起资源词典,资源文件还有一个很大的优势就是简单管理。Demo中只有一个名字Labels的资源文件,实际项目中得以按效用或模块分别创立相应的资源文件,解决了资源词典重名、相互覆盖、智能感知列表过长等问题。别的我引进应用VS的伸张程序ResXManager管理所有资源文件。
lovebet爱博体育 17

它可以在一个UI里管理所有语言的资源文件,极大地惠及了资源文件的使用。
lovebet爱博体育 18

3.8 ReSharper支持

对Resx资源文件,ReSharper也提供了不错的支撑。

当须要为某个资源修改Key时,可以按“资源文件名称”+”.”+”Key”来全局替换,平常那样已经足足放心。Re夏普er更进一步,它提供了重命名成效。假诺要将Labels的资源English重名为为Englishs,能够先在Labels.Designer.cs重命名,然后利用“Apply
rename refactoring”选项:
lovebet爱博体育 19

此时所有引用,包罗XAML都已选取新的称号:
lovebet爱博体育 20

唯独最终仍需自己出手在资源文件编辑器中修改Key。

除开,如果在XAML中动用了不当的Key,Re夏普(Sharp)er也有错误提醒:
lovebet爱博体育 21

在一些场面,ReShaper还可利用“Move To Resource”效率:
lovebet爱博体育 22
lovebet爱博体育 23

3.9 总结

运用Resx资源文件贯彻地点化有如下优点:

  • 资源管理有利于。
  • 不难在代码中使用。
  • 简单在程序集以内共享。
  • 支持TypeConverter,那样就可以运用除String以外的任何门类。
  • 支撑Binding的其他成效,如IValueConverter。
  • 包容性好,Silverlight及之后的XAML技术都得以利用。
  • 其三方工具援助。
  • 支撑图片、音频等资源。

缺点如下:

  • 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)
lovebet爱博体育,WPFLocalizationExtension
WPF Localization Guidance
XAML
Resources

CultureInfo
.aspx)
Supported
languages

6. 源码

LocalizationDemo

相关文章