ASP.NET Core 全球化和本地化Globalization and localization in ASP.NET Core

作者:Rick AndersonDamien BowdenBart CalixtoNadeem AfanaHisham Bin AteyaBy Rick Anderson, Damien Bowden, Bart Calixto, Nadeem Afana, and Hisham Bin Ateya

使用 ASP.NET Core 创建多语言网站,可让网站拥有更多受众。Creating a multilingual website with ASP.NET Core will allow your site to reach a wider audience. ASP.NET Core 提供的服务和中间件可将网站本地化为不同的语言和文化。ASP.NET Core provides services and middleware for localizing into different languages and cultures.

国际化涉及全球化本地化Internationalization involves Globalization and Localization. 全球化是设计支持不同区域性的应用程序的过程。Globalization is the process of designing apps that support different cultures. 全球化添加了对一组有关特定地理区域的已定义语言脚本的输入、显示和输出支持。Globalization adds support for input, display, and output of a defined set of language scripts that relate to specific geographic areas.

本地化是将已经针对可本地化性进行处理的全球化应用调整为特定的区域性/区域设置的过程。Localization is the process of adapting a globalized app, which you have already processed for localizability, to a particular culture/locale. 有关详细信息,请参阅本文档邻近末尾的全球化和本地化术语。For more information see Globalization and localization terms near the end of this document.

应用本地化涉及以下内容:App localization involves the following:

  1. 使应用内容可本地化Make the app's content localizable

  2. 为支持的语言和区域性提供本地化资源Provide localized resources for the languages and cultures you support

  3. 实施策略,为每个请求选择语言/区域性Implement a strategy to select the language/culture for each request

查看或下载示例代码如何下载View or download sample code (how to download)

使应用内容可本地化Make the app's content localizable

ASP.NET Core 中引入并架构了 IStringLocalizerIStringLocalizer<T>,以提高开发本地化应用的工作效率。Introduced in ASP.NET Core, IStringLocalizer and IStringLocalizer<T> were architected to improve productivity when developing localized apps. IStringLocalizer 使用 ResourceManagerResourceReader,在运行时提供区域性特定资源。IStringLocalizer uses the ResourceManager and ResourceReader to provide culture-specific resources at run time. 简单接口具有一个索引器和一个用于返回本地化字符串的 IEnumerableThe simple interface has an indexer and an IEnumerable for returning localized strings. IStringLocalizer 不要求在资源文件中存储默认语言字符串。IStringLocalizer doesn't require you to store the default language strings in a resource file. 你可以开发针对本地化的应用,且无需在开发初期创建资源资源文件。You can develop an app targeted for localization and not need to create resource files early in development. 下面的代码演示如何针对本地化包装字符串“About Title”。The code below shows how to wrap the string "About Title" for localization.

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> 实现来源于依赖关系注入In the code above, the IStringLocalizer<T> implementation comes from Dependency Injection. 如果找不到“About Title”的本地化值,则返回索引器键,即字符串“About Title”。If the localized value of "About Title" isn't found, then the indexer key is returned, that is, the string "About Title". 可将默认语言文本字符串保留在应用中并将它们包装在本地化工具中,以便你可集中精力开发应用。You can leave the default language literal strings in the app and wrap them in the localizer, so that you can focus on developing the app. 你使用默认语言开发应用,并针对本地化步骤进行准备,而无需首先创建默认资源文件。You develop your app with your default language and prepare it for the localization step without first creating a default resource file. 或者,你可以使用传统方法,并提供键以检索默认语言字符串。Alternatively, you can use the traditional approach and provide a key to retrieve the default language string. 对于许多开发者而言,不具有默认语言 .resx 文件且简单包装字符串文本的新工作流可以减少本地化应用的开销。For many developers the new workflow of not having a default language .resx file and simply wrapping the string literals can reduce the overhead of localizing an app. 其他开发者将首选传统工作流,因为它可以更轻松地使用较长字符串文本,更轻松地更新本地化字符串。Other developers will prefer the traditional work flow as it can make it easier to work with longer string literals and make it easier to update localized strings.

