Shawn de Wet
Shawn de Wet

Reputation: 5976

ASP.Net Core CookieRequestCultureProvider not working

I have an ASP.Net Core 3.1 app with the following startup.cs (I have tried various combinations of the below configuration based on web searches):

public void ConfigureServices(IServiceCollection services)
    {
        ...
        services.Configure<RequestLocalizationOptions>(options =>
        {
            options.RequestCultureProviders = new[] { new CookieRequestCultureProvider() };
        });

and

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            ...
            var options = app.ApplicationServices.GetService<IOptions<RequestLocalizationOptions>>();
            app.UseRequestLocalization(options.Value);

And in my app's logon method, I'm setting the Culture Cookie as follows:

HttpContext.Response.Cookies.Append(
                            CookieRequestCultureProvider.DefaultCookieName,
                            CookieRequestCultureProvider.MakeCookieValue(new RequestCulture([logged-in-user].CultureCode)));

In subsequent requests I can see the cookie in my browser dev tools Network tab: cookie seen in browser dev tools

Yet, on the server, any given request still maintains the default server culture (which is en-ZA) for my dev environment. (I'm seeing this by checking System.Threading.Thread.CurrentThread.CurrentCulture.Name in any breakpoint in my server action methods)

And I'm running into date conversion issues between my client and my server (e,g client with en-US culture as per screenshot above) sends a date of 3/5/2009 (March 5th) to the server, and the server is interpreting it as May 3rd.

Why is my server not honoring the CultureCookie? What am I missing?

Upvotes: 8

Views: 7773

Answers (2)

Noah Han
Noah Han

Reputation: 141

I'm on asp.net core 6.0.

Thanks Anduin for the response, that helped me a lot! The official doc was missleading.

I found the key points for me were:

  1. Service configuration:
builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");

builder.Services.AddMvc()
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
    .AddDataAnnotationsLocalization();

builder.Services.Configure<RequestLocalizationOptions>(options =>
{
    var supportedCultures = new[]
    {
        new CultureInfo("en-US"),
        new CultureInfo("zh-CN")
    };
    options.DefaultRequestCulture = new RequestCulture(culture: "en-US", uiCulture: "en-US");
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures;
});
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures;
});
  1. App support:
var SupportedCultures = new[]
{
    new CultureInfo("en"),
    new CultureInfo("zh")
};
app.UseRequestLocalization(new RequestLocalizationOptions
{
    DefaultRequestCulture = new RequestCulture(culture: "en", uiCulture: "en"),
    SupportedCultures = SupportedCultures,
    SupportedUICultures = SupportedCultures
});
  1. Resource files with lang name, but not ISO code:
.\Resources\Controllers\HomeController.zh.resx
.\Resources\Views\Home\Index.zh.resx
  1. Change culture programmatically using Ajax(I added an empty default):
@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 = new List<SelectListItem>();
    cultureItems.Add(new SelectListItem { Value = "", Text = Localizer["Select Lang"].Value });
    cultureItems.AddRange(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>
        [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);
        }
  1. The IStringLocalizer (I'm keeping the _logger, which is removable):
        private readonly ILogger<HomeController> _logger;
        private readonly IStringLocalizer<HomeController> _localizer;
        public HomeController(ILogger<HomeController> logger, IStringLocalizer<HomeController> localizer)
        {
            _localizer = localizer;
            _logger = logger;
        }
        public IActionResult Index()
        {
            ViewData["Message"] = _localizer["Test Text"];
            return View();
        }
  1. IViewLocalizer in index.cshtml:
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@{
    ViewData["Title"] = Localizer["Title Text"];
}

And define the keys Test Text and Title Text in resource files.

Upvotes: 3

Anduin Xue
Anduin Xue

Reputation: 3727

As you mentioned, you have registered your localization service in your ConfigureServices method.

My suggested way is to use it like:

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

services
    .AddControllersWithViews()
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
    .AddDataAnnotationsLocalization();

But don't forget to register the middleware:

// In StartUp.cs Configure method
var SupportedCultures = new CultureInfo[]
{
    new CultureInfo("en"),
    new CultureInfo("zh")
};
app.UseRequestLocalization(new RequestLocalizationOptions
{
    DefaultRequestCulture = new RequestCulture(defaultLanguage),
    SupportedCultures = SupportedCultures,
    SupportedUICultures = SupportedCultures
});

As for your cookie end-time issue, please try to specify the end date of your cookie. Like this:

Response.Cookies.Append(
    CookieRequestCultureProvider.DefaultCookieName,
    CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
                    new CookieOptions
                    {
                        Expires = DateTimeOffset.UtcNow.AddYears(1),
                        SameSite = SameSiteMode.None
                    });

Upvotes: 8

Related Questions