Localisation in .Net 5 MVC

Localisation in .Net 5 MVC

I have started work on a new .Net 5 MVC application which is intended to be an Azure hosted SaaS application. It may be necessary to provide a choice of languages for the interface, I have found it is better to start with this in mind than trying to retro fit later. I have decided to use the traditional approach of providing a key value used by the localiser implementation to retrieve the string from the default language .resx file. As I am dealing with a MVC application, string localisation will take place in all three components: Model; View; and Controller.

Configuration

Out of habit I create the SharedResources.resx file in the Properties folder of the project:

Properties.ShqredResources.PNG

This means you can use the SharedResource through the namespace .Properties, in this case:

namespace EasyLab.UI.Properties

No additional components are required for localisation, the services just need to be added to the dependency injection container when adding the Controllers and Views. The class for the .resx file that contains the dictionary of key value pairs is referenced by the data annotations provider, in this case SharedResource:

        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews()
                .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
                    .AddDataAnnotationsLocalization(options => {
                        options.DataAnnotationLocalizerProvider = (type, factory) =>
                            factory.Create(typeof(SharedResource));
                    });
        }

Models

To help inform the view used to display details of a model, Data Annotation attributes are generally utilised to provide display names and error messages. In this case the strings provided are key values that are used to reference the localised string from the SharedResource:

        [Required(ErrorMessage = "NameRequired")]
        [DisplayName("Name")]
        public string Name { get; set; }

Views

The implementation of the IViewLocalizer is injected into the view and used to retrieve localised strings from the SharedResource dictionary:

@using Microsoft.AspNetCore.Mvc.Localization
@using EasyLab.UI.Properties;

@inject IViewLocalizer Localizer

By using the .Properties namespace in the view it is possible to reference key values from the SharedResource instead of providing strings values, so instead of using "Index" to retrieve the localised string, SharedResource.Index can be used. This not only means you are sure the string exists, an error is generated during the build if the key no longer exists, but it also means that the key is updated if a global rename is performed:

@{
    ViewData["Title"] = Localizer[SharedResource.Index];
    Layout = "~/Views/Shared/_Layout.cshtml";
}

<h1>@Localizer[SharedResource.Brands]</h1>

Controllers

For each controller an IStringLocalizerFactory is injected in the constructor. Then, in this case, the factory is then used to create a localizer using the SharedResource:

        private readonly EasyLabDbContext _context;
        private readonly ILogger<BrandsController> _logger;
        private readonly IStringLocalizer _localizer;

        public BrandsController(EasyLabDbContext context, ILogger<BrandsController> logger, IStringLocalizerFactory factory)
        {
            _context = context;
            _logger = logger;

            var type = typeof(SharedResource);
            _localizer = factory.Create(type);
        }

The _localizer is then used to retrieve the localised string. Again using the .Properties namespace makes it possible to reference key values using the SharedResource instead of providing strings values:

return View(brand).WithWarning(_localizer[SharedResource.Error], _localizer[SharedResource.NameExists]);

I hope you find this post useful, I am afraid I can't share the source code at this time as the development is on going.