jsakamoto / Toolbelt.Blazor.I18nText

The class library that provides the ability to localize texts on your Blazor app!
Mozilla Public License 2.0
247 stars 25 forks source link

Issue with Cookie #42

Closed Vinko90 closed 3 years ago

Vinko90 commented 3 years ago

I am trying to use cookie in Blazor.Server .NET5 in order to remember the language setting of each user however I can't manage to make it work. For some reason when I start the page I can see that the main layout refresh twice, the first time I see the language from cookie (meaning last stored value) and then the second time the default language. Can someone please help me out?

Startup

    public void ConfigureServices(IServiceCollection services)
        {
           ....
            services.AddI18nText();
            var cultures = Configuration.GetSection("Cultures").GetChildren().ToDictionary(x => x.Key, x => x.Value);
            var supportedCultures = cultures.Keys.ToArray();

            services.Configure<RequestLocalizationOptions>(options => {
                options.DefaultRequestCulture = new RequestCulture("en");
                options.AddSupportedCultures(supportedCultures);
                options.AddSupportedUICultures(supportedCultures);
            });
            ...          
        }
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            ...
            app.UseRequestLocalization();
           ....
        }

Culture Controller

    [Route("[controller]/[action]")]
    public class CultureController : Controller
    {
        public IActionResult SetCulture(string culture, string redirectUri)
        {
            if (!string.IsNullOrEmpty(culture))
            {
                HttpContext.Response.Cookies.Append(CookieRequestCultureProvider.DefaultCookieName,
                    CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)));
            }

            return LocalRedirect(redirectUri);
        }
    } 

MainLayout.razor.cs

    public partial class MainLayout
    {
        private Dictionary<string, string> cultures = new();

        [Inject]
        private NavigationManager NavMan { get; set; }
        [Inject]
        private IConfiguration Configuration { get; set; }

        protected override async Task OnInitializedAsync()
        {
            base.OnInitialized();

            cultures = Configuration.GetSection("Cultures").GetChildren().ToDictionary(x => x.Key, x => x.Value);

            var lang = await I18nText.GetCurrentLanguageAsync();

            SetLanguageAsync(lang);            
        }

        private async Task RequestCultureChangeAsync(string value)
        {
            //This method is called when user click on one of the language buttons -> value: "en", "nl" etc..
            var uri = new Uri(NavMan.Uri).GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped);
            var query = $"?culture={Uri.EscapeDataString(value)}&redirectUri={Uri.EscapeDataString(uri)}";
            NavMan.NavigateTo("Culture/SetCulture" + query, forceLoad: true);
        }

        private async Task SetLanguageAsync(string value)
        {
            await I18nText.SetCurrentLanguageAsync(value);
        }
    }
jsakamoto commented 3 years ago

Please try the following step.

At first, rewrite your "Startup.cs" like this.

Before:

/* Startup.cs */
public void ConfigureServices(IServiceCollection services)
{
  ....
  services.AddI18nText();
  ....

After:

/* Startup.cs */
public void ConfigureServices(IServiceCollection services)
{
  ....
  services.AddI18nText(options =>
  {
    // 👇 Stop persisting current language by I18n text library's mechanism 
    //    because the current language is persisted in cookie now.
    options.PersistanceLevel = PersistanceLevel.None;

    // 👇 The language at the session starting is now provided by cookie 
    //    inside the HTTP request, and it applies to "CultureInfo.CurrentUICulture" 
    //    by the "RequestLocalization" middleware.
    //    So you have to override the detecting initial language function of 
    //    the I18n text library for returning the value of "CultureInfo.CurrentUICulture" to it.
    options.GetInitialLanguageAsync = (_, _) => ValueTask.FromResult(CultureInfo.CurrentUICulture.Name);
  });
  ....

And finally, please comment out some code that is no longer needed in your "MayLayout" component.

/*  MainLayout.razor.cs */
...
public partial class MainLayout
{
  ...
  // 👇 You don't have to access the I18n text service instance to get or set 
  //    the current language anymore.
  //    Just do it that get the current language via "CultureInfo", 
  //    set the language user changes to cookies.

  //protected override async Task OnInitializedAsync()
  //{
  //    base.OnInitialized();
  //    string lang = await I18nText.GetCurrentLanguageAsync();
  //    SetLanguageAsync(lang);
  //}

  private async Task RequestCultureChangeAsync(string value)
  {
    ...
  }

  //private async Task SetLanguageAsync(string value)
  //{
  //    await I18nText.SetCurrentLanguageAsync(value);
  //}

My Sample project: 📂 BlazorApp1.zip

Vinko90 commented 3 years ago

Thank you so much for providing explanation and example code, everything works as expected :)