讓 ASP.NET Core 應用程式的內容可當地語系化

作者:Hisham Bin AteyaDamien BowdenBart CalixtoNadeem Afana

當地語系化應用程式的其中一項工作是使用程式碼包裝可當地語系化的內容,協助為不同的文化特性取代該內容。

IStringLocalizer

IStringLocalizerIStringLocalizer<T> 的設計用意是,提高開發當地語系化應用程式時的生產力。 IStringLocalizer 會使用 ResourceManagerResourceReader,在執行階段提供特定文化特性的資源。 這個介面具有的索引子和 IEnumerable 可傳回當地語系化字串。 IStringLocalizer 不需要您將預設語言字串儲存在資源檔中。 您不必在開發初期建立資源檔,即可開發以當地語系化為目標的應用程式。

下列程式碼範例示範如何包裝 "About Title" 字串以進行當地語系化。

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"];
    }
}

在上述程式碼中,IStringLocalizer<T> 實作是來自相依性插入。 如果找不到 "About Title" 的當地語系化值,即會傳回索引子的索引鍵,也就是 "About Title" 字串。

您可以保留應用程式中的預設語言常值字串,並將其包裝在當地語系化工具中,以便專注於開發應用程式。 您不用先建立預設資源檔,即可使用預設語言來開發應用程式,並針對當地語系化步驟進行應用程式的準備。

或者,您可以使用傳統方法,並提供索引鍵以擷取預設語言字串。 對許多開發人員來說,新的工作流程 (單純包裝字串常值而不使用預設語言的 .resx 檔案) 可以降低當地語系化應用程式的額外負荷。 其他開發人員則偏好傳統的工作流程,因為這種方法更便於使用較長的字串常值,且更易於更新當地語系化的字串。

IHtmlLocalizer

若是包含 HTML 的資源,請使用 IHtmlLocalizer<TResource> 實作。 IHtmlLocalizer 會對資源字串中經過格式化的引數進行 HTML 編碼,但不會對資源字串本身進行 HTML 編碼。 在下列醒目提示的程式碼中,只有 name 參數的值會以 HTML 編碼。

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();
    }

注意:一般而言,只會將文字當地語系化,而不是 HTML。

IStringLocalizerFactory

在最低層級,可以從相依性插入擷取 IStringLocalizerFactory

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."];

        return View();
    }

上述程式碼示範這兩個 Factory Create 方法。

共用的資源

您可以依據控制器或區域來分割當地語系化的字串,也可以只用一個容器。 在範例應用程式中,針對共用資源使用名為 SharedResource 的標記類別。 永遠不會呼叫標記類別:

// Dummy class to group shared resources

namespace Localization;

public class SharedResource
{
}

下列範例會使用 InfoControllerSharedResource 當地語系化工具:

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;
    }

檢視當地語系化

IViewLocalizer 服務可提供檢視的當地語系化字串。 ViewLocalizer 類別會實作這個介面,並透過檢視檔案路徑來找出資源的位置。 下列程式碼示範如何使用 IViewLocalizer 的預設實作:

@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>

IViewLocalizer 的預設實作會依據檢視的檔案名稱來找出資源檔。 其中並沒有任何選項可以使用全域共用的資源檔。 ViewLocalizer 會使用 IHtmlLocalizer 來實作當地語系化工具,因此 Razor 不會對當地語系化的字串進行 HTML 編碼。 您可以參數化資源字串,IViewLocalizer 即會對參數 (而不是資源字串) 進行 HTML 編碼。 請考慮下列 Razor 標記:

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

法文資源檔可能包含下列值:

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

轉譯的檢視內容可能包含來自資源檔的 HTML 標記。

一般而言,只會將文字當地語系化,而不是 HTML。

若要在檢視中使用共用的資源檔,請插入 IHtmlLocalizer<T>

@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 當地語系化

