Blazor

Simple Localisation in Blazor

Firstly, let me apologise for the lack of blog posts in November, I was busy getting married and enjoying some time away with the wife! But I'm now all recharged and normal service should now be resumed.

In this post, I'm going to show you a way to set the current culture in your Blazor apps based on the users browser. Just to be clear this is going to be for client-side only apps. I've actually bundled this up as a Nuget package to make things a bit easier if you want to do the same in your app.

As a little disclaimer, I've not had a vast amount of experience with localising applications, I've mainly worked on in-house systems, so if I'm missing anything please let me know in the comments. But after doing a bit of testing this method seems to work pretty well.

Blazor and (lack of) localisation

For those of you who are not aware currently Blazor (client-side) does not have a built-in mechanism for handling localisation. This is not Blazors fault however, it is due to a missing timezone implementation in the Mono WASM runtime, which is being tracked here. This means that Blazor applications have no current culture and calling something like DateTime.Now will return in UTC format regardless of the users settings.

When developing client-side applications this is obviously a bit of an issue and can cause a lot of confusion for the user. Take a date such as 01/02/2018. Is that the 1st February 2018 or 2nd January 2018? Well that depends where in the world you are. Here in the UK it would be the 1st February. But in the US it would be 2nd January.

After a bit of messing about this is what I've come up with.  

Finding the users locale

First I need to know the locale of the user. This is usually expressed in the form of culture codes they look like this, en-GB. This code represents English (United Kingdom). It turns out browsers actually expose a few different ways to get this information.

  • navigator.language
  • navigator.languages
  • navigator.browserLanguage
  • navigator.userLanguage
  • Intl.DateTimeFormat().resolvedOptions().locale

Like most things JavaScript not all these options return the same thing or what you might expect. My initial attempt at solving this problem was to use Intl.DateTimeFormat().resolvedOptions().locale.

This seemed to work fine however I run a Mac and swap between MacOS and Windows 10 running on Parallels. I noticed on MacOS this returned en-GB as I was expecting but on Windows 10 it returned en-US. I checked all my settings and everything appeared to be correctly set to UK culture. This made me slightly concerned that this was not working completely correctly.

After a bit of Googling and reading I decided to go with a combination of the other options. Most people seem to agree this gives the most accurate result in the majority of situations. It looks like this.

getBrowserLocale: function () {
    return (navigator.languages && navigator.languages.length) ? navigator.languages[0] : navigator.userLanguage || navigator.language || navigator.browserLanguage || 'en';
}

Setting CurrentCulture in Blazor

Now I have a way of getting the users locale I just need a way of setting CultureInfo.CurrentCulture in Blazor.

I started by doing the following in the Main method of Program.cs just to see if it worked.

public static void Main(string[] args)
{
    CultureInfo.CurrentCulture = new CultureInfo("en-GB");
    CreateHostBuilder(args).Build().Run();
}

I then just printed out DateTime.Now on the default Blazor template homepage component.

IT WORKED!

Before
After

That's great but it needs to be set dynamically based on the value in the browser not hardcoded in Program.Main. It also needs to be set before anything tries to render any UI. The Startup.Configure method seemed the best bet, so I created an extension method on IBlazorApplicationBuilder. In order to make this work I had to make the call synchronous by down casting JSRuntime to IJSInProcessRuntime. The end product looks like this.

public static void UseBrowserLocalisation(this IBlazorApplicationBuilder app)
{
    var browserLocale = ((IJSInProcessRuntime)JSRuntime.Current).Invoke<string>("blazoredLocalisation.getBrowserLocale");
    var culture = new CultureInfo(browserLocale);

    CultureInfo.CurrentCulture = culture;
    CultureInfo.CurrentUICulture = culture;
}

Wiring it up

All that's left to do is add a call to UseBrowserLocalisation to Startup.Configure and I should now have Culture Info in Blazor! Just to be sure I printed out the current culture information onto the home component as well as you can see in the screenshot below.

public void Configure(IBlazorApplicationBuilder app)
{
    app.UseBrowserLocalization();
    app.AddComponent<App>("app");
}

Conclusion

In this post I've shown a way to set the current culture in a client-side Blazor application based on settings in the users browser. As it turns out it wasn't to bad, as usual the code can be found on my GitHub. I've also packaged it all up as a Nuget package which you can install from the nuget package manager or using the following dotnet CLI command.

dotnet add package BlazoredLocalisation

If you have any questions or have found a massive hole in my code please use the comments below or feel free to raise a PR on GitHub.