Kevin Yang的博客

It's all about sharing.
莫名其妙的Silverlight资源文件引用问题

问题描述

最近项目中遇到一个和资源文件相关的很奇怪的问题。有一个Silverlight应用会根据当前Url中的某个特定参数来决定使用什么文化的资源文件(关于资源的文件的使用见Silverlight 2 RTM 多国语言支持)。在其他机器上运行没有问题,传入zh-Hans和en-US界面都能正确显示相应语言的文字。但是在我的系统上却不管传入什么都是显示的英文文字。

问题分析

打开工程看了一下,在资源文件夹下只定义了两个资源,一个是主资源XXX.resx,一个是英文中立资源XXX.en.resx。编译之后,主资源会被打包到主程序集中,而其他资源则被编译成独立的程序集,称为卫星程序集。Silverlight代码中在提取Url参数的时候也加上了一个检测逻辑,就是如果传入的语言不是英文,那么就使用zh-Hans。也就是说在其他人的机器上,如果你输入了fr,那么也是显示中文。分析加调试弄了好一会之后,我判定应该不是程序代码的bug。因为在某些人的机器上是正常的,而在我的机器还有另外一个同事的机器上却不正常,而且问题是可重现的。这说明了问题和本地环境有关。

清缓存,清Silverlight独立存储之后发现问题依然存在。难道是操作系统的问题?!我用的是win7,同事用的是win2008,都是高版本的windows,而其他人用的不是xp就是2003。为了证实我的想法,我又找了一台2008的机器,测试结果和我机器上的一样。看来很有可能问题就在这。可是,为什么呢?

为了查找问题的原因,我重新查阅了一下Silverlight使用资源文件的相关材料(和DotNet其实差不多)。

Silverlight的资源查找方式

Silverlight采用中心辐射型结构来打包和部署资源文件程序集。围绕在中心的是主程序集里面的资源,这个资源也被称为中立或者默认资源。如果主程序集没有显式设定语言,那么一个资源要是查找失败,最终会使用主程序集中的资源。每个辐射点指向的是每一种语言对应的卫星程序集。

Silverlight在查找资源文件的时候采用了如下的方式:

image

最顶级是应用程序的默认程序集。第二级是语言相关程序集,它代表的是地域无关的语言文化,如英文,简体中文,繁体中文(zh-Hant)等;第三级是和地域相关的文化程序集,如美国英语,英国英语,大陆简体中文,新加坡简体中文。Silverlight在加载本地资源的时候首先会根据CultureInfo.CurrentUICulture属性来判断应该加载哪一种文化对应的资源。在查找的时候是按照上面的层次结构从下到上进行查找的。具体说来,当ResourceManager在查找一个资源时会经过以下几个步骤:

1. 查找具体语言相应的程序集是否存在。如设置了zh-CN,那么就查找XXX.zh-CN.resx资源文件是否存在(存在于卫星程序集中),如果程序集不存在或者在该程序集中找不到此资源的定义,那么查找失败。如果操作系统支持回溯(Fallback)逻辑,那么跳到第二步;否则跳到第三步。

2. 操作系统提供一份推荐的语言回溯清单,上面包含了具体语言文化字符串(如en-US)和相应的中立语言字符串(如en)。Resource Manager会根据这份清单去查找相应的卫星程序集和程序集内指定资源是否存在。如果查找失败,则跳到下一步。

3. 如果上面两步都失败,那么查找此具体文化的上一级文化的卫星程序集,也就是地域无关的文化,例如en-US查找失败,则查找en是否存在。

4. 如果上面3步都失败的话,那么Silverlight会查看主程序集的NeutralResourcesLanguageAttribute属性。NeutralResourcesLanguageAttribute是用在程序集上的属性,它是用来告知ResourceManager,某个程序集使用的语言是什么。ResourceManager会查看此属性的值,看是否满足条件。如果主程序集没有指定此属性,那么将会使用主程序集中的资源作为默认资源。

5. 如果第4步也查找失败,那么ResourceManager将使用文化无关(Invarient Culture)的资源。

问题所在

在网上查到MSDN的文章说,自打Vista开始,操作系统开始支持这种“Fallback”的逻辑了。通过系统API GetThreadPreferredUILanguages可以获取操作系统核心推荐使用的语言清单。

由于工程里面并没有指定zh-Hans的资源文件(于是也就没有zh-Hans的卫星程序集),也没有设置主程序集使用的语言是什么(通过NeutralResourcesLanguageAttribute属性指定)因此在第一步的时候查找失败;这个时候,在我win7的机器上,由于操作系统支持fallback逻辑,因此Silverlight会使用操作系统提供的语言清单去查找,由于我的系统是中文系统,因此清单上第一项肯定是zh-CN,接下来的猜测应该是en-US和en(老美搞的操作系统嘛,当然要把自家语言排前一点了)。查找zh-CN肯定又失败,但是查找en却能够找到,因此就出现了不管你参数中输入什么语言文化字符串,都会显示英文。

而在其他低版本的windows上,因为操作系统不支持这份清单,那么Silverlight始终没有办法找到zh-Hans相关的卫星程序集,而主程序集又没有显式的设置语言,因此ResourceManager就会使用主程序集里面的资源文件,也就是中文。

这就是为什么在低版本windows上,乱输入语言,显示的默认是中文,而在Vista等高版本windows上,默认显示的是英文的缘故。

再插一句,如果在主程序集中显式设置了NeutralResourcesLanguageAttribute为zh-Hans,然后在低版本的windows上访问此应用程序的时候输入例如fr等不支持的语言,那么Silverlight将会使用语言无关的资源(语言无关的资源具体被定义在哪不是很确定,可能是dotnet类库,也可能是本地操作系统),如果该语言无关的资源没有定义,则ResourceManager得到的是空字符串。因为此时ResourceManager就不会把主程序集里的资源当作最终Fallback的资源了。

解决问题的办法

经过上面的分析,解决问题的办法已经很清楚了,要么再添加一个zh-Hans的资源文件,要么显式的使用NeutralResourcesLanguageAttribute通知ResourceManager,主程序集使用的资源语言是zh-Hans。

——Kevin Yang

posted on 2009-07-14 01:24  Kevin Yang  阅读(1229)  评论(0编辑  收藏  举报