首页 新闻 论坛 群组 Blog 文档 下载 读书 Tag 网摘 搜索 开源 FAQ 第二书店 博文视点 程序员
频道: 研发 数据库 中间件 信息化 视频 .NET Java 游戏 移动 服务: 人才 外包 培训
    图书品种:235680
       
热门搜索: ASP.NET Ajax Spring Hibernate Java

14.5  支持本地化和多种语言

支持本地化的应用通常需要某种方法以多种语言和格式存储文本串和其他内容。常用的方法是通过工程中存储的资源(以标准.resx文件格式存储)。在ASP.NET中,建立一个页面或整个Web应用时,通过指定它需要的资源就可以实现本地化,而且在应用中可以采用声明式和编程式两种方法引用资源。

ASP.NET为标识文化提供了内置支持,文化定义了本地化输出所需的语言和其他设置。可以采用声明方式为控件、子对象及其属性定义资源键,而不需要编写任何代码,而且可以使用页面级或应用级资源对应用完成本地化,而不必创建和编译卫星程序集。最后,还可以使用新机制通过一个数据库或以另外某种定制格式存储和获取资源,支持资源的可扩展性。

在这一节中,你会了解采用“无代码”方法实现本地化的概念,我们将介绍多种文化处理技术,然后讨论如何使用新的表达式类型、资源类型和属性来完成页面本地化,重申一句,不需要编写任何代码。

14.5.1  无代码实现本地化

ASP.NET允许本地化一个Web应用,而不必编写任何代码。Microsoft在.NET Framework和Visual Studio 2005中提供了内置的本地化支持。可以使用隐式和/或显式表达式的组合以及页面级和/或应用级资源本地化一个应用。下面将通过示例解释这些表达式类型和资源类型。

1. 使用隐式表达式和本地资源

应用本地化最简单的办法是通过隐式表达式。Visual Studio 2005通过使用隐式表达式、页面级或本地级资源,以及声明式编程大大简化了页面的本地化,这样就为你提供了一种向控件属性应用资源的自动方法。代码清单14-22显示了这种方法的一个简单示例,这里的页面中包含一个 Button、一个TextBox和一个Label。

代码清单14-22  隐式表达式的简单展示

<%@Page Language="C#" %>

. . .

<asp:Button ID="Button1" runat="server" Text="English Button" />

<asp:TextBox ID="TextBox1" runat="server" Text="English TextBox" />

<asp:Label ID="Label1" runat="server" Text="English Label" />

可以在Visual Studio 2005中创建这个页面,切换到设计视图,点击Tools菜单上的Generate Local Resource(生成本地资源)。这会向各个控件写一个资源键,而且向Page指令也增加一个资源键,并增加Culture和UICulture属性,如代码清单14-23所示。

代码清单14-23  增加到示例页面的资源键

<%@Page Language="C" meta:resourceKey="PageResource1"

        Culture="auto" UICulture="auto" %>

...

<asp:Button ID="Button1" runat="server" Text="English Button"

     meta:resourcekey="Button1Resource1" />

<asp:TextBox ID="TextBox1" runat="server" Text="English TextBox"

     meta:resourcekey="TextBox1Resource1" />

<asp:Label ID="Label1" Runat="server" Text="English Label"

     meta:resourcekey="Label1Resource1" />

</asp:Label>

Page指令和各个服务器控件现在都有了一个资源键,由meta:resourcekey属性指示。这个属性和资源键的组合就是一个隐式表达式。Generate Local Resource命令完成以下功能:

q 创建一个App_LocalResources文件夹(如果还不存在这样一个文件夹),来存储页面特定的资源文件。

q 在这个文件夹中创建一个与文化无关的(文化中立)资源文件,命名为页面名再加上扩展名.resx,这个文件包含页面中声明的键的相应资源值。

可以在Solution Explorer窗口中双击这个.resx文件来查看文件的内容,这会在RESX编辑器中打开文件,如图14-19所示。

图14-19  一个文化中立资源文件的内容

对于控件中内部指定为可本地化的各个控件属性,.resx文件中都包含一个资源键和相应的值。Visual Studio设计者使用design-time-only属性作为一个指针,指示插入到.resx文件中的属性。注意这不是一个运行时特性。

每个控件中指定为可本地化的属性可以不同,不过大多数基于文本的属性都默认标志为可本地化。如图14-19所示,Button控件的Text和ToolTip等属性都默认标志为可本地化。这个文件对应Page指令还包含一个键/值对(设置页面的标题)。RESX编辑器将所有String类型属性显示在Strings类别中,所有其他类型(如Boolean)则显示在其他类别中,这些类别可以从Resources Editor窗口左上角的下拉列表得到。

