[ASP.NET MVC] 多國語系切換 – 使用資料庫管理與兼容 JavaScript 顯示
網站的多國語系切換是走向國際發展的第一步,本篇文章將會教你如何使用 ASP.NET MVC 的多國語系切換功能,教學內容是接近實務應用的作法,使用資料庫管理多國語言,以及讓 JavaScript 檔案也可以顯示不同語系。
在官方的教學中是使用 Resources 儲存多國語系,當有多個語系時就要同時編寫多個檔案。
我的設計方式是將多國語系存放在資料庫裡面統一管理,再利用程式將資料庫裡面語系輸出至 Resources 裡面,與官方教學模式有些不同,有興趣的朋友可以多學一種設計模式。
之前寫多國語系時,*.cshtml 上文字可以使用資源檔的值,可是 JavaScript 的 *.js 檔案就無法使用,所以我另外提供一種讓 JavaScript 檔案也可以顯示多語系的方法給各位參考。
在最後提供此範例的原始程式碼下載,有需要的朋友可以自行下載來執行。
Contents
建立專案
開啟 Visual Studio 2022,建立新專案為「ASP.NET Web 應用程式 (.NET Framework)」。
輸入專案名稱、位置之後,選擇「MVC」範本,就可以建立專案。
加入全域資源檔
全域資源檔是存放多語系的文件,主要是存放語系對照表,不同語系就要建立一個檔案。
在專案上按右鍵選「加入 > 加入 ASP.NET 資料夾 > App_GlobalResources(R)」。
接著在出現的「App_GlobalResources」再按右鍵「加入 > 資源檔」。
第 1 個要建立的主要語系,我建議檔名直接用 “Language.resx” 就可以了。
接著建立第 2 個語系檔,我這裡用英文來示範,一樣在「App_GlobalResources」再按右鍵「加入 > 資源檔」,檔名可用 “Language.en-US.resx”。
如果有很多語系,就預先將文件建立起來,檔名規則預設語系是 “Language.resx”,其他語系就用 “Language.{語系-國家}.resx” 來建立。
建立資料表
這裡用 SQL Server 做教學資料庫,尚未安裝 SQL Server 的話,可參考這篇文章: Windows Server 如何安裝 SQL Server 2019 免費開發版
開啟資料庫,我已建好教學資料庫 “Teach”,繼續建立新 Table,以下是新 Table Schema。
1 2 3 4 5 6 7 8 9 10 |
CREATE TABLE [dbo].[Language]( [Lang_Key] [nvarchar](50) NOT NULL, [Lang_zhTW] [nvarchar](50) NOT NULL, [Lang_enUS] [varchar](100) NOT NULL, CONSTRAINT [PK_Language] PRIMARY KEY CLUSTERED ( [Lang_Key] ASC )WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, OPTIMIZE_FOR_SEQUENTIAL_KEY = OFF) ON [PRIMARY] ) ON [PRIMARY] GO |
會建立資料表來管理語系只是一種習慣的應用,方便管理與維護,以官方建議的方法是寫在資源檔裡面,兩種方法都行,取決於維護方便就好。
而我使用資料庫還是會將資料庫的內容寫入資源檔裡面,所以兩者結果是一樣的,只是多了一道步驟而已。
寫入測試資料
用以下一些測試資料來示範。
1 2 3 4 5 6 7 |
insert into [dbo].[Language]([Lang_Key],[Lang_zhTW],[Lang_enUS]) values (N'星期一',N'星期一','Monday') insert into [dbo].[Language]([Lang_Key],[Lang_zhTW],[Lang_enUS]) values (N'星期二',N'星期二','Tuesday') insert into [dbo].[Language]([Lang_Key],[Lang_zhTW],[Lang_enUS]) values (N'星期三',N'星期三','Wednesday') insert into [dbo].[Language]([Lang_Key],[Lang_zhTW],[Lang_enUS]) values (N'星期四',N'星期四','Thursday') insert into [dbo].[Language]([Lang_Key],[Lang_zhTW],[Lang_enUS]) values (N'星期五',N'星期五','Friday') insert into [dbo].[Language]([Lang_Key],[Lang_zhTW],[Lang_enUS]) values (N'星期六',N'星期六','Saturday') insert into [dbo].[Language]([Lang_Key],[Lang_zhTW],[Lang_enUS]) values (N'星期日',N'星期日','Sunday') |
關於 Key 欄位的值用中文或英文都行,差別是取用時的 Key 不同,而 Key 欄位的值要單純一點,不可放入符號或語法。
而 Key 與預設語言會分開欄位,這與在 Resources 建立對照表時是相同,Resources 本身是 Key/Value 對照表,這裡對預設語系也有 Key/Value 的對照,也就是 Lang_Key 與 Lang_zhTW 的對照欄位。
讀取資料庫寫入資源檔
這裡會寫一個方法將資料庫裡面的值,分別寫入不同的語系檔裡面,當我們有修改資料庫內的值時,就可以呼叫這個方法。
這裡我示範時就先寫在 HomeController 的 UpdateLang() 動作裡面,方便程式呼叫。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
/// <summary> /// 同步更新資料庫語系檔至 Resources /// </summary> /// <returns></returns> public ActionResult UpdateLang() { // 資源檔可寫入物件 ResXResourceWriter resxWriterTW = new ResXResourceWriter(Server.MapPath("~/App_GlobalResources/Language.resx")); ResXResourceWriter resxWriterUS = new ResXResourceWriter(Server.MapPath("~/App_GlobalResources/Language.en-US.resx")); // 取得資料庫語系 string connStr = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["ConnDB"].ConnectionString; using (var cn = new SqlConnection(connStr)) { // 使用 Dapper 查詢資料庫 var list = cn.Query( "SELECT Lang_Key, Lang_zhTW, Lang_enUS FROM [Language]"); foreach (var item in list) { // 寫入預設語系資源檔 resxWriterTW.AddResource(item.Lang_Key, item.Lang_zhTW); // 寫入英文資源檔 resxWriterUS.AddResource(item.Lang_Key, item.Lang_enUS); } } // 關閉資源檔 resxWriterTW.Close(); resxWriterUS.Close(); // 導回首頁 return RedirectToAction("Index", "Home"); } |
這裡查詢資料庫的方法我使用了 Dapper 套件,Dapper 是 ORM 的一種解決方案,也可以使用一般 ADO.NET 或是 EF 來查詢資料庫,只要查詢出來的資料寫入資源檔裡面就行了。
關於更多 Dapper 的語法,可參考「黑暗執行緒」的文章教學。
資料庫連線設定
資料庫連線設定放在 Web.config 裡面,可以加入以下語法設定連線字串。
1 2 3 |
<connectionStrings> <add name="ConnDB" connectionString="Data Source=127.0.0.1;Initial Catalog=Teach;Persist Security Info=false;User ID=test;Password=test;" providerName="System.Data.SqlClient"/> </connectionStrings> |
加入 Dapper 套件
開啟專案的「參考 > 管理 NuGet 套件」。
搜尋 “Dapper” 找到最新的「Dapper」套件安裝。
安裝完成後,就可在引用錯誤的語法上加入 using Dapper; 語法。
測試更新語系資源檔
這裡完成了第一個方法,目的是將資料庫語系寫入資源檔裡面,執行專案後,只要網址執行 /Home/UpdateLang 就可以執行方法。
更新之後再打開「Language.resx」可以看到預設語系已經寫入了。
英文語系檔「Language.en-US.resx」也有寫入值。
未來資料庫有新增值或刪除值的時候,只要執行這個方法,都可以同步更新到資源檔裡面。
顯示目前語系
這裡要講解的是顯示目前使用者語系,會從 Cookie 裡面取語系名稱,如果 Cookie 裡面沒有設定的話,則顯示預設語系。
建立專案底層類別
取得多語系是每一個 Controller 在呼叫前都會執行的動作,這裡我建立一個新類別 “ProjectBase” 來繼承 Controller,同時讓所有 Controller 繼承此類別。
在「Controllers 按右鍵 > 加入 > 類別」,加入一個新類別。
新類別名稱為 “ProjectBase”。
在新建立的 “ProjectBase” 類別裡面,我們直接繼承 Controller。
在 ProjectBase 後面增加 : Controller
編寫 OnActionExecuting 事件
OnActionExecuting 事件是 ASP.NET MVC 生命週期中的一個事件,順序排在執行 Controller 之前,所以我們先在 OnActionExecuting 事件裡面完成語系讀取,再顯示出來。
OnActionExecuting 事件方法就寫入剛剛新增的 ProjectBase 類別裡面。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 |
/// <summary> /// 覆寫 ActionExecuting 事件 /// </summary> /// <param name="filterContext"></param> protected override void OnActionExecuting(ActionExecutingContext filterContext) { // 語系名稱 var langName = ""; //從cookie裡讀取語言設定 HttpCookie cookie = filterContext.HttpContext.Request.Cookies["Localization.CurrentUICulture"]; if (cookie != null && cookie.Value != "") { //根據 cookie 值設定語言 langName = cookie.Value; } else if (filterContext.HttpContext.Request.UserLanguages != null) { // 使用瀏覽器預設語言 if (filterContext.HttpContext.Request.UserLanguages.Length > 0) { langName = filterContext.HttpContext.Request.UserLanguages[0]; } } //自行判斷可接受的語系名稱,不符名稱則採用預設語系 if (langName != "zh-TW" && langName != "en-US") { langName = "zh-TW"; } ViewData["_Language"] = langName; // 更換語系設定 Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(langName); // 把設定儲存進cookie if (cookie == null) { cookie = new HttpCookie("cookie"); } cookie.Value = langName; cookie.Expires = DateTime.Now.AddMonths(1); //儲存 1 個月 cookie.Secure = true; cookie.HttpOnly = true; cookie.SameSite = SameSiteMode.Lax; filterContext.HttpContext.Response.Cookies.Add(cookie); base.OnActionExecuting(filterContext); } |
HomeController 繼承 ProjectBase
剛剛新增的 ProjectBase 類別就要讓所有的專案 Controller 繼承使用。
打開 \Controllers\HomeController.cs 將原本的 public class HomeController : Controller
改成 public class HomeController : ProjectBase
。
修改 View 頁面
這裡我用 \Views\Home\Index.cshtml 做為前端示範,先清空 \Views\Home\Index.cshtml 裡面原本的語法,然後加入以下語法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
<h3>目前語系:</h3> @ViewData["_Language"] <h3>顯示語系值</h3> @Resources.Language.星期一 <br /> @Resources.Language.星期二 <br /> @Resources.Language.星期三 <br /> @Resources.Language.星期四 <br /> @Resources.Language.星期五 <br /> @Resources.Language.星期六 <br /> @Resources.Language.星期日 |
執行專案後,就會顯示以下畫面。
更換語系
可以正常顯示語系值之後,接著來切換不同的語系,我們在 HomeController 裡面增加一個 ChangeLang() 的動作來執行切換語系,將語系名稱寫入 Cookie 就可以了。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
/// <summary> /// 切換語系 /// </summary> /// <param name="langCode"></param> /// <returns></returns> public ActionResult ChangeLang(string langName) { // 檢查輸入值 if (langName != "zh-TW" && langName != "en-US") { langName = "zh-TW"; } // 把設定儲存進cookie HttpCookie cookie = new HttpCookie("Localization.CurrentUICulture"); cookie.Value = langName; cookie.Expires = DateTime.Now.AddMonths(1); //儲存 1 個月 cookie.Secure = true; cookie.HttpOnly = true; cookie.SameSite = SameSiteMode.Lax; HttpContext.Response.Cookies.Add(cookie); // 導回首頁 return RedirectToAction("Index", "Home"); } |
編寫 View 語法
接著在前端新增呼叫切換語系的連結,在 \Views\Home\Index.cshtml 繼續加入以下語法。
1 2 |
<h3>切換語系</h3> <a href="@Url.Action("ChangeLang", "Home", new { langName = "zh-TW" })">中文</a> | <a href="@Url.Action("ChangeLang", "Home", new { langName = "en-US" })">英文</a> |
完成後,再按 F5 測試一下專案,就會出現「中文」與「英文」的連結,點下去之後,上面的語系值就會跟著切換了。
更換資料庫欄位語系
我們已經設計了將語系資源檔顯示在畫面上了,但如果遇到從資料庫讀取的值要更換成不同語系該怎麼做呢?
我建議可以在資料庫內的欄位存放「語系 Key 值」,程式讀取 Key 值後,再依當時的語系名稱轉換。
我在 HomeController 的 Index() 裡面新增一段語法,取得資料庫欄位,然後將欄位的值轉換成語系值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
//更換資料庫欄位語系 StringBuilder sbTest = new StringBuilder(); // 資料庫連線字串 string connStr = System.Web.Configuration.WebConfigurationManager.ConnectionStrings["ConnDB"].ConnectionString; // 目前語系檔 ResourceSet langSet = Language.ResourceManager.GetResourceSet(CultureInfo.CurrentUICulture, true, true); using (var cn = new SqlConnection(connStr)) { // 取得欄位資料 var list = cn.Query("SELECT Lang_Key FROM [Language]"); foreach (var item in list) { // 從目前語系檔裡面取得 Key/Value if (langSet.GetObject(item.Lang_Key) != null) { // 存在語系檔裡面 sbTest.Append(langSet.GetObject(item.Lang_Key) + "<br>"); } else { sbTest.Append(item.Lang_Key + "<br>"); } } } ViewData["ConvertColumnLang"] = sbTest.ToString(); |
編寫 View 語法
接著在前端新增呼叫切換語系的連結,在 \Views\Home\Index.cshtml 繼續加入以下語法。
1 2 |
<h3>資料庫欄位轉換語系值</h3> @Html.Raw(ViewData["ConvertColumnLang"]) |
完成後,再按 F5 測試一下專案,在下方就會出現從資料庫欄位的值轉換語系,未來你就可以在資料表放「語系 Key 值」就行了,語系可以轉換成使用者語系。
JavaScript 顯示語系值
在 Razor 顯示語系的語法是 @Resources.Language.{語系Key值}
可是這樣的用法無法直接放在 *.js 的檔案裡面使用。
所以我使用了另一種方法,讓 *.js 檔案也可以呼叫。
在 OnActionExecuting()
事件裡面,將語系對照檔轉為 JavaScript 物件,然後放在 View 裡面,就可以呼叫物件的方法來取得語系值,
可在 OnActionExecuting()
事件加入以下語法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// 產生 JavaScript 語系對照表 ResourceSet languageSet = Language.ResourceManager.GetResourceSet(CultureInfo.CurrentUICulture, true, true); var sbInitial = " var JsLangObj = {"; var sbRes = new StringBuilder(sbInitial); var resEnum = languageSet.GetEnumerator(); while (resEnum.MoveNext()) { if (sbRes.ToString() != sbInitial) { sbRes.Append(","); } sbRes.Append("\"" + resEnum.Key + "\":\"" + resEnum.Value.ToString().Replace("\r\n", "").Replace("\"", "\\\"") + "\""); } sbRes.Append("}"); ViewData["_JsLangObj"] = sbRes.ToString(); |
新增 MyScript.js 檔案
我們在 Scripts 裡面增加一個 JavaScript 檔。
檔名就用 “MyScript” 示範。
然後在 “MyScript” 裡面放一個簡單的 alert 語法。
1 2 3 4 |
// 顯示語系值 function showJsLangObj() { alert(JsLangObj['星期一'] + '\n' + JsLangObj['星期二'] + '\n' + JsLangObj['星期三'] + '\n' + JsLangObj['星期四'] + '\n' + JsLangObj['星期五'] + '\n' + JsLangObj['星期六'] + '\n' + JsLangObj['星期日']); } |
編寫 View 語法
接著在前端新增語法,顯示 JsLangObj 物件,引用 MyScript.js 檔案,並且寫一個測試按鈕來顯示語系值。
在 \Views\Home\Index.cshtml 繼續加入以下語法。
1 2 3 4 5 6 7 8 9 |
<h3>JavaScript 顯示語系</h3> <script type="text/jscript"> //多語系物件 @(Html.Raw(ViewData["_JsLangObj"])); </script> <script src="~/Scripts/MyScript.js"></script> <button onclick="showJsLangObj();">取得 JavaScript 語系值</button> |
呼叫語系值的方法是 JsLangObj['{語系Key值}']
,輸入 Key 就會顯示語系的內容。
呼叫的方法可以放在 *.js 檔案裡面也適用,只要注意順序就好,@(Html.Raw(ViewData["_JsLangObj"]));
要先執行才會產生可用的物件。
完成後,再按 F5 測試一下專案,按下「取得 JavaScript 語系值」,就可以在 JavaScript 顯示語系值了。
如果按 F12 檢視原始檔,可以發現其原理是產生 Key/Value 的物件而已,傳入 Key 可得到 Value。
重點整理
- 資源檔加入各語系的檔案
- 資料表設計欄位管理語系值
- 提供方法將資料表寫入各語系資源檔
- 在 OnActionExecuting 顯示語系值
- 提供方法切換語系名稱寫入 Cookie
- 在 OnActionExecuting 將語系轉 js 物件
- 資料表欄位及 JavaScript 都可以轉換語系
範例下載
相關學習文章
- [ASP.NET MVC] 產生 Bootstrap + Vue.js 多層式選單範本教學 (附範例)
- [ASP.NET MVC] 前台會員使用 Cookie 保持登入狀態範例教學 #CH5 (附範例)
- [ASP.NET MVC + Vue.js] 動態問卷表單製作教學
如果你在學習上有不懂的地方,需要諮詢服務,可以參考站長服務,我想辨法解決你的問題
如果文章內容有過時、不適用或錯誤的地方,幫我在下方留言通知我一下,謝謝