.NET系统学习----Globalization & Resources

.NET系统学习----Globalization & Resources

l 前言

l 了解资源文件

l 创建资源文件

l 在程序中使用资源文件

l 资源文件的命名和部署

l 参考

前言:

在学习如何使用.NET资源文件以及如何开发World-Ready程序之前,我们先通过一个例子来看看为什么要使用资源文件,以及使用它的好处。

假设要在程序中根据当前的Culutre来设置Form的Title和Logo:

private void Form1_Load(object sender, System.EventArgs e) {

CultureInfo ci = new CultureInfo(Thread.CurrentThread.CurrentUICulture.ToString());

switch (ci.ToString().ToLower()) {

case "zh-cn": // 中文版本

this.Text=FormTitle_ZH_CN;

imgLogo.Image = new Bitmap(Application.StartupPath + "/Logo_ZH_CN.jpg");

break;

case "en-us": // 英文版本

this.Text=FormTitle_EN_US;

imgLogo.Image = new Bitmap(Application.StartupPath + "/Logo_EN_US.jpg");

break;

default: // 默认版本

this.Text=FormTitle_Neutral;

imgLogo.Image = new Bitmap(Application.StartupPath + "/Logo_Neutral.jpg");

break;

}

}

这段代码有两个问题:

首先,Logo文件是暴露给用户的,而且是以普通文件的格式存储的,这导致其他程序或是用户很容易修改这些文件;节省硬盘空间的用户还可能会选择删除它,这些都可能会导致应用程序出错。确保图片或任何其他文件和代码在一起的唯一的安全方式是将它作为资源文件嵌入在程序集中并加载。

其次,这是一个World-Ready程序,如果需要新加入一个新的Culture,你可能不得不更改你的源代码,加入新的case,然后重新编译来适应新的Culture的需要,这对一个World-Ready程序来说是不现实的。开发World-Ready程序很重要的一点就是要保证程序的逻辑界面和资源界面的隔离。任何时候加入一个新的Culture资源,我们都不应该重新编译源程序,相反,我们只需要把新的资源文件准备好,然后发布给用户并部署在合适的目录下就可以了。应用程序应该能够根据不同的Culture来自动寻找合适的资源。

本文的目的就是通过实例来帮助读者了解什么是Resources,以及如何使用Resources来消除上面所提到的两个问题。

全文分为四部分:

第一部分是一些和资源相关的概念。

第二部分是一个实例程序(ResourceGenerator),用来说明如何创建资源文件。

第三部分是另外一个实例程序(WorldAPP),用来说明如何在程序中使用资源文件

第四部分是关于资源文件的命名和部署。分别介绍.NET中资源文件的命名方式和如何在World-Ready程序中配置资源文件。

第一部分 概念

先来了解一些概念:

1. 什么是资源文件

顾名思义,资源文件当然包含的全是资源。不过,什么是资源?这里所谓的资源就是程序中可利用的任何数据,譬如:字符串、图片或任何二进制格式的数据。一个资源文件可以有多种语言文化版本,比如,一个Culture.resources 文件可以有英语版、简体中文版日文版等。ResourceManager可以自动根据Culture和资源文件名来确认调用哪个版本。只不过不同的资源版本需要在文件名中加入语言文化信息(.resource文件有一套严格的命名规范,参考第四部分:资源文件的命名和部署)。

2. 资源文件的类型

System.Resources名称空间支持三种类型的资源:

.txt文件,只能有字符串资源。因为不能被嵌入到Assembly中,所以很容易暴露,被其他程序或用户修改。最大缺点是仅支持字符串资源,不推荐使用。

.resx文件,由XML组成,可以加入任何资源,包括二进制格式的。同样不能被嵌入到Assembly中。在System.Resources 名称空间中有专用读写的类。VS.NET中创建的这种文件也是将其转成.resources 文件然后根据设置将其嵌入到Assembly中。

.resources文件,PE格式,可以加入任何资源。是唯一可以被嵌入到Assembly的文件,在System.Resources名称空间中有专用读写的类(ResourceManager)。

3. 调用资源文件的几种方法

ResourceManager可以根据不同的UICulture设置返回不同的本地资源,不同Culture的资源文件有一套严格的命名规则,只有按照这个规则命名,CRL才可以根据Culture找到这个本地资源。PS:因为这个很重要,所以才一再出现J。参考第四部分:资源文件的命名和部署)