现在可以创建一个新的资源文件,其中包含德国(German)文化的资源值。将Value列中“English”的所有出现都替换为“German”,并把文件保存为有.de.resx文件扩展名(串"de"是German标准缩写)。Visual Studio向App_LocalResources文件夹增加这个新文件,并在Solution Explorer窗口中显示。现在有了两个.resx文件,其中一个.resx文件面向German文化,而原来的资源文件未标志任何语言缩写。没有加语言标志的文件是默认文件,用于未显式请求German的所有浏览器请求。换句话说,这是一个中立文化,有时称为回退文化。

Visual Studio会自动向页面增加UICulture="auto"属性,这会使 ASP.NET会读取客户浏览器在HTTP首部中发送的文化信息。从一个选择German文化的浏览器访问时,ASP.NET会显示该页面的German版本。这样无需编写任何代码就完成了页面的本地化。运行时,ASP.NET会编译这些.resx,将其增加到程序集,Resource Manager完成资源查找,并得到适当的值。图14-20显示了在默认文化中运行这个页面的结果。

图14-20  在默认文化中打开本地化页面的结果

要看特定于文化的资源如何显示,可以相应地为你的浏览器设置文化。将浏览器文化设置为German时,需要以下步骤:

(1) 在 Microsoft Internet Explorer中,从Tools菜单选择Internet Options。

(2) 在Internet Options对话框的General标签页中,点击Languages按钮。

(3) 点击Language Preference对话框中的Add按钮,从语言列表中选择German (Germany) [de]。

(4) 点击OK,关闭Add Language对话框。

(5) 再回到Language Preference对话框,从语言列表中选择German (Germany) [de],然后点击Move Up。

(6) 点击OK,关闭Language Preference对话框。

(7) 点击OK,关闭Internet Options对话框。

一旦将German设置为浏览器文化,ASP.NET会自动从.de.resx文件读取资源信息,并显示German内容(包括浏览器标题栏中的页面标题),如图14-21所示。如果将浏览器文化改为其他语言,页面将找不到相应的.resx文件,而转向文化中立的.resx文件(English版本)。

图14-21  在German文化下打开本地化页面的结果

只需创建相应的.resx文件,就可以支持更多的文化。可以把这些文件发送给翻译人员,并提供源页面的只读版本,使他们能了解文本的上下文。翻译之后,只需将这些.resx文件增加到App_Local Resources文件夹,然后在各种浏览器设置下运行页面,验证本地化的结果。

注解   本地化页面的设计视图总是使用文化中立的资源文件。

2. 使用显式表达式

上一节中,已经看到了可以使用隐式表达式实现本地化。另一种技术是利用显式表达式。通过表达式,可以采用声明方式为页面中一个控件或对象的几乎所有属性指定值。可以使用以下新的表达式语法(基于每个属性)对控件或对象完成本地化:

<%$ Resources: [NameSpace,] [ClassName.]ResourceKey %>

控件中有些属性未标志为可本地化,显式表达式对于实现这些属性的本地化很有用。例如,可以使用显式表达式本地化一个按钮的BackColor属性。必须首先定义一个颜色资源,它必须在所有适当的资源文件中都可用(包括文化中立资源文件)。

还应该将文化特定资源文件中手动定义的资源键/值对增加到文化中立的资源文件中。

可以在内置的资源编辑器中编辑这些资源文件,或者右键点击资源文件,再选择Open with...(打开方式),然后选择XML Editor。代码清单14-24显示了XML视图的一部分,其中使用data和value元素向默认资源文件和German文化的.resx资源文件增加了一个名为Mycolor的颜色资源。文化中立资源文件中的颜色设置为Red,在German文件中设置为Yellow。

代码清单14-24  对资源文件的补充,来支持一个颜色资源

Default .resx file

. . .

  <data name="MyColor" xml:space="preserve">

    <value>Red</value>

  </data>

</root>

German .resx file

. . .

  <data name="MyColor" xml:space="preserve">

    <value>Yellow</value>

  </data>

</root>

注意,MyColor的类型为System.String。ASP.NET会根据需要将串值自动转换为一个目标类型。现在可以使用这些资源文件根据当前文化来改变任何控件的颜色属性。在Visual Studio 2005中,可以使用图14-22所示Expressions对话框应用这个资源。在Button1属性窗口中的Expressions框中点击Expand (...)按钮,打开Expressions对话框。属性窗口中的粉色图标指示了本地化属性。

