Nguon: Microsoft Learn · .NET 8.0

Globalization (Toàn cầu hóa) và Localization (Bản địa hóa) trong ASP.NET Core

Nguồn: Globalization and localization in ASP.NET Core | Phiên bản: .NET 8.0

Tác giả: Rick Anderson, Damien Bowden, Bart Calixto, Nadeem Afana, và Hisham Bin Ateya

Một trang web đa ngôn ngữ cho phép tiếp cận đối tượng người dùng rộng hơn. ASP.NET Core cung cấp các dịch vụ và Middleware (phần mềm trung gian) để bản địa hóa nội dung sang các ngôn ngữ và Culture (văn hóa/ngôn ngữ) khác nhau.

Để biết hướng dẫn về Localization (bản địa hóa) trong Blazor — bao gồm hoặc thay thế hướng dẫn trong bài viết này — hãy xem ASP.NET Core Blazor globalization and localization.

Thuật ngữ

Mã ngôn ngữ và mã quốc gia/vùng lãnh thổ

Định dạng RFC 4646 cho tên Culture là <language code>-<country/region code>, trong đó <language code> xác định ngôn ngữ và <country/region code> xác định Culture con. Ví dụ: es-CL cho tiếng Tây Ban Nha (Chile), en-US cho tiếng Anh (Hoa Kỳ) và en-AU cho tiếng Anh (Úc). RFC 4646 là sự kết hợp của mã Culture ISO 639 gồm hai chữ cái thường liên kết với một ngôn ngữ và mã Culture con ISO 3166 gồm hai chữ cái hoa liên kết với một quốc gia hoặc vùng lãnh thổ. Để biết thêm thông tin, xem System.Globalization.CultureInfo.

Các nhiệm vụ để bản địa hóa ứng dụng

Việc toàn cầu hóa và bản địa hóa một ứng dụng bao gồm các nhiệm vụ sau:

Xem hoặc tải xuống mã mẫu (cách tải xuống)


Làm cho nội dung ứng dụng có thể bản địa hóa

IStringLocalizerIStringLocalizer&lt;T&gt; được thiết kế nhằm cải thiện năng suất khi phát triển các ứng dụng đã bản địa hóa. IStringLocalizer sử dụng ResourceManagerResourceReader để cung cấp tài nguyên theo Culture tại thời điểm chạy. Interface này có một indexer và IEnumerable để trả về các chuỗi đã được bản địa hóa. IStringLocalizer không yêu cầu lưu trữ các chuỗi ngôn ngữ mặc định trong một Resource file (file tài nguyên). Bạn có thể phát triển ứng dụng hướng đến việc bản địa hóa mà không cần tạo Resource file sớm trong quá trình phát triển. Đoạn code dưới đây cho thấy cách bao bọc chuỗi "About Title" để bản địa hóa.

csharp
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;

namespace Localization.Controllers
{
    [Route("api/[controller]")]
    public class AboutController : Controller
    {
        private readonly IStringLocalizer<AboutController> _localizer;

        public AboutController(IStringLocalizer<AboutController> localizer)
        {
            _localizer = localizer;
        }

        [HttpGet]
        public string Get()
        {
            return _localizer["About Title"];
        }
    }
}

Trong đoạn code trên, implementation của IStringLocalizer<T> đến từ Dependency Injection. Nếu giá trị đã bản địa hóa của "About Title" không được tìm thấy, thì khóa indexer sẽ được trả về, tức là chuỗi "About Title". Bạn có thể để các chuỗi literal ngôn ngữ mặc định trong ứng dụng và bao bọc chúng trong localizer, để bạn có thể tập trung vào việc phát triển ứng dụng. Bạn phát triển ứng dụng với ngôn ngữ mặc định của mình và chuẩn bị cho bước bản địa hóa mà không cần tạo Resource file mặc định trước. Ngoài ra, bạn có thể sử dụng cách tiếp cận truyền thống và cung cấp khóa để lấy chuỗi ngôn ngữ mặc định.

Sử dụng implementation IHtmlLocalizer<T> cho các tài nguyên có chứa HTML. IHtmlLocalizer HTML encode các đối số được định dạng trong chuỗi tài nguyên, nhưng không HTML encode bản thân chuỗi tài nguyên. Trong ví dụ được tô sáng bên dưới, chỉ có giá trị của tham số name được HTML encode.

