Dopo aver localizzato il front-end tramite file RESX, in questo articolo vedremo come localizzare il back-end. Ma perché? Qual è la necessità di localizzare un back-end? Di solito i testi e le label sono sul front-end, ma se avessimo un servizio che può generare messaggi, questi dovrebbero essere localizzati prima di arrivare al front-end e, ovviamente, localizzati nella lingua del browser.
Architettura del sistema
Per poter avere una soluzione chiara al problema, dobbiamo analizzare gli scenari e i casi d’uso. Per farla breve, dovremmo conoscere la lingua di ogni client connesso e mandare il messaggio nella sua lingua. Uno dei modi per farlo è il seguente
Notifica di messaggio
Il back-end invia una notifica al front-end informandolo che è presente un messaggio. Ovviamente dovrà anche comunicargli l’ID del messaggio.

Recupero messaggio in lingua
Quando il front-end riceve una notifica recupera il messaggio il lingua tramite una chiamata HTTP GET.

A questo punto il front-end visualizzerà il messaggio se lo riterrà opportuno.
Sporchiamoci le mani
Non ci resta che iniziare a scrivere il codice, recuperando il codice della parte 1 dell’articolo precedene. Per prima cosa dobbiamo permettere al front-end di ricevere delle notifiche dal back end. Utilizzeremo SignalR per questo lavoro.
Aggiungiamo SignalR al front end
Aggiungiamo i pacchetti NuGet necessari. Microsoft.AspNetCore.SignalR.Client
si occupa della comunicazione

