资源与本地化(三)
Satellite Assemblies(卫星程序集)
当你编译有如图6所示本地化资源的工程时,Visual Studio不会把所有的资源都编译进一个单独的输出程序集,相反它把每个本地化资源文件单独编译进它自己的程序集。每个本地化程序集仅仅包含资源没有代码,这种仅仅有本地化资源的程序集被称之为卫星程序集(Satellite Assembly)。
每个卫星程序集与被称之为中性程序集(neutral assembly)相关联。中性程序集包含所有的代码,并且根据当前用户的要求加载需要的卫星程序集,从而得到本地化资源。在我的例子中,LitwareSmartClient.exe是中性程序集,它包含了应用程序的所有代码,有几个卫星程序集与它相关联,每个卫星程序集都有相同的名称,LitwareSmartClient.resource.dll。
当所有
卫星程序集随着中性程序集一起部署在AppBase目录中时,它们根据.NET程序集加载器的规则进行部署。特别是,每个卫星程序集必需部署在按照它的本地化文化名称命名的目录下。例如,包含了LitwareSmartClient.exe文件的目录应该包含一个名称为fr-BE子目录,该目录下含有名称为LitwareSmarClient.resource.dll为比利时法语本地化的卫星程序集。只有遵从这些规则,.NET框架的程序集加载器才能在ResourceManager类的协助下加载所要求的正确资源。
幸运地,Visual Studio知道如何正确地命名卫星程序集,并且按照.NET程序集加载器的期望把它们部署在正确的目录结构下。为了更好的理解所有这些不同的方面是如何组合起来的,很简单,你只要编译下你的工程,并去检查下AppBase目录和包含卫星程序集的子目录的结构。
Loading Localized Resources加载本地化资源
一旦你已经建立并编辑了所有需要的本地化资源文件,而且编译了你的工程,是时候集中精力了解,你怎样使得你的程序加载被当前用户首先的正确的本地化字符串呢?有一个方法让你达到此目的,就是取得一个当前线程的引用,并把一个新建的CultureInfo对象赋给CurrentUICulture属性。如果你写的是用Visual Basic的Windows Form程序,你也能用如下代码:
My.Application.ChangeUICulture("fr-BE")
在我的程序中,相对于上面这句,我增加用户选择它们偏好语言的支持,并且让程序在关闭和重新打开时跟踪用户的语言偏好。虽然使用注册码(registry keys)来维持这种用户偏好是可能的,但Visual Studio 2005通过增加一个应用程序设置使得避免使用注册码是可能的,而这设置就是以一种user-by-user的方式追踪。我的示例程序为此目的追踪一个名称为UserLanguagePreference的用户范围级别的应用程序设置。程序也包含一个程序启动事件(见 图 7)。
示例程序也向用户提供了一组单选按钮,意思是从一种语言转换成另一种语言。图8显示了一段来自响应用户更改语言请求的事件处理的代码。
你已经看到了所有的运行用户转换语言的基础性的代码,.NET框架会响应对My.Application.ChangeUICulture方法的调用,下一次通过加载正确的卫星程序集,ResourceManager从工程级别的资源中重新取得字符串,程序然后能查询程序级别资源字符串,并且非常相同的代码把它们加载入表单中的控件上,那些代码就是你早些时候看到的在自定义的方法LoadResources中的那样:
Me.Text = My.Resources.MainFormCaption
Me.lblWelcomeMessage.Text = My.Resources.UserWelcome
注意,.NET程序集加载器会首先尝试发现与请求的文化名称和可用的卫星程序集的文化名称精确匹配的语言与区域,如果.NET程序集加载器不能找到这种精确匹配,它然后它寻找一个有匹配语言的可用的卫星程序集。例如,如果请求的语言是fr-CA加拿大法语,.NET框架首先寻找有该语言的卫星程序集,如果它不能定位到有fr-CA语言的卫星程序集,那么它就寻找文化名称为fr的卫星程序集,如果.NET框架不能定位到文化名称为fr的卫星程序集,那么将采取使用在中性程序集中发现的资源,因为中性程序集有一套默认的嵌入在里面的文化资源。就像你刚才看到的,.NET框架如果不能找到一个更加明确的卫星程序集,它总是能退回到中性程序集默认的文化资源。
当你编译中性程序集时,你能用一个特殊的特性(attribute)做标记,通知.NET框架,对于那些要求某一种特殊语言的用户,默认的文化资源是满足需要的。例如,你能在一个Windows Forms的应用程序工程的AssemblyInfo.vb文件中添加一个程序集级别(assembly-level)特性。
<Assembly: System.Resources.NeutralResourcesLanguage("en")>
在这儿,NeutralResourceLanguage特性通知.NET程序集加载器不管什么时候只要用户请求的程序本地化成英语时,就可用使用默认的资源。
本地化窗口和控件设置
你仅看到如何在工程范围本地化字符串资源,该技术涉及到复制和维护本地化的资源,Visual Studio 2005在当你需要对一个窗口以及它包含的控件本地化一些明确的字符串提供一些额外的帮助。
每个窗口有一个Localizable属性,它能设置成true或false,如果你设置成true,如图9所示,Visual Studio 2005将在后台为你建立并维护一套本地化资源文件。
当你把Localizable属性设置成true时,初始化语言设置是默认的,当你为窗口和它的控件在属性单中增加属性值时,Visual Studio在后台在一个资源文件中维护它们,该文件被编译进中性程序集。当你把窗口的语言属性改变成某一特殊语言时,例如“French(Belgium)”,那么Visual Studio将建立一个新的本地化资源文件,它会编译进卫星程序集。在后台,就如它们处理工程范围的资源那样一切都会准确地进行。仅仅是Visual Studio取消了你直接处理资源文件,而是允许你处理一个标准的属性单,这就像你为控件的text属性增加属性值一样。
Figure 9 Localizable Property
为了支持窗口本地化Visual Studio要求向窗口增加一些而外的代码,特别是,Visual Studio增加代码为了加载正确的本地化资源并并把它们的值赋给窗口和控件的属性。由Visual Studio 2005生成的代码使用一个更加特殊的名称为ComponentResourceManager的类来代替ResourceManager类,CompenentResourceManager类派生自ResourceManager类,定义在System.ComponentModel名称空间。
当一个本地化的窗口加载时,加载本地化资源所需要的任何东西都由Visual Studio生成的代码来完成。特别是,Visual Studio提供代码来建立一个ComponentResourceManager实例,它能加载恰当的一套资源,并给所有需要的控件赋值。
然而,如果窗口已经加载,用户改换了语言,你必需提供一些附加的代码来用请求的语言的资源刷新窗口,下面显示了一个示例,展示了使用ComponentResourceManager来达到此目的的代码:
Dim crm As ComponentResourceManager
crm = New ComponentResourceManager(GetType(
crm.ApplyResources(cmdAddCustomer, cmdAddCustomer.Name)
crm.ApplyResources(mnuFile, mnuFile.Name)
crm.ApplyResources(mnuFileAddCustomer, mnuFileAddCustomer.Name)
如你所见,你能通过传人关于窗口的类型信息新建并初始化一个ComponentResourceManager实例,在该例中,被命名为Main。
完整的示例程序,演示了所有在本专栏文章中讨论的本地化技术,如图10所示。应用程序现在支持US English, British English和Belgian French。也要注意,由于有一个没有任何确定区域信息的fr的本地化的卫星程序集,应用程序也支持一个为全世界说法语的用户通用的法语窗口。
Figure 10 Supporting Localized Versions
如果你想在将来你增加对其它语言的支持,不会太复杂,建立和维护另外的资源文件是种简单的行为。事实上,你增加对新语言的支持甚至不需要重新编译包含所有程序代码的中性程序集。这是.NET框架针对本地化应用程序和类库DLLs策略中最有价值的一个特性。
结论
这个月的专栏通过完成任何本地化一个简单基于Windows Forms的应用程序,讲述在.NET框架中的资源和本地化的基本原理和概念。在我下个月的专栏文章中,我将基于这个月所讲述的前进到讨论在ASP 2.0 中资源和本地化,在ASP 2.0 中有一些利用资源和本地化应用程序的有价值和独特的特性。