本地化您的 Windows Phone 7 应用程序(枚举和完成的项目)






4.82/5 (3投票s)
为您的 Windows Phone 7 应用程序提供语言支持
引言
在创建我的第一个名为 InputStudio 的 Windows Phone 7 应用程序时,我遇到了全球化和本地化问题,如果我们想至少支持英语、西班牙语、意大利语、法语和德语等标准/最低语言,这些问题是所有人都会面临的。
根据您希望(仅限于本国还是全世界)应用程序覆盖的受众,当然也取决于您在编码时的自律程度,甚至基于您的偏好,您可能从编码开始之初就支持不仅仅是您的母语,还支持您想覆盖的其他语言。
这篇简单的文章和代码为您带来了一种本地化应用程序中枚举数据类型的方法,并且还能帮助您在已经用母语(中性语言)完成项目后进行本地化。
背景
本地化 是针对特定文化或区域设置的应用程序定制。全球化是为支持多种文化中的用户提供本地化用户界面和区域数据而设计的应用程序。
本文是系列文章的第三部分:Windows Phone 7 应用程序之旅(从构思到部署)。
主页(英文)
| 枚举数据类型(英文)
|
![]() | ![]() |
主页(德文)
| 枚举数据类型(德文)
|
![]() | ![]() |
代码解释
演示应用程序是使用 MS Visual Studio 中的 MVVM Light Windows Phone 7 模板创建的,包含两个项目:MvvmLocalized
和有用的 XAMLQueryLib。
枚举数据类型本地化
让我们开始添加一个资源包装器类来处理 string
资源的绑定。
1 //Wraps access to the strongly typed resource classes so that you can bind
2 // control properties to resource strings in XAML
3 public sealed class ResourceWrapper {
4 private static ApplicationStrings
applicationStrings = new applicationStrings();
5
6 public ApplicationStrings Strings {
7 get {
8 return applicationStrings;
9 }
10 }
11 }
正如您在下面的代码中看到的,MainViewModel
类非常简单,只是为了向您展示如何根据当前设备(手机)语言创建实际的枚举列表。
1 // This class contains properties that the main View can data bind to.
2 public class MainViewModel : ViewModelBase {
3 // Initializes a new instance of the MainViewModel class.
4 public MainViewModel() {
5 if (IsInDesignMode) {
6 // Code runs in Blend --> create design time data.
7 }
8 else {
9 // Code runs "for real"
10 }
11 }
12
13 private List <eDataType> _dataTypes = new List<eDataType>();
14 public List<eDataType> DataTypes {
15 get {
16 if (_dataTypes.Count.Equals(0))
17 _dataTypes =
EnumsGeneric<eDataType>.EnumAsList(eDataType.Undefined);
18
19 return _dataTypes;
20 }
21 }
22 }
现在,从 ViewModelLocator
中添加 MainViewModel
类,键为“Locator
”,作为数据上下文;再添加 ResourcesWrapper
类,键为“StringsLocator
”,用于访问应用程序资源中本地化的语言 string
。
1 <application.resources>
2 <vm:viewmodellocator x:key="Locator" d:isdatasource="True"/>
3 <local:resourcewrapper x:key="StringsLocator"/>
4 </application.resources>
现在让我们看看 Language
属性,这样我们就可以用属性来装饰我们的枚举数据类型。这将是我们本地化枚举数据类型的基础。Ordinal
成员用于按值对 enum
进行排序。
1 [Flags]
2 public enum eLanguage
3 {
4 enUS = 1,
5 frFR = 2,
6 deDE = 4,
7 itIT = 8,
8 esES = 16
9 }
10
11 [AttributeUsage(AttributeTargets.All)]
12 public class LanguageAttribute : Attribute
13 {
14 public eLanguage Language;
15 public int Ordinal;
16
17 public LanguageAttribute(eLanguage language, int ordinal)
18 {
19 this.Language = language;
20 this.Ordinal = ordinal;
21 }
22 }
现在是时候定义我们的枚举数据类型了。
1 public enum eDataType {
2 [Language(eLanguage.enUS, 0)]
3 Undefined,
4 [Language(eLanguage.enUS | eLanguage.frFR |
eLanguage.deDE | eLanguage.itIT, 1)]
5 Boolean,
6 [Language(eLanguage.enUS, 2)]
7 DateTime,
8 [Language(eLanguage.enUS, 3)]
9 Decimal,
10 [Language(eLanguage.enUS | eLanguage.deDE, 4)]
11 Integer,
12 [Language(eLanguage.enUS | eLanguage.deDE, 5)]
13 ListOf,
14 [Language(eLanguage.enUS | eLanguage.frFR |
eLanguage.deDE | eLanguage.esES, 6)]
15
16
17 String,
18 [Language(eLanguage.enUS | eLanguage.frFR |
eLanguage.itIT | eLanguage.esES, 7)]
19 Url,
20 [Language(eLanguage.enUS | eLanguage.frFR | eLanguage.deDE, 8)]
21 Image,
22
23 [Language(eLanguage.frFR, 0)]
24 Indéfini,
25 [Language(eLanguage.frFR, 1)]
26 DateHeure,
27 [Language(eLanguage.frFR, 2)]
28 Décimal,
29 [Language(eLanguage.frFR, 3)]
30 Entier,
31 [Language(eLanguage.frFR, 4)]
32 Listedes,
33
34 [Language(eLanguage.deDE, 0)]
35 Undefiniert,
36 [Language(eLanguage.deDE, 1)]
37 DatumUhrzeit,
38 [Language(eLanguage.deDE, 2)]
39 Dezimal,
40 [Language(eLanguage.deDE, 3)]
41 URL,
42
43 [Language(eLanguage.itIT, 0)]
44 Indefinito,
45 [Language(eLanguage.itIT, 1)]
46 DataOra,
47 [Language(eLanguage.itIT, 2)]
48 Decimale,
49 [Language(eLanguage.itIT, 3)]
50 Intero,
51 [Language(eLanguage.itIT, 4)]
52 Elencodi,
53 [Language(eLanguage.itIT, 5)]
54 Stringa,
55 [Language(eLanguage.itIT, 6)]
56 Immagine,
57
58 [Language(eLanguage.esES, 0)]
59 Indefinido,
60 [Language(eLanguage.esES, 1)]
61 Boleano,
62 [Language(eLanguage.esES, 2)]
63 FechaHora,
64 [Language(eLanguage.esES, 3)]
65 Decimales,
66 [Language(eLanguage.esES, 4)]
67 Entero,
68 [Language(eLanguage.esES, 5)]
69 Listade,
70 [Language(eLanguage.esES, 6)]
71 Imagen,
72 }
枚举数据类型创建中的所有“魔法”都归功于 Language
属性和从当前线程获取的当前区域性名称。
1 public class EnumsGeneric<t> {
2 public static List<t> EnumAsList(T t) {
3 List<t> items = new List<t>();
4
5 var fields = from field in t.GetType().GetFields()
6 let attribute =
field.GetAttributes<languageattribute>(true).FirstOrDefault()
7 where attribute != null &&
attribute.Language.ToString().StartsWith(
Thread.CurrentThread.CurrentCulture.Name.Substring(0,2))
8 orderby attribute.Ordinal
9 select field;
10 foreach (FieldInfo fi in fields)
11 items.Add((T)fi.GetValue(t));
12
13 return items;
14 }
15 }
这就是枚举数据类型创建所需的所有代码。
收集所有页面中的文本字符串以进行本地化
这段代码的创建是基于这样的假设:您已经完成了项目,并且所有的文本 string
都已用您的母语(中性语言)编写。现在,我们需要本地化所有这些 string
,以便至少支持标准语言(如上所述)。
我在项目中为所有页面(包括主页)创建了一个基类。此类将把 Load
事件附加到您的所有页面,并处理将文本 string
保存到 IsolatedStorage
文件中,以便稍后通过电子邮件发送给您(在加载主页时)。
由于此项目只有一个页面(主页),您需要退出应用程序并重新进入才能通过电子邮件发送 XML 文本字符串。
请注意,文本字符串文件将在您点击页面标题时生成。
1 public class PhoneApplicationPageBase : PhoneApplicationPage {
2 public PhoneApplicationPageBase()
3 : base() {
4 this.Loaded += new RoutedEventHandler(PhoneApplicationPageBase_Loaded);
5 }
6
7 void PhoneApplicationPageBase_Loaded(object sender, RoutedEventArgs e) {
8 if (true /*model.IsInBuilStringResourcesMode*/)
9 this.ManipulationStarted += new EventHandler
10 <manipulationstartedeventargs>(
PhoneApplicationPageBase_ManipulationStarted);
11 }
12
13 void PhoneApplicationPageBase_ManipulationStarted(object sender,
ManipulationStartedEventArgs e) {
14 if (e.OriginalSource is TextBlock &&
((TextBlock)e.OriginalSource).RenderSize.Height.Equals(96))
15 BuildStringResources(this.GetType(), this);
16 }
17
18 public MainViewModel model { get { return this.DataContext as MainViewModel; } }
19
20 public static void BuildStringResources(Type type, FrameworkElement container) {
21 try {
22 using (IsolatedStorageFile file =
IsolatedStorageFile.GetUserStoreForApplication()) {
23 string fileName = string.Format("{0}.xml", type.ToString());
24 if (!file.FileExists(fileName)) {
25 StringBuilder sb = new StringBuilder();
26 ControlSet textBlocks = XamlQuery.ByType<textblock>(container);
27 textBlocks.ForEach(delegate(DependencyObject item) {
28 TextBlock tb = item as TextBlock;
29 //if(null == tb.GetBindingExpression(TextBlock.TextProperty))
30 sb.AppendFormat("<{1}>{0}",
tb.Text, "string");
31 });
32
33 using (XmlWriter w = XmlWriter.Create(file.OpenFile(fileName,
FileMode.Append, FileAccess.Write),
34 new XmlWriterSettings() { Indent = true, OmitXmlDeclaration = true })) {
35 w.WriteRaw(string.Format("<{1}>{0}",
sb.ToString(), "strings"));
36 }
37
38 MessageBox.Show(ApplicationStrings.ResourcesSaved,
ApplicationStrings.Done, MessageBoxButton.OK);
39 }
40 }
41 }
42 catch (IsolatedStorageException e) {
43 MessageBox.Show(e.StackTrace, e.Message, MessageBoxButton.OK);
44 }
45 }
46 }
一旦您收到包含 XML 文本字符串的电子邮件,您就可以应用 XSLT 转换来生成 XML 文本,您可以将其添加到 ApplicationStrings.<language>
;说到底,这些资源文件只是 XML 文件。
请参阅解决方案的 Xml 文件夹中的文件。
- ApplicationStrings.xslt 是 样式表 转换。
- input.xml 是您将发送给自己的实际文件样本。
- output.xml 是转换后的 XML,您可以将其复制/粘贴到您的资源文件中。
关注点
如果您需要为两个以上的项目执行此收集任务,您应该创建一个 Visual Studio 插件来自动化此过程。此插件还应使用 Microsoft 翻译 API 或 Simple Resx Editor 来自动翻译您的资源字符串。
历史
这是我在 CodeProject 的第一篇文章,能在这里发文是我的荣幸,因为我一直以来都在关注 CodeProject 的文章。
代码更新:在使用 CurrentCulture.Name
时,请仅使用前两个字符,以避免与其他子文化(如 en、en-US、en-AU 等)出现问题。