对包含 HTML 的资源使用 IHtmlLocalizer<T> 实现。Use the IHtmlLocalizer<T> implementation for resources that contain HTML. IHtmlLocalizer 对资源字符串中格式化的参数进行 HTML 编码,但不对资源字符串本身进行 HTML 编码。IHtmlLocalizer HTML encodes arguments that are formatted in the resource string, but doesn't HTML encode the resource string itself. 在下面突出显示的示例中,仅 name 参数的值被 HTML 编码。In the sample highlighted below, only the value of name parameter is HTML encoded.

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。Note: You generally want to only localize text and not HTML.

最低程度,你可以从依赖关系注入获取 IStringLocalizerFactoryAt the lowest level, you can get IStringLocalizerFactory out of Dependency Injection:

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

上面的代码演示了这两种工厂创建方法。The code above demonstrates each of the two factory create methods.

可以按控制器、区域对本地化字符串分区,或只有一个容器。You can partition your localized strings by controller, area, or have just one container. 在示例应用中,名为 SharedResource 的虚拟类用于共享资源。In the sample app, a dummy class named SharedResource is used for shared resources.

// Dummy class to group shared resources

namespace Localization
{
    public class SharedResource
    {
    }
}

某些开发者使用 Startup 类,以包含全局或共享字符串。Some developers use the Startup class to contain global or shared strings. 在下面的示例中,使用 InfoControllerSharedResource 本地化工具:In the sample below, the InfoController and the SharedResource localizers are used:

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

IViewLocalizer 服务可为视图提供本地化字符串。The IViewLocalizer service provides localized strings for a view. ViewLocalizer 类可实现此接口,并从视图文件路径找到资源位置。The ViewLocalizer class implements this interface and finds the resource location from the view file path. 下面的代码演示如何使用 IViewLocalizer 的默认实现:The following code shows how to use the default implementation of 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 的默认实现可根据视图的文件名查找资源文件。The default implementation of IViewLocalizer finds the resource file based on the view's file name. 没有使用全局共享资源文件的选项。There's no option to use a global shared resource file. ViewLocalizer 使用 IHtmlLocalizer 实现本地化工具,因此 Razor 不会对本地化字符串进行 HTML 编码。ViewLocalizer implements the localizer using IHtmlLocalizer, so Razor doesn't HTML encode the localized string. 你可以参数化资源字符串,IViewLocalizer 将对参数进行 HTML 编码,但不会对资源字符串进行。You can parameterize resource strings and IViewLocalizer will HTML encode the parameters, but not the resource string. 请考虑以下 Razor 标记:Consider the following Razor markup:

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

法语资源文件可以包含以下信息:A French resource file could contain the following:

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

呈现的视图可能包含资源文件中的 HTML 标记。The rendered view would contain the HTML markup from the resource file.

注意: 通常只想要本地化文本,而不是 HTML。Note: You generally want to only localize text and not HTML.

若要在视图中使用共享资源文件,请注入 IHtmlLocalizer<T>To use a shared resource file in a view, inject 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 localization

DataAnnotations 错误消息已使用 IStringLocalizer<T> 本地化。DataAnnotations error messages are localized with IStringLocalizer<T>. 使用选项 ResourcesPath = "Resources"RegisterViewModel 中的错误消息可以存储在以下路径之一:Using the option ResourcesPath = "Resources", the error messages in RegisterViewModel can be stored in either of the following paths:

  • Resources/ViewModels.Account.RegisterViewModel.fr.resxResources/ViewModels.Account.RegisterViewModel.fr.resx
  • Resources/ViewModels/Account/RegisterViewModel.fr.resxResources/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 和更高版本中,非验证属性已经进行了本地化。In ASP.NET Core MVC 1.1.0 and higher, non-validation attributes are localized. ASP.NET Core MVC 1.0 不会为非验证属性查找本地化字符串。ASP.NET Core MVC 1.0 does not look up localized strings for non-validation attributes.

对多个类使用一个资源字符串Using one resource string for multiple classes

下面的代码演示如何针对具有多个类的验证属性使用一个资源字符串:The following code shows how to use one resource string for validation attributes with multiple classes:

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

在上面的代码中,SharedResource 是对应于存储验证消息的 resx 的类。In the preceding code, SharedResource is the class corresponding to the resx where your validation messages are stored. 使用此方法,DataAnnotations 将仅使用 SharedResource,而不是每个类的资源。With this approach, DataAnnotations will only use SharedResource, rather than the resource for each class.

