创建多语言应用程序的指南

在全球化的今天,应用程序往往需要支持多种语言,以满足不同地区用户的需求。本文将介绍如何创建一个能够支持多种语言的WPF应用程序。将通过ResourceDictionary和MEF(Managed Extensibility Framework)来实现这一目标。

ResourceDictionary是基于XML的,它利用了XML规范中定义的全球化支持。可以为每种语言创建多个资源文件,并将它们添加到应用程序的根级别(App.xaml)中,以在整个应用程序中实现多语言支持。

创建资源

首先,需要创建资源。在WPF项目中,可以通过右键点击项目并选择"添加新项",然后从列表中选择"UserControl"。接下来,将UserControl转换为ResourceDictionary

为什么要先添加UserControl然后转换为ResourceDictionary,而不是直接添加ResourceDictionary呢?这是因为接下来将使用MEF(Import/Export)类。

给ResourceDictionary页面一个基于语言的合适名称,例如:EnglishLanguage.xaml,并编写如下字符串资源:

<ResourceDictionary x:Class="WPF_Globalization.Resources.EnglishLanguage" 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:s="clr-namespace:System;assembly=mscorlib" mc:Ignorable="d"> <s:String x:Key="keyEmployeeName">Employee Name:</s:String> <s:String x:Key="keyAddress">Address:</s:String> <s:String x:Key="keyCountry">Country:</s:String> <s:String x:Key="keyState">State:</s:String> <s:String x:Key="keyCity">City:</s:String> <s:String x:Key="keyPhone">Phone Number:</s:String> <s:String x:Key="keyDesignation">Designation:</s:String> </ResourceDictionary>

在上面的代码中,x:key用于字符串,是一个唯一的名称,用于标识字符串资源。

在应用程序中使用字符串资源

要在应用程序中使用全局文件资源,需要设置DynamicResource;对于本地文件资源,则需要设置StaticResource。

<TextBlock Grid.Row="0" Grid.Column="0" Text="{DynamicResource keyEmployeeName}" />

在这个应用程序中,创建了一个英语和法语的演示,可以根据需要创建更多的资源文件。同样的方式,为其他语言添加一个ResourceDictionary文件,例如FrenchLanguage.xaml,如下所示:

<ResourceDictionary x:Class="WPF_Globalization.Resources.FrenchLanguage" 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:s="clr-namespace:System;assembly=mscorlib" mc:Ignorable="d"> <s:String x:Key="keyEmployeeName">Nom de l'employé:</s:String> <s:String x:Key="keyAddress">adresse:</s:String> <s:String x:Key="keyCountry">pays:</s:String> <s:String x:Key="keyState">état:</s:String> <s:String x:Key="keyCity">ville:</s:String> <s:String x:Key="keyPhone">Numéro de téléphone:</s:String> <s:String x:Key="keyDesignation">désignation:</s:String> </ResourceDictionary>

使用MEF

Microsoft .NET Framework提供了一个System.ComponentModel.Composition命名空间,它提供了构成MEF(Managed Extensibility Framework)核心的类。有关更多详细信息,请访问MSDN。

MEF(Managed Extensibility Framework)是.NET的一个组合层,它提高了大型应用程序的灵活性、可维护性和可测试性。它允许应用程序开发人员发现和使用扩展,无需任何配置。通过使用MEF,开发人员可以轻松地封装代码并避免脆弱的硬依赖。

MEF的特点

MEF组件(类、方法、属性)指定了它的依赖项(Imports)和功能(Exports),这些依赖项和功能由运行时发现。当创建一个对象时,MEF组合引擎会用其他对象提供的内容满足其导入需求。它提供了导出,一个满足导入的对象。

在MEF中有许多可用的属性,除了它们之外,在本应用程序中使用了以下属性:

  • ExportAttribute:指定一个类型、属性、字段或方法提供了特定的导出。任何声明了匹配合同的导出都将满足此导入。
  • ImportAttribute:指定一个属性、字段或参数应该用匹配的导出填充。它将导入一系列操作。
  • ImportManyAttribute:指定一个属性、字段或参数应该用匹配的导出填充,它将导入一个操作列表。

