asp.net
一、 加入全球化資訊
在我的網站中,在建立資源檔並加入一些本地化資料後,我首先開始使用顯式本地化來設定控制項(例如,在我的網站中的標籤)的文本,以便它們可以從資源檔中得到它們的值。既然存在四種語言;所以,除一個完全可依賴的資源檔之外(沒有本地化命名),我建立了四個資源檔。
注意,這些資源檔都以本地化標記作為它們的中間名稱,因此,我需要把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產生)中的私密金鑰來簽名你的小程式集;因為,強型別名字的程式集要求小程式集也應該是強型別名字。