ASP.NET 多語言的實現(後臺訊息+前臺訊息+頁面自動繫結)

weixin_34402090發表於2014-03-06

一 前言

介面支援多種語言,在使用ASP.NET自帶的多語言方案時遇到下列問題:

  • 在做管理類的功能時,有新增、修改和檢視頁面,需要支援多語言的控制元件基本相同,但要維護多處,產生冗餘(ASP.NET有共享的資源,但它是全域性的,不能分 模 塊,我們不能所模組的資訊入在全域性資源中);
  • 在頁面中必須要指定資原始檔中的KEY;
  • 當頁面慢來慢多時,頁面與資源的匹配實在難以維護;

所以我認為一個理想的支援多語言框架,需要有以下特性:

  • 分模組解決資料冗餘問題;
  • 自動匹配頁面與資原始檔之間的聯絡;
  • 易於維護,能通過頁面快速定位到資原始檔中;
  • 支援後臺訊息的多語言實現
  • 支援前臺JS訊息的多語言實現

二 後臺訊息多語言的實現

在實現後臺訊息多言的實現時,主要利用ASP.NET的特性,通過修改當前執行緒的區域語言,來獲取對應版本的資源。因為ASP.NET在處理請求時,會使用一個單獨的執行緒來執行一次請求的所有程式碼,所以我們只需要在一個地方重寫當前執行緒的語言資訊,在其它任何地方都可以獲取當前語言版本的資原始檔。重寫當前執行緒的區域語言的程式碼如下:

        protected override void InitializeCulture()
        {
            string language = "en";//預設為英文
            if (Session["Language"] != null) language = Session["Language"].ToString();
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(language);
            Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(language);
        }

在命名資原始檔名時,規則如下:

  • 預設語言的版本直接當正常命名,如命名OrderResource.resx
  • 其它版本的資原始檔名,應加上區域名稱, 如OrderResource.zh-cn.resx, OrderResource.en-us.resx.

將後臺訊息放在資原始檔後,程式中只引用主的資原始檔,這樣線上程的區域資訊更改後,會自動的引用其它語言版本的資源。比如在程式中使用如下程式碼:

Response.Write(OrderResource.FiledRequired);//當區域資訊為英文時,輸出"The Filed is required!";當區域資訊為中文時,輸出"欄位必填!"

三 前臺JS多語言的實現

前臺的多語言實現,即在JS中需要彈出一些提示也需要多語言。前臺多語言的實現方法實現有很多,在本例中,我們採用的統一的管理方式,把JS多語言資訊同樣放在資原始檔中,只不過在一個公共的地方,把資源序列成一個json物件,然後JS中便可以使用了。當然,在實現此功能時,需要做一些快取的工作。主要程式碼如下:

public static Dictionary<string, string> JsonResources = new Dictionary<string, string>();

//把資原始檔序列化成JSON
        public static string GetJsonResource() 
        {
            string key = string.Format("JSResource.{0}", System.Threading.Thread.CurrentThread.CurrentCulture.Name);
            if (!JsonResources.ContainsKey(key)) 
            {
                JavaScriptSerializer serialzer = new JavaScriptSerializer();
                ResourceSet rs = JSResource.ResourceManager.GetResourceSet(System.Threading.Thread.CurrentThread.CurrentCulture, true, true);
                string json = string.Format("[{0}]", serialzer.Serialize(rs.OfType<DictionaryEntry>().ToDictionary(x => x.Key, x => x.Value)));
                JsonResources.Add(key, json);
            }
            return JsonResources[key];
        }

 protected override void OnPreRenderComplete(EventArgs e)
        {
            //輸出JSON物件到頁面
            Page.ClientScript.RegisterStartupScript(Page.GetType(), "", "<script language='javascript'>var json=eval(" + ResourceCache.GetJsonResource() + ");JSResource=json[0];</script>");
         }

