Глобализация и локализация в ASP.NET CoreGlobalization and localization in ASP.NET Core

Авторы: Рик Андерсон (Rick Anderson), Дэмиен Боуден (Damien Bowden), Барт Каликсто (Bart Calixto), Надим Афана (Nadeem Afana) и Хишам Бин Атея (Hisham Bin Ateya)By 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 интерфейсы IStringLocalizer и IStringLocalizer<T> призваны повысить производительность при разработке локализованных приложений.Introduced in ASP.NET Core, IStringLocalizer and IStringLocalizer<T> were architected to improve productivity when developing localized apps. Интерфейс IStringLocalizer использует классы ResourceManager и ResourceReader для предоставления ресурсов, связанных с определенным языком и региональными параметрами, во время выполнения.IStringLocalizer uses the ResourceManager and ResourceReader to provide culture-specific resources at run time. Этот простой интерфейс имеет индексатор и интерфейс IEnumerable для возврата локализованных строк.The 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.

Используйте реализацию IHtmlLocalizer<T> для ресурсов, содержащих код HTML.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. В выделенной ниже строке в HTML кодируется только значение параметра name.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.

На самом нижнем уровне интерфейс IStringLocalizerFactory можно получить из внедрения зависимостей:At 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. В примере ниже используются средства локализации InfoController и SharedResource.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>

Локализация DataAnnotationsDataAnnotations 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 позволяет указывать два значения языка и региональных параметров: SupportedCultures и SupportedUICultures.ASP.NET Core allows you to specify two culture values, SupportedCultures and SupportedUICultures. Объект CultureInfo для SupportedCultures определяет результаты функций, зависящих от языка и региональных параметров: форматирование дат, времени, чисел, денежных единиц и т. д.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 имеет объекты CurrentCulture и CurrentUICulture.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 имеет значение "ru-RU" (русский, Россия), выводится строка "четверг, 18 февраля 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 (файлов ресурсов с приветствиями на испанском языке) со словом "Hello" в столбце "Имя" и словом "Hol"a ("Hello" на испанском) в столбце "Значение"

    В Visual Studio отобразится файл Welcome.es.resx.Visual Studio shows the Welcome.es.resx file.

    Обозреватель решений с файлом ресурсов на испанском языке

Именование файлов ресурсовResource file naming

Имена ресурсов представляют собой полные имена типов соответствующего класса за исключением имени сборки.Resources are named for the full type name of their class minus the assembly name. Например, ресурс на французском языке в проекте, главная сборка которого имеет имя LocalizationWebsite.Web.dll, для класса LocalizationWebsite.Web.Startup будет иметь имя 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", поэтому относительный путь к файлу ресурсов на французском языке для контроллера домашней страницы в проекте будет иметь вид 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 PathPath

Файлы ресурсов, для которых используется директива @inject IViewLocalizer в представлениях Razor, следуют той же модели.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 присвоено значение "Resources". В этом случае файл ресурсов на французском языке, связанный с представлением 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.

Предупреждение

Это может произойти, если имя проекта — недопустимый идентификатор .NET.This can occur when a project's name is not a valid .NET identifier. Например, my-project-name.csproj будет использовать корневое пространство имен my_project_name и имя сборки my-project-name, что повлечет эту ошибку.For instance my-project-name.csproj will use the root namespace my_project_name and the assembly name my-project-name leading to this error.

