Update RequestLocalizationOptions while application is running and a new culture is added in .NET

296 Views Asked by At

How can I update RequestLocalizationOptions while application is running and a new culture is added avoiding to restart the application?

Hello! I have an application that supports localizations. All the resources and values are stored in the database. I will show you how I configured the RequestLocalizationOptions.

public static IServiceCollection AddRequestLocalizationOptions(this IServiceCollection services)
        {
            services.Configure<RequestLocalizationOptions>(async options =>
            {
                using var scope = services?.BuildServiceProvider().CreateScope();
                var cultureService = scope!.ServiceProvider!.GetService<ICultureService>();
                if (cultureService is not null)
                {
                    var cultures = await cultureService.GetFromDatabase(x => x.Available == true);

                    IList<CultureInfo> supportedCultures = new List<CultureInfo>();
                    foreach (var culture in cultures)
                    {
                        supportedCultures.Add(new CultureInfo($"{culture.language}-{culture.CountryCodeIso2}")); // en-US, it-IT and so on
                    }

                    options.DefaultRequestCulture = new RequestCulture("en-US");
                    options.SupportedCultures = supportedCultures;
                    options.SupportedUICultures = supportedCultures;
                    options.RequestCultureProviders = new List<IRequestCultureProvider>
                {
                    new CookieRequestCultureProvider()
                };

                    var cookieRequestCultureProvider = options.RequestCultureProviders.OfType<CookieRequestCultureProvider>().FirstOrDefault();
                    if (cookieRequestCultureProvider is not null)
                        cookieRequestCultureProvider.CookieName = CookieDefaults.CultureKey;
                }
            });

            return services;
        }

The final configuration step is to enable the localization middleware using the UseRequestLocalization method.

app.UseRequestLocalization();

The UseRequestLocalization method initializes the RequestLocalizationOptions object we configured above. UseRequestLocalization enables localization and internationalization by setting the culture and UI culture for each user request based on their language preferences.

Everything work fine except one case. Let's say I have 3 cultures available in the database this means that options.SupportedCultures it will contains all 3 cultures. But if I will update the database and I will add another 3 available cultures while application is running, how can I update the RequestLocalizationOptions to specify that now I support 6 cultures without restarting the application? Because the AddRequestLocalizationOptions extension it will be executed on the startup only one time, but I need somehow to refresh the options while the application is running. If I restart the application, it works as expected but I don't want to restart the application when it will be on production every time I'm adding a new culture..

1

There are 1 best solutions below

5
Florent Bunjaku On

I would suggest that first you change the way how you are Configuring RequestLocalizationOptions as following:

builder.Services.AddSingleton<IConfigureOptions<RequestLocalizationOptions>>(provider =>
{
    return new ConfigureNamedOptions<RequestLocalizationOptions>(Options.DefaultName, options =>
    {
        using var scope = provider.CreateScope();
        var cultureService = scope!.ServiceProvider!.GetService<ICultureService>();
        if (cultureService is not null)
        {
            var cultures = cultureService.GetFromDatabase(x => x.Available == true).GetAwaiter().GetResult();

            IList<CultureInfo> supportedCultures = new List<CultureInfo>();
            foreach (var culture in cultures)
            {
                supportedCultures.Add(new CultureInfo($"{culture.language}-{culture.CountryCodeIso2}")); // en-US, it-IT and so on
            }

            options.DefaultRequestCulture = new RequestCulture("en-US");
            options.SupportedCultures = supportedCultures;
            options.SupportedUICultures = supportedCultures;
            options.RequestCultureProviders = new List<IRequestCultureProvider>
            {
                new CookieRequestCultureProvider()
            };

            var cookieRequestCultureProvider = options.RequestCultureProviders.OfType<CookieRequestCultureProvider>().FirstOrDefault();
            if (cookieRequestCultureProvider is not null)
                cookieRequestCultureProvider.CookieName = CookieDefaults.CultureKey;
        }
    });
});

for two reasons:

  1. Building the service provider your self from the service collection, might cause singleton services to be resolved twice.
  2. Using async is the same as using async on void methods which is a bad practice.

After you do that change, use IOptionsSnapshot<RequestLocalizationOptions> on your middleware.`

At this point it would be more straight forward to write the code that fetches the options from database into the middleware it self. So you will avoid the blocking call to GetFromDatabase as well.