为支持的语言和区域性提供本地化资源Provide localized resources for the languages and cultures you support

SupportedCultures 和 SupportedUICulturesSupportedCultures and SupportedUICultures

ASP.NET Core 允许指定两个区域性值,SupportedCulturesSupportedUICulturesASP.NET Core allows you to specify two culture values, SupportedCultures and SupportedUICultures. SupportedCulturesCultureInfo 对象可决定区域性相关函数的结果,如日期、时间、数字和货币格式等。The CultureInfo object for SupportedCultures determines the results of culture-dependent functions, such as date, time, number, and currency formatting. SupportedCultures 确定文本的排序顺序、大小写约定和字符串比较。SupportedCultures also determines the sorting order of text, casing conventions, and string comparisons. 请参阅 CultureInfo.CurrentCulture 详细了解服务器如何获取区域性。See CultureInfo.CurrentCulture for more info on how the server gets the Culture. SupportedUICultures 可确定哪些转换字符串(.resx 文件中)按 ResourceManager 查找。The SupportedUICultures determines which translates strings (from .resx files) are looked up by the ResourceManager. ResourceManager 只需查找 CurrentUICulture 决定的区域性特定字符串。The ResourceManager simply looks up culture-specific strings that's determined by CurrentUICulture. .NET 中的每个线程都具有 CurrentCultureCurrentUICulture 对象。Every thread in .NET has CurrentCulture and CurrentUICulture objects. 呈现区域性相关函数时,ASP.NET Core 可检查这些值。ASP.NET Core inspects these values when rendering culture-dependent functions. 例如,如果当前线程的区域性设置为“en-US”(英语,美国),DateTime.Now.ToLongDateString() 将显示“Thursday, February 18, 2016”,但如果 CurrentCulture 设置为“es-ES”(西班牙语,西班牙),则输出将为“jueves,18 de febrero de 2016”。For example, if the current thread's culture is set to "en-US" (English, United States), DateTime.Now.ToLongDateString() displays "Thursday, February 18, 2016", but if CurrentCulture is set to "es-ES" (Spanish, Spain) the output will be "jueves, 18 de febrero de 2016".

资源文件Resource files

资源文件是将可本地化的字符串与代码分离的有用机制。A resource file is a useful mechanism for separating localizable strings from code. 非默认语言的转换字符串是独立的 .resx 资源文件。Translated strings for the non-default language are isolated .resx resource files. 例如,你可能想要创建包含转换字符串、名为 Welcome.es.resx 的西班牙语资源文件。For example, you might want to create Spanish resource file named Welcome.es.resx containing translated strings. “es”是西班牙语的语言代码。"es" is the language code for Spanish. 要在 Visual Studio 中创建此资源文件,请支持以下操作:To create this resource file in Visual Studio:

  1. 在“解决方案资源管理器”中,右键单击将包含资源文件的文件夹 >“添加” > “新项”。In Solution Explorer, right click on the folder which will contain the resource file > Add > New Item.

    嵌套的上下文菜单:在“解决方案资源管理器”中,“资源”可打开上下文菜单。

  2. 在“搜索已安装的模板”框中,输入“资源”并命名该文件。In the Search installed templates box, enter "resource" and name the file.

    “添加新项”对话框

  3. 在“名称”列中输入键值(本机字符串),在“值”列中输入转换字符串。Enter the key value (native string) in the Name column and the translated string in the Value column.

    Welcome.es.resx 文件(西班牙语版 Welcome 资源文件)的单词 Hello 位于“名称”列,Hola(西班牙语版 Hello)位于“值”列

    Visual Studio 将显示 Welcome.es.resx 文件。Visual Studio shows the Welcome.es.resx file.

    显示 Welcome Spanish (es) 资源文件的解决方案资源管理器

资源文件命名Resource file naming