csharp
using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Localization;

namespace Localization.Controllers
{
    public class BookController : Controller
    {
        private readonly IHtmlLocalizer<BookController> _localizer;

        public BookController(IHtmlLocalizer<BookController> localizer)
        {
            _localizer = localizer;
        }

        public IActionResult Hello(string name)
        {
            ViewData["Message"] = _localizer["<b>Hello</b><i> {0}</i>", name];

            return View();
        }

Lưu ý: Thông thường, chỉ nên bản địa hóa văn bản, không phải HTML.

Ở mức thấp nhất, bạn có thể lấy IStringLocalizerFactory từ Dependency Injection:

csharp
{
    public class TestController : Controller
    {
        private readonly IStringLocalizer _localizer;
        private readonly IStringLocalizer _localizer2;

        public TestController(IStringLocalizerFactory factory)
        {
            var type = typeof(SharedResource);
            var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
            _localizer = factory.Create(type);
            _localizer2 = factory.Create("SharedResource", assemblyName.Name);
        }  

        public IActionResult About()
        {
            ViewData["Message"] = _localizer["Your application description page."]
                + " loc 2: " + _localizer2["Your application description page."];

Đoạn code trên minh họa cả hai phương thức factory create.

Bạn có thể phân vùng các chuỗi đã bản địa hóa theo controller, area, hoặc chỉ có một container. Trong ứng dụng mẫu, một class giả tên SharedResource được dùng cho các tài nguyên chia sẻ.

csharp
// Dummy class to group shared resources

namespace Localization
{
    public class SharedResource
    {
    }
}

Một số developer dùng class Startup để chứa các chuỗi toàn cục hoặc chia sẻ. Trong ví dụ bên dưới, các localizer InfoControllerSharedResource được sử dụng:

csharp
public class InfoController : Controller
{
    private readonly IStringLocalizer<InfoController> _localizer;
    private readonly IStringLocalizer<SharedResource> _sharedLocalizer;

    public InfoController(IStringLocalizer<InfoController> localizer,
                   IStringLocalizer<SharedResource> sharedLocalizer)
    {
        _localizer = localizer;
        _sharedLocalizer = sharedLocalizer;
    }

    public string TestLoc()
    {
        string msg = "Shared resx: " + _sharedLocalizer["Hello!"] +
                     " Info resx " + _localizer["Hello!"];
        return msg;
    }

View localization (Bản địa hóa View)

Dịch vụ IViewLocalizer cung cấp các chuỗi đã bản địa hóa cho một view. Class ViewLocalizer implement interface này và tìm vị trí tài nguyên từ đường dẫn file view. Đoạn code sau đây cho thấy cách sử dụng implementation mặc định của IViewLocalizer:

cshtml
@using Microsoft.AspNetCore.Mvc.Localization

@inject IViewLocalizer Localizer

@{
    ViewData["Title"] = Localizer["About"];
}
<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>

<p>@Localizer["Use this area to provide additional information."]</p>

Implementation mặc định của IViewLocalizer tìm Resource file (file tài nguyên) dựa trên tên file view. Không có tùy chọn để sử dụng một Resource file chia sẻ toàn cục. ViewLocalizer implement localizer bằng cách sử dụng IHtmlLocalizer, vì vậy Razor không HTML encode chuỗi đã bản địa hóa. Bạn có thể tham số hóa các chuỗi tài nguyên và IViewLocalizer sẽ HTML encode các tham số, nhưng không encode chuỗi tài nguyên. Xem xét markup Razor sau đây:

cshtml
@Localizer["<i>Hello</i> <b>{0}!</b>", UserManager.GetUserName(User)]

Một Resource file tiếng Pháp có thể chứa nội dung sau:

KeyValue
<i>Hello</i> <b>{0}!</b><i>Bonjour</i> <b>{0} !</b>

View được render sẽ chứa markup HTML từ Resource file.

Lưu ý: Thông thường, chỉ nên bản địa hóa văn bản, không phải HTML.

Để sử dụng một Resource file chia sẻ trong một view, inject IHtmlLocalizer<T>:

cshtml
@using Microsoft.AspNetCore.Mvc.Localization
@using Localization.Services

@inject IViewLocalizer Localizer
@inject IHtmlLocalizer<SharedResource> SharedLocalizer

@{
    ViewData["Title"] = Localizer["About"];
}
<h2>@ViewData["Title"].</h2>

<h1>@SharedLocalizer["Hello!"]</h1>

DataAnnotations localization (Bản địa hóa DataAnnotations)

Các thông báo lỗi của DataAnnotations được bản địa hóa bằng IStringLocalizer<T>. Sử dụng tùy chọn ResourcesPath = "Resources", các thông báo lỗi trong RegisterViewModel có thể được lưu trữ ở một trong các đường dẫn sau:

csharp
public class RegisterViewModel
{
    [Required(ErrorMessage = "The Email field is required.")]
    [EmailAddress(ErrorMessage = "The Email field is not a valid email address.")]
    [Display(Name = "Email")]
    public string Email { get; set; }

    [Required(ErrorMessage = "The Password field is required.")]
    [StringLength(8, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }
}

Các attribute không phải validation cũng được bản địa hóa.

Sử dụng một chuỗi tài nguyên cho nhiều class

Đoạn code sau đây cho thấy cách sử dụng một chuỗi tài nguyên cho các attribute validation với nhiều class:

csharp
public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc()
        .AddDataAnnotationsLocalization(options => {
            options.DataAnnotationLocalizerProvider = (type, factory) =>
                factory.Create(typeof(SharedResource));
        });
}

Trong đoạn code trên, SharedResource là class tương ứng với resx nơi các thông báo validation của bạn được lưu trữ. Với cách tiếp cận này, DataAnnotations sẽ chỉ sử dụng SharedResource, thay vì tài nguyên cho từng class.

Cung cấp tài nguyên đã bản địa hóa cho các ngôn ngữ và Culture bạn hỗ trợ

SupportedCultures và SupportedUICultures

ASP.NET Core cho phép bạn chỉ định hai giá trị Culture: SupportedCulturesSupportedUICultures. Đối tượng CultureInfo cho SupportedCultures xác định kết quả của các hàm phụ thuộc vào Culture, chẳng hạn như định dạng ngày, giờ, số và tiền tệ. SupportedCultures cũng xác định thứ tự sắp xếp văn bản, quy ước về chữ hoa/thường và so sánh chuỗi. SupportedUICultures xác định những chuỗi đã được dịch (từ các file .resx) nào được ResourceManager tra cứu. ResourceManager tra cứu các chuỗi theo Culture cụ thể được xác định bởi CurrentUICulture. Mỗi thread trong .NET đều có các đối tượng CurrentCultureCurrentUICulture. Framework kiểm tra các giá trị này khi render các hàm phụ thuộc vào Culture. Nếu Culture của thread hiện tại được đặt thành en-US (English, United States), DateTime.Now.ToLongDateString() hiển thị Thursday, February 18, 2016; nhưng nếu CurrentCulture được đặt thành es-ES (Spanish, Spain), đầu ra là jueves, 18 de febrero de 2016.

Resource file (File tài nguyên)

Một Resource file là một cơ chế hữu ích để tách các chuỗi có thể bản địa hóa khỏi code. Các chuỗi đã được dịch cho ngôn ngữ không phải ngôn ngữ mặc định được tách biệt trong các Resource file .resx. Ví dụ: bạn có thể muốn tạo Resource file tiếng Tây Ban Nha có tên Welcome.es.resx chứa các chuỗi đã được dịch. "es" là mã ngôn ngữ cho tiếng Tây Ban Nha. Để tạo Resource file này trong Visual Studio:

  1. Trong Solution Explorer, nhấp chuột phải vào thư mục sẽ chứa Resource file > Add > New Item.

!Nested contextual menu: In Solution Explorer, a contextual menu is open for Resources. A second contextual menu is open for Add showing the New Item command highlighted.

  1. Trong hộp Search installed templates, nhập "resource" và đặt tên file.

!Add New Item dialog

  1. Nhập giá trị khóa (chuỗi gốc) vào cột Name và chuỗi đã dịch vào cột Value.

!Welcome.es.resx file (the Welcome resource file for Spanish) with the word Hello in the Name column and the word Hola (Hello in Spanish) in the Value column

Visual Studio hiển thị file Welcome.es.resx.

!Solution Explorer showing the Welcome Spanish (es) resource file

Đặt tên Resource file

Tài nguyên được đặt tên theo tên type đầy đủ của class trừ đi tên assembly. Ví dụ: một tài nguyên tiếng Pháp trong một project có assembly chính là LocalizationWebsite.Web.dll cho class LocalizationWebsite.Web.Startup sẽ được đặt tên là Startup.fr.resx. Một tài nguyên cho class LocalizationWebsite.Web.Controllers.HomeController sẽ được đặt tên là Controllers.HomeController.fr.resx. Nếu namespace của class mục tiêu không giống với tên assembly, bạn sẽ cần tên type đầy đủ. Ví dụ: trong project mẫu, một tài nguyên cho type ExtraNamespace.Tools sẽ được đặt tên là ExtraNamespace.Tools.fr.resx.

Trong project mẫu, phương thức ConfigureServices đặt ResourcesPath thành "Resources", vì vậy đường dẫn tương đối của project cho Resource file tiếng Pháp của home controller là Resources/Controllers.HomeController.fr.resx. Ngoài ra, bạn có thể dùng thư mục để tổ chức Resource file. Đối với home controller, đường dẫn sẽ là Resources/Controllers/HomeController.fr.resx. Nếu bạn không sử dụng tùy chọn ResourcesPath, file .resx sẽ nằm trong thư mục gốc của project.

Tên tài nguyênQuy ước đặt tên
Resources/Controllers.HomeController.fr.resxDùng dấu chấm
Resources/Controllers/HomeController.fr.resxDùng đường dẫn

Các Resource file sử dụng @inject IViewLocalizer trong Razor views tuân theo một mẫu tương tự. Resource file cho một view có thể được đặt tên theo quy ước dấu chấm hoặc đường dẫn. Các Resource file Razor view bắt chước đường dẫn của file view liên kết. Giả sử chúng ta đặt ResourcesPath thành "Resources", Resource file tiếng Pháp liên kết với view Views/Home/About.cshtml có thể là một trong các đường dẫn sau:

Nếu bạn không sử dụng tùy chọn ResourcesPath, file .resx cho một view sẽ nằm trong cùng thư mục với view đó.

RootNamespaceAttribute

Attribute RootNamespaceAttribute cung cấp root namespace của một assembly khi root namespace của assembly khác với tên assembly.

Cảnh báo: Điều này có thể xảy ra khi tên project không phải là một .NET identifier hợp lệ. Ví dụ: my-project-name.csproj sẽ sử dụng root namespace my_project_name và tên assembly my-project-name, dẫn đến lỗi này.

Nếu root namespace của assembly khác với tên assembly:

Nếu RootNamespace khác với AssemblyName, hãy thêm nội dung sau vào AssemblyInfo.cs (thay thế các giá trị tham số bằng giá trị thực tế):

csharp
using System.Reflection;
using Microsoft.Extensions.Localization;

[assembly: ResourceLocation("Resource Folder Name")]
[assembly: RootNamespace("App Root Namespace")]

Đoạn code trên cho phép phân giải thành công các file resx.

Hành vi fallback của Culture

Khi tìm kiếm tài nguyên, quá trình Localization thực hiện "culture fallback" (dự phòng Culture). Bắt đầu từ Culture được yêu cầu, nếu không tìm thấy, nó sẽ chuyển về Culture cha của Culture đó. Thuộc tính CultureInfo.Parent đại diện cho Culture cha. Điều này thường (nhưng không phải lúc nào cũng) có nghĩa là xóa ký hiệu quốc gia khỏi ISO. Ví dụ: phương ngữ tiếng Tây Ban Nha được nói ở Mexico là "es-MX". Nó có Culture cha là "es" — tiếng Tây Ban Nha không đặc trưng cho bất kỳ quốc gia nào.

Hãy tưởng tượng trang web của bạn nhận được một request cho tài nguyên "Welcome" sử dụng Culture "fr-CA". Hệ thống Localization tìm kiếm các tài nguyên sau theo thứ tự và chọn kết quả khớp đầu tiên:

Ví dụ: nếu bạn xóa ký hiệu Culture ".fr" và đặt Culture thành tiếng Pháp, Resource file mặc định sẽ được đọc và các chuỗi được bản địa hóa. Resource manager chỉ định một tài nguyên mặc định hoặc dự phòng khi không có gì phù hợp với Culture được yêu cầu. Nếu bạn muốn chỉ trả về khóa khi thiếu tài nguyên cho Culture được yêu cầu, bạn không được có Resource file mặc định.

Tạo Resource file bằng Visual Studio

Nếu bạn tạo một Resource file trong Visual Studio mà không có Culture trong tên file (ví dụ: Welcome.resx), Visual Studio sẽ tạo một class C# với một thuộc tính cho mỗi chuỗi. Đó thường không phải là điều bạn muốn với ASP.NET Core. Thông thường bạn không có một Resource file .resx mặc định (một file .resx không có tên Culture). Chúng tôi đề xuất bạn tạo file .resx với tên Culture (ví dụ: Welcome.fr.resx). Khi bạn tạo file .resx với tên Culture, Visual Studio sẽ không tạo file class.

Thêm các Culture khác

Mỗi tổ hợp ngôn ngữ và Culture (ngoại trừ ngôn ngữ mặc định) yêu cầu một Resource file riêng. Bạn tạo Resource file cho các Culture và locale khác nhau bằng cách tạo các Resource file mới trong đó mã ngôn ngữ ISO là một phần của tên file (ví dụ: en-us, fr-caen-gb). Các mã ISO này được đặt giữa tên file và phần mở rộng .resx, như trong Welcome.es-MX.resx (tiếng Tây Ban Nha/Mexico).

Triển khai chiến lược chọn ngôn ngữ/Culture cho mỗi request

Cấu hình Localization

Localization (bản địa hóa) được cấu hình trong phương thức Startup.ConfigureServices:

csharp
services.AddLocalization(options => options.ResourcesPath = "Resources");

services.AddMvc()
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
    .AddDataAnnotationsLocalization();

Localization Middleware (Phần mềm trung gian bản địa hóa)

Culture hiện tại trên một request được đặt trong Localization Middleware. Localization Middleware được bật trong phương thức Startup.Configure. Localization Middleware phải được cấu hình trước bất kỳ Middleware nào có thể kiểm tra Culture của request (ví dụ: app.UseMvcWithDefaultRoute()). Localization Middleware phải xuất hiện sau Routing Middleware nếu sử dụng RouteDataRequestCultureProvider. Để biết thêm thông tin về thứ tự Middleware, xem ASP.NET Core Middleware.

csharp
var supportedCultures = new[] { "en-US", "fr" };
var localizationOptions = new RequestLocalizationOptions().SetDefaultCulture(supportedCultures[0])
    .AddSupportedCultures(supportedCultures)
    .AddSupportedUICultures(supportedCultures);

app.UseRequestLocalization(localizationOptions);

UseRequestLocalization khởi tạo một đối tượng RequestLocalizationOptions. Trên mỗi request, danh sách RequestCultureProvider trong RequestLocalizationOptions được duyệt và provider đầu tiên có thể xác định thành công Culture của request sẽ được sử dụng. Các provider mặc định đến từ class RequestLocalizationOptions:

  1. QueryStringRequestCultureProvider
  2. CookieRequestCultureProvider
  3. AcceptLanguageHeaderRequestCultureProvider

Danh sách mặc định đi từ cụ thể nhất đến ít cụ thể nhất. Ở phần sau của bài viết, chúng ta sẽ thấy cách bạn có thể thay đổi thứ tự và thậm chí thêm một custom culture provider. Nếu không có provider nào xác định được Culture của request, thì DefaultRequestCulture sẽ được sử dụng.

QueryStringRequestCultureProvider

Một số ứng dụng sẽ sử dụng query string để đặt CultureInfo. Đối với các ứng dụng sử dụng cách tiếp cận cookie hoặc Accept-Language header, việc thêm query string vào URL rất hữu ích để debug và test code. Theo mặc định, QueryStringRequestCultureProvider được đăng ký là provider Localization đầu tiên trong danh sách RequestCultureProvider. Bạn truyền các tham số query string cultureui-culture. Ví dụ sau đặt Culture cụ thể (ngôn ngữ và vùng) thành tiếng Tây Ban Nha/Mexico:

http://localhost:5000/?culture=es-MX&ui-culture=es-MX

Nếu bạn chỉ truyền một trong hai (culture hoặc ui-culture), query string provider sẽ đặt cả hai giá trị bằng cách sử dụng giá trị bạn đã truyền. Ví dụ: chỉ đặt Culture sẽ đặt cả CultureUICulture:

code
http://localhost:5000/?culture=es-MX

CookieRequestCultureProvider

Các ứng dụng production thường cung cấp cơ chế để đặt Culture bằng cookie Culture của ASP.NET Core. Sử dụng phương thức MakeCookieValue để tạo cookie.

DefaultCookieName của CookieRequestCultureProvider trả về tên cookie mặc định được dùng để theo dõi thông tin Culture ưa thích của người dùng. Tên cookie mặc định là .AspNetCore.Culture.

Định dạng cookie là c=%LANGCODE%|uic=%LANGCODE%, trong đó cCultureuicUICulture, ví dụ:

code
c=en-UK|uic=en-US

Nếu bạn chỉ chỉ định một trong hai thông tin Culture và UI Culture, Culture được chỉ định sẽ được dùng cho cả thông tin Culture và UI Culture.

Accept-Language HTTP header

Accept-Language header có thể được đặt trong hầu hết các trình duyệt và ban đầu được thiết kế để chỉ định ngôn ngữ của người dùng. Cài đặt này cho biết trình duyệt đã được cấu hình để gửi gì hoặc đã kế thừa từ hệ điều hành nền. Accept-Language HTTP header từ request của trình duyệt không phải là cách chắc chắn để phát hiện ngôn ngữ ưa thích của người dùng (xem Setting language preferences in a browser). Một ứng dụng production nên bao gồm cách để người dùng tùy chỉnh lựa chọn Culture của họ.

Đặt Accept-Language HTTP header trong IE

  1. Từ biểu tượng gear, nhấn Internet Options.
  2. Nhấn Languages.

!Internet Options

  1. Nhấn Set Language Preferences.
  2. Nhấn Add a language.
  3. Thêm ngôn ngữ.
  4. Nhấn ngôn ngữ, sau đó nhấn Move Up.

Sử dụng custom provider

Giả sử bạn muốn cho phép khách hàng lưu trữ ngôn ngữ và Culture của họ trong cơ sở dữ liệu. Bạn có thể viết một provider để tra cứu các giá trị này cho người dùng. Đoạn code sau đây cho thấy cách thêm một custom provider:

csharp
private const string enUSCulture = "en-US";

services.Configure<RequestLocalizationOptions>(options =>
{
    var supportedCultures = new[]
    {
        new CultureInfo(enUSCulture),
        new CultureInfo("fr")
    };

    options.DefaultRequestCulture = new RequestCulture(culture: enUSCulture, uiCulture: enUSCulture);
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures;

    options.AddInitialRequestCultureProvider(new CustomRequestCultureProvider(async context =>
    {
        // My custom request culture logic
        return await Task.FromResult(new ProviderCultureResult("en"));
    }));
});

Sử dụng RequestLocalizationOptions để thêm hoặc xóa các Localization provider.

Thay đổi thứ tự của các request culture provider

RequestLocalizationOptions có ba request culture provider mặc định: QueryStringRequestCultureProvider, CookieRequestCultureProviderAcceptLanguageHeaderRequestCultureProvider. Sử dụng thuộc tính RequestLocalizationOptions.RequestCultureProviders để thay đổi thứ tự của các provider này như được hiển thị trong ví dụ sau:

csharp
app.UseRequestLocalization(options =>
{
    var questStringCultureProvider = options.RequestCultureProviders[0];  
    options.RequestCultureProviders.RemoveAt(0);
    options.RequestCultureProviders.Insert(1, questStringCultureProvider);
});

Trong ví dụ trên, thứ tự của QueryStringRequestCultureProviderCookieRequestCultureProvider được hoán đổi, vì vậy RequestLocalizationMiddleware sẽ tìm kiếm Culture từ cookie trước, sau đó mới đến query string.

Như đã đề cập trước đó, hãy thêm một custom provider thông qua AddInitialRequestCultureProvider, phương thức này đặt thứ tự thành 0, vì vậy provider này có quyền ưu tiên hơn các provider khác.

Đặt Culture theo chương trình

Project mẫu Localization.StarterWeb trên GitHub có UI để đặt Culture. File Views/Shared/_SelectLanguagePartial.cshtml cho phép bạn chọn Culture từ danh sách các Culture được hỗ trợ:

cshtml
@using Microsoft.AspNetCore.Builder
@using Microsoft.AspNetCore.Http.Features
@using Microsoft.AspNetCore.Localization
@using Microsoft.AspNetCore.Mvc.Localization
@using Microsoft.Extensions.Options

@inject IViewLocalizer Localizer
@inject IOptions<RequestLocalizationOptions> LocOptions

@{
    var requestCulture = Context.Features.Get<IRequestCultureFeature>();
    var cultureItems = LocOptions.Value.SupportedUICultures
        .Select(c => new SelectListItem { Value = c.Name, Text = c.DisplayName })
        .ToList();
    var returnUrl = string.IsNullOrEmpty(Context.Request.Path) ? "~/" : $"~{Context.Request.Path.Value}";
}

<div title="@Localizer["Request culture provider:"] @requestCulture?.Provider?.GetType().Name">
    <form id="selectLanguage" asp-controller="Home"
          asp-action="SetLanguage" asp-route-returnUrl="@returnUrl"
          method="post" class="form-horizontal" role="form">
        <label asp-for="@requestCulture.RequestCulture.UICulture.Name">@Localizer["Language:"]</label> <select name="culture"
          onchange="this.form.submit();"
          asp-for="@requestCulture.RequestCulture.UICulture.Name" asp-items="cultureItems">
        </select>
    </form>
</div>

File Views/Shared/_SelectLanguagePartial.cshtml được thêm vào phần footer của file layout để nó sẽ có sẵn cho tất cả các view:

cshtml
<div class="container body-content" style="margin-top:60px">
    @RenderBody()
    <hr>
    <footer>
        <div class="row">
            <div class="col-md-6">
                <p>&copy; @System.DateTime.Now.Year - Localization</p>
            </div>
            <div class="col-md-6 text-right">
                @await Html.PartialAsync("_SelectLanguagePartial")
            </div>
        </div>
    </footer>
</div>

Phương thức SetLanguage đặt cookie Culture.

csharp
[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);
}

Model binding route data và query string

Xem Globalization behavior of model binding route data and query strings.

Các thuật ngữ Globalization và Localization

Quá trình bản địa hóa ứng dụng của bạn cũng đòi hỏi hiểu biết cơ bản về các bộ ký tự có liên quan thường được sử dụng trong phát triển phần mềm hiện đại và hiểu các vấn đề liên quan đến chúng. Mặc dù tất cả các máy tính đều lưu trữ văn bản dưới dạng số (code), các hệ thống khác nhau lưu trữ cùng một văn bản bằng các số khác nhau. Quá trình Localization đề cập đến việc dịch giao diện người dùng (UI) của ứng dụng cho một Culture/locale cụ thể.

Localizability là một quá trình trung gian để xác minh rằng một ứng dụng đã được toàn cầu hóa sẵn sàng cho việc bản địa hóa.

Định dạng RFC 4646 cho tên Culture là <languagecode2>-<country/regioncode2>, trong đó <languagecode2> là mã ngôn ngữ và <country/regioncode2> là mã Culture con. Ví dụ: es-CL cho tiếng Tây Ban Nha (Chile), en-US cho tiếng Anh (Hoa Kỳ) và en-AU cho tiếng Anh (Úc).

Internationalization (Quốc tế hóa) thường được viết tắt là "I18N". Chữ viết tắt lấy chữ cái đầu tiên và cuối cùng cùng với số lượng chữ cái ở giữa, vì vậy 18 đại diện cho số chữ cái giữa chữ "I" đầu tiên và chữ "N" cuối cùng. Tương tự áp dụng cho Globalization (G11N) và Localization (L10N).

Các thuật ngữ:

Lưu ý: Bạn có thể không nhập được dấu phẩy thập phân trong các trường thập phân. Để hỗ trợ jQuery validation cho các locale không phải tiếng Anh sử dụng dấu phẩy (",") làm dấu thập phân và các định dạng ngày không phải US-English, bạn phải thực hiện các bước để toàn cầu hóa ứng dụng. Xem GitHub comment 4076 để được hướng dẫn thêm dấu phẩy thập phân.

Lưu ý: Trước ASP.NET Core 3.0, các ứng dụng web ghi một log loại LogLevel.Warning cho mỗi request nếu Culture được yêu cầu không được hỗ trợ. Việc ghi một LogLevel.Warning cho mỗi request có thể tạo ra các file log lớn với thông tin dư thừa. Hành vi này đã được thay đổi trong ASP.NET Core 3.0. RequestLocalizationMiddleware ghi một log loại LogLevel.Debug, giúp giảm kích thước log production.

Tài nguyên bổ sung