通过本地化可以扩大控件部件的市场。已本地化的控件使用所在国家/地区的语言来显示文本(包括窗口标题行、标题和错误信息),而不用控件制作者国家/地区的语言。各国家/地区使用其本地化的控件来开发应用程序,。
本主题专门探讨 ActiveX 控件的本地化问题。关于本地化的一般问题,请参阅《程序员指南》的“国际化”。
在使用 Visual Basic 编译可执行文件时,Visual Basic 版本的 LocaleID(也称为 LCID)将被编译进去。例如用 Visual Basic 的 German 版本编译的应用程序含有的 LocaleID 将是 &H0407,Germany。
以同样的方式,LocaleID 也被编译进 Visual Basic 创建的 ActiveX 控件部件中。这将成为控件的缺省 LocaleID。如果只能如此,那么控件的制作者为了为不同的国家/地区提供新版本的部件,将不得不使用不同国家/地区的 Visual Basic 版本进行编译。
幸运的是,控件部件比编译的应用程序具有更大的灵活性。控件可以与任何国家/地区版本的 Visual Basic 一起使用,甚至可以与支持其它区域的开发工具一起使用,因为它们能在运行时确定正确的 LocaleID。
AmbientProperties 对象的 LocaleID 属性返回使用控件程序的 LocaleID。AmbientProperties 对象是 UserControl 对象的属性,因而是可以得到的,请参阅本章前面的“使用环境对象与容器保持一致”。
一旦把控件的实例放置到容器上,就可以测试 Ambient 环境属性;即在 InitProperties 或 ReadProperties 事件中。一旦知道了 LocaleID,就可以调用代码从资源文件或非基本语种 DLL 中加载特定语种的标题、错误信息文本等,详情请参阅本主题后面的描述。
两个事件中都需要调用区域代码,因为仅当控件实例第一次被放到容器上时,InitProperties 事件才会发生。以后控件实例改为接收 ReadProperties 事件,请参阅本章前面的“理解 UserControl 存活期和关键事件”。
在 AmbientChanged 事件中也应该调用区域代码,因为使用控件的应用程序可能按 Windows 控制面板的设置值重置其区域,而该设置值可以在任何时侯改变。如果用作子控件,控件也能接收 AmbientChanged 事件,详情请参阅本主题后面的描述。
UserControl 上的子控件通过检查 AmbientProperties 对象来查明 LocaleID,象所有好的容器一样,UserControl 使其子控件可使用该对象。这是自动发生的,不需要您的工作。
当 Initialize 事件发生时,控件已被创建,所有的子控件也都被创建并放置到控件的 UserControl 对象上。然而,控件还没有被放置在容器上,所以 UserControl 不能为子控件提供正确的 LocaleID。
如果 Intialize 事件中的代码访问子控件的属性和方法,那么得到的将是编译部件时使用的 Visual Basic 版本的 LocaleID,而不是编译控件所在应用程序时的 LocaleID。例如,方法调用可能会返回错误的语言字符串。
要避免这种情况,就不要在 Initialize 事件中访问子控件。
每当控件所在容器上的某个环境属性变化时,AmbientChanged 事件就会发生,请参阅本章前面的“使用环境对象与容器保持一致”。
使用 Visual Basic 编译的应用程序使用编译它们的 Visual Basic 版本的 LocaleID。然而,控件可能被用于用 Microsoft Visual C++ 等开发工具编写的应用程序中,其中有可能会改变应用程序的 LocaleID 来响应系统信息。
例如,如果用户打开“控制面板”并改变区域,应用程序会收到关于此变化的通知,并相应地重新设置自己。通过添加代码动态地改变区域,控件可以处理这种情况,请参阅如下示例:
Private Sub UserControl_AmbientChanged( _ PropertyName As String) Select Case PropertyName Case "LocaleID" '
从资源文件或非基本语种DLL
中'
加载本地化的标题、信息等'
的代码,见后面的描述。'
其它属性的Case
语句。End Select
End Sub
如果把控件作为其它控件的子控件使用,也能发生区域的变化。正如前面所述,当第一次被放到 UserControl 对象上时,子控件不会得到正确的 LocaleID。当最外面的控件放置到应用程序的窗体上之后,所有的子控件将收到带有正确 LocaleID 的 AmbientChanged 事件。
最灵活的本地化技术是,以潜在的最大市场的国家/地区的语言作为文本字符串和错误信息的缺省语言,并用它编译控件部件。将其它国家/地区语言的文本字符串和错误信息放到非基本语种 ActiveX DLL 中,每个国家/地区对应于一个非基本语种 ActiveX DLL。
这一设计使部件对于需要开发多语种版本程序的开发者特别具有吸引力,因为他们可以在一台开发机器上处理多种区域。
非基本语种 DLL 对于多语种国家/地区的用户也具有吸引力。这样的用户可能要求程序员为不同的语种编译程序;如果两个这样的程序都使用了您的控件部件,非基本语种 DLL 允许它们共存在同一个用户的计算机上。
重点 如果所需的非基本语种 DLL 未找到,控件不应该产生错误,因为这将引起整个应用程序的失败。在非基本语种 DLL 不可用的事件中,应该简单地使用控件部件被创建时的缺省区域。
如果非基本语种 DLLs 使用尾部开放(open-ended)命名约定,以后就可以提供其它的 DLL 而不用重新编译程序。这种命名约定的一个例子是在 DLL 的名称中包含 LocaleID。使用这种约定,用于 Belgian、French、German 和 US English 国家/地区的非基本语种 DLL 可以分别命名为 MyControls20C.dll、MyControls407.dll 和 MyControls409.dll。
如果使用 Windows API 调用从非基本语种 DLLs 中加载和提取资源,把 LocaleID 转成字符串,并把它附加到基本名称后面,即可得到 DDL 的名称。(注意前面的例子使用了 LocaleID 的十六进制表示。)
除了使用 API 调用,还可将非基本语种的 DLLs 作为 Visual Basic ActiveX DLL 工程创建。要做到这一点,可以创建带有获取资源方法的类模块。类的名字可以类似于“Localizer”。在每个 DLL 工程添加此类模块。
将尾部开放命名约定用于 Project Name,使在 Windows 注册表中的每个 DLL 具有唯一的程序 ID,或 ProgID。每次编译新的非基本语种 DLL,将创建新的 Localizer 类,它的完整程序 ID 包含此 DLL 的 Project Name。
然后,在 ActiveX 控件工程中可以使用如下代码来创建适当 Localizer 类的实例:
Dim strProgID As String Dim objLoc As Object '
为适当的非基本语种DLL
生成Localizer
'
对象的ProgID
。strProgID = "MyControls" & Hex$(AmbientProperties.LocaleID) _
& ".Localizer"
Set objLoc = CreateObject(strProgID)
If objLoc Is Nothing Then
'
未找到非基本语种DLL
;使用缺省区域。Else
'
调用Localizer
对象的方法来'
获取本地化的字符串和位图资源。End If
以上代码使用了后期约束(即,变量 objLoc
被声明为 As Object)。通过 Visual Basic 的 Implements 功能,使用前期约束可以获得更好的性能。可以不生成 Localizer 类的资源提取方法成员,而是在名为 IResources 的抽象类中定义它们。
在 Localizer 类中,使用 Implements 语句将 IResources 作为第二接口实现。可以通过前期约束来调用此接口的方法,如下所示:
'IResources
接口的前期约束的变量。Dim ires As IResources
'
得到Localizer
对象的Iresources
'
接口,该对象由非基本语种DLL
获得。Set ires = objLoc
'
调用IResources
接口的方法,'
以提取本地化资源。Set cmdOK.Caption = ires.GetString(ID_CMDOK)
正象对后期约束的 Localizer 对象那样,可以简单地在每个非基本语种 DLL 工程中添加 Localizer 类模块及其第二接口。这种可在几个不同的类中添加相同接口的能力叫做多态性。
详细信息 关于 Implements 功能的内容,请参阅“部件设计的一般准则”的“用 Implementing 接口提供多态性”。
关于访问非基本语种 DLLs 的内容,请参阅《程序员指南》“国际化”。
关于为 Visual Basic 工程添加资源文件的内容,请参阅《程序员指南》的“再论编程”。
非基本语种 DLLs 的一个替换办法是把文本串和错误信息放在一个资源文件中,并把该文件编译到控件部件中。不过这一技术有些缺点:
如果要对属性、方法和事件的名称进行本地化,那么就必须分别为每个国家/地区编译自己的控件版本。如果又需要允许控件的多个版本共存于同一台计算机上,那么不同版本中的控件必须使用不同的名称。
接口和控件名称不同带来的结果是,多语种开发者在使用您的控件时,必须分别编写每种语言版本的程序代码。这会使控件对开发者失去吸引力。
Microsoft 应用程序,如 Visual Basic,不对接口成员的名称进行本地化。
Microsoft 应用程序对属性页面进行本地化,而不是对属性名称。如果采用这一方案,显示在属性页面上的标题就会与“属性”窗口中的属性名不一致。
在对属性页面上的属性标题进行本地化时,请谨慎地选择可以清楚地标识属性的标题。也可以在括号中加上属性的名称。
关于简化本地化工作的窗体设计原理,请参阅《程序员指南》的“国际化”。
没有办法从本地化的 DLL 或资源文件中提取浏览器字符串,所以浏览器字符串必须被编译到类型库中去。为了制造本地化的类型库,必须使用“过程属性”对话框改变所有属性、方法、和事件的浏览器字符串。然后必须重新编译可执行文件。
本地化的类型库信息会限制利用非基本语种 DLL 进行本地化的能力。通常可以使浏览器字符串保持控件缺省语言形式。
详细信息 请参阅《程序员指南》的“国际化”。