图14-22  使用Expressions对话框设置BackColor属性

在Expressions对话框点击OK会创建一个显式表达式,为所编辑的属性指定资源:

<asp:Button ID="Button1" runat="server"

     BackColor="<%$ Resources: MyColor %>"

     Text="English Button"

     meta:resourceKey="Button1Resource1" />

这个表达式指示BackColor属性会使用MyColor资源。ASP.NET在运行时根据用户浏览器的文化设置选择资源的具体值。它把资源值转换为控件属性所需的System.Drawing.Color类型的值。

与隐式表达式一样,使用显式表达式无需编写任何代码。图14-23显示了German文化下设置TextBox和Label的BackColor属性后的结果。

图14-23  German文化中使用显式表达式的结果

当然,也可以不使用Expressions对话框,而是在源代码视图中向控件声明直接键入显式表达式。表达式不区分大小写。

3. 使用全局应用资源

本地资源特别适于为各个页面存储唯一的资源键。不过,共享资源最好存储为全局应用资源。所有应用级资源都在Web应用级存储在App_Global Resources文件夹中。可以在显式表达式语法中指定命名空间和/或类名来使用这些资源。

例如,可以在MyResources.aspx和 MyResources.de.aspx(定义了文化资源的页面)中使用一个名为MySharedKey的共享资源来设置Label的Text属性,如下所示:

<asp:Label ID="Label1" runat="server"

     BackColor="<%$ Resources: MyColor%>"

     Text="<%$ Resources: MyResources, MySharedKey %>"

     meta:resourceKey="Label1Resource1" />

在这个例子中,Text属性使用一个显式表达式设置。不过,这个表达式使用了一条扩展语法,其中包括资源文件名(MyResources)和资源(MySharedKey)。通过使用这种扩展语法,可以将属性指向应用资源文件中的一个资源,这就允许从多个页面访问同一个资源文件。

隐式表达式和显式表达式还可以一起使用,如前面的例子所示。在这种情况下,隐式表达式的优先级比显式表达式更高。换句话说,.NET Framework会使用Label1Resource1.Text的值而不是MySharedKey值设置Text属性。

为了说明这一点,在前面的示例代码中,标题栏中的文本和标签的Tooltip属性会使用本地资源文件,因为meta:resourceKey属性指向本地资源文件。BackColor属性会使用本地资源文件的MyColor资源,Text属性则使用应用级资源文件(MyResources)中的MySharedKey值。可以根据需要创建多个文化特定应用资源文件,还可以通过编程访问应用级和页面级资源(稍后会介绍这个内容)。

4. 本地化静态内容

ASP.NET还支持静态文本标记的本地化。为此,只需把静态内容包围在一个<asp:Localize>元素中:

<asp:Localize runat="server">This will be localized</asp:Localize>

While this will not.

将需要本地化的文本包围在<asp:Localize>元素中后,再使用Visual Studio中的Generate Local Resource命令。它会为<asp:Localize>元素创建一个资源键,对 <asp:Localize>元素包围的文本进行编码,并把结果作为资源键的值存储在页面的文化中立资源文件中。

只能在源代码视图中处理<asp:Localize>元素。这个元素只能包含静态内容,而不能包括ASP.NET Web控件。ASP.NET将其中的所有内容都认为是静态内容,并按其在页面源代码中的原样放到资源文件中,所以ASP.NET在运行时不会执行<asp:Localize>元素包含的任何服务器控件。

图14-24显示了一个使用本地(页面级)和全局(应用级)资源的示例页面,还显示了文化名。可以从当前线程使用以下代码得到文化名:

String culture = Thread.CurrentThread.CurrentUICulture.ToString();

图14-24  使用本地和全局资源对静态内容本地化

注意,必须在代码文件中导入System.Threading命名空间,才能访问Thread类。在C#中,需要在文件中增加以下语句:

using System.Threading;

4. 排除控件的本地化

如果不希望本地化一个控件或Page指令,可以将meta:localize属性设置为false:

<asp:Button ID="Button1" Runat="server"

            Text="Don't Localize Me!"

            meta:localize="false" />

对于所有文化,按钮都将显示默认文本“Don’t Localize Me!”。使用Generate Local Resource命令时,不会对meta:localize属性设置为false的控件生成资源键。

