Implement Globalization And Localization In .NET 6 And Blazor WebAssemly App

What is Globalization and Localization?

Globalization and Localization is a technique to design our application architecture such that it is not bound to any specific language and at any point of time we have the capability to introduce more languages without any rework. We design our application to be language neutral. To be more precise, when we globalize our application we are not hardcoding the output values as raw strings, instead we use a key to identify texts. We can identify anything as a key or understandable text that can be used by us in the phase of localization. Localization means we add support for different languages on top of globalized applications. Without globalization we cannot localize the applications. There can be exceptions at times where some action cannot be translated to other languages that can be ignored. Also, this has nothing to do with the language of our source code.  

The best way to roll out this process is shown above.

  1. Get the translated content. 
  2. Globalize the hardcoded values to keys.
  3. Create the resource files and add translated content.

Globalization

To implement globalization we will convert each text in our application to a key for example. This value will be available using IStringLocalizer<T> localizer as dependency injection, here T will represent our Razor component name for which we are creating resources.  The key and value will reside in our .resx file and code snippet in our razor component.

Value Key Code snippet
Hello World greet_hello localizer[‘greet_hello’]
About Us title_about localizer[‘title_about’]

For example below is IStringLocalizer<NavMenu> Localizer is injected as dependency and we are referencing key names to get value.

Localization

To implement localization we will install Microsoft Extension Localization package.

<ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Localization" Version="6.0.5" />
</ItemGroup>

In our Program.cs we will add middleware,

builder.Services.AddLocalization();

Now, we will create a resource file for language and component combinations. We have to follow convention <ComponentName>.<language>.resx

Project structure

Resource file NavMenu.fr-CA-resx

Now, we will pick a language based on users' preference. To implement this we will store user selected language in the browser in localstorage. Below is a simple component for reference.

LanguageSelector.razor

<ul>
@foreach(var lang in new string[] { "en-IN","hi","pa","fr-CA" })
{
    <li><a href="#" onclick="(localStorage.setItem('lang','@lang'),window.location.reload())();">@lang</a></li>
}
</ul>

To pick language from localstorage and use it in the application, our final code will look like this.

index.html

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="utf-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>BlazorGlobalizationTutorial</title>
    <base href="/" />
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    <link href="BlazorGlobalizationTutorial.Client.styles.css" rel="stylesheet" />
<script>
//Function to get localizer language for webassembly C# code
    window.getCultureLang = () => {
        return window.localStorage['lang']
    };
</script>
</head>

<body>
    <div id="app">Loading...</div>
    <div id="blazor-error-ui">
        An unhandled error has occurred.
        <a href="" class="reload">Reload</a>
        <a class="dismiss">๐Ÿ—™</a>
    </div>
    <script src="_framework/blazor.webassembly.js" autostart="false"></script>
    <script>
//Function to get localizer language for application startup. If language is not set use โ€˜en-INโ€™
        let languageSet = (localStorage.getItem('lang') || 'en-IN');
        console.log(languageSet);
        Blazor.start({
            applicationCulture: languageSet
        });
    </script>
</body>
</html>

 Program.cs

using BlazorGlobalizationTutorial.Client;
using Microsoft.AspNetCore.Components.Web;
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using System.Globalization;
using Microsoft.JSInterop;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add < App > ("#app");
builder.RootComponents.Add < HeadOutlet > ("head::after");
builder.Services.AddScoped(sp => new HttpClient {
    BaseAddress = new Uri(builder.HostEnvironment.BaseAddress)
});
builder.Services.AddLocalization(); //Localizer added
var host = builder.Build();
/*Localizer setting start*/
var js = host.Services.GetRequiredService < IJSRuntime > ();
var lang = await js.InvokeAsync < string > ("getCultureLang");
CultureInfo.DefaultThreadCurrentCulture = string.IsNullOrWhiteSpace(lang) ? CultureInfo.CurrentCulture : new CultureInfo(lang);
CultureInfo.DefaultThreadCurrentUICulture = string.IsNullOrWhiteSpace(lang) ? CultureInfo.CurrentCulture : new CultureInfo(lang);
/*Localizer setting end*/
await host.RunAsync();

For reference, please check the uploaded code. Thanks for reading.