建立多語系檔案
在專案中建立 Resources 資料夾,在資料夾上點選滑鼠右鍵 > 加入 > 新增項目:選擇「資源檔」
這裡的檔名要依照使用的位置來命名。
例如要在 Controllers 資料夾內的 UserController.cs 檔案使用則檔名就要叫做 Controllers.UserController.resx (預設資源檔),語系為 zh-TW 則是 Controllers.UserController.zh-TW.resx
覺得這樣檔名太長還可以使用資料夾的方式,就會變成 Controllers/UserController.resx 和 Controllers/UserController.zh-TW.resx 本文的範例如下:
Controllers/UserController.zh-TW.resx
Hello 哈囉
Hello_Name 哈囉 {0}!
Controllers/UserController.en-US.resx
Hello Hello
Hello_Name Hello {0}!
在 Program.cs 中加入以下程式碼:
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
builder.Services.Configure<RequestLocalizationOptions>(options =>
{
// 預設語言 (不指定就會使用裝置的預設語系)
options.DefaultRequestCulture = new RequestCulture("zh-TW");
// 支援的語言
var supportedCultures = new[]
{
new CultureInfo("en-US"),
new CultureInfo("zh-TW"),
};
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
});
var app = builder.Build();
app.UseRequestLocalization();
app.Run();
常見錯誤
在存取 IStringLocalizer _localizer 時出現錯誤:
fail: Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware[1]
An unhandled exception has occurred while executing the request.
System.InvalidOperationException: Unable to resolve service for type 'Microsoft.Extensions.Localization.IStringLocalizerFactory' while attempting to activate 'WebApplicationI18nTest0706.Controllers.MyController'.
at Microsoft.Extensions.DependencyInjection.ActivatorUtilities.ThrowHelperUnableToResolveService(Type type, Type requiredBy)
at lambda_method3(Closure, IServiceProvider, Object[])
at Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider.>>c__DisplayClass6_0.>CreateControllerFactory>g__CreateController|0(ControllerContext controllerContext)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvoker.InvokeInnerFilterAsync()
--- End of stack trace from previous location ---
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.>InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.>InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.>InvokeAsync>g__Awaited|17_0(ResourceInvoker invoker, Task task, IDisposable scope)
at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context)
at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext)
at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider)
at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)
代表少了下面的程式碼,補上即可:
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");
依照語系替換內容
這裡最簡單示範單純替換文字和可以帶入參數替換的兩種多語系替換方式:
using System.Globalization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
[ApiController]
[Route("[controller]")]
public class UserController : ControllerBase
{
private readonly IStringLocalizer<UserController> _localizer;
public UserController(IStringLocalizer<UserController> localizer)
{
_localizer = localizer;
}
[HttpGet]
public IActionResult Get()
{
// 取得當前 API 的語系
Console.WriteLine($"CultureInfo: {CultureInfo.CurrentCulture}");
var hello = _localizer["Hello"].Value;
Console.WriteLine($"Hello: {hello}");
string input = "Ruyut";
var helloName = _localizer["Hello_Name", input].Value;
Console.WriteLine($"Hello_Name: {helloName}");
return Ok(helloName);
}
}
API 指定語言
在呼叫 API 時,可以在 QueryString 中帶入 culture 參數來指定語言,下面示範將語言指定為 en-us :
http://localhost:5202/User?culture=en-us
也可以透過 Header 加入 Accept-Language 參數來達成:
curl --location 'http://localhost:5202/User' \
--header 'Accept-Language: en-us'
註:在筆者的測試中發現不區分大小寫, en-US 和 en-us 相同,zh-tw 和 zh-TW 也是相同。
輸出範例
zh-TW (預設)
Hello: 哈囉
Hello_Name: 哈囉 Ruyut!
en-US
Hello: Hello
Hello_Name: Hello Ruyut!
每個功能都需要一個單獨的資源檔很麻煩怎麼辦?筆者後來研究出來了,可以查看這篇:ASP.NET Core Web API 多語系(i18n) 共用資源檔
參考資料:
Microsoft.Learn - Globalization and localization in ASP.NET Core
Microsoft.Learn - Make an ASP.NET Core app's content localizable
Microsoft.Learn - Troubleshoot ASP.NET Core localization
StackOverflow - ASP.NET Core Request Localization Options
留言
張貼留言
如果有任何問題、建議、想說的話或文章題目推薦,都歡迎留言或來信: a@ruyut.com