5. 在运行时隐藏控件

某些情况下,可能不是对控件的属性本地化,而只是想针对某些文化隐藏这个控件。例如,你的页面可能包含一个图片,你希望只是对某些文化显示这个图片,而对于另外一些文化不显示。为了在一个特定文化下隐藏控件,需要为控件的Visible属性增加一个资源值,并设置为false。在第一个例子中,如果想对German文化隐藏文本框,需要向.de.resx文件增加键TextBox1Resource1.Visible,并设置值为false。运行时,对于German文化下的请求,ASP.NET不会在输出中包括TextBox控件,但是对所有其他文化则会显示这个控件。

6. 使用什么表达式类型,何时使用

一般情况下,应当使用隐式表达式来完成控件或对象的本地化。利用简写记法,可以在控件级声明资源键,而不必为控件的各个属性定义一个表达式。不过,只能对本地资源使用隐式表达式,这意味着,对于每个页面必须有一组.resx文件。

如果使用共享(应用级)资源,必须使用显式表达式来本地化一个控件或对象的属性。对于Page指令,可以使用任何一种表达式类型,而且同一个控件可以同时使用隐式表达式和显式表达式(前面已经见到这种情况)。这种组合对未默认本地化的属性可能很有用。表14-1显示了不同的表达式类型对于不同资源类型表现如何。

表14-1  使用不同的表达式和资源类型

是否能引用本地资源

是否能引用全局(应用级)资源

隐式表达式

显式表达式

14.5.2  用户可选择的本地化

有时你可能希望用户能选择本地化环境来查看网站。对此典型的实现是由一个页面显示一些标志(各标志对应所支持的语言),选择适当的标志就会设置文化。在ASP.NET中,可以把选择的文化存储为各个用户的概要中的一个属性来达到目的(第12章介绍过ASP.NET中的概要特性)。

要想在各个页面加载时设置文化或在母版页中设置文化是不可能的。不过,对此有一个解决方案:在Global.asax中使用某个应用事件。考虑代码清单14-25,其中CurrentThread的CurrentCulture和CurrentUICulture设置为用户概要的UserCulture属性中存储的文化。如果没有存储概要,代码会设置一个默认值。

代码清单14-25  Global.asax中使用代码设置本地化

<%@Application Language="C#" %>

<%@Import Namespace="System.Globalization" %>

<%@Import Namespace="System.Threading" %>

<script runat="server">

  protected void Application_PostAcquireRequestState(

                             object sender, EventArgs e)

  {

    CultureInfo ci = null;

    if (Profile.UserCulture != String.Empty)

    {

      ci = new CultureInfo(Profile.UserCulture);

    }

    else

    {

      ci = new CultureInfo("en-gb");

    }

    Thread.CurrentThread.CurrentCulture = ci;

    Thread.CurrentThread.CurrentUICulture = ci;

  }

</script>

不过有一个问题,因为文化在页面加载前设置,所以在页面重新加载或用户加载另外一个不同的页面之前,改变文化设置的任何用户交互都不会起作用。一种解决办法是向一个超链接的查询串增加文化,如代码清单14-26所示。

代码清单14-26  选择所需语言的标志图标和链接

<asp:Hyperlink ID="Hyperlink1" runat="server"

     ImageUrl="images/en-us.gif" ToolTip="US Culture"

     NavigateUrl="localize-select.aspx?culture=en-us" />

<asp:Hyperlink ID="Hyperlink2" runat="server"

     ImageUrl="images/de-de.gif" ToolTip="German Culture"

     NavigateUrl="localize-select.aspx?culture=de-de" />

<asp:Hyperlink ID="Hyperlink3" runat="server"

     NavigateUrl="localize-select.aspx"

     Text="<%$ Resources: Hyperlink3.Text1 %>" />

之后,在Page_Load事件处理器或控件的相应事件处理器(导致回送新文化值的事件)中,可以检查用户是否选择了一个不同于用户当前概要设置的语言。如果是这样,可以更新概要(但确保不要将其包含的String值设置为null),然后使用Response.Redirect方法重新加载页面(参见代码清单14-27)。这个代码还显示了当前文化设置。

代码清单14-27  设置概要属性值,并重新加载页面

protected void Page_Load()

{

  String query = Request.QueryString["culture"];

  if (query == null)

  {

    query = String.Empty;

  }

  if (Profile.UserCulture != query)

  {

    // set profile value

    Profile.UserCulture = query;

    // reload page to pick up new culture

    Response.Redirect(Request.Url.ToString(), true);

  }

  lblCulture.Text = Thread.CurrentThread.CurrentUICulture.ToString();

}

