前四回(1,2,3,4)介紹了在ASP.NET MVC 3使用Razor ViewEngine時實現多國語言的解決方案,本以為就夠用了,沒料到今天居然在使用時又遇到新問題了。
先說需求,最近做了一套全新的頁面樣式,基本思路是在iframe中顯示內容,那麼毫無疑問,這些內容頁就是MVC的視圖了,但是首頁以何種形式存在呢?.html?.cshtml?.aspx?如果是.html的話,最主要的問題就是實現多圖語言,伺服器端不處理,難道使用js不成?而且不知道未來還會不會有必須伺服器端參與處理的事情呢;.aspx我也不願意,說實話,我不喜歡aspx那繁瑣的生命週期,更不喜歡.aspx那種自以為是,老是替你作決定;於是決定使用.cshtml。
在之前建的Website項目中,我發現能添加.cshtml的檔案,那在MVC的項目中,想必也能使用單獨的.cshtml來作頁面了,於是建立一個.cshtml頁面,把之前html頁面複製進去,運行,發現果然如願開啟了頁面,接下來把html中的預留位置換成資源檔,如:
<a href="#">歡迎: </a>
換成
@Html.Lang("Welcome")
但是發現Html下面居然沒有Lang擴充方法,在我的記憶中,view中輸入@Html,自動完成列表中就會出現Lang方法了,為啥不行呢,擴充方法出不來的第一原因就是沒有引入命名空間,於是在檔案最上面加入
@using System.Web.Mvc
但是還是不行,想想也不行,@Html都有了,沒有理由不出現擴充方法啊, 要麼換一種方法調用,直接調用靜態方法:
@LocalizationHelper.Lang(Html, "Welcome")
這下總行了吧,結果輸入完成就發現問題不對勁了,這行代碼下居然告訴我參數不匹配,就這麼兩個參數,一個Html屬性,一個string,居然還不對,看了半天沒有看出端倪,乾脆我讓VS自動產生匹配的方法,到底是啥樣的方法簽名,結果這一產生終於發現問題所在了:
public static object Lang(WebPages.Html.HtmlHelper htmlHelper, string p) { throw new NotImplementedException(); }
這個HtmlHelper的命名空間是WebPages.Html,而我之前定義的方法中參數HmtlHelper的命名空間是System.Web.Mvc!怪不得呢,原來不是一個東西啊,看起來使用RazorEngine時,視圖和頁面不是同一種類型
本打算那就使用這個HtmlHelper類型當參數吧,又發現這個類型中只有一些輔助方法,沒有Request、Response、Server之類的對象執行個體,基於我在Lang方法內部是需要請求詳細資料的,於是我打算使用將頁面本身(WebPageBase)作為參數,於是修改為下面的樣子:
public static string Lang(this WebPageBase page, string key){}
接下來就是重構,最終LocalizationHelper變成了下面的樣子:
using System;using System.Collections.Generic;using System.Linq;using System.Text;using System.IO;using CleverSoft.WebUtil;using System.Runtime.Caching;using System.Resources;using System.Collections;using System.Web.WebPages;namespace System.Web.Mvc{ public static class LocalizationHelper { public static string Lang(this HtmlHelper htmlhelper, string key) { return Lang(htmlhelper.ViewDataContainer as WebViewPage, key); } private static IEnumerable<DictionaryEntry> GetResx(string resxKey) { ObjectCache cache = MemoryCache.Default; IEnumerable<DictionaryEntry> resxs = null; if (cache.Contains(resxKey)) { resxs = cache.GetCacheItem(resxKey).Value as IEnumerable<DictionaryEntry>; } else { if (File.Exists(resxKey)) { resxs = new ResXResourceReader(resxKey).Cast<DictionaryEntry>(); cache.Add(resxKey, resxs, new CacheItemPolicy() { Priority = CacheItemPriority.NotRemovable }); } } return resxs; } public static string Lang(this WebPageBase page, string key) { var pagePath = page.VirtualPath; var pageName = pagePath.Substring(pagePath.LastIndexOf('/'), pagePath.Length - pagePath.LastIndexOf('/')).TrimStart('/'); var filePath = page.Server.MapPath(pagePath.Substring(0, pagePath.LastIndexOf('/') + 1)) + "App_LocalResources"; var langs = page.Request.UserLanguages != null ? page.Request.UserLanguages.Union<string>(new string[] { "" }).ToArray<string>() : new string[] { "" }; IEnumerable<DictionaryEntry> resxs = null; foreach (var lang in langs) { var resxKey = string.IsNullOrWhiteSpace(lang) ? string.Format(@"{0}\{1}.resx", filePath, pageName) : string.Format(@"{0}\{1}.{2}.resx", filePath, pageName, lang); resxs = GetResx(resxKey); if (resxs != null) { break; } } return (string)resxs.FirstOrDefault<DictionaryEntry>(x => x.Key.ToString() == key).Value; } }}
最終在Razor頁面使用
@this.Lang("Welcome")
終於出現結果了
最終經過測試,在視圖中使用@Html.Lang()也正常工作