WPF 全球化和本地化概述

当你将自己的产品限制为只能通过一种语言使用时,便将潜在的客户群限制为全球 75 亿人口中的一小部分。 如果想让自己的应用程序被全球用户所接受,那么对产品进行经济而有效的本地化将是赢得更多客户的最好、最经济的方法。

本概述文章介绍 WPF 中的全球化和本地化提供的全球化设计功能,包括自动布局、附属程序集以及本地化属性和注释。

本地化是针对应用程序所支持的特定区域性将应用程序资源转换为本地化版本的过程。 在 WPF 中进行本地化时,可使用 System.Windows.Markup.Localizer 命名空间中的 API。 这些 API 支持 LocBaml 工具示例命令行工具。 有关如何生成和使用 LocBaml 的信息,请参阅对应用程序进行本地化

在 WPF 中进行全球化和本地化的最佳做法

按照本部分提供的与 UI 设计和本地化相关的提示操作,可以最大限度地利用 WPF 内置的大部分全球化和本地化功能。

WPF UI 设计的最佳做法

设计 UI 时,请考虑实施下列最佳做法:

  • 使用代码编写 UI。 使用 XAML 创建 UI 时,应通过内置的本地化 API 公开 UI。

  • 避免使用绝对位置和固定大小对内容进行布局;改用相对位置或自动调整大小。

    • 使用 SizeToContent,并将宽度和高度设置为 Auto

    • 避免使用 Canvas 对 UI 进行布局。

    • 使用 Grid 及其大小共享功能。

  • 在边距中提供额外的空间,因为本地化文本通常需要更多空间。 额外空间为可能会延伸的字符预留了余地。

  • TextBlock 启用 TextWrapping,以避免剪裁。

  • 设置 xml:lang 特性。 此特性描述特定元素及其子元素的区域性。 此属性的值可更改 WPF 中几种功能的行为。 例如,它可以更改断字、拼写检查、数字替换、复杂脚本成型和字体回退的行为。 有关设置 XAML 中 xml:lang 的处理的详细信息,请参阅 WPF 的全球化

  • 创建自定义的复合字体,以更好地控制用于不同语言的字体。 默认情况下,WPF 使用 Windows\Fonts 目录中的 GlobalUserInterface.composite 字体。

  • 当创建的导航应用程序可能在以从右到左的格式显示文本的区域性中进行本地化时,请显式设置每页的 FlowDirection,以确保该页不从 NavigationWindow 继承 FlowDirection

  • 当创建在浏览器之外托管的独立导航应用程序时,请将初始应用程序的 StartupUri 设置为 NavigationWindow 而不是页面(例如 <Application StartupUri="NavigationWindow.xaml">)。 此设计使你可以更改窗口和导航栏的 FlowDirection。 有关详细信息和示例,请参阅 Globalization Homepage Sample(全球化主页示例)。

WPF 本地化的最佳做法

对基于 WPF 的应用程序进行本地化时,请考虑实施下列最佳做法:

  • 使用本地化注释为本地化人员提供额外的上下文。

  • 使用本地化特性控制本地化,而不是选择性地省略元素的 Uid 属性。 有关详细信息,请参阅本地化特性和注释

  • 使用 msbuild -t:updateuid-t:checkuid 在 UI 中添加和检查 Uid 属性,该任务通常很繁琐且不太准确。

    • 开始进行本地化之后,请勿编辑或更改 Uid 属性。

    • 请勿使用重复的 Uid 属性(使用“复制并粘贴”命令时,请记住此提示)。

    • 在 AssemblyInfo.* 中设置 UltimateResourceFallback 位置,以指定合适的回退语言(例如 [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)])。

      如果决定通过在项目文件中省略 <UICulture> 标记,在主程序集中添加源语言,请将 UltimateResourceFallback 位置设置为主程序集而不是附属程序集(例如 [assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.MainAssembly)])。

对 WPF 应用程序进行本地化

