ASP.NET2.0的全球化與本地化之全球化
一、 加入全球化信息
在我的網站中,在創建資源文件并加入一些本地化數據后,我首先開始使用顯式本地化來設置控件(例如,在我的網站中的標簽)的文本,以便它們可以從資源文件中得到它們的值。既然存在四種語言;所以,除一個完全可依賴的資源文件之外(沒有本地化命名),我創建了四個資源文件。
注意,這些資源文件都以本地化標記作為它們的中間名稱,因此,我需要把UICulture設置為與該本地化相同的名字以便ASP.NET存取這些資源文件。
但是,問題是:我該怎樣在PostBack事件中動態地改變文化呢?幸好,ASP.NET在Page類中提供了一種可重載的方法: InitializeCulture()。這個方法在頁面生命周期(在生成任何控件之前)中執行得很早,并且在此,我們能夠設置當前線程的UICulture和Culture。
由于這個方法位于Page類中,并且我不想針對每一個web頁面都重復相同的代碼,所以我創建了一個BasePage類,我的應用程序中的所有的aspx頁面都派生自這個BasePage類。但是現在,我又面臨另一個問題。下面,讓我進行解釋:
回到UI設計:我使用了一個MasterPage和一個Header用戶控件(在一個ContentPlaceHolder內)。我把一個缺省的頁面與該MasterPage相關聯。整個站點必須動態地實現本地化。因此,在頂部,有一個下拉框,用戶可以從中選擇一種語言/文化。在BasePage的InitilializeCulture方法中,我必須取得用戶從下拉框選擇的項的值;但是,因為它還沒有被初始化,所以,我還不能存取任何控件的值。答案是:使用表單集合(從響應對象內)。下面是實現代碼:
請注意,在此,我是如何使用"parentControl:ChildControl"方法從表單集合中存取控件的。通過使用這一約定,你可以存取任何ASP.NET生成的嵌套控件。借助于表單集合中選擇的值,我可以通過一個switch case語句來進行文化設置:
因此,用戶在他/她選擇的語言中會看到此內容。我們需要把該文件選擇保存到一個會話或一個Cookie變量中,因為如果用戶移動到同一應用程序中的其它一些頁面,那么,當新的頁面類一開始被實例化時,該線程的文化信息將會丟失(HTTP是無狀態的!)。注意,在用戶的會話到期時,如果你不想丟失當前線程的文化信息,那么你可以使用Cookies。
一旦我們從web應用程序中提取了所有的內容并且基于用戶選擇和使用Resources.TestWebSite.XXXPropertyName設置好了Culture和UICulture,那么,我們就已經為我們的全球化框架作好了準備。現在,剩下的唯一事情是把資源特定的數據添加到相應的資源文件中。針對每一種文件類型,我們需要有一個單獨的(和適當命名的)資源文件。這個過程稱為本地化。在我的web.config文件中,我使用了下列屬性:
注意,這里使用了編碼屬性-utf-8(8位Unicode轉換格式),因為它是可變長度字符編碼;并且,除了它是ASCII兼容的之外,還能夠代表例如Greek,Arabic等語言。有關UTF-8編碼的更多信息,請參考下面這個鏈接:
http://en.wikipedia.org/wiki/UTF-8
另外,特別值得注意的是,盡管我們能夠在發布服務器上擁有原始XML形式的資源文件(這樣,用戶可以方便地編輯它們而不必重新編譯整個站點),但是,如果我們對資源文件作出任何修改的話,應用程序將重新開始運行。這有可能妨礙此發布的應用程序的性能。
二、設置語言方向相應的dir屬性
許多時候,我們還需要設置本地化文本的方向(這是使用<html>或<body>標簽的dir屬性設置的)。這是必需的,因為有些語言從右到左(RTL)讀取,例如Arabic,不同于象Hindi和English這樣語言的標準的從左到右(LTR)的讀取方式。這可以通過把.resx文件中的dir屬性設置為適當的值來實現。
首先,你可以在所有資源文件中創建一個Direction(你可以使用任何名)域,并基于單個資源文件把它的屬性設置為RTL或LTR。對于Arabic,這個域的值是RTL,而對于Hindi則是LTR。然后,把<body>標簽的dir屬性設置為如下:
這樣就可以設置正確的方向,因為該值來自于資源文件(基于當前線程文化)。
三、使用數據庫實現本地化
我們已經看到了如何本地化控件的文本和UI描述。但是,存儲在數據庫中的內容會怎么呢?其實,這一部分內容也需要本地化,但是由于它存儲在一個DB中,所以我們不能使用資源文件來實現相同目的。為此,我們需要創建新的表格。
假定我有一個存儲用戶評價的表格。該表格結構如下所示:
現在,我們想實現以本地化的文本來顯示Comments和Name字段,但是,我們不可能把所有這些域的不同語言版本都存儲在同一個表格中(既然存在不需要被本地化但卻重復的其它域)。因此,我們需要重新組織該表格結構并且創建另一個表格來存儲這兩個域的本地化版本。首先,我們需要從這個表格中刪除這兩個域并創建一個如下所示的新表格:
在此,我們添加了一個新域:CultureID,它等價于LCID(或Locale標識符)。我們能夠按如下所示添加文化特定的本地化數據:
現在,我們可以使用以CultureID(LCID)作為參數的SQL查詢來取得本地化內容。我們還能夠提供一個用戶接口來把本地化數據輸入到這樣的表格以便能夠以一種交互方式創建相應的內容。
四、總結
在本文中,我們討論了在ASP.NET 2.0開發中有關實現全球化的一些重要方面,并且看到,這是非常容易實現的事情;但是,也存在許多值得注意的重要問題:
1.不要依賴于web瀏覽器的設置。你可以在應用程序中顯示一個鏈接(可以在頭部位置)以便用戶能夠通過點擊它來選擇他們的語言。
2.使用資源文件來把GUI中與描述相關的數據分離開來。資源fallback是ASP.NET使用的方法-當它不能找到相應于一種特定文化的資源文件時。它將首先試用中立資源文件,然后是缺省的資源文件或fallback資源文件(TestSiteResource.resx)。
3.使用數據庫表格把數據存儲到一個DB中。為此,你需要創建單獨的表格來存儲本地化內容。
4.如果你使用sn.exe來為你的主應用程序程序集創建一個強類型名,那么,你需要使用同一對密鑰對(由sn.exe生成)中的私鑰來簽名你的小程序集;因為,強類型名字的程序集要求小程序集也應該是強類型名字。
在我的網站中,在創建資源文件并加入一些本地化數據后,我首先開始使用顯式本地化來設置控件(例如,在我的網站中的標簽)的文本,以便它們可以從資源文件中得到它們的值。既然存在四種語言;所以,除一個完全可依賴的資源文件之外(沒有本地化命名),我創建了四個資源文件。
![]() |
注意,這些資源文件都以本地化標記作為它們的中間名稱,因此,我需要把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形式的資源文件(這樣,用戶可以方便地編輯它們而不必重新編譯整個站點),但是,如果我們對資源文件作出任何修改的話,應用程序將重新開始運行。這有可能妨礙此發布的應用程序的性能。
二、設置語言方向相應的dir屬性
許多時候,我們還需要設置本地化文本的方向(這是使用<html>或<body>標簽的dir屬性設置的)。這是必需的,因為有些語言從右到左(RTL)讀取,例如Arabic,不同于象Hindi和English這樣語言的標準的從左到右(LTR)的讀取方式。這可以通過把.resx文件中的dir屬性設置為適當的值來實現。
首先,你可以在所有資源文件中創建一個Direction(你可以使用任何名)域,并基于單個資源文件把它的屬性設置為RTL或LTR。對于Arabic,這個域的值是RTL,而對于Hindi則是LTR。然后,把<body>標簽的dir屬性設置為如下:
| <body runat="server" dir="<%$ Resources: TestSiteResources, Direction %>"> |
這樣就可以設置正確的方向,因為該值來自于資源文件(基于當前線程文化)。
三、使用數據庫實現本地化
我們已經看到了如何本地化控件的文本和UI描述。但是,存儲在數據庫中的內容會怎么呢?其實,這一部分內容也需要本地化,但是由于它存儲在一個DB中,所以我們不能使用資源文件來實現相同目的。為此,我們需要創建新的表格。
假定我有一個存儲用戶評價的表格。該表格結構如下所示:
![]() |
現在,我們想實現以本地化的文本來顯示Comments和Name字段,但是,我們不可能把所有這些域的不同語言版本都存儲在同一個表格中(既然存在不需要被本地化但卻重復的其它域)。因此,我們需要重新組織該表格結構并且創建另一個表格來存儲這兩個域的本地化版本。首先,我們需要從這個表格中刪除這兩個域并創建一個如下所示的新表格:
![]() |
在此,我們添加了一個新域:CultureID,它等價于LCID(或Locale標識符)。我們能夠按如下所示添加文化特定的本地化數據:
![]() |
現在,我們可以使用以CultureID(LCID)作為參數的SQL查詢來取得本地化內容。我們還能夠提供一個用戶接口來把本地化數據輸入到這樣的表格以便能夠以一種交互方式創建相應的內容。
四、總結
在本文中,我們討論了在ASP.NET 2.0開發中有關實現全球化的一些重要方面,并且看到,這是非常容易實現的事情;但是,也存在許多值得注意的重要問題:
1.不要依賴于web瀏覽器的設置。你可以在應用程序中顯示一個鏈接(可以在頭部位置)以便用戶能夠通過點擊它來選擇他們的語言。
2.使用資源文件來把GUI中與描述相關的數據分離開來。資源fallback是ASP.NET使用的方法-當它不能找到相應于一種特定文化的資源文件時。它將首先試用中立資源文件,然后是缺省的資源文件或fallback資源文件(TestSiteResource.resx)。
3.使用數據庫表格把數據存儲到一個DB中。為此,你需要創建單獨的表格來存儲本地化內容。
4.如果你使用sn.exe來為你的主應用程序程序集創建一個強類型名,那么,你需要使用同一對密鑰對(由sn.exe生成)中的私鑰來簽名你的小程序集;因為,強類型名字的程序集要求小程序集也應該是強類型名字。



