The Will Will Web

記載著 Will 在網路世界的學習心得與技術分享

ASP.NET MVC 開發心得分享 (15):全球化和當地語系化

之前幾篇文章有講過 ASP.NET MVC 事實上共用許多 ASP.NET 原本就有的東西,這當然連 ASP.NET 全球化和當地語系化的支援也包含在內,在建置多國語言網站時我們通常會利用 App_GlobalResources 儲存全域資源檔,與頁面有關的本地資源檔會放在每一個 *.aspx 所在目錄下的 App_LocalResources 目錄下,這樣的架構在 ASP.NET MVC 之中也一樣可以利用,只是需要一些小技巧才能方便在 View 中使用,今天會分享一個 HtmlHelper 幫助我們在 View 中取用資源檔的資料。

首先我要感謝 Matt Hawley 寫了一個超好用的 HtmlHelper 幫助大家在 ASP.NET MVC 中讀取資源檔,我們可以來看看程式碼,一共分成三個部分:

1. 自訂 WebFormViewEngine 與 WebFormView 讓在 View 中可以取得 View 的實際所在路徑

LocalizationWebFormView.cs

using System.IO;
using System.Web.Mvc;

namespace MvcApplication
{
    public class LocalizationWebFormView : WebFormView
    {
        internal const string ViewPathKey = "__ViewPath__";

        public LocalizationWebFormView(string viewPath) : base(viewPath)
        {
        }

        public LocalizationWebFormView(string viewPath, string masterPath) : base(viewPath, masterPath)
        {
        }

        public override void Render(ViewContext viewContext, TextWriter writer)
        {
            // there seems to be a bug with RenderPartial tainting the page's view data
            // so we should capture the current view path, and revert back after rendering
            string originalViewPath = (string) viewContext.ViewData[ViewPathKey];
            
            viewContext.ViewData[ViewPathKey] = ViewPath;
            base.Render(viewContext, writer);
            
            viewContext.ViewData[ViewPathKey] = originalViewPath;
        }
    }
}

LocalizationWebFormViewEngine.cs

using System.Web.Mvc;

namespace MvcApplication
{
    public class LocalizationWebFormViewEngine : WebFormViewEngine
    {
        protected override IView CreateView(ControllerContext controllerContext, string viewPath, string masterPath)
        {
            return new LocalizationWebFormView(viewPath, masterPath);
        }

        protected override IView CreatePartialView(ControllerContext controllerContext, string partialPath)
        {
            return new LocalizationWebFormView(partialPath, null);
        }
    }
}

2. 建立一個 Resource HtmlHelper ,方便在 View 中直接以 Html Helper 的方式取用資源檔

ResourceExtensions.cs

using System.Globalization;
using System.Web;
using System.Web.Compilation;
using System.Web.Mvc;

namespace MvcApplication
{
    public static class ResourceExtensions
    {
        public static string Resource(this Controller controller, string expression, params object[] args)
        {
            ResourceExpressionFields fields = GetResourceFields(expression, "~/");
            return GetGlobalResource(fields, args);
        }

        public static string Resource(this HtmlHelper htmlHelper, string expression, params object[] args)
        {
            string path = (string)htmlHelper.ViewData[LocalizationWebFormView.ViewPathKey];
            if (string.IsNullOrEmpty(path))
                path = "~/";

            ResourceExpressionFields fields = GetResourceFields(expression, path);
            if (!string.IsNullOrEmpty(fields.ClassKey))
                return GetGlobalResource(fields, args);

            return GetLocalResource(path, fields, args);
        }

        static string GetLocalResource(string path, ResourceExpressionFields fields, object[] args)
        {
            return string.Format((string)HttpContext.GetLocalResourceObject(path, fields.ResourceKey, CultureInfo.CurrentUICulture), args);
        }

        static string GetGlobalResource(ResourceExpressionFields fields, object[] args)
        {
            return string.Format((string)HttpContext.GetGlobalResourceObject(fields.ClassKey, fields.ResourceKey, CultureInfo.CurrentUICulture), args);
        }

        static ResourceExpressionFields GetResourceFields(string expression, string virtualPath)
        {
            var context = new ExpressionBuilderContext(virtualPath);
            var builder = new ResourceExpressionBuilder();
            return (ResourceExpressionFields)builder.ParseExpression(expression, typeof(string), context);
        }
    }
}

3. 修改 Global.asax.cs 將預設的 WebFormViewEngine 換成 LocalizationWebFormViewEngine

protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    RegisterRoutes(RouteTable.Routes);

    ViewEngines.Engines.Clear();
    ViewEngines.Engines.Add(new LocalizationWebFormViewEngine());
}

這樣就大功告成了!接著我們看看如何在 View 中運用這個 Html Helper,如下圖示是我寫的一個範例:

專案內容包括:

  1. 由上述新增的三個檔案
  2. 修改過的 Global.asax.cs
  3. 新增的 App_GlobalResources 目錄與一個 Resource.resx 資源檔
  4. 在 Views\Home 下新增一個 App_LocalResources 目錄與三個本地資源檔 (Index.aspx.resx , Index.aspx.zh-TW.resx , Index.aspx.en-US.resx )

Views\Home\Index.aspx 中取用資源的範例如下:

其中有三點必須注意:

  1. 由於 ASP.NET MVC 的 Html Helper 透過擴充方法(Extension Method),所以必須要載入命名空間才能取用自訂的擴充方法,如果不希望每個 View 都匯入命名空間可以參考我的另一篇文章:ASP.NET 如何預設匯入指定的命名空間(Namespace)
  2. 讀取「全域資源檔」的格式與之前在寫 ASP.NET 的時候一樣:
    資源檔的檔名 + 逗號 + 資源名稱
  3. 讀取「本地資源檔」的時候可以直接輸入「資源名稱」就好

若想要測試不同語系的資料,可以參考下圖或我另外兩篇文章:

 

 

本文範例可以從這裡下載:
ASP.NET MVC Globalization and Localization Sample.zip (314.66 kb)

 

相關連結