Globalization and localization in ASP.NET Core Part 1
前言
MSDN官網文章:ASP.NET Core 全球化和當地語系化
稍微看了官網文章覺得真是文言文Orz,自己整理一下,簡單提供Sample Code和說明,有需要的網友就自己Copy Paste
這種多國語系功能只適合網頁上不變動文字,如果網頁內容有ckeditor套件或留言版等等會讓使用者自行上稿文章的話,想實作多國語系功能就考慮使用Google相關API
實作
※本文版本為ASP.net Core 2.1
1.CultureInfo.CurrentCulture和日期、貨幣格式有關;CultureInfo.CurrentUICulture和語言有關。
2.每個Request預設根據RequestLocalizationOptions物件的Provider決定文化特性
預設Provider包含:
QueryStringRequestCultureProvider:在網址列加上查詢字串?culture=en-US或?culture=zh-TW,如下(個人覺得偏向開發偵錯使用)
CookieRequestCultureProvider: 讀取用戶端的Cookie來判斷是否有支持的文化特性,如果要讓用戶自己手動切換網頁語系,會需要用到這個
※2018.09.05追加 CookieRequestCultureProvider 的範例程式碼,請見文章下方
AcceptLanguageHeaderRequestCultureProvider: 讀取用戶端瀏覽器Header中的Accept-Language來判斷是否有支持的文化特性,通常用在自動切換網頁語系,本文範例程式碼以此為主。
上述預設Provider採用優先順序為QueryStringRequestCultureProvider > CookieRequestCultureProvider > AcceptLanguageHeaderRequestCultureProvider
說白話點,雖然瀏覽器header中的Accept-Language有zh-TW,但如果用戶在網址列上加入查詢字串?culture=en-US而且剛好en-US程式有支持的話,那麼會優先使用QueryStringRequestCultureProvider (文章最後有執行圖片可參考)
反之,如果用戶在網址列上加入查詢字串?culture=ja-JP,剛好程式不支持ja-JP的話,那程式就有可能採用CookieRequestCultureProvider或AcceptLanguageHeaderRequestCultureProvider
3.如果從QueryString、Cookie、瀏覽器的Accept-Language,程式都無法判斷該使用哪個Provider的話,則文化特性會採用DefaultRequestCulture的設定當做預設值(待會看程式碼就懂了)
4.在Controller使用 IStringLocalizer<T>實現多國語系功能
5.在View使用 IViewLocalizer實現多國語系功能
6.最後還有資源檔*.resx,存放路徑和命名有一定規則
7.Data Annotation當地語系化.....讓我拖搞一下,有空研究Orz
以下是Startup.cs程式碼
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
/*引用命名空間*/
using System.Globalization;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Localization;
namespace StarterM
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
//資源檔所在資料夾
services.AddLocalization(options=>options.ResourcesPath="Resources");
services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix);//要使用View多國語系的話就加這行程式碼
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
var supportedCultures = new CultureInfo[] {
new CultureInfo("en-US"),
new CultureInfo("zh-TW"),
};
app.UseRequestLocalization(new RequestLocalizationOptions()
{
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures,
//當預設Provider偵測不到用戶支持上述Culture的話,就會是↓
DefaultRequestCulture = new RequestCulture("zh-CN")//Default UICulture、Culture
});
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
}
接著看資源檔擺放的路徑和命名↓
↑Controller和View使用到的資源檔要分開,沒有加上文化特性名稱的 *.resx(HomeController.resx、Index.resx)就當做是en-US、zh-TW以外共用的語系資源
下面是資源檔的內容,注意存取修飾詞一律選擇「沒有程式碼產生」
然後在HomeController使用IStringLocalizer來讀取資源檔
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Diagnostics;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
namespace StarterM.Controllers
{
public class HomeController : Controller
{
private readonly IStringLocalizer<HomeController> _localizer;
public HomeController(IStringLocalizer<HomeController> localizer)
{
this._localizer = localizer;
}
public IActionResult Index()
{
ViewBag.msg = _localizer["Hello"];
//此行測試用
IRequestCultureFeature rcf = HttpContext.Features.Get<IRequestCultureFeature>();
return View(rcf);
}
}
}
以下是Index的View
@using System.Globalization
@using Microsoft.AspNetCore.Localization
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer MyViewLocalizer
@model IRequestCultureFeature
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Test 多國語系</title>
</head>
<body>
<h3>Provider: @Model?.Provider?.GetType()?.Name</h3>
<ul>
<li>Culture: @CultureInfo.CurrentCulture </li>
<li>CultureUI: @CultureInfo.CurrentUICulture</li>
<li>RequestCulture.Culture: @Model.RequestCulture.Culture</li>
<li>RequestCulture.UICulture: @Model.RequestCulture.UICulture</li>
<li>DateTime: @DateTime.Now.ToString() </li>
<li>Currency: @(10000.ToString("c"))</li>
</ul>
<!--以下文字會從資源檔讀取-->
<div>
Hello: @ViewBag.msg
</div>
<div>
<h1>CompanyName: @MyViewLocalizer["CompanyName"]</h1>
</div>
</body>
</html>
使用Postman軟體的執行結果:
最後展示一下QueryString優先權大於AcceptLanguage↓
2018.09.05 追加CookieRequestCultureProvider的範例程式碼
Startup.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
/*引用命名空間*/
using System.Globalization;
using Microsoft.AspNetCore.Mvc.Razor;
using Microsoft.AspNetCore.Localization;
namespace WebApplication1CookieCore
{
public class Startup
{
public void ConfigureServices(IServiceCollection services)
{
//資源檔所在資料夾
services.AddLocalization(options => options.ResourcesPath = "Resources");
services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix);//要使用View多國語系的話就加這行程式碼
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
var supportedCultures = new CultureInfo[] {
new CultureInfo("en-US"),
new CultureInfo("zh-TW"),
};
app.UseRequestLocalization(new RequestLocalizationOptions()
{
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures,
//當預設Provider偵測不到用戶支持上述Culture的話,就會是↓
DefaultRequestCulture = new RequestCulture("zh-TW")//Default UICulture、Culture
});
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
}
}
資源檔都和上述一樣,然後是HomeController
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
/*引用命名空間*/
using Microsoft.Extensions.Localization;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc.Rendering;
namespace WebApplication1CookieCore.Controllers
{
public class HomeController : Controller
{
private readonly IStringLocalizer<HomeController> _localizer;
public HomeController(IStringLocalizer<HomeController> localizer)
{
this._localizer = localizer;
}
public IActionResult Index()
{
ViewBag.msg = _localizer["Hello"];
//抓出目前使用哪個RequestCulture
IRequestCultureFeature rcf = HttpContext.Features.Get<IRequestCultureFeature>();
string c = rcf.RequestCulture.UICulture.Name;
//下拉選單的option
List<SelectListItem> options = new List<SelectListItem>() {
new SelectListItem(){ Text="zh-TW",Value="zh-TW",Selected=(c=="zh-TW"?true:false) },
new SelectListItem(){ Text="en-US",Value="en-US",Selected=(c=="en-US"?true:false) }
};
ViewBag.options = options;
return View(rcf);
}
/// <summary>
/// 用戶手動切換語系
/// </summary>
/// <param name="lang"></param>
/// <returns></returns>
[HttpPost]
public IActionResult ChangeLang(string c)
{
string lang = CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(c));
Response.Cookies.Append(CookieRequestCultureProvider.DefaultCookieName,lang);
return RedirectToAction("Index", "Home");//重新導向至Index Action
}
//清除cookie中的文化特性
public IActionResult Clear()
{
//這一行↓
Response.Cookies.Delete(CookieRequestCultureProvider.DefaultCookieName);
return Content("Clear OK");
}
}
}
↓Index的View
@using System.Globalization
@using Microsoft.AspNetCore.Localization
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer MyViewLocalizer
@model IRequestCultureFeature
@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title>Test 多國語系</title>
</head>
<body>
@using (Html.BeginForm("ChangeLang","Home",FormMethod.Post))
{
@Html.DropDownList("c", (List<SelectListItem>)ViewBag.options)
<input type="submit" value="變更語言" />
}
<h3>Provider: @Model?.Provider?.GetType()?.Name</h3>
<ul>
<li>Culture: @CultureInfo.CurrentCulture </li>
<li>CultureUI: @CultureInfo.CurrentUICulture</li>
<li>RequestCulture.Culture: @Model.RequestCulture.Culture</li>
<li>RequestCulture.UICulture: @Model.RequestCulture.UICulture</li>
<li>DateTime: @DateTime.Now.ToString() </li>
<li>Currency: @(10000.ToString("c"))</li>
</ul>
<!--以下文字會從資源檔讀取-->
<div>
Hello: @ViewBag.msg
</div>
<div>
<h1>CompanyName: @MyViewLocalizer["CompanyName"]</h1>
</div>
</body>
</html>
執行結果↓
手動切換回中文語系↓
結語
最後提供一個小技巧
本文的預設文化特性使用 DefaultRequestCulture = new RequestCulture("zh-CN"),其實是為了Demo才這樣寫
實務上的話,我會從zh-TW或en-US挑其中一種當做預設值↓ (因為早已準備好zh-TW、en-US兩種資源檔)
DefaultRequestCulture = new RequestCulture("en-US")
這樣有個好處,就不用再多準備通用語系資源檔 *.resx,Index.resx和HomeController.resx這兩個檔案就可以刪除啦~