.NET 5.0 Blazor IStringLocalizer and case sensitivity

Viewed 364

I'm using a IStringLoclizer approach with a resx file containing key-value pairs to localize my app as decribed in the documentation.

I've just discovered that:

On the one hand the .resx file is case insensitive (storing the key Firstname and FirstName will throw an error of key already existing).

On the second hand, when I want to RETRIEVE the keys, it IS case sensitive (if the key Firstname exists and I want to get the value of FirstName, the value is not retrieved by IStringLoclizer - which makes sense from the key-value pair point of view!).

Is there a way to override the IStringLocalizer getters, in order to implement some logic (e.g. change all keys to lowercase, and search any key by lowercase)? The key of a valid solution is to avoid changing all keys in the .resx file AND wherever I call the IStringLocalizer.

Edit I found out about ResourceManager.IgnoreCase here, but it is not clear to me how to access the resource manager - probably this has to be done in Startup.cs somehow?

1 Answers

This can be achieved by overwriting the ResourceManagerStringLocalizerFactory.

public class CaseInsensitiveResourceManagerStringLocalizerFactory : ResourceManagerStringLocalizerFactory
    {
        public CaseInsensitiveResourceManagerStringLocalizerFactory(IOptions<LocalizationOptions> localizationOptions, ILoggerFactory loggerFactory) : base(localizationOptions, loggerFactory)
        {
        }

        //unfortunately we need to use reflection to the the ResourceManager as the field is private 
        private readonly FieldInfo _field = typeof(ResourceManagerStringLocalizer).GetField("_resourceManager", BindingFlags.NonPublic | BindingFlags.Instance);

        //override this method to access the resource manager at the time it is created
        protected override ResourceManagerStringLocalizer CreateResourceManagerStringLocalizer(Assembly assembly, string baseName)
        {
            //call the base method to get the localizer, I would like to override this implementation but the fields used in the construction are private
            var localizer = base.CreateResourceManagerStringLocalizer(assembly, baseName);
            if (_field == null) return localizer;
            //set the resource manager to ignore case
            if (_field.GetValue(localizer) is ResourceManager resourceManager) resourceManager.IgnoreCase = true;
            return localizer;
        }
    }

The you need to register the factory before you add the localization.

public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IStringLocalizerFactory, CaseInsensitiveResourceManagerStringLocalizerFactory>();
            services.AddLocalization(options => { options.ResourcesPath = "Resources"; });

            //other service configurations....
    }

I hope in the future these class are made more extensible. Private fields in both in ResourceManagerStringLocalizerFactory and ResourceManagerStringLocalizer necessitates reflection.

Related