65.9K
CodeProject 正在变化。 阅读更多。
Home

简单的 WPF 本地化

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (28投票s)

2008年10月9日

CPOL

7分钟阅读

viewsIcon

307033

downloadIcon

4474

一篇关于如何轻松有效地完全本地化WPF应用程序的文章。

引言

在互联网上搜索WPF应用程序本地化方法一段时间后,我找到了两个非常好的解决方案,可以在这里这里找到。然而,这两个解决方案对我来说都显得太复杂了,于是我决定实现一个简单有效的解决方案,它只需要满足我的需求。

我的解决方案远没有上述解决方案的功能丰富,但它仍然能完美地完成我的工作:本地化我项目中的文本。

背景

基本目标是

  • 易于使用的本地化,允许在设计时直接本地化XAML
  • 使用嵌入式资源
  • 语言可以在运行时更改
  • 能够在Visual Studio的设计器中查看本地化资源
  • 实现尽可能简单,并且不引入性能问题

易于使用

为了实现第一个目标,我借鉴了另外两个解决方案中的一个绝妙主意,即使用WPF扩展。扩展可以直接在XAML中使用,为属性赋值,这对于此目的非常有用。

例如,XAML中`TextBlock`的本地化将如下所示

<TextBlock Text="{Loc Text_Hello}"/>

使用嵌入式资源

第二个目标可以通过将资源管理器连接到本地化组件来实现。

当你在Visual Studio中创建WPF应用程序时,Visual Studio会在项目的`Properties`文件夹中创建一个`Resources.resx`文件。由于这是存放所有可本地化资源的一个完美位置,我选择默认将本地化组件连接到该资源文件。这就是你在代码隐藏中使用`Properties.Resources.MyResourceName`表达式通常访问的同一个资源文件。

为了灵活性,可以随时显式连接任意资源管理器到本地化组件。这还将导致所有本地化内容都更新为新值。

设计时支持

设计时支持仅限于默认区域性(即,你在`Properties/Resources.resx`文件中放置的资源)。这意味着你可以在设计器中看到任何本地化的资源,但只能是你放在上述文件中的那些。你放在其他资源文件(如`Resources.fr-FR.resx`)中的特定区域性的资源,在设计时不可见,除非你临时重命名这些文件来查看更改。如果你找到任何解决这个问题的简单方法,请告诉我。

设计时问题

代码虽然简单,但在设计时可能会遇到一些问题,例如可能看不到最近的更改,或者根本看不到任何本地化的资源。

第一个问题很容易解决,只需重新编译项目,Visual Studio就会重新加载项目的主程序集,然后刷新设计器区域。

第二个问题更为微妙。

在运行时,通过获取应用程序的主入口程序集并创建一个引用该文件的资源管理器,可以轻松找到默认资源文件。但这在设计时不起作用,因为只有当应用程序是由该程序集启动,或通过`AppDomain.ExecuteAssembly`方法启动时,该程序集才被视为应用程序的入口程序集(有关更多信息,请参阅`System.Windows.Application.ResourceAssembly`属性的文档)。因此,本地化组件使用以下过程尝试在设计时查找应用程序的主程序集:获取当前域中所有已加载程序集的列表,并搜索第一个同时包含`Main`方法和直接或间接派生自`System.Windows.Application`的`MyAssemblyName.App`类的程序集。如果组件找到这样的程序集,它将创建一个连接到该程序集的`MyAssemblyName.Properties.Resources`资源文件的资源管理器。

如果你在设计时遇到上述问题,可以通过将`LocalizationManager.GetResourceManager`类中的部分代码替换为类似`Assembly.Load("MyassemblyName")`的代码来直接加载你的应用程序的程序集,并将其用作资源程序集。

第三个和第四个目标严格来说是实现问题,本文不涵盖。我唯一要提的是,实现只包含两个非常简单的类,并且不会给你的应用程序带来任何性能损失。

Using the Code

要使用该解决方案,你需要执行以下操作

  1. 添加对`WPFLocalization`程序集的引用,或者更好的是——将两个源文件包含在你伴随你的应用程序的某个类库项目中。第二种解决方案更好,因为一个只包含两个类的库会给你的应用程序增加开销。
  2. 如果你选择将这两个源文件包含在你的应用程序中,请注意以下几点

    1. 我建议你将类添加到你的类库之一,而不是主应用程序项目中,因为每次有编译错误时,你可能会遇到设计时问题(即,Visual Studio可能无法加载程序集,并在设计器中引发错误)。
    2. 如果你将命名空间从“`WPFLocalization`”更改为其他名称,你需要在`LocExtension.cs`文件开头的`System.Windows.Markup.XmlnsDefinition`程序集属性的第二个参数中进行相同的更改。
  3. 提取所有可本地化的资源用于默认区域性,并将它们放入`Properties/Resources.resx`文件。当然,你也可以将资源放在其他任何地方的资源文件中,但这会破坏设计时支持,除非你修改代码。
  4. 对于每个额外的区域性,在`Properties`文件夹中添加一个相应的资源文件(例如,`Properties/Resources.fr.resx`),并创建一个本地化版本的资源。

  5. 在XAML中使用本地化扩展来引用资源。

虽然步骤1和2很简单,但步骤3需要额外的解释。

使用本地化扩展

要使用本地化扩展,你只需在XAML中将其应用于你想要本地化的属性上,类似于以下代码

<TextBlock Text="{Loc MyResourceKey}"/>  

`MyResourceKey`是你想要引用的`Properties/Resources.resx`文件中的资源的名称。

要使用格式化,请使用类似的代码

<TextBlock Text="{Loc CreatorName, Format='Created by {0}'}"/>  

你无需在XAML文件开头添加任何命名空间引用(如`xmlns:loc="..."`),因为该扩展会在启动时注册到Microsoft的默认命名空间(这是我从另一个解决方案中借鉴的另一个想法)。

图像

我没有测试过该扩展与图像一起使用,也不知道它是否有效。处理图像很可能需要额外的努力,而这并非我计划的。

缺少资源

如果你引用的资源未找到,则适用以下情况

  • 如果应用程序是调试版本,则该资源将被文本“`[Resource: ResourceKey]`”替换。
  • 如果应用程序是发布版本,则该资源将被空字符串替换。

在运行时更改当前区域性

当前区域性可以在运行时随时通过为`LocalizationManager.UICulture`属性赋值来更改。对该属性的更改将执行以下操作

  1. 将当前线程的UI区域性更改为指定的区域性(即,将值赋给`Thread.CurrentThread.CurrentUICulture`)。
  2. 更新所有由扩展本地化的属性。

使用不同的资源管理器

要使用不同的资源管理器,只需为`LocalizationManager.ResourceManager`属性赋值。赋值也将更新本地化属性。

结论

任何评论和/或建议都欢迎。

历史

  • 2008-10-10:初始版本。
  • 2008-11-28:添加了对模板的支持。

更新版本

  • 2009-07-10:添加了对非依赖属性(即标准属性)的支持,以支持`Microsoft.Windows.Controls.Ribbon`控件。
  • 2009-07-10:如果在**设计时**找不到资源,则会抛出`ArgumentOutOfRangeException`异常,而不是显示`[Resource: ResourceKey]`消息。这样可以更轻松地发现缺失的资源。
© . All rights reserved.