打包和部署资源
更新:2007 年 11 月
.NET Framework 使用轮辐式模型来打包和部署资源。集中式模型是主程序集,它包含不可本地化的可执行代码以及用于单个区域性(称作非特定区域性或默认区域性)的资源。默认区域性是应用程序的后备区域性。每一辐条均连接到一个附属程序集,该附属程序集包含单个区域性的资源,但不包含任何代码。
此模型具有以下几项优点:
您可以在应用程序开发结束后为新区域性逐渐添加资源。因为以后的特定于区域性的资源的开发可能需要耗费大量的时间,所以这一优点使您可以首先发布您的主应用程序,并在以后提供特定于区域性的资源。
您无需重新编译应用程序,即可更新和更改应用程序的附属程序集。
应用程序只需加载那些包含特定区域性所需的资源的附属程序集。这可以显著减少对系统资源的占用。
但是,此模型也存在一些不足:
您必须管理多个资源组。
测试应用程序的初始成本将增加,因为您必须测试几个配置。请注意,从长远角度来看,同测试及维护几个并行的国际版本相比较,测试具有几个附属程序集的核心应用程序更简便、成本更低。
资源命名规则
在打包您的应用程序的资源时,您必须使用适合于公共语言运行库的资源命名规则命名这些资源。运行库通过资源的区域性签名(即名称)标识一个资源。将为每个区域性给定一个唯一的名称,该名称由与语言相关的两个小写字母的区域性名称和(如果需要)与国家或地区相关的两个大写字母的子区域性名称组成。子区域性名称位于区域性名称之后,用短划线 (-) 隔开。这方面的示例包括:ja-JP 表示日本日语、en-US 表示美国英语、de-DE 表示德国德语(作为对比的示例是,de-AT 表示奥地利德语)。有关区域性名称的完整列表,请参见 CultureInfo 类。
资源后备进程
用于打包和部署资源的轮辐式模型使用后备进程来定位合适的资源。如果应用程序的用户请求不可用的 ResourceSet,则公共语言运行库搜索该区域性的层次结构,查找与该用户请求最匹配的适当后备资源,并且只在迫不得已的情况下才引发异常。在层次结构的每一级别,只要发现了适当的资源,运行库就使用该资源。如果未找到合适的资源,则继续在下一个级别进行搜索。资源后备进程将在下面的步骤中说明:
运行库首先检查全局程序集缓存,以找到与为应用程序请求的区域性匹配的程序集。
全局程序集缓存可以存储由许多应用程序共享的资源程序集。这使您免去不得不在您创建的每一应用程序的目录结构中包括特定资源组之苦。如果运行库找到了对程序集的引用,则它将搜索该程序集以找到请求的资源。如果它在程序集中找到了该项,将使用请求的资源。如果它没有找到该项,将继续搜索。
运行库接下来检查当前执行的程序集的目录以找到与请求的区域性匹配的目录。如果它找到了匹配的目录,它将搜索该目录以找到请求的区域性的有效附属程序集。然后运行库搜索该有效附属程序集以找到请求的资源。如果它在程序集中找到了该资源,则使用这一资源。如果它没有找到该资源,将继续搜索。
运行库接下来再次搜索全局程序集缓存,这一次是为了找到请求的资源的父程序集。如果在全局程序集缓存中存在资源的父程序集,则运行库搜索该程序集以找到请求的资源。
父程序集被定义为合适的后备区域性。将父程序集视作最适合的候选;提供任意资源要比引发一个异常更可取。此进程还允许您重复使用资源。只有在子区域性不需要本地化请求的资源时,您才需要包括父级别的特定的资源。例如,如果您提供 en(非特定英语)的附属程序集:en-GB(英国英语)和 en-US(美国英语),则 en 附属程序集应包含公共术语,并且 en-GB 和 en-US 附属程序集可能只对那些不同的术语提供重写。
运行库接下来检查当前执行的程序集的目录,以查看该目录中是否包含父目录。如果存在父目录,则运行库搜索该目录以找到父区域性的有效附属程序集。如果它找到了有效附属程序集,则运行库搜索该程序集以找到请求的资源。如果它找到了该资源,则使用它。如果它没有找到该资源,将继续搜索。
运行库接下来在许多可能的级别搜索父程序集(如前面步骤中所述)。每一区域性只有一个父区域性,但一个父区域性可能还有其自己的父区域性。
如果对最初指定的区域性以及所有父区域性都进行了搜索但仍然未找到所需资源,则使用默认(后备)区域性的资源。从 .NET Framework 2.0 版开始,您可以指定资源的最终后备位置是附属程序集,而不是主程序集。通过将 NeutralResourcesLanguageAttribute 用于 UltimateResourceFallbackLocation 枚举,可以控制资源的最终后备位置是在主程序集中还是在附属程序集中。
说明: 默认资源是由主程序集编译的唯一资源。除非使用 NeutralResourcesLanguageAttribute 指定附属程序集,否则它是最终后备(最终父程序集)。因此,强烈建议您始终将默认的资源组包括在您的主程序集中。这有助于确保不会引发异常。通过包括默认的资源文件,您可以为所有资源提供后备,并且确保对于该用户始终提供至少一个资源,即使该资源不是特定于区域性的。
最后,如果运行库没有找到默认(后备)区域性的资源,则将引发异常,指出未能找到资源。
下面举例说明如何搜索请求的资源,假定用户请求本地化为墨西哥西班牙语所需的资源。与上述资源命名规则相符,运行库首先搜索全局程序集缓存以找到与请求的区域性“es-MX”相匹配的程序集。如果没有找到该程序集,则运行库搜索当前执行的程序集的目录以找到“es-MX”目录。如果也没有找到该目录,运行库再次搜索全局程序集缓存以找到反映适当的后备区域性,在此例子中为“es”(西班牙语)的父程序集。如果没有找到父程序集,则运行库搜索所有潜在的父程序集级别以找到“es-MX”区域性,直到找到相应的资源为止。如果还是没有找到一个合适的资源,则运行库使用默认区域性的资源。
将最终后备放置在附属程序集中
从 .NET Framework 2.0 版开始,您可以选择将资源从主程序集中移除并指定将最终后备资源放置在与特定区域性相关的附属程序集中。为控制后备进程,可以使用 NeutralResourcesLanguageAttribute。NeutralResourcesLanguageAttribute 类中已添加了一个新的构造函数,它多采用了一个 UltimateResourceFallbackLocation 参数来指定 ResourceManager 提取后备资源的位置:主程序集还是附属程序集。
下面的示例演示如何在类级别应用属性:
[assembly: NeutralResourcesLanguageAttribute("de" , UltimateResourceFallbackLocation.Satellite)]
对于最终后备位置,此示例指示 ResourceManager 在当前执行程序集的目录的“de”子目录中查找资源。
建议的替代打包
由于时间或预算的约束,为您的应用程序支持的每一子区域性均创建一组资源是不切实际的。在此情况中,您可以为所有相关子区域性可能使用的父区域性创建单个附属程序集。例如,您可以提供单个英语附属程序集 (en),请求特定于区域的英语资源的用户将检索该程序集,并且为请求特定于区域的德语资源创建单个德语附属程序集。例如,对德国德语 (de-DE)、奥地利德语 (de-AT) 和瑞士德语 (de-CH) 的请求都将后备到该德语附属程序集 (de)。选择要由主程序集进行编译的默认资源时应十分仔细。默认资源是最终后备资源,因而应是您的大多数应用程序用户将请求的资源。尽管此解决方案部署较少特定于区域性的资源,但它能够显著降低您的应用程序的本地化成本。