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>&copy;@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>&copy;@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以上才可以

好煩喔@@ 到最後還是版本問題在搞我

留言

這個網誌中的熱門文章

ASP.net MVC 多國語言 globalization (part 1)

[公路車] 代打 - 日月潭環湖 (首次電變)