关于.NET应用程序中的资源

来源:互联网 发布:安禄山 知乎 编辑:程序博客网 时间:2024/04/30 17:24

.NET应用程序中的资源

几乎每个人都要使用资源,资源是任何可被跟随应用程序逻辑部署的非可执行数据。一个资源可以是在应用程序出错是显示的一个错误消息,或者是用户界面的一部分。资源可以包含多种形式的数据,包括字符串,图像和任何可被持久化到资源文件中的对象,这些对象必须是可序列化的。

1.    创建资源

有三种方式来创建资源。如果你的资源只包含字符串数据,最简单的方法就是手工创建文本文件。如果资源中包含对象和字符串的组合,就必须创建.resx文件和.resources文件。

Resx文件可以在Visual Studio中创建并编辑,不过你也可以使用.NET Framework提供的类来创建和读取Resx文件,创建和读取Resx文件的两个类分别是ResXResourceWriterResXResourceReader。按照.NET的设计原则,不能将一个.resx文件嵌入一个可执行文件或者程序集中。你必须通过resgen.exe.resx文件转换为.resources文件,再将.resources文件嵌入程序集中。

ResourceWriter类被设计用来创建.resources文件。只有.resources文件格式才能被嵌入到程序集中。既可以使用ResourceWriter类来创建.resources文件,也可以使用resgen.exe创建.resources文件。

2.    使用resgen.exe

Resgen工具能够将resx文件转换为.resources文件,在内部使用了ResourceWriter来完成这项工作。Resgen工具同样封装了ResourceReader,允许你做反向转换。在将resx文件转换为resources文件的时候,备注信息将不会被写入resources文件。

.NET Framework使用轮毂和辐条模型来打包和部署资源。轮毂是包含了执行代码和单一资源的主程序集,该单一资源只针对单个文化,称作中性或者默认文化。默认文化是应用程序的备用文化。每个辐条连接到一个包含了针对单个文化的卫星程序集,但是它不包含任何代码。

这种模型有很多好处,你可以对已经部署的应用程序增量的添加新的文化支持。因为后续的针对特定文化的开发需要大量的时间,你可以先发布你的主应用程序,稍后在交付特定文化的资源。你还可以更新和修改卫星程序集而不需要重新编译整个应用程序。一个应用程序只需要加载包含特定文化的卫星程序集,这将会极大降低对系统资源的使用。

3.    资源名称的约定

当你打包你的资源的时候,你必须遵守CLR预期的资源命名约定。CLR使用文化签名来标识一个资源。每个文化都被给予了一个唯一的命名,该命名一般是两个字符的语言名称(用小写字符表示),另外再加两个字符的区域文化名称(用大写字符表示)。两个名称之间用横杠链接。例如US Englishen-US表示。可以查看CultureInfo类的备注来获取文化名称的列表。

4.    资源反馈流程

轮毂和辐条模型使用反馈流程来定位资源。如果应用程序用户请求一个不存在的ResourceSetCLR将会沿着文化的层次来查找一个最能接近用户请求的资源。如果最终没有找到资源,那么将会引发异常。资源反馈流程如下:

1)   CLRGAC中查找匹配请求文化的资源。

2)   CLR检查当前程序所在目录下的目录名称是不是匹配请求文化的名称。如果找到了这个目录,就在这个目录下查找有效的卫星程序集,并从程序集中查找资源。

3)   CLR再次搜索GAC,这次它将会在父程序集中查找请求的资源。“父”的意思是请求文化的父级文化。比如说你提供支持en-GBen-US的卫星程序集,就可以在支持en的卫星程序集中包含共享的资源,然后在en-GBen-US卫星程序集中包含需要针对GBUS的资源。

4)   CLR检查当前程序目录下有没有包含一个当前请求文化的父目录,如果存在,CLR在父目录中搜索有效的卫星程序集。例如:请求en-US文化的卫星程序集的时候,如果未找到,那么CLR将会在en文件夹中查找有效的资源程序集。