资源名称是类的完整类型名称减去程序集名称。Resources are named for the full type name of their class minus the assembly name. 例如,类 LocalizationWebsite.Web.Startup 的主要程序集为 LocalizationWebsite.Web.dll 的项目中的法语资源将命名为 Startup.fr.resx。For example, a French resource in a project whose main assembly is LocalizationWebsite.Web.dll for the class LocalizationWebsite.Web.Startup would be named Startup.fr.resx. LocalizationWebsite.Web.Controllers.HomeController 的资源将命名为 Controllers.HomeController.fr.resx。A resource for the class LocalizationWebsite.Web.Controllers.HomeController would be named Controllers.HomeController.fr.resx. 如果目标类的命名空间与将需要完整类型名称的程序集名称不同。If your targeted class's namespace isn't the same as the assembly name you will need the full type name. 例如,在示例项目中,类型 ExtraNamespace.Tools 的资源将命名为 ExtraNamespace.Tools.fr.resx。For example, in the sample project a resource for the type ExtraNamespace.Tools would be named ExtraNamespace.Tools.fr.resx.

在示例项目中,ConfigureServices 方法将 ResourcesPath 设置为“资源”,因此主控制器的法语资源文件的项目相对路径是 Resources/Controllers.HomeController.fr.resx。In the sample project, the ConfigureServices method sets the ResourcesPath to "Resources", so the project relative path for the home controller's French resource file is Resources/Controllers.HomeController.fr.resx. 或者,你可以使用文件夹组织资源文件。Alternatively, you can use folders to organize resource files. 对于主控制器,该路径将为 Resources/Controllers/HomeController.fr.resx。For the home controller, the path would be Resources/Controllers/HomeController.fr.resx. 如果不使用 ResourcesPath 选项,.resx 文件将转到项目的基目录中。If you don't use the ResourcesPath option, the .resx file would go in the project base directory. HomeController 的资源文件将命名为 Controllers.HomeController.fr.resx。The resource file for HomeController would be named Controllers.HomeController.fr.resx. 选择使用圆点或路径命名约定具体取决于你想如何组织资源文件。The choice of using the dot or path naming convention depends on how you want to organize your resource files.

资源名称Resource name 圆点或路径命名Dot or path naming
Resources/Controllers.HomeController.fr.resxResources/Controllers.HomeController.fr.resx 圆点Dot
Resources/Controllers/HomeController.fr.resxResources/Controllers/HomeController.fr.resx 路径Path

Razor 视图中使用 @inject IViewLocalizer 的资源文件遵循类似的模式。Resource files using @inject IViewLocalizer in Razor views follow a similar pattern. 可以使用圆点命名或路径命名约定对视图的资源文件进行命名。The resource file for a view can be named using either dot naming or path naming. Razor 视图资源文件可模拟其关联视图文件的路径。Razor view resource files mimic the path of their associated view file. 假设我们将 ResourcesPath 设置为“资源”,与 Views/Home/About.cshtml 视图关联的法语资源文件可能是下面其中之一:Assuming we set the ResourcesPath to "Resources", the French resource file associated with the Views/Home/About.cshtml view could be either of the following:

  • Resources/Views/Home/About.fr.resxResources/Views/Home/About.fr.resx

  • Resources/Views.Home.About.fr.resxResources/Views.Home.About.fr.resx

如果不使用 ResourcesPath 选项,视图的 .resx 文件将位于视图所在的文件夹。If you don't use the ResourcesPath option, the .resx file for a view would be located in the same folder as the view.

RootNamespaceAttributeRootNamespaceAttribute

RootNamespace 属性在程序集的根命名空间不同于程序集名称时,提供程序集的根命名空间。The RootNamespace attribute provides the root namespace of an assembly when the root namespace of an assembly is different than the assembly name.

如果程序集的根命名空间不同于程序集名称:If the root namespace of an assembly is different than the assembly name:

  • 默认情况下无法进行本地化。Localization does not work by default.
  • 因程序集内搜索资源的方式导致本地化失败。Localization fails due to the way resources are searched for within the assembly. RootNamespace 是生成时间值,不可用于正在执行的进程。RootNamespace is a build-time value which is not available to the executing process.

如果 RootNamespace 不同于 AssemblyName,请在 AssemblyInfo.cs 中包括以下内容(参数值替换为实际值):If the RootNamespace is different from the AssemblyName, include the following in AssemblyInfo.cs (with parameter values replaced with the actual values):

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

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

