ASP.NET 2.0的全球化与本地化之全球化2010-12-08 天极 朱先忠一、 加入全球化信息在我的网站中,在创建资源文件并加入一些本地化数据后,我首先开始使用 显式本地化来设置控件(例如,在我的网站中的标签)的文本,以便它们可以从 资源文件中得到它们的值。既然存在四种语言;所以,除一个完全可依赖的资源 文件之外(没有本地化命名),我创建了四个资源文件。

注意,这些资源文件都以本地化标记作为它们的中间名称,因此,我需要把 UICulture设置为与该本地化相同的名字以便ASP.NET存取这些资源文件。但是,问题是:我该怎样在PostBack事件中动态地改变文化呢?幸好, ASP.NET在Page类中提供了一种可重载的方法: InitializeCulture()。这个方 法在页面生命周期(在生成任何控件之前)中执行得很早,并且在此,我们能够设 置当前线程的UICulture和Culture。由于这个方法位于Page类中,并且我不想针对每一个web页面都重复相同的代 码,所以我创建了一个BasePage类,我的应用程序中的所有的aspx页面都派生自 这个BasePage类。但是现在,我又面临另一个问题。下面,让我进行解释:回到UI设计:我使用了一个MasterPage和一个Header用户控件(在一个 ContentPlaceHolder内)。我把一个缺省的页面与该MasterPage相关联。整个站 点必须动态地实现本地化。因此,在顶部,有一个下拉框,用户可以从中选择一 种语言/文化。在BasePage的InitilializeCulture方法中,我必须取得用户从下 拉框选择的项的值;但是,因为它还没有被初始化,所以,我还不能存取任何控 件的值。答案是:使用表单集合(从响应对象内)。下面是实现代码:
///<SUMMARY>
///从通用的页面头部的下拉框列表中选择的语言名。
///我们需要使用这个名字,因为我们还没有任何其它控件属性-现在控件本 身还没有被初始化。
///因此,我们使用"嵌套的"下拉框列表名,从中我们可以从Request.Form[] 集合中得到该下拉框列表的值。
/// </SUMMARY>
public const string LanguageDropDownID = "ctl00$cphHeader$Header1 $ddlLanguage";
/// <SUMMARY>
///在一个回寄表单中的PostBack事件目标域的名字。你可以使用
///它来确定是哪个控件触发了PostBack:
/// Request.Form[PostBackEventTarget] .
/// </SUMMARY>
public const string PostBackEventTarget = "__EVENTTARGET";请注意,在此,我是如何使用"parentControl:ChildControl"方法从表单集 合中存取控件的。通过使用这一约定,你可以存取任何ASP.NET生成的嵌套控件 。借助于表单集合中选择的值,我可以通过一个switch case语句来进行文化设 置:
/// <SUMMARY>
///重载InitializeCulture方法来设置在当前线程中用户选择的选项
///。注意,这个方法在Page生命周期的早期调用
///,并且目前我们不存在任何控件
///,因此必须使用Form集合.
/// </SUMMARY>
protected override void InitializeCulture()
{
///<remarks><REMARKS>
///检查是否PostBack发生.不能使用在此方法中使用IsPostBack
///,因为这个属性还没有设置。
///</remarks>
if (Request[PostBackEventTarget] != null) {
string controlID = Request[PostBackEventTarget];
if (controlID.Equals(LanguageDropDownID)) {
string selectedValue = Request.Form[Request [PostBackEventTarget]].ToString();
switch (selectedValue)
{
case "0": SetCulture("hi-IN", "hi-IN");
break;
case "1": SetCulture("en-US", "en-US");
break;
case "2": SetCulture("en-GB", "en-GB");
break;
case "3": SetCulture("fr-FR", "fr-FR");
break;
default: break;
}
}
}
///<remarks>
///从会话中取得文件,如果控制给导航到同一程序中的一个新页面。
///</remarks>
if (Session["MyUICulture"] != null && Session ["MyCulture"] != null)
{
Thread.CurrentThread.CurrentUICulture = (CultureInfo)Session ["MyUICulture"];
Thread.CurrentThread.CurrentCulture = (CultureInfo)Session ["MyCulture"];
}
base.InitializeCulture();
}
/// <Summary>
///使用参数设置当前的UICulture和CurrentCulture
/// </Summary>
/// <PARAM name="name"></PARAM>
/// <PARAM name="locale"></PARAM>
protected void SetCulture(string name, string locale) {
Thread.CurrentThread.CurrentUICulture = new CultureInfo (name);
Thread.CurrentThread.CurrentCulture = new CultureInfo (locale);
///<remarks>
///由用户把当前线程的文化集保存在会话中
///,以便它能够在当前应用程序中跨页面应用。
///</remarks>
Session["MyUICulture"] = Thread.CurrentThread.CurrentUICulture;
Session["MyCulture"] = Thread.CurrentThread.CurrentCulture;
}因此,用户在他/她选择的语言中会看到此内容。我们需要把该文件选择保存 到一个会话或一个Cookie变量中,因为如果用户移动到同一应用程序中的其它一 些页面,那么,当新的页面类一开始被实例化时,该线程的文化信息将会丢失 (HTTP是无状态的!)。注意,在用户的会话到期时,如果你不想丢失当前线程的 文化信息,那么你可以使用Cookies。一旦我们从web应用程序中提取了所有的内容并且基于用户选择和使用 Resources.TestWebSite.XXXPropertyName设置好了Culture和UICulture,那么 ,我们就已经为我们的全球化框架作好了准备。现在,剩下的唯一事情是把资源 特定的数据添加到相应的资源文件中。针对每一种文件类型,我们需要有一个单 独的(和适当命名的)资源文件。这个过程称为本地化。在我的web.config文件中 ,我使用了下列属性:
<globalization responseEncoding"=utf-8" requestEncoding="utf -8" fileEncoding="utf-8" />注意,这里使用了编码属性-utf-8(8位Unicode转换格式),因为它是可变长 度字符编码;并且,除了它是ASCII兼容的之外,还能够代表例如Greek,Arabic 等语言。有关UTF-8编码的更多信息,请参考下面这个链接:http://en.wikipedia.org/wiki/UTF-8另外,特别值得注意的是,尽管我们能够在发布服务器上拥有原始XML形式的 资源文件(这样,用户可以方便地编辑它们而不必重新编译整个站点),但是,如 果我们对资源文件作出任何修改的话,应用程序将重新开始运行。这有可能妨碍 此发布的应用程序的性能。