Я работаю над добавлением локализации в свое веб-приложение. Я настроил IStringLocalizer
, и он правильно считывает строковые ресурсы из двух разных файлов resx, в зависимости от настроек браузера. Затем он сопоставляет эти строковые ресурсы с ViewData, из которого мой View получает текст на правильном языке (не уверен, что это лучший подход, но пока я не хочу тратить на это больше времени).
Дело в том, что у меня также есть раскрывающийся список в моем пользовательском интерфейсе, который позволяет пользователям вручную переключать язык. Я читаю значение, установленное пользователем в моем действии контроллера, и добавляю его в файлы cookie, но теперь я также хотел бы установить для своих приложений культуру, соответствующую строке в файле cookie.
Можно ли установить культуру приложения из действия контроллера в MVC Core? Если да, то как это правильно сделать?
Обновлено:
Я только что узнал, что могу делать что-то вроде этого:
<a class = "nav-item nav-link" asp-route-culture = "en-US">English</a>
и он добавит ?culture=en-US
к моему маршруту, что установит для меня культуру страницы. Есть ли способ сделать то же самое, не сохраняя его в адресной строке?
Обновлено еще раз:
Относительно ответа Адама Саймона:
CookieRequestCultureProvider
- это то, что я хотел бы использовать в своем приложении, но проблема в том, что он не создает никаких файлов cookie. В документации говорится, что ядро .net решит, какого поставщика использовать, проверив, какой из них даст рабочее решение, начиная с QueryStringRequestCultureProvider
, затем переходя к CookieRequestCultureProvider
, а затем к другим поставщикам.
Мой текущий запуск выглядит так:
public class Startup
{
private const string defaultCulutreName = "en-US";
public void ConfigureServices(IServiceCollection services)
{
services.AddLocalization();
services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new[]
{
new CultureInfo(defaultCulutreName),
new CultureInfo("pl-PL")
};
options.DefaultRequestCulture = new RequestCulture(defaultCulutreName, defaultCulutreName);
options.SupportedCultures = supportedCultures;
options.SupportedUICultures = supportedCultures;
});
services.AddMvc()
.AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix);
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseStaticFiles();
//TRIED PLACING IT BEFORE AND AFTER UseRequestLocalization
//CookieRequestCultureProvider.MakeCookieValue(new RequestCulture("pl-PL", "pl-PL"));
app.UseRequestLocalization(app.ApplicationServices
.GetService<IOptions<RequestLocalizationOptions>>().Value);
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture("pl-PL", "pl-PL"));
app.UseMvc(ConfigureRoutes);
}
private void ConfigureRoutes(IRouteBuilder routeBuilder)
{
routeBuilder.MapRoute("Default", "{controller=About}/{action=About}");
}
}
Что касается CookieRequestCultureProvider.MakeCookieValue(new RequestCulture("pl-PL", "pl-PL"))
, я пробовал поместить его в RequestLocalizationOptions
в ConfigureServices
, в Configure
перед UseRequestLocalization
и после этого. Все с одинаковым результатом.
С этим решением возникают следующие «проблемы»:
Метод MakeCookieValue
не создает никаких файлов cookie .AspNetCore.Culture
Браузер Chrome с языком, установленным на PL, использует культуру pl-PL
правильно, но Firefox использует культуру en-US
с языком, установленным на PL
в опциях (несмотря на закомментированную строку options.DefaultRequestCulture =
new RequestCulture(defaultCulutreName, defaultCulutreName)
)
Как-то моя локализация работает по умолчанию без использования запроса строки или файлы cookie, чтобы предоставить культуру для приложения, но это не так, как я бы хотел, чтобы это работало, так как я не могу это контролировать
Каким-то образом вы должны привязать выбранную культуру к пользователю, поэтому, если вы не хотите переносить ее в URL-адрес, вы должны найти другой способ сохранить эту часть информации между запросами. Ваши варианты:
При нормальных обстоятельствах использование файла cookie для хранения языковых предпочтений - идеальный выбор.
В ASP.NET Core Лучшее место для получения и установки культуры для текущего запроса - это промежуточное ПО. К счастью, фреймворк включает один, который можно поместить в конвейер запросов, вызвав app.UseRequestLocalization (...) в вашем методе Запуск. Настроить. По умолчанию это промежуточное ПО будет пытаться получить текущую культуру из URL-адреса запроса, файлов cookie и HTTP-заголовка Accept-Language в этом порядке.
Итак, подведем итог: вам нужно использовать промежуточное ПО для локализации запросов, сохранить предпочтения культуры пользователя в файле cookie, отформатированном как c=%LANGCODE%|uic=%LANGCODE%
(например, c=en-US|uic=en-US
), и все готово.
Вы найдете все подробности в эта статья MSDN.
Бонус:
It then maps those string resources to ViewData, from which my View is getting text in correct language (not sure if that is the best approach, but for now I don't want to spent more time on this).
Передача локализованного текста в представления в ViewData обременительна и подвержена ошибкам. В ASP.NET Core для этого есть посмотреть локализацию. Вам просто нужно внедрить компонент IViewLocalizer в ваши представления, чтобы получить хороший и удобный способ доступа к вашим локализованным текстовым ресурсам. (Под капотом IViewLocalizer использует IStringLocalizer.)
О РЕДАКТИРОВАНИИ 2
MakeCookieValue method is not producing any .AspNetCore.Culture cookie
Метод CookieRequestCultureProvider.MakeCookieValue - это просто помощник для генерации значения cookie в правильном формате. Он просто возвращает строку и все. Но даже если бы это было предназначено для добавления файла cookie в ответ, вызывать его в Запуск. Настроить было бы совершенно неправильно, поскольку вы настраиваете конвейер запросов там. (Мне кажется, вы немного запутались в обработке запросов и промежуточном программном обеспечении в ASP.NET Core, поэтому я предлагаю изучить Эта тема.)
Итак, правильный настройка конвейера запросов выглядит примерно так:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
#region Localization
// REMARK: you may refactor this into a separate method as it's better to avoid long methods with regions
var supportedCultures = new[]
{
new CultureInfo(defaultCultureName),
new CultureInfo("pl-PL")
};
var localizationOptions = new RequestLocalizationOptions
{
DefaultRequestCulture = new RequestCulture(defaultCultureName, defaultCultureName),
SupportedCultures = supportedCultures,
SupportedUICultures = supportedCultures,
// you can change the list of providers, if you don't want the default behavior
// e.g. the following line enables to pick up culture ONLY from cookies
RequestCultureProviders = new[] { new CookieRequestCultureProvider() }
};
app.UseRequestLocalization(localizationOptions);
#endregion
app.UseStaticFiles();
app.UseMvc(ConfigureRoutes);
}
(Замечание к вышесказанному: регистрировать RequestLocalizationOptions в контейнере DI необязательно.)
Затем вы можете настроить действие контроллера cookie культуры:
[HttpPost]
[ValidateAntiForgeryToken]
public IActionResult SetCulture(string culture, string returnUrl)
{
HttpContext.Response.Cookies.Append(
CookieRequestCultureProvider.DefaultCookieName,
CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
// making cookie valid for the actual app root path (which is not necessarily "/" e.g. if we're behind a reverse proxy)
new CookieOptions { Path = Url.Content("~/") });
return Redirect(returnUrl);
}
Наконец, пример того, как вызвать это из Посмотреть:
@using Microsoft.AspNetCore.Localization
@using Microsoft.AspNetCore.Http.Extensions
@{
var httpContext = ViewContext.HttpContext;
var currentCulture = httpContext.Features.Get<IRequestCultureFeature>().RequestCulture.UICulture;
var currentUrl = UriHelper.BuildRelative(httpContext.Request.PathBase, httpContext.Request.Path, httpContext.Request.QueryString);
}
<form asp-action = "SetCulture" method = "post">
Culture: <input type = "text" name = "culture" value = "@currentCulture">
<input type = "hidden" name = "returnUrl" value = "@currentUrl">
<input type = "submit" value = "Submit">
</form>
Chrome browser with language set to PL is using pl-PL culture correctly, yet Firefox is using en-US culture with language set to PL in options (despite commenting out options.DefaultRequestCulture = new RequestCulture(defaultCulutreName, defaultCulutreName) line)
Я подозреваю, что браузер Chrome отправляет языковые предпочтения в заголовке Accept-Language, а FF - нет.
Somehow my localization is working by default without using query strings nor cookies to provide culture for application, but this is not how I'd like it to work, as I do not have any control over it
Я повторяю:
By default this middleware will try to pick up the current culture from the request URL, cookies and Accept-Language HTTP header, in this order.
Вы можете настроить это поведение, изменив или заменив список RequestLocalizationOptions.RequestCultureProviders.
Еще раз спасибо. Вы правы, меня все еще путают со всем этим ASP.Net Core. Это мое первое веб-приложение «песочница», и после нескольких лет разработки для настольных ПК его не так просто перенести в Интернет ... Я объявил вознаграждение по этому вопросу, чтобы вознаградить вас за помощь мне в этой теме. Я уже несколько дней борюсь с этим, так что вы полностью это заслужили :) Я смогу отметить это как ответ с наградой через 24 часа с этого момента
Эй, это очень мило с твоей стороны, спасибо! Несомненно, веб-разработка - это совсем другое животное, огромная тема (особенно в наши дни со всей этой JS-фальшивкой), требующая опыта во многих технологиях. Если бы только Silverlight сделал это ... По крайней мере, с WebAssembly мы вернули надежду (есть несколько действительно интересных проектов, таких как Платформа Uno). А пока у нас есть ASP.NET Core, довольно приличный фреймворк. Просто продолжайте читать (очень хорошо написанный) официальные документы, и вы быстро с ним ознакомитесь.
Спасибо за Ваш ответ. Вы пролили свет на эту тему для меня, но я все еще борюсь с ней. Я попытался использовать CookieRequestCultureProvider, к сожалению, безуспешно ... Я обновил свой вопрос, чтобы предоставить дополнительную информацию.