DataAnnotations 錯誤訊息會使用 IStringLocalizer<T> 來當地語系化。 使用 ResourcesPath = "Resources" 選項時,RegisterViewModel 中的錯誤訊息會儲存在下列路徑之一:

  • Resources/ViewModels.Account.RegisterViewModel.fr.resx
  • Resources/ViewModels/Account/RegisterViewModel.fr.resx
using System.ComponentModel.DataAnnotations;

namespace Localization.ViewModels.Account;

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; }
}

無驗證屬性已當地語系化。

如何針對多個類別使用一個資源字串

下列程式碼會示範如何針對含有多個類別的驗證屬性使用同一個資源字串:

    services.AddMvc()
        .AddDataAnnotationsLocalization(options => {
            options.DataAnnotationLocalizerProvider = (type, factory) =>
                factory.Create(typeof(SharedResource));
        });

在先前的程式碼中,SharedResource 是與儲存驗證訊息的 .resx 檔案對應的類別。 使用這個方法時,DataAnnotations 只會使用 SharedResource,而不是每個類別的資源。

設定當地語系化服務

當地語系化服務設定於 Program.cs 中:

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");

builder.Services.AddMvc()
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
    .AddDataAnnotationsLocalization();
  • AddLocalization 會將當地語系化服務新增至服務容器,包括 IStringLocalizer<T>IStringLocalizerFactory 的實作。 上述程式碼也會將資源路徑設為 "Resources"。

  • AddViewLocalization 可支援當地語系化的檢視檔案。 在此範例中,檢視的當地語系化會以檢視檔案的後置詞為依據。 例如 Index.fr.cshtml 檔案中的 "fr"。

  • AddDataAnnotationsLocalization 可支援透過 IStringLocalizer 抽象概念而來的當地語系化 DataAnnotations 驗證訊息。

注意

您可能無法在小數欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期欄位支援 jQuery 驗證,您必須採取將應用程式全球化的步驟。 請參閱此 GitHub 問題 4076,以取得加入十進位逗號的指示。

下一步

當地語系化應用程式也涉及下列工作:

其他資源

Rick AndersonDamien BowdenBart CalixtoNadeem AfanaHisham Bin Ateya 提供

當地語系化應用程式的其中一項工作是使用程式碼包裝可當地語系化的內容,協助為不同的文化特性取代該內容。

IStringLocalizer

IStringLocalizerIStringLocalizer<T> 的設計用意是,提高開發當地語系化應用程式時的生產力。 IStringLocalizer 會使用 ResourceManagerResourceReader,在執行階段提供特定文化特性的資源。 這個介面具有的索引子和 IEnumerable 可傳回當地語系化字串。 IStringLocalizer 不需要您將預設語言字串儲存在資源檔中。 您不必在開發初期建立資源檔,即可開發以當地語系化為目標的應用程式。

下列程式碼範例示範如何包裝 "About Title" 字串以進行當地語系化。

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"];
        }
    }
}

在上述程式碼中,IStringLocalizer<T> 實作是來自相依性插入。 如果找不到 "About Title" 的當地語系化值,即會傳回索引子的索引鍵,也就是 "About Title" 字串。

您可以保留應用程式中的預設語言常值字串,並將其包裝在當地語系化工具中,以便專注於開發應用程式。 您不用先建立預設資源檔,即可使用預設語言來開發應用程式,並針對當地語系化步驟進行應用程式的準備。

或者,您可以使用傳統方法,並提供索引鍵以擷取預設語言字串。 對許多開發人員來說,新的工作流程 (單純包裝字串常值而不使用預設語言的 .resx 檔案) 可以降低當地語系化應用程式的額外負荷。 其他開發人員則偏好傳統的工作流程,因為這種方法更便於使用較長的字串常值,且更易於更新當地語系化的字串。

IHtmlLocalizer

若是包含 HTML 的資源,請使用 IHtmlLocalizer<T> 實作。 IHtmlLocalizer 會對資源字串中經過格式化的引數進行 HTML 編碼,但不會對資源字串本身進行 HTML 編碼。 在下列醒目提示的程式碼中,只有 name 參數的值會以 HTML 編碼。

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();
        }

