
คู่มือโลคัลไลเซชัน ASP.NET Core อย่างครบถ้วน
ตั้งแต่ IStringLocalizer จนถึงระบบจริง ตั้งค่าโลคัลไลเซชันแบบใช้ทรัพยากรใน ASP.NET Core แล้วทำให้การแปลเป็นอัตโนมัติด้วย AI
เปิดใช้บริการโลคัลไลเซชัน
ลงทะเบียนบริการโลคัลไลเซชันใน Program.cs ด้วย AddLocalization() กำหนดค่าวัฒนธรรมที่รองรับ และเพิ่มมิดเดิลแวร์โลคัลไลเซชันคำขอ วิธีนี้เชื่อมไปป์ไลน์โลคัลไลเซชันทั้งหมดสำหรับแอป ASP.NET Core
using Microsoft.AspNetCore.Localization;
using System.Globalization;
var builder = WebApplication.CreateBuilder(args);
// 1. Register localization services
builder.Services.AddLocalization(o => o.ResourcesPath = "Resources");
// 2. Add MVC with view/data-annotation localization
builder.Services.AddControllersWithViews()
.AddViewLocalization()
.AddDataAnnotationsLocalization();
var app = builder.Build();
// 3. Configure supported cultures
var supportedCultures = new[] { "en", "de", "ja", "es", "pt-BR" }
.Select(c => new CultureInfo(c)).ToArray();
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("en"),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures,
});
app.UseStaticFiles();
app.UseRouting();
app.MapControllers();
app.Run();สร้างไฟล์ทรัพยากร RESX
ASP.NET Core ใช้ไฟล์ RESX (ทรัพยากร XML) สำหรับคำแปล สร้างหนึ่งไฟล์ต่อวัฒนธรรมต่อคลาส เช่น HomeController.en.resx, HomeController.de.resx และอื่นๆ เฟรมเวิร์กจะค้นหาไฟล์ที่ถูกต้องตามวัฒนธรรมของคำขอปัจจุบัน
<!-- Resources/Controllers/HomeController.en.resx -->
<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="Welcome" xml:space="preserve">
<value>Welcome to our application</value>
</data>
<data name="Greeting" xml:space="preserve">
<value>Hello, {0}!</value>
</data>
</root>
<!-- Resources/Controllers/HomeController.de.resx -->
<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="Welcome" xml:space="preserve">
<value>Willkommen in unserer Anwendung</value>
</data>
<data name="Greeting" xml:space="preserve">
<value>Hallo, {0}!</value>
</data>
</root><!-- Resources/SharedResource.en.resx — shared across controllers -->
<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="AppName" xml:space="preserve">
<value>My Application</value>
</data>
<data name="Save" xml:space="preserve"><value>Save</value></data>
<data name="Cancel" xml:space="preserve"><value>Cancel</value></data>
</root>
// Marker class (empty — only used for type lookup)
namespace MyApp;
public class SharedResource { }ใช้ IStringLocalizer ในคอนโทรลเลอร์และบริการ
ฉีด IStringLocalizer<T> ลงในคอนโทรลเลอร์ บริการ หรือมิดเดิลแวร์ผ่านการฉีดการพึ่งพา พารามิเตอร์ชนิดทั่วไป T กำหนดว่าจะโหลดไฟล์ RESX ใด ใช้ไวยากรณ์วงเล็บ localizer["Key"] เพื่อดึงข้อความที่แปลแล้วพร้อมพารามิเตอร์รูปแบบเสริม
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
public class HomeController(
IStringLocalizer<HomeController> localizer,
IStringLocalizer<SharedResource> shared) : Controller
{
public IActionResult Index()
{
ViewData["Welcome"] = localizer["Welcome"];
ViewData["AppName"] = shared["AppName"];
// String interpolation with format parameters
var greeting = localizer["Greeting", User.Identity?.Name ?? "Guest"];
return View(new HomeViewModel { Greeting = greeting });
}
}กำหนดค่ามิดเดิลแวร์วัฒนธรรมคำขอ
ASP.NET Core เลือกวัฒนธรรมคำขอจากลำดับผู้ให้บริการ ได้แก่ สตริงคำขอ คุกกี้ และส่วนหัว Accept-Language ตามลำดับ คุณเพิ่มผู้ให้บริการแบบกำหนดเองได้ เช่น อ่านวัฒนธรรมจากเซกเมนต์เส้นทาง URL อย่าง /de/home
// Culture resolved in order: QueryString, Cookie, Accept-Language
// Custom provider: read culture from URL route segment /de/home
public class RouteDataRequestCultureProvider : RequestCultureProvider
{
public override Task<ProviderCultureResult?> DetermineProviderCultureResult(
HttpContext httpContext)
{
var culture = httpContext.GetRouteValue("culture")?.ToString();
if (string.IsNullOrEmpty(culture))
return NullProviderCultureResult;
return Task.FromResult<ProviderCultureResult?>(
new ProviderCultureResult(culture));
}
}
// Register in Program.cs (route provider first = highest priority):
app.UseRequestLocalization(new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture("en"),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures,
RequestCultureProviders = new List<IRequestCultureProvider>
{
new RouteDataRequestCultureProvider(),
new QueryStringRequestCultureProvider(),
new CookieRequestCultureProvider(),
new AcceptLanguageHeaderRequestCultureProvider(),
}
});// Language switcher: persist choice in cookie
[HttpPost]
public IActionResult SetLanguage(string culture, string returnUrl)
{
Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) });
return LocalRedirect(returnUrl);
}ทำโลคัลไลเซชันคำอธิบายข้อมูล
แอตทริบิวต์ตรวจสอบอย่าง [Required], [StringLength] และ [Display] ทำโลคัลไลเซชันได้ด้วยการตั้งพร็อพเพอร์ตี ErrorMessage หรือ Name เป็นชื่อคีย์ RESX เรียก AddDataAnnotationsLocalization() ใน Program.cs เพื่อเปิดใช้
using System.ComponentModel.DataAnnotations;
public class RegisterViewModel
{
[Required(ErrorMessage = "NameRequired")]
[Display(Name = "FullName")]
[StringLength(100, ErrorMessage = "NameLength", MinimumLength = 2)]
public string Name { get; set; } = string.Empty;
[Required(ErrorMessage = "EmailRequired")]
[EmailAddress(ErrorMessage = "EmailInvalid")]
[Display(Name = "EmailAddress")]
public string Email { get; set; } = string.Empty;
[Required(ErrorMessage = "PasswordRequired")]
[StringLength(100, ErrorMessage = "PasswordLength", MinimumLength = 8)]
[Display(Name = "Password")]
public string Password { get; set; } = string.Empty;
}
// RESX keys map to ErrorMessage/Name values:
// RegisterViewModel.de.resx: NameRequired = "Name ist erforderlich"
// RegisterViewModel.de.resx: FullName = "Vollständiger Name"จัดการพหูพจน์และข้อความ ICU
.NET ไม่รองรับกฎพหูพจน์ในตัวอย่าง ICU สำหรับกรณีง่าย ให้ใช้คีย์ RESX แยก (ItemCount_One, ItemCount_Other) พร้อม switch ในโค้ด สำหรับ ICU MessageFormat เต็มรูปแบบในทุกหมวดหมู่พหูพจน์ CLDR ให้ใช้ไลบรารี MessageFormat.NET
// Option 1: Separate RESX keys with code switch
// HomeController.en.resx: ItemCount_One = "You have {0} item"
// HomeController.en.resx: ItemCount_Other = "You have {0} items"
public string GetItemCount(int count)
{
var key = count == 1 ? "ItemCount_One" : "ItemCount_Other";
return _localizer[key, count];
}
// Option 2: ICU MessageFormat (dotnet add package MessageFormat.NET)
using Jeffijoe.MessageFormat;
var formatter = new MessageFormatter();
var pattern = "{count, plural, one {# item} other {# items}} in your cart";
var result = formatter.FormatMessage(pattern,
new Dictionary<string, object> { { "count", 5 } });
// => "5 items in your cart"
// Arabic: 6 plural forms (zero, one, two, few, many, other)
var arPattern = @"{count, plural,
zero {لا عناصر} one {عنصر واحد} two {عنصران}
few {# عناصر} many {# عنصرًا} other {# عنصر}}";ทำโลคัลไลเซชันมุมมอง Razor
ใช้ IViewLocalizer ในมุมมอง Razor ผ่าน @inject โดยจะค้นหาไฟล์ RESX ตามพาธไฟล์ของมุมมอง สำหรับข้อความที่ปลอดภัยต่อ HTML และมีมาร์กอัป ให้ใช้ IHtmlLocalizer ส่วน Tag Helper อย่าง asp-for และ asp-validation-for จะใช้แอตทริบิวต์ Display กับ ErrorMessage ฉบับโลคัลไลซ์โดยอัตโนมัติ
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@inject IHtmlLocalizer<SharedResource> SharedHtml
<h1>@Localizer["Welcome"]</h1>
<p>@Localizer["Greeting", User.Identity?.Name]</p>
@* IHtmlLocalizer: does NOT escape — use for RESX values with HTML *@
<p>@SharedHtml["TermsNotice"]</p>
@* Tag Helpers auto-localize Display/ErrorMessage attributes *@
<form asp-action="Register">
<label asp-for="Name"></label>
<input asp-for="Name" />
<span asp-validation-for="Name"></span>
<button type="submit">@Localizer["Submit"]</button>
</form>ทำให้การแปล RESX เป็นอัตโนมัติ
เมื่อตั้งค่าโลคัลไลเซชันเสร็จแล้ว ให้แปลไฟล์ RESX ด้วย AI โดยบอกผู้ช่วย AI ใน IDE ให้แปล RESX ต้นฉบับ หรือใช้ CLI ของ i18n Agent ในไปป์ไลน์ CI/CD เพื่อให้คำแปลซิงค์กัน
# In your IDE, ask your AI assistant:
> Translate Resources/Controllers/HomeController.en.resx to German, Japanese, Spanish
# HomeController.de.resx created (1.2s)
# HomeController.ja.resx created (1.5s)
# HomeController.es.resx created (1.1s)
# Or use the CLI in CI/CD:
npx i18n-agent translate Resources/Controllers/HomeController.en.resx --lang de,ja,esทำให้คุณภาพการแปลเป็นอัตโนมัติ
แก้การใช้ภาษาสำรองด้วย LocaleChain.NET
ลำดับชั้น CultureInfo.Parent ในตัวของ .NET ใช้เฉพาะการตัดทอน BCP 47 โดย pt-BR ถอยไปใช้ pt แล้ว InvariantCulture และข้าม pt-PT LocaleChain.NET มีลำดับการใช้ภาษาสำรองที่กำหนดค่าได้รายภาษาสำหรับระบบ .NET ทั้งหมด
dotnet add package I18nAgent.LocaleChainusing I18nAgent.LocaleChain;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddLocalization(o => o.ResourcesPath = "Resources");
// Zero-config: built-in chains (pt-BR -> pt-PT -> pt -> en, etc.)
LocaleChain.Configure();
// Or customize specific chains:
LocaleChain.Configure(new Dictionary<string, string[]>
{
["pt-BR"] = new[] { "pt-PT", "pt", "en" },
["es-MX"] = new[] { "es-419", "es", "en" },
});
// Register the chain-aware string localizer
builder.Services.AddSingleton(
typeof(IStringLocalizer<>),
typeof(LocaleChainStringLocalizer<>));ข้อผิดพลาดที่พบบ่อย
ไม่ได้ตั้งวัฒนธรรมในคำขอ
หาไฟล์ RESX ไม่พบ
ลำดับมิดเดิลแวร์ไม่ถูกต้อง
เธรดเบื้องหลังใช้วัฒนธรรมผิด
โครงสร้างโปรเจกต์ที่แนะนำ
MyAspNetApp/
├── Controllers/
│ └── HomeController.cs
├── ViewModels/
│ └── RegisterViewModel.cs
├── Views/
│ └── Home/
│ └── Index.cshtml
├── Resources/
│ ├── Controllers/
│ │ ├── HomeController.en.resx # English (source)
│ │ ├── HomeController.de.resx # German
│ │ └── HomeController.ja.resx # Japanese
│ ├── ViewModels/
│ │ ├── RegisterViewModel.en.resx
│ │ └── RegisterViewModel.de.resx
│ ├── Views/Home/
│ │ ├── Index.en.resx
│ │ └── Index.de.resx
│ └── SharedResource.en.resx
├── SharedResource.cs # Marker class
├── Program.cs
└── MyAspNetApp.csprojแปลรายการต่อไปนี้ได้ด้วย :
ลองใช้ i18n Agent ตอนนี้
ลากและวางไฟล์แปลของคุณที่นี่
JSON, YAML, PO, XML, CSV, Markdown, Properties
หรือคลิกเพื่อเลือกไฟล์
ภาษาเป้าหมาย
การใช้ภาษาสำรองด้วย I18nAgent.LocaleChain
เมื่อไม่มีคีย์คำแปลในภาษาตามภูมิภาคอย่าง pt-BR .NET จะข้ามไปใช้วัฒนธรรมคงที่ทันทีแทนที่จะตรวจภาษาหลัก pt ก่อน
dotnet add package I18nAgent.LocaleChainusing I18nAgent.LocaleChain;
LocaleChain.Configure(new Dictionary<string, string[]>
{
["pt-BR"] = new[] {"pt", "en"},
["zh-Hant-HK"] = new[] {"zh-Hant", "zh", "en"},
});ดูคู่มือการใช้ภาษาสำรองของเราสำหรับรายการเฟรมเวิร์กที่รองรับทั้งหมดและลำดับสำเร็จรูป 75 รายการ Learn more →