对 WPF 应用程序进行本地化时,有多种选择。 例如,可以将应用程序中的可本地化资源绑定到 XML 文件,在 resx 表中存储可本地化文本,或者让本地化人员使用 Extensible Application Markup Language (XAML) 文件。 本部分介绍使用 XAML 的 BAML 形式的本地化工作流,这种工作流提供以下几个好处:

  • 可以在生成之后进行本地化。

  • 可以从较旧版本 XAML 的 BAML 形式更新到本地化的较新版本 XAML 的 BAML 形式,以便在开发的同时进行本地化。

  • 因为 XAML 的 BAML 形式是 XAML 的已编译形式,所以可以在编译时验证原始源元素和语义。

本地化生成过程

开发 WPF 应用程序时,本地化的生成过程如下:

  • 开发人员创建 WPF 应用程序并将其全球化。 在项目文件中,开发人员设置 <UICulture>en-US</UICulture>,以便在编译应用程序时生成一个中性语言的主程序集。 此程序集具有一个附属 .resources.dll 文件,其中包含所有可本地化的资源。 因为本地化 API 支持从主程序集进行提取,所以可选择在主程序集中保留源语言。

  • 将文件编译到生成中时,会将 XAML 转换为 XAML 的 BAML 形式。 将向说英语的客户发布非特定区域性的 MyDialog.exe 和区域性相关的(英语)MyDialog.resources.dll 文件。

本地化工作流

本地化过程在生成未本地化的 MyDialog.resources.dll 文件之后开始。 使用 System.Windows.Markup.Localizer 下的 API 将原始 XAML 中的 UI 元素和属性从 XAML 的 BAML 形式提取为键值对。 本地化人员使用键/值对来对应用程序进行本地化。 在本地化完成之后,可以从新值生成一个新的 .resource.dll。

键值对的键是在本地化人员开始进行本地化之后,开发人员放置在原始 UI 中的 x:Uid 值,你可以将开发更改与已完成的本地化工作进行合并,使损失的翻译工作降至最少。

下图显示了一个基于 XAML 的 BAML 形式的典型本地化工作流。 此关系图假设开发人员用英语编写应用程序。 开发人员创建 WPF 应用程序并将其全球化。 在项目文件中,开发人员设置 <UICulture>en-US</UICulture>,以便在生成时会生成一个中性语言的主程序集,该程序集具有一个包含所有可本地化资源的附属 .resources.dll。 或者,因为 WPF 本地化 API 支持从主程序集进行提取,所以还可以保留主程序集中的源语言。 生成过程结束之后,XAML 会编译为 BAML。 将向说英语的客户提供非特定区域性的 MyDialog.exe.resources.dll。

Diagram showing the Localization workflow.

Diagram showing the Unlocalized workflow.

WPF 本地化示例

本部分包含几个本地化应用程序示例,有助于你了解如何生成和本地化 WPF 应用程序。

“运行”对话框示例

下图显示“运行”对话框示例的输出。

英语:

Screenshot showing an English Run dialog box.

德语:

Screenshot showing a German Run dialog box.

设计全球化“运行”对话框

此示例使用 WPF 和 XAML 生成一个“运行”对话框。 此对话框与 Microsoft Windows“开始”菜单中提供的“运行”对话框等效。

生成全球化对话框的一些要点包括:

自动布局

在 Window1.xaml 中:

<Window SizeToContent="WidthAndHeight">

以前的 Window 属性会根据内容大小自动调整窗口大小。 此属性可以防止窗口切断在本地化之后大小增加的内容;它还可以在内容由于本地化而大小减小时删除不必要的空格。

<Grid x:Uid="Grid_1">

为了使 WPF 本地化 API 正确运行,需要使用 Uid 属性。

它们由具有较旧的 UI 本地化的 UI 使用。 通过在命令行界面中运行 msbuild -t:updateuid RunDialog.csproj,可以添加 Uid 属性。 因为手动添加 Uid 属性通常比较费时并且准确性较差,所以建议使用此方法来添加这些属性。 可以通过运行 msbuild -t:checkuid RunDialog.csproj 来检查是否正确设置了 Uid 属性。

使用 Grid 控件可以构造 UI,这是一个有用的控件,它可以利用位于每个单元格中的 UI 元素中的自动布局,适应本地化过程中大小的增加和减小。

