ASP.net MVC 多國語言 globalization (part 2)
ASP.net MVC 多國語言 globalization (part 2)
上次大概了解了所謂的Globalization,並且用Resource File 當作我們的語言資料抓取,最後利用連結呼叫Controller改變我們顯示的語系。 但上一次有很明顯的問題:
- 切到別的畫面這功能就沒用了
- 在別的畫面沒辦法切換語系
- 切換語系之後再換頁面設定就跑掉了
所以這次我們就要來解決這些問題。
要能解決這些問題,目標其實挺明顯的,就是這項功能要有全域化的設定,第一個想到的就是Global.asax這個檔案,它位於整個專案的根目錄,這一篇大約介紹了這個檔案大概是做甚麼的。 簡單來說,每當有任何請求來的時候都會先經過這裡,加上基於它是HttpApplication的衍伸。
所以很直覺的我們當初在HomeController設定的CurrentCulture與CurrentUICulture應該要在這裡設定才對,並且當有請求的時候我們都要設定一次,這也是替等等的語系切換做準備,所以打開Global.asax 並在裡面增加 Application_BeginRequest的方法,並且把上次的語系設定程式加進來:
protected void Application_BeginRequest(object sender,EvenArgs e){
String cultureName ="en-US";
System.Threading.Thread.CurrentThread.CurrentCulture =
new System.Globalization.CultureInfo(cultureName);
System.Threading.Thread.CurrentThread.CurrentUICulture =
new System.Globalization.CultureInfo(cultureName);
}
不要忘了,我們是要測試多個畫面的語系切換,所以至少再開一個畫面,並且裡面也加一個Resource file 裡面的資訊。我這裡的例子一樣是利用.net MVC預設提供的About.cshtml,當然Resource file也不要忘記加了
About.cshtml (隨便找一個位置放就好)
<p>@Resuorces.Resource.about</p>
Resource file (我這邊增各加了一個about)
設定好了之後,記得把之前原本在HomeController的程式刪掉,然後執行看看,並且在"首頁"與"關於"這兩個換面切換看看,看是不是都是同一個語系。如果是的話我們就完成一步拉!
String cultureName ="en-US";
這是怎麼回事,怎麼寫死英文語系了!?
不要緊張,上次我們不是提到只要把cultureName改成一個傳遞變數就好了嗎,那這邊當然也是同理,我們只要把值傳到Global不就.....好...了...嗎....? 欸~那要怎麼傳到Global 阿?
上面有提到Global.asax是HttpApplication的衍伸,自然的就可以想到用Cookie拉
我們將Global內程式做修改
protected void Application_BeginRequest(Object sender, EventArgs e) {
HttpCookie cultureCookie = Request.Cookies["_culture"];
var cultureName = "en-US";
if (cultureCookie != null) {
cultureInfoName = cultureCookie.Value;
} else {
HttpApplication application = (HttpApplication)sender;
HttpContext context = application.Context;
if (context.Request.UserLanguages != null && Request.UserLanguages.Length > 0) {
cultureName = Request.UserLanguages[0];
}
}
System.Threading.Thread.CurrentThread.CurrentCulture = new System.Globalization.CultureInfo(cultureName );
System.Threading.Thread.CurrentThread.CurrentUICulture = new System.Globalization.CultureInfo(cultureName );
}
稍微簡單說明一下:
- 首先,我們先嘗試抓取Cookies裡面有沒有"_culture"
- 如果有就直接設定Cookies內的值
- 如果沒有我們就抓取request header裡面的語言當作是預設語言。(也就是瀏覽器丟給我們的)
到目前為止,感覺我們又回到起點了,因為目前使用者沒辦法切換語言,再加上很明顯我們Cookies完全還沒有動到,所以就會變得跟當初只依靠瀏覽器丟過來的資訊狀況一樣。
但還是不要緊張,至少我們已經做好接Cookie的準備了,接下來想辦法把Cookie設定好,我們就差不多快成功拉!
通常我們可以信任瀏覽器丟給我們的語系設定,畢竟某方面來說這也是使用者的設定,所以當Cookie裡面有資訊的時候,通常就是他們對網頁有所操作,就好比說語系變動,所以每當有語系更動,我們就應該要把更動的項目記到Cookie裡面,讓我們在網頁之間切換的時候依然記住使用者設定的資訊。
語系更動其實我們在上一次已經有做了,就是那些超連結,問題是這樣就只有那個畫面能切換語系,如果我們想要在任何畫面上都能切換呢?
答案其實很簡單,我們就利用.net的共同layout就好啦,不論哪個畫面都會有最上面的Menu bar跟最底下的footer對吧!擺在那邊就好啦
在Views/Shared/_Layout.cshtml裡面,我這邊是加到footer裡面
<footer>
<p>©@Data.Now.Year - 我的 ASP.NET 應用程式</p>
<p>
@Html.ActionLink("English","Index","Home",new { lang = "en-US"},null);
@Html.ACtionLink("中文","Index","Home",new { lang = "zh-TW"},null);)
</p>
</footer>
老師!已經完成複製貼上,但是完全沒有作用!
傻孩子,當然沒有作用拉
第一:在還沒改程式的狀態下,這個超連結還是呼叫HomeController,而HomeController的程式都被你刪光拉
第二:就算沒刪掉程式點擊下去會發現到頭來又回到首頁了
最後我們連Cookie都沒有設定阿!
但是我們好好想一下,這應該是HomeController要處理的事情嗎?既然是每個畫面都有可能會觸發的前提下,那我們就應該丟給每一個畫面都會處理到的Controller才對。這時候我就會想到BaseController
一個讓其他Controller都繼承的BaseController,這不只是在多國語言有用,只要是每個Controller都有的共通的功能,都可以寫在BaseController,所以我們就手動新增一個BaseController吧
BaseController繼承了Controller,所以在其他Controller在繼承的時候繼承BaseController就夠了
public class BaseController : Controller{
public BaseController() {
}
public ActionResult SetCulture(string culture, string returnUrl){
HttpCookie cookie = Request.Cookies["_culture"];
if(cookie == null){
cookie = new HttpCookies["_culture"];
cookie.Value = culture;
cookie.Expires = DataTime.Now.AddYears(1);
} else {
cookie.Value = culture;
}
Response.Cookies.Add(cookie);
return Redirect(returnUrl);
}
}
非常單純的程式碼
- 新增一個方法叫SetCulture,並且有culture與returnUrl兩個參數
- 讀取"_culture" Cookie
- 如果裡面沒有東西,新增一個給它,並設定值等於使用者傳遞過來的culture值,設定一年後過期
- 如果裡面有東西了,用新的culture值蓋過去
- 最後加回Response裡面
- 返回到當初呼叫這個方法的網址returnUrl
最後就是我們畫面上的連結拉
回到_Layout.cshtml我們來修改一下吧
<footer>
<p>©@Data.Now.Year - 我的 ASP.NET 應用程式</p>
<p>
@Html.ActionLink("English","SetCulture","Base",new { lang = "en-US",returnUrl = this.Request.RawUrl},null);
@Html.ACtionLink("中文","SetCulture","Base",new { lang = "zh-TW",returnUrl = this.Request.RawUrl},null);
</p>
</footer>
我們把呼叫的方法、呼叫的Controller、多傳一個returnUrl的值都做了更新
基本上我們就已經大功告成拉!\(@^0^@)/
但是,就是這個但是搞了我整整一天,Chrome 80 後針對第三方 Cookie 的規則調整
所以我們在Controller最後要把Cookie加入Respones之前必須多打一行
cookie.SameSite = SameSiteMode.Lax;
而且要注意,要能使用SameSite必須更新.net Framwork 4.7.2以上才可以
好煩喔@@ 到最後還是版本問題在搞我
留言
張貼留言