.txt 文件:

不可以直接调用,得先将其转换成 .resources 文件才能使用。

.resx 文件:

可以用ResXResourceReader来读取,但是这种方法不直观也不安全,不推荐直接调用.resx文件。正确的方法是将其转换成.resources文件,然后用ResourceManager读取。注意,如果是在VS.NET中添加的.resx文件,那么它们自动被设为 Embedded Resource,然后被转成.resources文件后嵌入到Assembly中。

.resources 文件:

分成两种情况:

· 被嵌入或编译成卫星程序集(Satellite Assembly):

用ResourceManager的各种constructor来获得在Assembly中的资源。

· 单独文件,没有被编译或嵌入到Assembly中:

可以用ResourceManager.CreateFileBasedResourceManager来获得资源集(ResourceSet),就是所有的资源。

特殊情况:

还有一种特殊情况,那就是当你直接嵌入一资源时,也就是说,不通过一个资源文件(.resources)而直接将一资源(Object)嵌入到 Assembly 中。这可以通过AL.exe(Assembly Linker)的参数/embed:<object>把资源嵌入在Assembly中。在这种情况下ResourceManager就没有用了,因为它只能获取.resources资源文件(在或不在Assembly中)。

调用这类直接嵌入在Assembly中的资源,我们就需要利用Reflection的一些特性来完成。在System.Reflection.Assembly类中有一些相关函数可以帮助我们拿到这些资源。通过Assembly.GetManifestResourceNames可以拿到所有的资源的名字,然后我们就可以通过Assembly.GetManifestResourceStream(<object_name>)这个函数拿到对应的资源并以stream的方式返回,然后我们可以将这个stream转成在.NET中可用的对象。比如,如果嵌入资源是一图片,那么我们可以利用New Bitmap(Stream)的constructor获得这个图片资源的Bitmap对象。

第二部分 创建资源文件

创建资源文件有两种方式,一种是使用.NET SDK自带的resgen工具来创建,另外一种是自己写code来创建。分别来介绍:

1. Resgen:

这个工具是.NET自带的,它可以把.txt,.resX,转换为.resources文件。.resources文件是以一种以键-值方式对应存储的XML格式文件,每一个键<data>对应一个值<value>,这个<value>可以是任何的二进制格式。如果是格式为(键=值)对应得.txt文件,resgen会自动生成键-值对应的XML文件。但是resgen有一个局限性,它不能直接嵌入其他格式的文件,比如你就不能把.bmp以键-值得方式对应起来,因为你首先不能很容易得把.bmp以(键=值)对应的格式储存在.txt文件中。所以resgen主要是针对txt文件使用。

一个例子:company1.txt文件内容为:

Title = Company1

Address = Company1 Address

Phone = 12345678

----------------------------------------------------------------

Resgen company.txt <outputfilename>.resources

如果不指定<outputfilename>,默认会生成company1.resources。

然后就可以通过ResourceManager来使用了。