<Grid.ColumnDefinitions>
  <ColumnDefinition x:Uid="ColumnDefinition_1" />
  <ColumnDefinition x:Uid="ColumnDefinition_2" />

放置 Open: 标签和 ComboBox 的前两列占据 UI 总宽度的 10%。

  <ColumnDefinition x:Uid="ColumnDefinition_3" SharedSizeGroup="Buttons" />
  <ColumnDefinition x:Uid="ColumnDefinition_4" SharedSizeGroup="Buttons" />
  <ColumnDefinition x:Uid="ColumnDefinition_5" SharedSizeGroup="Buttons" />
</Grid.ColumnDefinitions>

请注意,该示例使用 Grid 的共享调整大小功能。 最后三列通过将自身放置在相同的 SharedSizeGroup 中利用此功能。 正如属性名称所示,此属性允许不同的列采用相同大小。 因此,在将“Browse…”本地化为更长的字符串“Durchsuchen…”时,所有按钮的宽度都会增加,而不是显示一个小的“OK”按钮和一个大得不相称的“Durchsuchen…”按钮。

xml:lang

xml:lang="en-US"

请注意放置在 UI 根元素中的 XAML 中的 xml:lang 处理。 此属性描述给定元素及其子元素的区域性。 WPF 中的多项功能都使用此值,在本地化过程中应对此值进行相应的更改。 此值会更改在断字以及对字词进行拼写检查时所使用的字典。 它还会影响数字的显示以及字体回退系统选择所用字体的方式。 最后,该属性会影响数值的显示方式,形成在复杂脚本中编写文本的方式。 默认值为“en-US”。

生成附属资源程序集

在 .csproj 中:

编辑 .csproj 文件并将以下标记添加到无条件 <PropertyGroup>

<UICulture>en-US</UICulture>

请注意,增加了 UICulture 值。 如果将此属性设置为有效的 CultureInfo 值(例如,“en-US”),生成项目时会生成一个包含所有可本地化资源的附属程序集。

<Resource Include="RunIcon.JPG">

<Localizable>False</Localizable>

</Resource>

不需要对 RunIcon.JPG 进行本地化,因为它对所有区域性都应显示相同的外观。 Localizable 设置为 false,以使其保留在中性语言主程序集而不是附属程序集中。 所有不可编译资源的默认值是 Localizable 设置为 true

本地化“运行”对话框

Parse

生成应用程序之后,对其进行本地化的第一步是将可本地化的资源从附属程序集中分析出来。 本主题使用 LocBaml 工具示例上的示例 LocBaml 工具。 请注意,LocBaml 只是一个示例工具,用于帮助你了解有关如何生成适合你的本地化过程的本地化工具的入门知识。 使用 LocBaml 可以运行下面的命令进行分析:LocBaml /parse RunDialog.resources.dll /out:,从而生成“RunDialog.resources.dll.CSV”文件。

本地化

使用你喜欢的支持 Unicode 的 CSV 编辑器来编辑此文件。 筛选掉本地化类别为“None”的所有项。 应看到下面的项:

资源键 本地化类别
Button_1:System.Windows.Controls.Button.$Content Button OK
Button_2:System.Windows.Controls.Button.$Content Button 取消
Button_3:System.Windows.Controls.Button.$Content Button 浏览...
ComboBox_1:System.Windows.Controls.ComboBox.$Content ComboBox
TextBlock_1:System.Windows.Controls.TextBlock.$Content 文本 Windows 将根据您所输入的名称,为您打开相应的程序、文件夹、文档或 Internet 资源。
TextBlock_2:System.Windows.Controls.TextBlock.$Content 文本 未解决:
Window_1:System.Windows.Window.Title 标题 运行

将该应用程序本地化为德语版本需要进行下面的翻译:

资源键 本地化类别
Button_1:System.Windows.Controls.Button.$Content Button OK
Button_2:System.Windows.Controls.Button.$Content Button Abbrechen
Button_3:System.Windows.Controls.Button.$Content Button Durchsuchen…
ComboBox_1:System.Windows.Controls.ComboBox.$Content ComboBox
TextBlock_1:System.Windows.Controls.TextBlock.$Content 文本 Geben Sie den Namen eines Programms, Ordners, Dokuments oder einer Internetresource an.
TextBlock_2:System.Windows.Controls.TextBlock.$Content 文本 打开:
Window_1:System.Windows.Window.Title 标题 运行