上述代码可成功解析 resx 文件。The preceding code enables the successful resolution of resx files.

区域性回退行为Culture fallback behavior

在搜索资源时,本地化会进行“区域性回退”。When searching for a resource, localization engages in "culture fallback". 从所请求的区域性开始,如果未能找到,则还原至该区域性的父区域性。Starting from the requested culture, if not found, it reverts to the parent culture of that culture. 另外,CultureInfo.Parent 属性代表父区域性。As an aside, the CultureInfo.Parent property represents the parent culture. 这通常(但并不是总是)意味着从 ISO 中移除区域签名。This usually (but not always) means removing the national signifier from the ISO. 例如,墨西哥的西班牙语方言为“es-MX”。For example, the dialect of Spanish spoken in Mexico is "es-MX". 它具备一个父级“es”西班牙,没有特别指定国家/地区。It has the parent "es"—Spanish non-specific to any country.

假设你的站点接收到了一个区域性为“fr-CA”的“Welcome”资源的请求。Imagine your site receives a request for a "Welcome" resource using culture "fr-CA". 本地化系统按顺序查找以下资源,并选择第一个匹配项:The localization system looks for the following resources, in order, and selects the first match:

  • Welcome.fr-CA.resxWelcome.fr-CA.resx
  • Welcome.fr.resxWelcome.fr.resx
  • Welcome.resx(如果 NeutralResourcesLanguage 为“fr-CA”)Welcome.resx (if the NeutralResourcesLanguage is "fr-CA")

例如,如果删除了“.fr”区域性指示符,而且已将区域性设置为“法语”,将读取默认资源文件,并本地化字符串。As an example, if you remove the ".fr" culture designator and you have the culture set to French, the default resource file is read and strings are localized. 对于不满足所请求区域性的情况,资源管理器可指定默认资源或回退资源。The Resource manager designates a default or fallback resource for when nothing meets your requested culture. 缺少适用于请求区域性的资源时,如果只想返回键,不得具有默认资源文件。If you want to just return the key when missing a resource for the requested culture you must not have a default resource file.

使用 Visual Studio 生成资源文件Generate resource files with Visual Studio

如果在 Visual Studio 中创建文件名没有区域性的资源文件(例如 Welcome.resx),Visual Studio 将创建一个 C# 类,并且具有每个字符串的属性。If you create a resource file in Visual Studio without a culture in the file name (for example, Welcome.resx), Visual Studio will create a C# class with a property for each string. 这通常不是你想在 ASP.NET Core 中使用的。That's usually not what you want with ASP.NET Core. 你通常没有默认的 .resx 资源文件(没有区域性名称的 .resx 文件)。You typically don't have a default .resx resource file (a .resx file without the culture name). 建议创建具有区域性名称(例如 Welcome.fr.resx)的 .resx 文件。We suggest you create the .resx file with a culture name (for example Welcome.fr.resx). 创建具有区域性名称的 .resx 文件时,Visual Studio 不会生成类文件。When you create a .resx file with a culture name, Visual Studio won't generate the class file. 我们预计许多开发者不会创建默认语言资源文件。We anticipate that many developers won't create a default language resource file.

添加其他区域性Add other cultures

每个语言和区域性组合(除默认语言外)都需要唯一资源文件。Each language and culture combination (other than the default language) requires a unique resource file. 通过新建 ISO 语言代码属于名称一部分的资源文件,为不同的区域性和区域设置创建资源文件(例如,en-us、fr-ca 和 en-gb)。You create resource files for different cultures and locales by creating new resource files in which the ISO language codes are part of the file name (for example, en-us, fr-ca, and en-gb). 这些 ISO 编码位于文件名和 .resx 文件扩展之间,如 Welcome.es-MX.resx(西班牙语/墨西哥)。These ISO codes are placed between the file name and the .resx file extension, as in Welcome.es-MX.resx (Spanish/Mexico).

实施策略,为每个请求选择语言/区域性Implement a strategy to select the language/culture for each request

配置本地化Configure localization

通过 Startup.ConfigureServices 方法配置本地化:Localization is configured in the Startup.ConfigureServices method:

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

