MVC Core устанавливает культуру приложения из действия контроллера

Я работаю над добавлением локализации в свое веб-приложение. Я настроил 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, чтобы предоставить культуру для приложения, но это не так, как я бы хотел, чтобы это работало, так как я не могу это контролировать

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
8
0
6 003
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Каким-то образом вы должны привязать выбранную культуру к пользователю, поэтому, если вы не хотите переносить ее в URL-адрес, вы должны найти другой способ сохранить эту часть информации между запросами. Ваши варианты:

  • печенье
  • сессия
  • база данных
  • Заголовок HTTP
  • скрытый ввод

При нормальных обстоятельствах использование файла 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.

Спасибо за Ваш ответ. Вы пролили свет на эту тему для меня, но я все еще борюсь с ней. Я попытался использовать CookieRequestCultureProvider, к сожалению, безуспешно ... Я обновил свой вопрос, чтобы предоставить дополнительную информацию.

Kacper Stachowski 13.08.2018 11:56

Еще раз спасибо. Вы правы, меня все еще путают со всем этим ASP.Net Core. Это мое первое веб-приложение «песочница», и после нескольких лет разработки для настольных ПК его не так просто перенести в Интернет ... Я объявил вознаграждение по этому вопросу, чтобы вознаградить вас за помощь мне в этой теме. Я уже несколько дней борюсь с этим, так что вы полностью это заслужили :) Я смогу отметить это как ответ с наградой через 24 часа с этого момента

Kacper Stachowski 14.08.2018 15:52

Эй, это очень мило с твоей стороны, спасибо! Несомненно, веб-разработка - это совсем другое животное, огромная тема (особенно в наши дни со всей этой JS-фальшивкой), требующая опыта во многих технологиях. Если бы только Silverlight сделал это ... По крайней мере, с WebAssembly мы вернули надежду (есть несколько действительно интересных проектов, таких как Платформа Uno). А пока у нас есть ASP.NET Core, довольно приличный фреймворк. Просто продолжайте читать (очень хорошо написанный) официальные документы, и вы быстро с ним ознакомитесь.

Adam Simon 14.08.2018 18:16

Другие вопросы по теме