Generate

本地化的最后一步涉及创建新进行本地化的附属程序集。 可以使用下面的 LocBaml 命令完成此操作:

LocBaml.exe /generate RunDialog.resources.dll /trans:RunDialog.resources.dll.CSV /out: . /cul:de-DE

在德语版 Windows 上,如果此 resources.dll 放置在主程序集旁边的 de-DE 文件夹中,则会自动加载此资源而不是 en-US 文件夹中的资源。 如果没有德语版的 Windows 来测试这种情况,请将区域性设置为你所使用的 Windows 的任何区域性(例如,en-US),并替换原始的资源 DLL。

附属资源加载

MyDialog.exe en-US\MyDialog.resources.dll de-DE\MyDialog.resources.dll
代码 原始英语版 BAML 本地化的 BAML
非特定区域性资源 其他英语资源 已本地化为德语的其他资源

.NET 根据应用程序的 Thread.CurrentUICulture 自动选择要加载的附属资源程序集。 其默认值为你的 Windows OS 的区域性。 如果使用德语版 Windows,则会加载 de-DE\MyDialog.resources.dll 文件。 如果使用英语版 Windows,则会加载 en-US\MyDialog.resources.dll 文件。 通过在项目的 AssemblyInfo 文件中指定 NeutralResourcesLanguage 属性,可以设置应用程序的最终回退资源。 例如,如果指定:

[assembly: NeutralResourcesLanguage("en-US", UltimateResourceFallbackLocation.Satellite)]

并且 de-DE\MyDialog.resources.dll 和 de\MyDialog.resources.dll 都不可用,则德语版 Windows 将使用 en-US\MyDialog.resources.dll 文件。

Microsoft 沙特阿拉伯主页

下图显示了英语和阿拉伯语主页。 有关生成这些图形的完整示例,请参阅 Globalization Homepage Sample(全球化主页示例)。

英语:

Screenshot showing an English home page.

阿拉伯语:

Screenshot showing an Arabic home page.

设计 Microsoft 全球主页

Microsoft 沙特阿拉伯网站的这个实体模型说明了针对从右向左布局语言提供的全球化功能。 希伯来语和阿拉伯语等语言具有从右到左的阅读顺序,因此 UI 的布局方式通常必须与英语等从左到右的语言截然不同。 从从左到右布局语言本地化到从右到左布局语言可能相当困难,反之亦然。 WPF 可使此类本地化工作变得更容易。

FlowDirection

Homepage.xaml:

<Page x:Uid="Page_1" x:Class="MicrosoftSaudiArabiaHomepage.Homepage"
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"   
    FlowDirection="LeftToRight" 
    Localization.Comments="FlowDirection(This FlowDirection controls the actual content of the homepage)"
    xml:lang="en-US">

请注意 PageFlowDirection 属性。 将此属性更改为 RightToLeft 将更改 Page 及其子元素的 FlowDirection,以使此 UI 的布局翻转成从右到左布局,从而满足阿拉伯语用户的需要。 可以通过在任何元素上指定显式的 FlowDirection 来替代继承行为。 FlowDirection 属性对所有 FrameworkElement 或文档相关元素都可用,并且具有隐式值 LeftToRight

请注意,当根 FlowDirection 发生更改时,甚至连背景渐变画笔都进行了正确的翻转:

FlowDirection="LeftToRight"

Screenshot showing the gradient flow from left to right.

FlowDirection="RightToLeft"

Screenshot showing the gradient flow from right to left.

避免对面板和控件使用固定维度

浏览 Homepage.xaml 时会注意到,除了为顶部 DockPanel 的整个 UI 指定的固定宽度和高度之外,没有其他固定维度。 请勿使用固定尺寸,以防裁剪可能比源文本长的本地化文本。 WPF 面板和控件将根据其包含的内容自动调整大小。 大多数控件还具有最小和最大维度,设置这些维度可以加强控制(例如 MinWidth= "20")。 通过 Grid,还可以使用“*”设置相对宽度和高度(例如 Width="0.25*"),或使用其单元格大小共享功能。