如果启用了网站的个性化,还有另外一种方法,使用页面Load事件发生前执行的HTTP模块来设置文化。第12章已经用这种方法为一个页面设置主题,其中从用户的当前概要抽取了他(她)的选择。如12.4.4节所述,首先要创建一个实现了IHttpModule接口的类,再把它增加到web.config文件中,这样ASP.NET就会将其作为请求过程的一部分来执行。在这个模块中,可以从概要抽取所选的文化,并应用到页面。

图14-25和图14-26显示了这个示例页面,首先选择的是US-English文化,然后选择了German文化。

图14-25  在示例页面中选择US-English文化

图14-26  在示例页面中选择German文化

14.5.3  通过编程访问资源

资源不是强类型的,但是可以通过编程由Resource Manager或GetPageResourceObject和GetGlobalResourceObject方法来访问。代码清单14-28 显示了一个简单的例子。注意,必须创建正确类型的实例,才能将其指定给控件属性,比如创建一个System.Drawing.Color实例来设置Button控件的BackColor。

代码清单14-28  通过编程访问本地和应用级资源

protected void Page_Load(object sender, EventArgs e)

{

  Page.Title = GetLocalResourceObject("PageTitle").ToString();

  Button1.Text = GetLocalResourceObject("MyButtonText").ToString();

  String clrName = GetGlobalResourceObject("MyResources",

                                           "MyColor").ToString();

  Color clr = Color.FromName(clrName);

  Button1.BackColor = clr;

  TextBox1.Text = GetGlobalResourceObject(

                  "MyResources", "MyTextBoxText").ToString();

}

14.5.4  可扩展性

资源键及其值的来源并不仅限于基于文件的.resx格式。可以使用ASP.NET的富扩展性机制在一个数据库或任何定制文件格式存储和获取资源键及其值。通过使用数据库,能更容易地为Web场中跨多个服务器的应用集中管理资源,并确保一致性。

.NET Framework和Visual Studio 2005都支持可扩展性。使用ResourceProviderFactory和DesignTimeResourceProviderFactory类可以由一个定制源读写资源值。有关的更多详细信息,见http://msdn2.microsoft.com/en-us/library/9b1d2yze.aspx和http://msdn2.microsoft.com/en-us/ library/system.web.ui.design.designtimeresourceproviderfactory.aspx。

14.5.5  Web资源

除了程序集外,控件开发人员通常还必须提供额外的静态文件,如样式表和图像。利用ASP.NET中新的资源模型,只需使用System.Web.UI命名空间的WebResource性质,可以将这些静态文件作为资源嵌在程序集中,如代码清单14-29所示。

代码清单14-29  将图像作为资源存储在一个程序集中

[assembly: WebResource("MyImage.gif", "image/gif")]

namespace MyDemoControls

{

  public class MyImageControl : WebControl

  {

    // over-ride creation of child controls

    protected override void CreateChildControls()

    {

      // create new Image element and set ImageUrl

      Image img = new Image();

      img.ImageUrl = this.Page.ClientScript.GetWebResourceUrl(

                          typeof(MyWebControl), "MyImage.gif");

      Controls.Add(img);

    }

  }

}

编译时,使用以下命令将这个资源嵌在程序集中:

csc /t:library /out:MyImageControl.dll

    /r:System.dll,System.web.dll

    /res:MyImage.gif,MyImage.gif

    MyImageControl.cs

Web资源特性并不仅限于图像。例如,要嵌入 JavaScript来提供客户端支持,可以增加以下代码:

[assembly:WebResource("MyScript.js", "text/javascript", true)]

public class MyControl

{

  // control code here

}

这个性质的第三个参数指示ASP.NET应当为这个资源调用Web资源解析器(这不是默认行为)。这样可以确保当客户使用嵌入资源时资源将是合法的。ASP.NET页面和TreeView等控件都使用这个技术来提供所需的图像和JavaScript。还可以通过代码在ASP.NET页面中直接访问嵌入的Web资源,如下所示:

img1.Image = Page.ClientScript.GetWebResourceUrl(

                      Type.GetType("MyControl"), "MyImage.gif")

衷心感谢Dietrich Birngruber的大力支持,完成了本章中所用的德文翻译。

查看所有评论(0)条】

最近评论



正在载入评论列表...
热点评论