5)   如果通过NeutralReourcesLanguageAttribute属性指定了默认资源,则当以上查找失败后,最终会使用该属性指定的资源。如果没有用该属性指定默认资源,那么默认资源就是随主程序集一起编译的资源。强烈建议使用在主程序集中包含默认资源,这会让你未能找到指定资源后仍然有一个默认资源可用,而不是抛出异常。

6)   如果应用程序最终没有找到资源,会抛出资源没有找到的异常。

举例说明,假如你要请求一个本地化为墨西哥西班牙语的资源,根据以上描述的命名规则,CLR将会在GAC中查找匹配指定文化的资源,这里是es-MX;如果没有找到,CLR在当前可执行程序集目录中查找ex-MX目录;如果没有找到,CLR将会在GAC中查找父程序集,这里是“es”;如果还是没有找到,CLR将会在当前程序集目录下查找es目录,并在其中查找有效的资源;如果还是没有找到,就会使用默认资源。

5.    创建卫星程序集

可以使用al.exeAssembly Linker)将.resources文件编译为资源程序集。根据定义,卫星程序集只能包含资源,不能包含可执行代码。下面的命令行从strings.de.resources文件为myapp程序创建一个卫星程序集。

al /t:lib /embed:strings.de.resources /culture:de /out:MyApp.resources.dll

下面是al.exe的选项使用方法:

选项

描述

/t:lib

/t选项指定了你的卫星程序集的格式为.dll文件。由于卫星程序集不可能是可执行程序集,所以,该选项是必须的。

/embed:strings.de.resources

/embed选项指定了al的输入文件,al使用此输入文件创建卫星程序集,输入文件名中必须包含文化名称,如“de”,而且此名称必须与/Culture选项指定的文化相同。你可以指定多个输入文件。

/culture:de

/culture选项指定了资源的文化,CLR会利用这个信息来查找指定文化的资源。如果你忽略了这个选项,al.exe仍然会生成卫星程序集,不过CLR将会无法找到该资源。每个程序集都必须指定文化,如果未指定,则默认为neutral。该属性值可以从AssemblyName类中得到。

/out:MyApp.resources.dll

/out选项指定了输出文件的名称。该名称必须遵守命名标准:baseName.resources.dllbaseName是主程序集名称,注意CLR无法根据输入文件名确定输出的卫星程序集的文化,所以必须使用/culture选项来指定输出程序集的文化。

/template:filename

/template选项指定了一个可以从之继承元数据的程序集,文化信息除外。该程序集必须有一个强名称。

 

6.    获取卫星程序集中的资源

可以使用ResourceManager类来获取资源,获取资源时使用的文化是当前线程的文化,可以使用CultureInfo.CurrentUICulture属性来获取。下面的示例演示了如何从资源中获取一个字符串。

ResourceManager rm = new ResourceManager("MyStrings", GetType().Assembly); Console.Writeline(rm.GetString("string1"));

           下面的示例演示了如何获取一个二机制资源。

ResourceManager rm = new ResourceManager("MyImages", GetType().Assembly);

PictureBox.Image = (System.Drawing.Image)rm.GetObject("MyObject");

7.    一个具体而微的DEMO

1)   下面是用一个Console程序来演示如何创建,编译,获取资源来实现本地化。

a.    创建HelloWorldLocalization Console项目。

b.    添加一个Resource File到根目录下,并将其命名为strings.resx

c.     在资源文件编辑器中添加一项字符串资源:名称为Greeting,值为Hello World

d.    Main方法中获取该字符串资源的代码如下:

namespace HelloWorldLocalization

{

 class Program

 {

   static void Main(string[] args)

   {

     ResourceManager rm = new ResourceManager("HelloWorldLocalization.strings",typeof(Program).Assembly);

     string greeting = rm.GetString("Greeting");

     Console.WriteLine(greeting);

     Console.ReadLine();

   }

 }

}

e.    主要到ResourceManager的构造函数的第一个参数为baseName,使用了HelloWorldLocalization.strings应该是由于VisualStudio编译工具自动将命名空间加到了资源名称之前(还没有发现如何去除这一功能)