还可以再进一步,通过AL.exe把resources文件变为一个assembly(使用assembly有很多好处(比如可以加入版本信息和Culture信息等)详见(.NET系统学习----Assembly)。

Al /out:company1.dll /embed:company1.resources

通过设置ResourcesManager的不同的constructor就可以访问Assembly中包含的.resources文件(下面的例子会讲到)。

2. 通过编程使用IResourcsWrite来生成资源文件

上面的方法的一个最大的缺点是不能很方便的嵌入其他格式的资源,因为把其他格式的资源变为键-值对应得txt文件并不是一件很容易的事。所以我们介绍另一种方法,通过编程,使用.NET提供的IResourcesWrite类来实现把任何资源嵌入到resources文件中。

ResourceGenerator就是用这种方式实现的。

程序的主界面:

用到的主要方法就是:

private void OnGenerateResource(object sender, System.EventArgs e)

{

IResourceWriter rw = new ResourceWriter(“C:\test.resources”);

switch (sType)

{

case "system.string":

rw.AddResource(sKey,sValue);

break;

case "system.drawing.bitmap":

Bitmap bmp = new Bitmap(sValue);

rw.AddResource(sKey,bmp);

break;

case "system.drawing.image":

Image img= new Bitmap(sValue);

rw.AddResource(sKey,img);

break;

}

}

根据资源的类型,如果不是string类型的,我们就把它分实例化为相应的stream,然后加入到resoruces中即可(string类型可以直接加入)。生成的就是.NET可以直接使用的.resources文件。但是这样生成的资源CLR并不能根据不同的Culture自动识别。要想CRL自动识别并加载正确的资源文件,首先必须把.resources转换为Assembly,并根据严格的命名方式命名(参考第四部分:资源文件的命名和部署),并部署到正确的目录下,然后CLR就可以根据不同的Culture来加载正确的资源。

第三部分 在程序中使用资源文件

WorldApp.cs是一个World-Ready的程序,它的逻辑界面和资源界面是分开的,可以实现逻辑界面只Bulid一次,运行时根据当前的Culture调用相应的Satellite Assembly(卫星资源程序集)来实现本地化。添加一个新的Culture资源不需要重新Build源程序,只需要把相应的资源程序集部署到合适的目录就可以了。

下面说明WorldApp的实现方式:

程序主界面:

程序在启动的时候会根据当前的CurrentUICulutre去加载相应的资源文件。

读取资源文件的代码为:

private void SetCulture( CultureInfo ci )

{

// Change current UI culture

Thread.CurrentThread.CurrentUICulture = ci;

// Load culture resources.

String AssemblyPath = Application.StartupPath + "\\Culture.dll";

Assembly asm = Assembly.LoadFrom(AssemblyPath);

// ResoruceManager constructor will load different resources acording to the

// CurrentUICulture. which means, if CurrentUICulutre is "en-US", rm will load

// "Culture.en-US.resources" automaticly.

// When loaded, give the resource name only.

ResourceManager rm = new ResourceManager("Culture", asm);

// Set title, culture info and logo.

this.lblTitle.Text = rm.GetString("Title");

this.lblCulture.Text = rm.GetString("Culture");

this.lblLogo.Text = rm.GetString("LogoTitle");

this.imgLogo.Image=(Bitmap)rm.GetObject("Logo");

}

如果当前的UICulture改变,可以通过显式调用SetCulture( CultureInfo ci )来加载相应的Culture资源。

现在如果我们有了一个新的Culture资源版本,我们只需要把它部署在对应的Culture目录下,WorldApp.exe就可以自动加载,WorldApp.exe程序本身并不用做任何更改(不需要编译)。

你可以通过上面制作的小工具ResoruceGenerator来生成对应不同Culture的资源,然后把生成的Assembly正确部署就可以了。WorldApp就又有了一个新的Culture版本。哈!!

第四部分 资源文件的命名和部署

这部分说明资源文件的部署方式和CLR是如何识别并加载不同的Culture资源的。

· 资源文件的命名方式

假设我们的应用程序名为WorldApp.exe,默认的资源文件为culture.resources,根据这个资源文件生成的Assembly为culutre.dll(这个是默认版本的资源文件)。然后我们有了一个en-US Culture版本的资源文件,则en-US的资源文件得名称必须为culture.en-US.resources,根据这个资源文件生成的en-US版本的Assembly必须命名为culture.resources.dll且必须加入Culture信息(把一个.resources生成一个Assembly:resgen /out:cluture.resources.dll /c:en-US /embedcluture.en-US.resources),生成的Assembly必须放在程序运行目录下的en-US目录下,这样CLR才能自动找到。同样,如果我们有了一个zh-CN版本的资源文件,则资源文件的名称必须为culture.zh-CN.resources,生成的Assembly必须为culture.resources.dll,并放在zh-CN目录下。

重要:因为生成的.resources文件本身并不包含Culture信息,它的Culture信息就体现在它的文件名上,所以.resources的命名必须加入Cluture信息(如果不加的话,生成的就是默认版本)。从.resources生成Assembly时,因为Assembly可以指定Culture信息(通过/c:<culture>来指定),所以Assembly的名称中不需要加入Culture信息,但是Assembly的名字必须是:默认版本名+<.resources>.dll,就是:culture.[resources].dll。

· 资源文件的部署方式

应用程序正确的部署方式(目录结构)应该是:

<WorldApp> (应用程序主目录)

WorldApp.exe (主程序)

Culture.dll (包含culture.resources资源文件)

<en-US> (en-US资源目录)

Culture.resources.dll (包含culture.en-US.resources资源文件)

<zh-CN> (zh-CN资源目录)

Culture.resources.dll (包含culture.zh-CN.resources资源文件)

<new-Culture> (net-Culture资源目录)

Culture.resources.dll (包含cluture.new-Culture.resources)

<…>

有了上面的部署,App.exe在运行时,会首先根当前Thread的CurrentUICuluture到对应的目录去寻找资源文件,比如当前的CurrentUICulture=”en-US”,则en-US目录下的Culture.resources.dll Assembly中的culture.en-US.resources会被加载。如果CLR遍历整个目录还没有找到对应的资源文件,则默认的资源文件版本就被加载(MSDN中称为Hub and Spoke model方式 详见:ms-help://MS.MSDNQTR.2004APR.1033/cpguide/html/cpconPackagingDeployingResources.htm)。

· CLR如何加载资源文件

重要:CLR在匹配资源文件的时候,不是按文件来匹配的,它是按照<data>字段一个key一个key的去匹配。举个例子:

默认版本的Culture资源文件中包含四个key:Title, Culture, LogoTitle,Logo。

中文版本的Culture资源文件中包含只有三个Key:Title, Culture, Logo。(没有LogoTitle)

如果当前的Culture是”zh-CN”,则zh-CN版本的Title, Culture, Logo都会被加载,但是因为zh-CN版本没有LogoTitle,所以CLR会自动加载和zh-CN文化最匹配的一个资源版本的LogoTitle。如果都没有,最后才会去加载默认版本的资源文件。

这样做有一个很大的好处:就是说并不是所有资源都必须要有对应Culture的版本,我们可以把共通的资源放在默认版本中,只把和特定Culture相关的资源隔离就可以了。

重要:关于Culture:

Culture信息是由主标记(文化)和次标记(地域)两部分组成的。举个例子:

en-US (英语-美国)

en-GB (英语-英国)

en-AU (英语-澳大利亚)

主标记是en,表示Culture都是英语文化,次标记(地域)区分了它们分别是哪个地区的英语。

说这个有什么用呢?

因为CLR在寻找资源的时候是以一种回退的方式来寻找的,就是说,他会首先去寻找最批的那个资源文件,如果没有,则会搜索文化层次结构,以查找最接近于请求的匹配资源文件,并把生成异常作为最后一种手段。比如CLR在寻找en-US资源的时候没有找到,CLR不会立即就去用默认版本匹配,而是会首先搜索文化层次结构,以查找最接近于en-US的资源(可能是en-GB或别的),如果找到,运行时就使用这个资源,如果还找不到,则会继续搜索下一层,最后才会用默认版本匹配(如果默认版本也没有,则会抛出一个异常)。

参考资料:

l Applied Microsoft .NET Framework Programming ---- Jeffrey Richter

l MSND Library

时间: 2024-09-18 09:35:14

.NET系统学习----Globalization &amp; Resources的相关文章

java系统学习指导与规划

很多同学刚刚学习java,不清楚java应该如何系统学习,今天我就给各位同学做一个java系统学习指导和规划,为大家讲讲如何系统的学习java. 第一:学java首先你要学 J2SE,它是java体系的基础,也是重中之重.很多人往往不重视基础,其实这是舍本逐末的做法.说这么多就是希望大家能重视基础,能在这条路上走的更远. 学j2se有下面几个目标: 1.你要能真正理解面向对象的优势,理解为什么不是面向过程. 2.掌握java语法基础.包括异常处理.多线程.网络编程.GUI编程等 3.如果你对sw

想系统学习android,有没有好的视频或书籍?

问题描述 有jee开发经验,android学过基础的一些东西,现在想系统学习下android,有没有高手能指点下? 解决方案 最好的学习方法是,多用几款自己觉得比较有意思,但又比较简单的应用,然后自己去照着做一个出来. 做的时候把功能点一个一个列出来,然后就知道需要哪些方面的技术.根据功能点的优先级,一步一步的来.基本上做一两个应用就熟悉了,剩下的主要就是优化和提高代码质量. 书籍只是一个工具,用来入门可以,这样你可以很快知道开发android应用会用到哪些基础知识和技术.不是很推荐刚开始就去去

想系统学习计算机的法学专业学生

这学期新接了一个法学专业的"计算机科学"课程.计算思维逐渐成为了各专业公共的要求,我也在其中做些尝试.有名学生显然不满足于"大部队"的节奏了.好吧,贺老师就喜欢看到这样. [来信] 我是您法学院的学生,大一这上半年跟着您上计算此机受益匪浅我也对计算机产生了浓厚的兴趣.鉴于此我想系统的学习一下计算机,希望老师能够给予指导.除此之外呢,我还有几个问题想咨询您一下希望老师能够给予解答. 1.老师我想咨询您关于您带计算机专业学生在大学这四年所要学习的教材,这样的话我可以在空

破除“系统学习”的情结

       常有同学想在自己课外学习的安排中,"系统地"学习点什么.这样的事情常发生在喜欢做A专业的事,却上了B专业,且A不等于B,甚至相似度(A, B)趋于无穷.对有些同学,一不留神发现自己的学习落后了不少,比如学计算机的,到大三了,却发现连简单程序也编不出来,再如学完高数了,才知道高数原来如此重要,于是想要系统地重学一遍. 何为系统学习?系统学习就是要按照已有的一套规定,先学什么,再学什么,按步就班地走完一个流程.为什么要系统学习?将做事能专业些.规定从何而来?业界专家.系统学习

我国近半科技人员未系统学习科研道德知识

中新社北京 七月十日电 (记者 孙自法)孤道寡中国科学技术协会十日在北京发布其最新完成的第二次全国科技工作者状况调查报告显示,近半数科技工作者没有系统学习过科研道德和学术规范相关知识,自认对科研道德和学术规范知识"了解很少"和"基本不了解"者达百分之三十八点六,女性.年轻人.低学历者和低职称者对学术规范了解情况相对较差. 是次调查发现,分别有百分之四十三点四.百分之四十五点二.百分之四十二的科技工作者认为,中国当前"抄袭剽窃"."弄虚作

深度对话:系统学习还是野路子?

< 破除"系统学习"的情结 >一文发布已经有几年.对这篇文章映像很深,因为在写的过程中,自己也想通了不少现实的问题. 有网友做了一个比较深入的评论,我利用回复的机会,将其中的要点进一步阐述.以本文记录这则深入的对话. [评论] 今天看到这篇文章,确实感慨很多.我自己的思考是,对于计算机科班生,以后想从事这一行的话,扎实打好理论基础很重要,一些专业核心课不能忽略,这些课除了完成学校的内容以外,至少阅读一本国外经典.这样严格来说其实在该领域任然不深,但没关系,以后再遇到这个领域

mysql dba系统学习(22)数据库事务详解

上个星期去面试数据库管理员的工作,笔试通过之后就是直接的面试,他问了我一个问题,叫我介绍哈数据库的事务的看法和理解,但是不知所错的没有章法的乱答一气,唉唉,基础不扎实啊. 下面来好好的学习哈mysqlinnodb引擎的事务相关的知识 一,我们为什么需要数据库事务呢? 这个问题看似很简单,但是真的能够说明白还是要看看我们的基本功是不是扎实 例如,用户A给用户B通过ATM机转账1000元,那么A账户上就会少1000元,而B用户会多1000元 在这个过程中,两个环节是关联的.第一个账户划出款项必须保证

WPF学习之资源-Resources

WPF通过资源来保存一些可以被重复利用的样式,对象定义以及一些传统的资源如二进制数据,图片等等,而在其支持上也更能体现出这些资源定义的优越性.比如通过ResourceDictionary的支持就可以通过资源来实现换肤功能,在ExpressionBlend中设计的酷炫造型也可以通过导出成资源来很容易的被程序员所引用,本地化的实现,访问另外程序集的嵌入式资源等等.这些都给我们提供了丰富的手段通过资源访问架构来构建丰富的富媒体应用程序.本文简单讲解了WPF Resources的分类及其常见用法,并简单

牛腩新闻发布系统--学习Web的小技巧汇总

        2014年11月10日,是个难忘的日子,这一天,小编的BS学习开始了,BS的开头,从牛腩新闻发布系统开始,之前学习的内容都是CS方面的知识,软考过后,开始学习BS,接触BS有几天的时间了,跟着牛腩老师敲着牛腩新闻发布系统,感觉棒棒哒,看了前面二十集,发现牛腩老师真的是把党的思想贯彻的淋漓尽致,跟着牛腩老师一起学习,发现了很多让我怦然心动的小知识,每次都让我的小宇宙爆发,现在就把这些小技巧分享给小编的小伙伴.该博文小编主要讲解三个方面的知识.第一:如何让代码整齐美观,第二:代码的注