导出类(资源)

打开EnglishLanguage.xaml.cs文件,并编写以下代码以导出此类(资源):

[ExportMetadata("Culture", "en-US")] [Export(typeof(ResourceDictionary))] public partial class EnglishLanguage : ResourceDictionary { public EnglishLanguage() { InitializeComponent(); } }

ExportMetadata指定类型的元数据(或者说它将在键值对中实现操作)。

同样的方式为FrenchLanguage类附加元数据。

导入所有导出的类

现在,创建一个类,它有一个属性来导入所有导出的类,如下所示:

public class ImportModule { [ImportMany(typeof(ResourceDictionary))] public IEnumerable<Lazy<ResourceDictionary, IDictionary<string, object>>> ResourceDictionaryList { get; set; } }

上述代码片段从不同的程序集中导入所有具有匹配类型ResourceDictionary的类。

组合容器

组合容器是MEF的核心。它用于通过使用可组合部分目录来发现部分(对象)。目录可以是托管中的任何给定类型(如DirectoryCatalog、AssemblyCatalog、AggregateCatalog等)。

创建一个Singleton类,并为ImportModule类添加一个属性。

public class BaseModel { private static BaseModel _instance; public static BaseModel Instance { get { if (_instance == null) _instance = new BaseModel(); return _instance; } } private ImportModule _importCatalog; public ImportModule ImportCatalog { get { _importCatalog = _importCatalog ?? new ImportModule(); return _importCatalog; } } }

应用程序中使用组合容器

在App.xaml.cs文件的OnStartup事件中编写以下代码,使用目录导入所有类。

string path = AppDomain.CurrentDomain.BaseDirectory; DirectoryCatalog catalog = new DirectoryCatalog(path); CompositionContainer container = new CompositionContainer(catalog); container.ComposeParts(BaseModel.Instance.ImportCatalog);

创建语言类

创建一个具有Code和Name属性的语言类,用于绑定。

public class Languages { public string Code { get; set; } public string Name { get; set; } }

在ViewModel中添加语言列表

在ViewModel中创建一个属性,它有一个语言列表。

private List<Languages> _languageList; public List<Languages> LanguageList { get { return _languageList; } set { _languageList = value; RaisePropertyChanged("LanguageList"); } } LanguageList = new List<Languages>(); LanguageList.Add(new Languages() { Code = "en-US", Name = "English" }); LanguageList.Add(new Languages() { Code = "fr-FR", Name = "French" });

在Usercontrol中添加语言选择ComboBox

在Usercontrol中添加一个ComboBox,用于更改语言,并将ViewModel类中的语言绑定到它。

<ComboBox x:Name="LanguageComboBox" Width="150" Margin="5" HorizontalAlignment="Left" DisplayMemberPath="Name" ItemsSource="{Binding LanguageList}" SelectedItem="{Binding SelectedLanguage, Mode=TwoWay}" SelectionChanged="LanguageComboBox_SelectionChanged" />

应用选定的语言资源

编写代码以在从ComboBox更改选定语言时应用选定的语言资源。

private void LanguageComboBox_SelectionChanged(object sender, SelectionChangedEventArgs e) { var currentResourceDictionary = ( from d in BaseModel.Instance.ImportCatalog.ResourceDictionaryList where d.Metadata.ContainsKey("Culture") && d.Metadata["Culture"].ToString().Equals(vm.SelectedLanguage.Code) select d).FirstOrDefault(); if (currentResourceDictionary != null) { Application.Current.Resources.MergedDictionaries.Add(currentResourceDictionary.Value); CultureInfo cultureInfo = new CultureInfo(vm.SelectedLanguage.Code); Thread.CurrentThread.CurrentCulture = cultureInfo; Thread.CurrentThread.CurrentUICulture = cultureInfo; Application.Current.MainWindow.Language = XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag); } }
沪ICP备2024098111号-1
上海秋旦网络科技中心:上海市奉贤区金大公路8218号1幢 联系电话:17898875485