Локализация
Для локализации .net core
приложений пользуемся встроенными инструментами фреймворка. Через IoC
контейнер для локализации можно получить реализации следующих интерфейсов:
IStringLocalizer<>
: для получения локализованной строки для конкретного классаIHtmlLocalizer<>
: для получения локализованной строки для конкретного класса, игнорируются html тегиIViewLocalizer
: для локализации представлений
Microsoft
предлагает в качестве ключа для поиска локализованной строки использовать саму строку на английском языке. Этот подход считаем для себя неприменимым, т.к.:
- при изменении самой строки нужно обновить везде ресурсы
- в коде будут магические текстовые строки
Поэтому отдельно в каталоге Resources
создается статический класс ResourceKeys
, в котором содержатся ключи локализуемых строк:
/// <summary>
/// Класс с ключами ресурсов.
/// </summary>
/// <remarks>
/// Все ключи должны вноситься сюда
/// </remarks>
public static class ResourceKeys
{
#region SharedResources
public const string HomePage_Title = "HomeTitle";
public const string AboutView_Title= "AboutTitle";
public const string ContactView_Title= "ContactTitle";
#endregion
public const string Required = "Required";
public const string NotAValidEmail = "NotAValidEmail";
public const string YourEmail = "YourEmail";
#region HomeController
public const string HomeController_SuccessMessage = "Success";
public const string HomeController_AboutContent = "About";
public const string HomeController_ContactContent = "Contact";
}
Т.к. файл общий и строк будет много, то нужно использовать регионы (#region
) для группировки их по тем классам, где используется локализуемая строка.
Локализация включается в методе ConfigureServices
класса Startup
или ручным добавлением к IoC
следующим образом:
// подключаем локализацию
services.AddLocalization(
opts =>
{
// указываем путь к ресурсам
opts.ResourcesPath = "Resources";
});
services.AddMvc()
// подкючаем локализацию MVC
.AddViewLocalization(
// выбираем тип поиска ресурсов
LanguageViewLocationExpanderFormat.SubFolder,
opts =>
{
// указываем путь к ресурсам
opts.ResourcesPath = "Resources";
})
// подкючаем локализацию аннотаций для MVC
.AddDataAnnotationsLocalization();
// настраиваем поддерживаемые локали и локаль по умолчанию
services.Configure<RequestLocalizationOptions>(
opts =>
{
var supportedCultures = new List<CultureInfo>
{
new CultureInfo("en"),
new CultureInfo("ru-RU"),
new CultureInfo("ru")
};
// культура по умолчанию для запросов
opts.DefaultRequestCulture = new RequestCulture("ru");
// список доступных культур
opts.SupportedCultures = supportedCultures;
opts.SupportedUICultures = supportedCultures;
});
services.AddSingleton<ILocalisedService, LocalizedService>();
В ASP.Net Core можно выбрать несколько способ получения локали пользователя: по строке запроса, на основе Cookies и на основе содержимого заголовка запроса. Все задается в методе ConfigureServices
класса Startup
:
app.UseRequestLocalization(app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>().Value);
По умолчанию, используются все три способа в следующем порядке:
- строка запроса
- Cookies
- заголовки
Можно добавить свое правило. Подробности в полезных ссылках ниже.
Организация пространства ресурсов
Все ресурсы должны распологаться в директории Resources
. Поиск файла с ресурсами может работать по двум стратегиям:
поиск по директориям
: в диеркторииResources
должна быть воссоздана точная иерархия пространств имен проекта, сам ресурс должен называться также, как и класс, для которого эти ресурсы предназначеныпоиск по имени
: директории не нужны, требуется, чтобы полное имя ресусрса совпадало с именем класса, для которого этот ресурс предназначен.
Мы выибираем первый поход, поиск по диреториям
, т.к. поиск ресурсов будет более интутивно-понятным и не будет "свалки" из ресурсов.
Локаль по умолчанию
Локалью по умолчанию считаем русскую (ru
). Для файла с ресурсами этой локали никаких суффиксов указывать не надо. Для остаьных обязательно указывает локаль. Примеры:
HomeController.resx
HomeController.en.resx
Общие ресурсы
Чтобы не дублировать сообщения, которые встречаются часто, их можно вынести в общие ресурсы. Для этого в директории Resources
необходимо создать ресурсы с названием SharedResources[.local].resx
. В корне проекта добавить статический пустой класс SharedResources
:
/// <summary>
/// Класс для обращения к общим ресурсам
/// </summary>
/// <remarks>
/// Должен быть пустым и находиться в корне проекта
/// </remarks>
public class SharedResources
{
}
Чтобы пользоваться общими ресурсами, надо получить реализацию IStringLocalizer<SharedResources>
.
Подводные камни
Internal visibility
Чтобы ключи ресурсов и сами реусурсов не были доступны извне сборки, их стоит помечать атрибутом internal
. Однако в таком случаем не будет работать представления, т.к.в ходе компиляции они будут помещены в другую сборку, и ресурсы станут недоступными. В качестве решения можно вынести View
в отдельную сборку, подробнее на StackOverflow.
Пример
Пример локализованного ASP.Net приложения доступен в примерах этого репозитория