注意

一般而言,只會將文字當地語系化,而不是 HTML。

IStringLocalizerFactory

您可以在最底層的相依性插入中,將 IStringLocalizerFactory 移出:

{
    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."];

上述程式碼示範這兩個 Factory Create 方法。

共用的資源

您可以依據控制器或區域來分割當地語系化的字串,也可以只用一個容器。 在範例應用程式中,會針對共用資源使用名為 SharedResource 的虛擬類別。

// Dummy class to group shared resources

namespace Localization
{
    public class SharedResource
    {
    }
}

有些開發人員會使用 Startup 類別來包含全域或共用字串。 下列範例會使用 InfoControllerSharedResource 當地語系化工具:

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;
    }

檢視當地語系化

IViewLocalizer 服務可提供檢視的當地語系化字串。 ViewLocalizer 類別會實作這個介面,並透過檢視檔案路徑來找出資源的位置。 下列程式碼示範如何使用 IViewLocalizer 的預設實作:

@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>

IViewLocalizer 的預設實作會依據檢視的檔案名稱來找出資源檔。 其中並沒有任何選項可以使用全域共用的資源檔。 ViewLocalizer 會使用 IHtmlLocalizer 來實作當地語系化工具,因此 Razor 不會對當地語系化的字串進行 HTML 編碼。 您可以參數化資源字串,IViewLocalizer 即會對參數 (而不是資源字串) 進行 HTML 編碼。 請考慮下列 Razor 標記:

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

法文資源檔可能包含下列值:

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

轉譯的檢視內容可能包含來自資源檔的 HTML 標記。

注意

一般而言,只會將文字當地語系化,而不是 HTML。

若要在檢視中使用共用的資源檔,請插入 IHtmlLocalizer<T>

@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 當地語系化

DataAnnotations 錯誤訊息會使用 IStringLocalizer<T> 來當地語系化。 使用 ResourcesPath = "Resources" 選項時,RegisterViewModel 中的錯誤訊息會儲存在下列路徑之一:

  • Resources/ViewModels.Account.RegisterViewModel.fr.resx
  • Resources/ViewModels/Account/RegisterViewModel.fr.resx
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; }
}

在 ASP.NET Core MVC 1.1.0 和更新版本中,系統會將非驗證屬性當地語系化。

如何針對多個類別使用一個資源字串

下列程式碼會示範如何針對含有多個類別的驗證屬性使用同一個資源字串:

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

在先前的程式碼中,SharedResource 是與儲存驗證訊息的 .resx 檔案對應的類別。 使用這個方法時,DataAnnotations 只會使用 SharedResource,而不是每個類別的資源。

設定當地語系化服務

當地語系化服務設定於 Startup.ConfigureServices 方法中:

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

services.AddMvc()
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
    .AddDataAnnotationsLocalization();
  • AddLocalization 會將當地語系化服務新增至服務容器,包括 IStringLocalizer<T>IStringLocalizerFactory 的實作。 上述程式碼也會將資源路徑設為 "Resources"。

  • AddViewLocalization 可支援當地語系化的檢視檔案。 在此範例中,檢視的當地語系化會以檢視檔案的後置詞為依據。 例如 Index.fr.cshtml 檔案中的 "fr"。

  • AddDataAnnotationsLocalization 可支援透過 IStringLocalizer 抽象概念而來的當地語系化 DataAnnotations 驗證訊息。

注意

您可能無法在小數欄位中輸入小數逗號。 若要對使用逗號 (",") 作為小數點的非英文地區設定和非英文日期欄位支援 jQuery 驗證,您必須採取將應用程式全球化的步驟。 請參閱此 GitHub 問題 4076,以取得加入十進位逗號的指示。

下一步

當地語系化應用程式也涉及下列工作:

其他資源