在頁面中就可以使用JSResource物件了,如alert(window.JSResource.JSMsg);

四 頁面中的元素自動匹配資原始檔

此功能有一個前提就是資原始檔中的KEY為控制元件ID並且為頁面指定特定資原始檔,這樣我們就可以為控制元件自動匹配了。在匹配的過程中,如果在指定的資原始檔中未找到,那麼會到公共的檔案中查詢。

注意:

1>為頁面指定特定資原始檔,主要是用於多個頁面(模組)共享一個資源,模組的大小可以自己控制;

2>有一個公共的資原始檔,在整個專案中,總有一些資訊是各個模組所通用的;

 主要程式碼:

    public class ResourceFactory
    {
        public static ResourceAccess GetResource(System.Resources.ResourceManager resMgr)
        {
            return new ResourceAccess(resMgr, CommonResource.ResourceManager);
        }
    }

    public class ResourceAccess
    {
        private ResourceManager resourceManager = null;

        private ResourceManager commonResourceManager = null;

        public ResourceAccess(ResourceManager resourceManager,ResourceManager commonResourceManager) 
        {
            this.resourceManager = resourceManager;
            this.commonResourceManager = commonResourceManager;
        }

        public string GetString(string name) 
        {
            string str =  this.resourceManager.GetString(name);

            if (string.IsNullOrEmpty(str))
            {
                str = this.commonResourceManager.GetString(name);
                if (string.IsNullOrEmpty(str))
                {
                    str = string.Format("【{0}】not exist", name);
                }
            }
            return str;
        }
    }

    public class BasePage:Page
    {
        #region Mult Language
        protected override void InitializeCulture()
        {
            string language = "en";//預設為英文
            if (Session["Language"] != null) language = Session["Language"].ToString();
            Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(language);
            Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(language);
        }

        public System.Resources.ResourceManager PageResourceManager { set; get; }
        private ResourceAccess resourceAccess;
        private Literal lit;
        private Button btn;
        private LinkButton libtn;

        protected override void OnPreRenderComplete(EventArgs e)
        {
            //輸出JSON物件到頁面
            Page.ClientScript.RegisterStartupScript(Page.GetType(), "", "<script language='javascript'>var json=eval(" + ResourceCache.GetJsonResource() + ");JSResource=json[0];</script>");
            if (!IsPostBack)
            {
                //繫結頁面的文字
                if (PageResourceManager != null)
                {
                    resourceAccess = ResourceFactory.GetResource(PageResourceManager);
                    //we do not use recursion, because it create too much stack.
                    foreach (Control c in Page.Controls)//Large control of page,like HTML,Form
                    {
                        foreach (Control childC in c.Controls)//Normal control of page, like button, literal
                        {
                            BindControlLanguage(childC);

                            foreach (Control childCC in childC.Controls) //Container control of page(if one contrl has child controls)
                            {
                                BindControlLanguage(childCC);

                                foreach (Control childCCC in childCC.Controls)
                                {
                                    BindControlLanguage(childCCC);
                                    foreach (Control childCCCC in childCCC.Controls)
                                    {
                                        BindControlLanguage(childCCCC);
                                    }
                                }
                            }
                        }
                    }
                }
            }
            base.OnPreRenderComplete(e);
        }

        private void BindControlLanguage(Control c)
        {
            if (c is Literal)
            {
                lit = (Literal)c;
                if (lit.Text == "")
                {
                    lit.Text = resourceAccess.GetString(lit.ID);
                }
            }
            else if (c is Button)
            {
                btn = (Button)c;
                btn.Text = resourceAccess.GetString(btn.ID);
            }
            else if (c is LinkButton)
            {
                libtn = (LinkButton)c;
                if (libtn.Text == "")
                {
                    libtn.Text = resourceAccess.GetString(libtn.ID);
                }
            }
        }
        #endregion
    }
View Code

五 最終效果

英文版本

中版本

全部程式碼下載

相關文章