Fatto ciò non ci resta che scrivere il codice necessario a ricevere la notifica dal back end. Apriamo il sorgente di MainLayout.razor e aggiungiamo il Navigation Manager in questo modo:
@inject NavigationManager NavigationManager
poi creiamo un file nella stessa directory Shared chiamato MainLayout.razor.cs a cui aggiungeremo il seguente codice
public partial class MainLayout
{
private HubConnection hubConnection;
public string Message { get; private set; }
protected override async Task OnInitializedAsync()
{
// SignalR Hub
hubConnection = new HubConnectionBuilder()
.WithUrl(NavigationManager.ToAbsoluteUri("/communicationhub"))
.WithAutomaticReconnect(new[]{TimeSpan.FromSeconds(1)})
.Build();
hubConnection.On<string>("MessageAvailable", (e) =>
{
Message = e;
StateHasChanged();
});
await hubConnection.StartAsync();
}
}
La prima cosa da fare è un hub SignalR. Per fortuna la libreria ci fornisce un oggetto che dichiareremo a livello di classe e la instanzieremo nel metodo OnInitializedAsync. Alla creazione, andremo a specificare anche l’endpoint del backend al quale fare riferimento, che avremo avuto l’accortezza di aggiungere al Program.cs del back-end in questo modo
endpoints.MapHub<UiCommunicationHub>("/communicationhub");
endpoints.MapFallbackToFile("index.html");
E all’inizio, sempre del file Program.cs ma subito dopo la creazione del builder, aggiungiamo questo
builder.Services.AddSignalR();
Dove UiCommunicationHub.cs
sarà una classe del BackEnd che si occuperà proprio di gestire i messaggi SignalR e sarà fatta nel seguente modo
public class UiCommunicationHub : Hub
{
public async Task MessageAvailable(string e)
{
await Clients.All.SendAsync("MessageAvailable", e);
}
}
Una seconda classe che ci permetterà di inviare messaggi al front -nd la chiameremo UiSender.cs
e sarà fatta in questo modo
public class UiSender
{
private HubConnection hubConnection;
public UiSender()
{
Init();
}
void Init()
{
try
{
if (hubConnection != null) return;
hubConnection = new HubConnectionBuilder()
.WithUrl(new Uri("https://localhost:7117/communicationhub"))
.WithAutomaticReconnect(new[]{TimeSpan.FromSeconds(1)})
.Build();
Task.Run(async () =>
{
try
{
await hubConnection.StartAsync();
}
catch (Exception ex)
{
Console.WriteLine("TASK-" + ex.Message);
}
});
}
catch (Exception ex)
{
Console.WriteLine("INIT-" + ex.Message);
}
}
public async void SendAsync(string s)
{
try
{
await hubConnection.SendAsync("MessageAvailable", s);
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
}
}
Questa classe la instanzieremo come Singleton perché dovrà essere “viva” per tutta la durata dell’applicazione. Quindi in Program.cs del Server inseriremo
builder.Services.AddSingleton<UiSender>();
ed useremo la dependency injection per passarlo al costruttore del controller in questo modo
public MessagesController(ILogger<MessagesController> logger, UiSender uiSender)
{
this.logger = logger;
this.uiSender = uiSender;
}
ed eccoci arrivati finalmente all’invio di una notifica al front-end! Nel nostro controller aggiungiamo un metodo che servirà a dare inizio all’invio delle notifiche in questo modo
private async Task SendSimpleMessageAsync(string message)
{
this.uiSender.SendAsync(message);
}
che richiameremo ogni volta che dovremo inviare una notifica al front-end.
Ora però dobbiamo trovare una sorgente di messaggi. Per semplificare le cose possiamo usare anche nel back-end i files di risorse per le traduzioni in lingua. Potremmo anche utilizzare un database o qualunque altro metodo ci venga in mente. Ricordiamoci solo che questi messaggi saranno inviati da un servizio e il nostro backend ne avrà una copia localizzata. Ad esempio se il nostro servizio è un servizio meteo, avremo la traduzione della parola “Temperatura” piuttosto che “Nuvoloso” o “Soleggiato”. Magari il servizio fornisce la traduzione, ma se non dovesse farlo saremo noi a fornire la traduzione.
Aggiungiamo le traduzioni
Aggiungiamo quindi una directory che chiameremo Resources all’interno della quale andremo a creare due files:
- Language.resx
- Language.it.resx
i quali conterranno i messaggi rispettivamente per la lingua inglese e la lingua italiana.
Per il nostro esempio, sarà necessario un file di risorse di questo tipo:

Recuperiamo il messaggio in lingua
Non ci resta che chiamare il backend e recuperare il messaggio nella lingua richiesta. Lo faremo tramite una richiesta get
[HttpGet("{language}/{ID}")]
public IActionResult Get(string language, string ID)
{
var culture = new CultureInfo(language);
var t = Language.ResourceManager.GetString(ID, culture);
return Ok(t);
}
Il nostro metodo GET avrà due parametri: la lingua espressa come biletterale e l’ID del messaggio, che era arrivato al front-end tramite SignalR.
Richiameremo il metodo GET tramite questa chiamata:
await Http.GetAsync(@$"Messages/{ culture.TwoLetterISOLanguageName}/{Layout!.Message}");
che ci restituisce un oggetto di tipo HttpResponseMessage, il quale ha un content che può essere letto tramite il metodo ReadAsStringAsync nel seguente modo:
await s.Content.ReadAsStringAsync();
Mischiamo l’insalata con stile
Ora che abbiamo tutti i pezzi, dobbiamo mettere tutto assieme e rendere funzionante la nostra app di esempio. Come ogni rotta che si rispetti, se non abbiamo una cartina per orientarci non riusciremo mai ad arrivare a destinazione, dunque è meglio dare un elenco dei passaggi da eseguire per meglio comprendere come dovrà funzionare il nostro meccanismo di localizzazione.
Dunque:
- Per prima cosa avremo un simulatore di messaggi che invierà un ID del messaggio al front end in questo modo:
for (var i = 1; i < 10; i++)
{
await Task.Delay(TimeSpan.FromSeconds(1));
SendSimpleMessageAsync(i.ToString("000"));
}
per comodità lo metteremo in un Get per poterlo far partire alla richiesta del front-end
- Il metodo
SendSimpleMessageAsync
invia tramite SignalR il messaggio al front-end - Il front-end lo riceve nella callback di SignalR che valorizzerà la property
Message
che si trova nel nostro MainLayout - Proprio il MainLayout sarà passato come parametro alla nostra pagina Index, in modo da condividere la property.
- Sfruttando il metodo
OnParametersSet
della nostra pagina (metodo fornito da Blazor, quindi dobbiamo creare l’override di questo metodo), intercettiamo quando la nostra propertyMessage
delMainLayout
cambia - E sarà proprio in quest’ultimo metodo che andremo a richiedere il reale messaggio da visualizzare.
Richiediamo il messaggio da visualizzare
Come abbiamo appena detto, nel metodo OnParameterSet
andremo a chiedere il messaggio reale al backend. Per fare questo ci creiamo una classe di servizio che ci permetterà di fare le chiamate al back end in modo separato dal front end in questo modo:
HttpClient http;
public LocalizedMessageService(HttpClient http)
{
this.http = http;
}
public async Task InitializeBackEndMessaging()
{
_ = await http.GetAsync(@$"Messages");
}
public async Task<string> GetMessageAsync(string messageId, CultureInfo culture)
{
var returnValue = string.Empty;
var s = await http.GetAsync(@$"Messages/{ culture.TwoLetterISOLanguageName}/{messageId}");
if (s != null)
{
if (messageId.Length > 0)
{
returnValue = messageId + " - " + await s.Content.ReadAsStringAsync();
}
}
return returnValue;
}
}
Questa classe la inseriremo nella classe Program come Scoped e sarà a disposizione delle DI per poter essere utilizzata. Quindi in Program.cs inseriamo:
builder.Services.AddScoped<LocalizedMessageService>();
E quindi nel file Index.razor andremo a richiamare un’istanza di questa classe in questo modo
@inject LocalizedMessageService localizedMessageService
Ora potremo completare il nostro metodo OnParameterSet
in questo modo:
protected override async void OnParametersSet()
{
txt = await localizedMessageService.GetMessageAsync(Layout!.Message, Thread.CurrentThread.CurrentCulture);
StateHasChanged();
base.OnParametersSet();
}
Eureka! Funziona!
Se facciamo partire il nostro esempio, vedremo funzionare il tutto.

In questa piccola animazione, potete vedere due client allo stesso server. Uno in inglese e l’altro in italiano. Notate che i messaggi arrivano contemporaneamente nelle due lingue su entrambi i client.
La localizzazione del back-end non è affatto un argomento banale e questa è una semplice introduzione all’argomento. Come sempre potrete trovare i sorgenti a questo link.
Alla prossima!