services.AddMvc()
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
    .AddDataAnnotationsLocalization();
  • AddLocalization 将本地化服务添加到服务容器。AddLocalization Adds the localization services to the services container. 上面的代码还可将资源路径设置为“资源”。The code above also sets the resources path to "Resources".

  • AddViewLocalization 添加对本地化视图文件的支持。AddViewLocalization Adds support for localized view files. 在此示例视图中,本地化基于视图文件后缀。In this sample view localization is based on the view file suffix. 例如,Index.fr.cshtml 文件中的“fr”。For example "fr" in the Index.fr.cshtml file.

  • AddDataAnnotationsLocalization 添加通过 IStringLocalizer 抽象对本地化 DataAnnotations 验证消息的支持。AddDataAnnotationsLocalization Adds support for localized DataAnnotations validation messages through IStringLocalizer abstractions.

本地化中间件Localization middleware

在本地化中间件中设置有关请求的当前区域性。The current culture on a request is set in the localization Middleware. Startup.Configure 方法中启用本地化中间件。The localization middleware is enabled in the Startup.Configure method. 必须在中间件前面配置本地化中间件,它可能检查请求区域性(例如,app.UseMvcWithDefaultRoute())。The localization middleware must be configured before any middleware which might check the request culture (for example, app.UseMvcWithDefaultRoute()).

var supportedCultures = new[]
{
    new CultureInfo("en-US"),
    new CultureInfo("fr"),
};

app.UseRequestLocalization(new RequestLocalizationOptions
{
    DefaultRequestCulture = new RequestCulture("en-US"),
    // Formatting numbers, dates, etc.
    SupportedCultures = supportedCultures,
    // UI strings that we have localized.
    SupportedUICultures = supportedCultures
});

app.UseStaticFiles();
// To configure external authentication, 
// see: http://go.microsoft.com/fwlink/?LinkID=532715
app.UseAuthentication();
app.UseMvcWithDefaultRoute();

UseRequestLocalization 初始化 RequestLocalizationOptions 对象。UseRequestLocalization initializes a RequestLocalizationOptions object. 在每个请求上,枚举了 RequestLocalizationOptionsRequestCultureProvider 列表,使用了可成功决定请求区域性的第一个提供程序。On every request the list of RequestCultureProvider in the RequestLocalizationOptions is enumerated and the first provider that can successfully determine the request culture is used. 默认提供程序来自 RequestLocalizationOptions 类:The default providers come from the RequestLocalizationOptions class:

  1. QueryStringRequestCultureProvider
  2. CookieRequestCultureProvider
  3. AcceptLanguageHeaderRequestCultureProvider

默认列表从最具体到最不具体排序。The default list goes from most specific to least specific. 在本文的后面部分,我们将了解如何更改顺序,甚至添加一个自定义区域性提供程序。Later in the article we'll see how you can change the order and even add a custom culture provider. 如果没有一个提供程序可以确定请求区域性,则使用 DefaultRequestCultureIf none of the providers can determine the request culture, the DefaultRequestCulture is used.

QueryStringRequestCultureProviderQueryStringRequestCultureProvider

某些应用将使用查询字符串来设置区域性和 UI 区域性Some apps will use a query string to set the culture and UI culture. 对于使用 Cookie 或接受语言标题方法的应用,向 URL 添加查询字符串有助于调试和测试代码。For apps that use the cookie or Accept-Language header approach, adding a query string to the URL is useful for debugging and testing code. 默认情况下,QueryStringRequestCultureProvider 注册为 RequestCultureProvider 列表中的第一个本地化提供程序。By default, the QueryStringRequestCultureProvider is registered as the first localization provider in the RequestCultureProvider list. 传递查询字符串参数 cultureui-cultureYou pass the query string parameters culture and ui-culture. 下面的示例将特定区域性(语言和区域)设置为“西班牙语/墨西哥”:The following example sets the specific culture (language and region) to Spanish/Mexico:

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