本地化注释

在很多情况下,内容可能不太明确,难以翻译。 开发人员或设计人员可通过本地化注释为本地化人员提供额外的上下文和注释。 例如,下面的 Localization.Comments 阐明了字符“|”的用法。

<TextBlock 
  x:Uid="TextBlock_2" 
  DockPanel.Dock="Right" 
  Foreground="White" 
  Margin="5,0,5,0"
  Localization.Comments="$Content(This character is used as a decorative rule.)">
  |
</TextBlock>

此注释与 TextBlock_1 的内容相关联,并且在使用 LocBaml 工具(请参阅对应用程序进行本地化)时,可以在输出 .csv 文件的 TextBlock_1 行的第六列看到此注释:

资源键 类别 可读性 可修改性 评论
TextBlock_1:System.Windows.Controls.TextBlock.$Content 文本 TRUE TRUE 此字符用作装饰性规则。 |

使用下面的语法可以将注释放置在任何元素的内容或属性上:

<TextBlock 
  x:Uid="TextBlock_1" 
  DockPanel.Dock="Right" 
  Foreground="White" 
  Margin="5,0,5,0"
  Localization.Comments="$Content(This is a comment on the TextBlock's content.)
     Margin(This is a comment on the TextBlock's Margin property.)">
  |
 </TextBlock>

本地化特性

通常,开发人员或本地化经理需要控制本地化人员能够阅读和修改的内容。 例如,可能不希望本地化人员翻译你公司的名称或法律用语。 WPF 提供了一些特性,使用这些特性可以设置元素的内容或属性(本地化工具可以使用这些内容或属性锁定、隐藏元素或对元素进行排序)的可读性、可修改性和类别。 有关详细信息,请参阅 Attributes。 此示例中 LocBaml 工具仅输出这些特性的值。 WPF 控件对这些特性都使用默认值,但可以替代这些默认值。 例如,以下示例替代 TextBlock_1 的默认本地化特性,并将其内容设置为可供本地化人员阅读,但不能被其修改。

<TextBlock
x:Uid="TextBlock_1"
Localization.Attributes=
"$Content(Readable Unmodifiable)">
  Microsoft Corporation
</TextBlock>

除了可读性和可修改性特性之外,WPF 还提供了常见 UI 类别 (LocalizationCategory) 的枚举,使用该枚举可以为本地化人员提供更多上下文。 平台控件的 WPF 默认类别也可以在 XAML 中替代:

<TextBlock x:Uid="TextBlock_2">
<TextBlock.ToolTip>
<TextBlock
x:Uid="TextBlock_3"
Localization.Attributes=
"$Content(ToolTip Readable Unmodifiable)">
Microsoft Corporation
</TextBlock>
</TextBlock.ToolTip>
Windows Vista
</TextBlock>

WPF 提供的默认本地化特性还可以通过代码进行替代,因此可以正确地为自定义控件设置合适的默认值。 例如:

[Localizability(Readability = Readability.Readable, Modifiability=Modifiability.Unmodifiable, LocalizationCategory.None)]
public class CorporateLogo : TextBlock
{
    // ...
}

在 XAML 中设置的实例特定特性优先于在自定义控件的代码中设置的值。 有关特性和注释的详细信息,请参阅本地化特性和注释

字体回退和复合字体

如果指定一种不支持给定代码点范围的字体,WPF 将使用 Windows\Fonts 目录中的 Global User Interface.compositefont 自动回退到支持该范围的字体。 复合字体与任何其他字体的用法相同,通过设置元素的 FontFamily(例如 FontFamily="Global User Interface")可以显式使用复合字体。 通过创建你自己的复合字体并指定针对特定代码点范围和语言所使用的字体,可以指定你自己的字体回退首选项。

有关复合字体的详细信息,请参阅 FontFamily

本地化 Microsoft 主页

可以按照“运行”对话框示例中的相同步骤对此应用程序进行本地化。 在 Globalization Homepage Sample(全球化主页示例)中可以找到本地化的阿拉伯语 .csv 文件。