f.      下面将该字符串本地化为汉语表示的文化zh-CN

g.    拷贝strings.resx文件到一个单独的目录(可随意选取,主要是为了避免将本地化过程产生的文件和源代码目录混在一起),我这里是D:/Resource。用VisualStudio打开此文件,看到的结果应该与刚才一样,不过该文件并不属于HelloWorldLocalization项目的一部分,这里,我们只是将VisualStudio当作了一个资源编辑工具而已。

h.    将“Hello World”改为“你好,世界”

i.       打开VisualStudio命令行工具并键入D:/resource>resgen strings.resx HelloWorldLocalization.strings.zh-CN.resources,执行该命令后我们获得了HelloWorldLocalization.strings.zh-CN.resources文件,该文件将我们之前的资源文件转换为二进制资源,方便下一步使用。

j.      继续在命令行中键入D:/resource>al /t:lib /embed:HelloWorldLocalization.strings.zh-CN.resources /culture:zh-CN /out:HelloWorldLocalization.resources.dll,执行命令后我们获取了HelloWorldLocalization.resources.dll文件。之后就可以将该文件打包在语言包中分发了。

k.    HelloWorldLocalization项目的Debug目录下创建zh-CN文件夹,并将HelloWorldLocalization.resources.dll拷贝其下。

l.       设置当前线程文化为zh-CN后,运行程序,可以看到输出为“你好,世界”。(非中文操作系统下的控制台可能无法显示中文)

Thread.CurrentThread.CurrentUICulture =CultureInfo.CreateSpecificCulture("zh-CN");

ResourceManager rm =new ResourceManager("HelloWorldLocalization.strings",typeof(Program).Assembly);

string greeting = rm.GetString("Greeting");

Console.WriteLine(greeting);

Console.ReadLine();

 

2)   使用非特定区域的文化

a.    对于英文语言来说,如果没有足够的时间为每个区域做本地化(比如澳大利亚英语,美国英语,加拿大应用)。可以让所有的英语文化共享一个资源程序集,具体做法如下。

b.    修改名称为Greeting的值域为“Hello English world”。

c.     使用如下命令后生成.resources文件,D:/resource>resgen strings.resx HelloWorldLocalization.strings.en.resources

d.    使用如下命令后生成卫星程序集,D:/resource>al /t:lib /embed:HelloWorldLocalization.strings.en.resources /culture:en /out:HelloWorldLocalization.resources.dll

e.    HelloWorldLocalization项目的Debug目录下创建en文件夹,并将HelloWorldLocalization.resources.dll拷贝其下。

f.      运行代码查看获取的greeting变量的值,发现在当前的加拿大英语文化下,成功的获取了en文化下的值。你可以将文化指定为en-GB或者其他英语文化,结果应相同。

Thread.CurrentThread.CurrentUICulture =CultureInfo.CreateSpecificCulture("en-CA");

ResourceManager rm =new ResourceManager("HelloWorldLocalization.strings",typeof(Program).Assembly);

string greeting = rm.GetString("Greeting");

Console.WriteLine(greeting);

Console.ReadLine();

 

关于资源的一点备注:

如果直接将项目中的一个文件编译为资源,在文件的属性页中选择Build Action=Resource,编译器会将该资源添加到AssemblyName.g.resources文件,并把该文件作为嵌入的资源编译到程序集中。需要做本地化时也需要使用此文件的基名称命名,例如本地化为en-us的全局资源名称应为AssemblyName.g.en-US.resources

尽量不要使用BuildAction=EmbededResource而使用Resource,将会提高程序的可维护性。

 关于WPF程序:

微软对WPF程序的本地化提供了一套流程及工具。不过在我看来,这套流程和工具就是垃圾,只会让本地化工作变得复杂和难以管理。

例如XAML的本地化,默认情况下,MSBUILD编译XAML的时候,将其作为资源嵌入到主程序集中,为了将XAML编译到资源程序集中,需要在csproj文件中加入如下标记:

<UICulture>en-US</UICulture>

原创粉丝点击