如果仅传入两种区域性之一(cultureui-culture,查询字符串提供程序将使用你传入的区域性设置这两个值。If you only pass in one of the two (culture or ui-culture), the query string provider will set both values using the one you passed in. 例如,仅设置区域性将同时设置 CultureUICultureFor example, setting just the culture will set both the Culture and the UICulture:

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

CookieRequestCultureProviderCookieRequestCultureProvider

通常,生产应用将提供一种机制来使用 ASP.NET Core 区域性 Cookie 设置区域性。Production apps will often provide a mechanism to set the culture with the ASP.NET Core culture cookie. 若要创建 Cookie,请使用 MakeCookieValue 方法。Use the MakeCookieValue method to create a cookie.

CookieRequestCultureProvider DefaultCookieName 将返回用来跟踪用户首选区域性信息的默认 Cookie 名称。The CookieRequestCultureProvider DefaultCookieName returns the default cookie name used to track the user's preferred culture information. 默认 Cookie 名称是 .AspNetCore.CultureThe default cookie name is .AspNetCore.Culture.

Cookie 格式为 c=%LANGCODE%|uic=%LANGCODE%,其中cCultureuicUICulture,例如:The cookie format is c=%LANGCODE%|uic=%LANGCODE%, where c is Culture and uic is UICulture, for example:

c=en-UK|uic=en-US

如果仅指定其中一个区域性信息和 UI 区域性,则指定的区域性将同时用于区域性信息和 UI 区域性。If you only specify one of culture info and UI culture, the specified culture will be used for both culture info and UI culture.

接受语言 HTTP 标题The Accept-Language HTTP header

接受语言标题在大多数浏览器中可设置,最初用于指定用户的语言。The Accept-Language header is settable in most browsers and was originally intended to specify the user's language. 此设置指示浏览器已设置为发送或已从基础操作系统继承的内容。This setting indicates what the browser has been set to send or has inherited from the underlying operating system. 浏览器请求的接受语言 HTTP 标题不是检测用户首选语言的可靠方法(请参阅 Setting language preferences in a browser(在浏览器中设置首选项)。The Accept-Language HTTP header from a browser request isn't an infallible way to detect the user's preferred language (see Setting language preferences in a browser). 生产应用应包括一种用户可以自定义区域性选择的方法。A production app should include a way for a user to customize their choice of culture.

在 IE 中设置接受语言 HTTP 标题Set the Accept-Language HTTP header in IE

  1. 在齿轮图标中,点击“Internet 选项”。From the gear icon, tap Internet Options.

  2. 点击“语言”。Tap Languages.

    Internet 选项

  3. 点击“设置语言首选项”。Tap Set Language Preferences.

  4. 点击“添加语言”。Tap Add a language.

  5. 添加语言。Add the language.

  6. 点击语言,然后点击“向上移动”。Tap the language, then tap Move Up.

使用自定义提供程序Use a custom provider

假设你想要让客户在数据库中存储其语言和区域性。Suppose you want to let your customers store their language and culture in your databases. 你可以编写一个提供程序来查找用户的这些值。You could write a provider to look up these values for the user. 下面的代码演示如何添加自定义提供程序:The following code shows how to add a custom provider:

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.RequestCultureProviders.Insert(0, new CustomRequestCultureProvider(async context =>
    {
        // My custom request culture logic
        return new ProviderCultureResult("en");
    }));
});

使用 RequestLocalizationOptions 添加或删除本地化提供程序。Use RequestLocalizationOptions to add or remove localization providers.

以编程方式设置区域性Set the culture programmatically

GitHub 上的示例项目 Localization.StarterWeb 包含设置 Culture 的 UI。This sample Localization.StarterWeb project on GitHub contains UI to set the Culture. Views/Shared/_SelectLanguagePartial.cshtml 文件允许你从支持的区域性列表中选择区域性:The Views/Shared/_SelectLanguagePartial.cshtml file allows you to select the culture from the list of supported cultures:

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

Views/Shared/_SelectLanguagePartial.cshtml 文件添加到了布局文件的 footer 部分,使它将可供所有视图使用:The Views/Shared/_SelectLanguagePartial.cshtml file is added to the footer section of the layout file so it will be available to all views:

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

SetLanguage 方法可设置区域性 Cookie。The SetLanguage method sets the culture 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);
}

不能将 _SelectLanguagePartial.cshtml 插入此项目的示例代码。You can't plug in the _SelectLanguagePartial.cshtml to sample code for this project. GitHub 上的项目 Localization.StarterWeb 包含代码,可部分通过依赖关系注入容器将 RequestLocalizationOptions 流到 Razor。The Localization.StarterWeb project on GitHub has code to flow the RequestLocalizationOptions to a Razor partial through the Dependency Injection container.

