使 ASP.NET Core 应用内容可本地化

作者:Hisham Bin AteyaDamien BowdenBart CalixtoNadeem Afana

本地化应用的一项任务是使用代码包装可本地化的内容,以便为不同的区域性替换该内容。

IStringLocalizer

已为 IStringLocalizerIStringLocalizer<T> 设置架构,可以为开发本地化应用提高工作效率。 IStringLocalizer 使用 ResourceManagerResourceReader 在运行时提供特定于区域性的资源。 接口具有一个索引器和一个用于返回本地化字符串的 IEnumerableIStringLocalizer 不要求在资源文件中存储默认语言字符串。 你可以开发针对本地化的应用,且无需在开发初期创建资源资源文件。

下面的代码示例演示如何针对本地化包装字符串“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();
    }

前面的代码演示了这两种工厂创建方法。

共享资源

可以按控制器或区域对本地化字符串分区,或只有一个容器。 在示例应用中,名为 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 编码,但不会对资源字符串进行 HTML 编码。 请考虑以下 Razor 标记:

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

法语资源文件可以包含以下值:

密钥 “值”
<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 在运行时提供特定于区域性的资源。 接口具有一个索引器和一个用于返回本地化字符串的 IEnumerableIStringLocalizer 不要求在资源文件中存储默认语言字符串。 你可以开发针对本地化的应用,且无需在开发初期创建资源资源文件。

下面的代码示例演示如何针对本地化包装字符串“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."];

前面的代码演示了这两种工厂创建方法。

共享资源

可以按控制器或区域对本地化字符串分区,或只有一个容器。 在示例应用中,名为 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 编码,但不会对资源字符串进行 HTML 编码。 请考虑以下 Razor 标记:

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

法语资源文件可以包含以下值:

密钥 “值”
<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

后续步骤

本地化应用还涉及以下任务:

其他资源