Если корневое пространство имен сборки отличается от имени сборки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.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 StudioGenerate 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). Мы рекомендуем создать файл RESX с указанием языка и региональных параметров (например, Welcome.fr.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.

Добавление других языков и региональных параметровAdd other cultures

Каждое сочетание языка и региональных параметров (кроме языка по умолчанию) требует уникального файла ресурсов.Each language and culture combination (other than the default language) requires a unique resource file. Создавая файлы ресурсов для разных языков, языковых стандартов и региональных параметров, вы включаете в их имена коды языков ISO (например, en-us, ru-ru и fr-ca).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. В приведенном выше коде также задается путь к ресурсам "Resources".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. например "fr" в файле Index.fr.cshtml.For example "fr" in the Index.fr.cshtml file.

  • Метод AddDataAnnotationsLocalization добавляет поддержку локализованных сообщений проверки DataAnnotations посредством абстракций IStringLocalizer.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. При каждом запросе проверяется список поставщиков RequestCultureProvider в объекте RequestLocalizationOptions и используется первый поставщик, который может успешно определить язык и региональные параметры запроса.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. Если ни один из поставщиков не может определить язык и региональные параметры запроса, используется DefaultRequestCulture.If none of the providers can determine the request culture, the DefaultRequestCulture is used.

QueryStringRequestCultureProviderQueryStringRequestCultureProvider

Некоторые приложения используют строку запроса для указания языка и региональных параметров самого приложения и пользовательского интерфейса.Some apps will use a query string to set the culture and UI culture. Если в приложении для этого применяется файл cookie или заголовок Accept-Language, добавление строки запроса к 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. Вы передаете параметры строки запроса culture и ui-culture.You 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

Если передан только один из двух параметров (culture или ui-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. Например, если передан только язык и региональные параметры, будут заданы оба значения Culture и UICulture:For example, setting just the culture will set both the Culture and the UICulture:

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

CookieRequestCultureProviderCookieRequestCultureProvider

Рабочие приложения часто предоставляют механизм для указания языка и региональных параметров с помощью соответствующего файла cookie ASP.NET Core.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.Culture.The default cookie name is .AspNetCore.Culture.

Файл cookie имеет формат c=%LANGCODE%|uic=%LANGCODE%, где c — это Culture, а uic — это UICulture, например:The cookie format is c=%LANGCODE%|uic=%LANGCODE%, where c is Culture and uic is UICulture, for example:

c=en-UK|uic=en-US

Если указаны либо только язык и региональные параметры, либо только язык и региональные параметры пользовательского интерфейса, это значение будет использоваться для обоих параметров.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 Accept-LanguageThe Accept-Language HTTP header

Заголовок Accept-Language может задаваться в большинстве браузеров и изначально предназначался для указания языка пользователя.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 Accept-Language в запросе из браузера — это не самый надежный способ определения предпочтительного языка пользователя (см. статью Настройка языковых предпочтений в браузере).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.

Задание заголовка HTTP Accept-Language в Internet ExplorerSet the Accept-Language HTTP header in IE

  1. Щелкните значок шестеренки и выберите пункт Свойства браузера.From the gear icon, tap Internet Options.

  2. Нажмите кнопку Языки.Tap Languages.

    Свойства браузера

  3. Нажмите Задать предпочитаемые языки.Tap Set Language Preferences.

  4. Нажмите Добавить язык.Tap Add a language.

  5. Добавьте язык.Add the language.

  6. Выберите язык, а затем нажмите кнопку Вверх.Tap the language, then tap Move Up.

Заголовок HTTP Content-LanguageThe Content-Language HTTP header

Заголовок сущности Content-Language:The Content-Language entity header:

  • Используется для описания языков, предназначенных для аудитории.Is used to describe the language(s) intended for the audience.
  • Позволяет пользователю различать в соответствии с предпочтительным языком пользователя.Allows a user to differentiate according to the users' own preferred language.

Заголовки сущностей используются как в HTTP-запросах, так и в ответах.Entity headers are used in both HTTP requests and responses.

Заголовок Content-Language можно добавить, задав свойство ApplyCurrentCultureToResponseHeaders.The Content-Language header can be added by setting the property ApplyCurrentCultureToResponseHeaders.

Добавление заголовка Content-Language:Adding the Content-Language header:

  • Позволяет RequestLocalizationMiddleware задать заголовок Content-Language с CurrentUICulture.Allows the RequestLocalizationMiddleware to set the Content-Language header with the CurrentUICulture.
  • Устраняет необходимость явной установки заголовка ответа Content-Language.Eliminates the need to set the response header Content-Language explicitly.
app.UseRequestLocalization(new RequestLocalizationOptions
{
    ApplyCurrentCultureToResponseHeaders = true
});

Использование пользовательского поставщика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");
    }));
});
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 new ProviderCultureResult("en");
    }));
});

Для добавления или удаления поставщиков локализации используйте объект RequestLocalizationOptions.Use RequestLocalizationOptions to add or remove localization providers.

Задание языка и региональных параметров программным образомSet the culture programmatically

В образце проекта Localization.StarterWeb в GitHub есть пользовательский интерфейс для задания значения Culture.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. В проекте Localization.StarterWeb в GitHub есть код для передачи объекта RequestLocalizationOptions в частичное представление Razor посредством контейнера внедрения зависимостей.The Localization.StarterWeb project on GitHub has code to flow the RequestLocalizationOptions to a Razor partial through the Dependency Injection container.

Данные маршрутов привязки модели и строки запросовModel binding route data and query strings

См. раздел Поведение глобализации для данных маршрутов привязки модели и строк запросов.See Globalization behavior of model binding route data and query strings.

Термины, относящиеся к глобализации и локализации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. Под процессом локализации понимается перевод пользовательского интерфейса приложения для определенного языка и региональных параметров.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.

Вместо слова "интернационализация" (internationalization) часто используют аббревиатуру "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". То же самое касается глобализации (Globalization — G11N) и локализации (Localization — 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. Инструкции по добавлению десятичной запятой см. в вопросе № 4076 на сайте GitHub.See this GitHub issue 4076 for instructions on adding decimal comma.

Примечание

Если запрашиваемый язык и региональные параметры не поддерживаются, перед запуском ASP.NET Core 3.0 веб-приложения записывают по одному журналу типа LogLevel.Warning на запрос.Prior to ASP.NET Core 3.0 web apps write one log of type LogLevel.Warning per request if the requested culture is unsupported. Запись одного LogLevel.Warning на запрос может привести к большим файлам журнала с избыточной информацией.Logging one LogLevel.Warning per request is can make large log files with redundant information. Эту реакцию на событие изменено в ASP.NET 3.0.This behavior has been changed in ASP.NET 3.0. RequestLocalizationMiddleware записывает журнал типа LogLevel.Debug, который сокращает размер рабочих журналов.The RequestLocalizationMiddleware writes a log of type LogLevel.Debug, which reduces the size of production logs.

Дополнительные ресурсыAdditional resources