全球化和本地化术语Globalization and localization terms

本地化应用的过程还要求基本了解现代软件开发中常用的相关字符集,以及与之相关的问题。The process of localizing your app also requires a basic understanding of relevant character sets commonly used in modern software development and an understanding of the issues associated with them. 尽管所有计算机将文本都存储为数字(代码),但不同的系统使用不同的数字存储相同的文本。Although all computers store text as numbers (codes), different systems store the same text using different numbers. 本地化过程是指针对特定区域性/区域设置转换应用的用户界面 (UI)。The localization process refers to translating the app user interface (UI) for a specific culture/locale.

本地化性是一个中间过程,用于验证全球化应用是否准备好进行本地化。Localizability is an intermediate process for verifying that a globalized app is ready for localization.

区域性名称的 RFC 4646 格式为 <languagecode2>-<country/regioncode2>,其中 <languagecode2> 是语言代码,<country/regioncode2> 是子区域性代码。The RFC 4646 format for the culture name is <languagecode2>-<country/regioncode2>, where <languagecode2> is the language code and <country/regioncode2> is the subculture code. 例如,es-CL 表示西班牙语(智利),en-US 表示英语(美国),而 en-AU 表示英语(澳大利亚)。For example, es-CL for Spanish (Chile), en-US for English (United States), and en-AU for English (Australia). RFC 4646 是一个与语言相关的 ISO 639 双小写字母的区域性代码和一个与国家/地区相关的 ISO 3166 双大写字母子区域性代码的组合。RFC 4646 is a combination of an ISO 639 two-letter lowercase culture code associated with a language and an ISO 3166 two-letter uppercase subculture code associated with a country or region. 请参阅语言区域性名称See Language Culture Name.

国际化常缩写为“I18N”。Internationalization is often abbreviated to "I18N". 缩写采用第一个和最后一个字母以及它们之间的字母数,因此 18 代表第一个字母“I”和最后一个“N”之间的字母数。The abbreviation takes the first and last letters and the number of letters between them, so 18 stands for the number of letters between the first "I" and the last "N". 这同样适用于全球化 (G11N) 和本地化 (L10N)。The same applies to Globalization (G11N), and Localization (L10N).

术语:Terms:

  • 全球化 (G11N):使应用支持不同语言和区域的过程。Globalization (G11N): The process of making an app support different languages and regions.
  • 本地化 (L10N):针对给定语言和区域自定义应用的过程。Localization (L10N): The process of customizing an app for a given language and region.
  • 国际化 (I18N):介绍了全球化和本地化。Internationalization (I18N): Describes both globalization and localization.
  • 区域性:它是一种语言和区域(可选)。Culture: It's a language and, optionally, a region.
  • 非特定区域性:具有指定语言但不具有区域的区域性。Neutral culture: A culture that has a specified language, but not a region. (例如,“en”,“es”)(for example "en", "es")
  • 特定区域性:具有指定语言和区域的区域性。Specific culture: A culture that has a specified language and region. (例如,“en-US”,“en-GB”,“es-CL”)(for example "en-US", "en-GB", "es-CL")
  • 父区域性:包含特定区域性的非特定区域性。Parent culture: The neutral culture that contains a specific culture. (例如,“en”是“en-US”和“en-GB”的父区域性)(for example, "en" is the parent culture of "en-US" and "en-GB")
  • 区域设置:区域设置与区域性相同。Locale: A locale is the same as a culture.

备注

可能无法在小数字段中输入十进制逗号。You may not be able to enter decimal commas in decimal fields. 若要使 jQuery 验证支持使用逗号(“,”)表示小数点的的非英语区域设置,以及支持非美国英语日期格式,必须执行使应用全球化的步骤。To support jQuery validation for non-English locales that use a comma (",") for a decimal point, and non US-English date formats, you must take steps to globalize your app. 有关添加十进制逗号的说明,请参阅 GitHub 问题 4076This GitHub issue 4076 for instructions on adding decimal comma.

其他资源Additional resources