GithubHelp home page GithubHelp logo

markchipman / aspnetcore-localization-demo Goto Github PK

View Code? Open in Web Editor NEW

This project forked from brightsoul/aspnetcore-localization-demo

0.0 0.0 0.0 1.1 MB

An ASP.NET Core 3.1 application that localizes both content and paths

License: MIT License

JavaScript 0.66% C# 77.64% CSS 3.96% HTML 17.74%

aspnetcore-localization-demo's Introduction

ASP.NET Core Localization Demo

Un'applicazione dimostrativa ASP.NET Core 3.1 che localizza sia i contenuti che i percorsi in tre lingue.

demo.gif

Nel ramo content-only di questo repository si trova una versione semplificata di questo progetto che include solo la localizzazione dei contenuti e non dei percorsi.

Avviare l'applicazione

È sufficiente clonare il repository o scaricare il pacchetto zip. Poi aprire il progetto con Visual Studio o Visual Studio Code e premere F5 per avviare il debug.

Punti salienti

Ecco alcune informazioni necessarie a comprendere come funziona l'applicazione.

File di risorse

I testi tradotti nelle tre lingue si trovano in file di risorse all'interno della directory Resources (un file per ciascuna lingua). Leggi questo articolo propedeutico che spiega come creare e modificare i file di risorse.

https://www.aspitalia.com/script/1333/Usare-File-Risorse-ASP.NET-Core.aspx

All'interno di ciascun file di risorse si trovano sia le chiavi per i contenuti che quelle per i percorsi. Ecco un estratto di codice da Resources/Shared.it.resx:

  <data name="Chapter2.Title" xml:space="preserve">
    <value>Capitolo 2</value>
  </data>
  <data name="Chapter2.Text" xml:space="preserve">
    <value>Così ho trascorso la mia vita...</value>
  </data>
  <data name="Routing.Book" xml:space="preserve">
    <value>Libro</value>
  </data>
  <data name="Routing.Book.Chapter1" xml:space="preserve">
    <value>Capitolo1</value>
  </data>
  <data name="Query.comments" xml:space="preserve">
    <value>commenti</value>
  </data>

Come si vede:

  • Chapter2.Title e Chapter2.Text sono chiavi associate a testi che appariranno nelle pagine dell'applicazione;
  • Routing.Book e Routing.Book.Chapter1 identificano rispettivamente i nomi che il controller BookController e la sua action Chapter1 assumeranno nella barra degli indirizzi del browser;
  • Query.comments serve a localizzare il nome di parametri querystring.

Impostazione della Culture appropriata

In questa applicazione, la lingua viene fornita attraverso il percorso in questa forma: /lingua/NomeController/NomeAction, cioè ad esempio: /it/Libro/Capitolo1

Vedi il paragrafo Configurazione della route MVC per capire come impostare questa route

Per fare in modo che ASP.NET Core imposti la Culture appropriata, in base al frammento /lingua indicato nell'url, bisogna innanzitutto abilitare la localizzazione della richiesta corrente andando nel file Startup.cs e usando il seguente middleware nel metodo Configure.

// È importante che sia DOPO app.UseRouting();
app.UseRequestLocalization();

ASP.NET Core, di per sé, non è in grado di determinare la Culture corrente a partire dall'URL, perciò dobbiamo aiutarlo con il request culture provider che si trova in Models/Localization/RouteRequestCultureProvider.cs

Dopodiché va registrato nel file Startup.cs al metodo ConfigureServices. Ne approfittiamo anche per indicare le Culture supportate (in questo caso inglese, italiano e francese).

services.Configure<RequestLocalizationOptions(options =>
{
    options.DefaultRequestCulture = new RequestCulture(supportedCultures[0].TwoLetterISOLanguageName);
    options.SupportedCultures = supportedCultures;
    options.SupportedUICultures = supportedCultures;
    // Ecco il request culture provider, lo inseriamo come primo
    options.RequestCultureProviders.Insert(0, new RouteRequestCultureProvider(supportedCultures));
});

In questo modo, se l'utente richiede /it/Libro/Capitolo1, verrà impostata la Culture italiana per la richiesta corrente. Se invece richiede /en/Book/Chapter1, verrà impostata la Culture inglese.

Culture supportate

Per questa demo, l'elenco delle Culture supportate è cablato in un campo privato di Startup.cs.

private readonly List<CultureInfo>supportedCultures = new List<CultureInfo> {
    new CultureInfo("en"),
    new CultureInfo("it"),
    new CultureInfo("fr")
};

La prima delle Culture in elenco viene usata come predefinita. Questo elenco è stato anche registrato per la dependency injection in questo modo, dal metodo ConfigureServices di Startup.cs

services.AddSingleton<IEnumerable<CultureInfo>>(supportedCultures);

Perciò può essere ricevuto da altri componenti dell'applicazione come parametro di tipo IEnumerable<CultureInfo> nel loro costruttore.

TODO: In produzione sarebbe meglio esporre questo elenco tramite un servizio apposito, che abbia un nome più intellegibile.

Visualizzazione dei testi localizzati

Impostata correttamente una Culture, nelle view Razor viene usato il servizio di ASP.NET Core IStringLocalizer che permette di ottenere una stringa localizzata dal file di risorse relativo alla Culture della richiesta corrente. La sua implementazione concreta si trova nel file Models/Localization/ResourceBasedLocalizer.cs.

Ecco un esempio di codice in cui lo si riceve grazie dalla dependency injection grazie alla direttiva @inject e poi si accede al valore grazie al suo indexer.

@inject IStringLocalizer localizer
<p>@localizer["Chapter1.Text"]</p>

Il servizio IStringLocalizer viene usato anche in altri punti dell'applicazione, per la riscrittura dei percorsi localizzati e per la generazione dei link.

Configurazione della route MVC

Questa applicazione usa la tecnica del Dynamic Controller Routing per esaminare il percorso richiesto e riscriverlo prima ancora che avvenga la selezione dell'endpoint.

Ecco il frammento di codice che si trova nel file Startup.cs nel metodo Configure.

app.UseEndpoints(endpoints =>
{
    endpoints.MapDynamicControllerRoute<LocalizationTransformer>(
        pattern: "{language=en}/{controller=Home}/{action=Index}/{id?}");
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{language=en}/{controller=Home}/{action=Index}/{id?}");
});

Come si vede, è stato indicato un tipo di transformer LocalizationTransformer, il cui funzionamento è descritto nel paragrafo Riscrittura dei percorsi localizzati,

Inoltre, purtroppo, oltre alla Dynamic Controller Route è anche necessario definire una "normale" controller route che usa lo stesso pattern a causa della Issue #16965 che impedisce la generazione degli link quando è presente la sola dynamic controller route.

Come conseguenza di ciò, le pagine saranno raggiungibili non solo dal percorso localizzato come /it/Libro/Capitolo1 ma anche dal loro percorso originale /it/Book/Chapter1.

Questo problema non è stato risolto in questa applicazione perché dovrebbe risolversi da sé quando la suddetta Issue viene chiusa. Per il momento, in produzione, si consiglia di aggiungere un tag <link rel="canonical" alla view di layout per evitare che i motori di ricerca percepiscano i due diversi percorsi come contenuto duplicato. Serve solo come precauzione dato che, per come è costruita questa applicazione, nelle pagine non dovrebbero esserci tracce del percorso originale /it/Book/Chapter1, dato che i link vengono generati localizzati.

Generazione dei link

Ogni volta che si deve generare un link, ad esempio mediante gli attributi asp-action e asp-controller del tag a (cioè l'AnchorTagHelper) o con il metodo RedirectToAction del Controller, ASP.NET Core 3.x usa internamente un servizio chiamato LinkGenerator.

Affinché i link vengano localizzati, il LinkGenerator è stato "avvolto" dall'implementazione che si trova in Models/Localization/LocalizedLinkGenerator.cs e che compie le necessarie localizzazioni prima che il LinkGenerator di default esegua la sua logica.

Ecco come è stato registrato per la dependency injection nel metodo ConfigureServices di Startup.cs.

var serviceProvider = services.BuildServiceProvider();
// Ottengo un riferimento al LinkGenerator originale
var defaultLinkGenerator = serviceProviderGetService<LinkGenerator>();
var stringLocalizer = serviceProviderGetService<IStringLocalizer>();
// Lo sostituisco con un'implementazione personalizzata che lo avvolge
services.AddSingleton<LinkGenerator>(new LocalizedLinkGenerator(defaultLinkGenerator,stringLocalizer, supportedCultures));

Purtroppo non c'è un modo più semplice di registrarlo perché l'implementazione concreta di Microsoft, il DefaultLinkGenerator, è una classe internal sealead da cui perciò non si può derivare.

Comunque, a questo punto, un tag a come il seguente:

<a asp-route-language="it" asp-controller="Book" asp-action="Chapter1">1</a>

Produrrà il seguente output, ovvero il risultato voluto.

<a href="/it/Libro/Capitolo1">1</a>

Generazione dei link con parametri querystring

Vediamo anche un altro esempio in cui viene localizzato anche un parametro querystring. Un tag a come il seguente:

<a asp-route-language="it" asp-controller="Book" asp-action="Chapter1" asp-route-comments="1">1</a>

Produrrà il seguente output...

<a href="/it/Libro/Capitolo1?commenti=1">1</a>

...purché nel file di risorse per l'italiano sia stata indicata una voce come la seguente. Notare il prefisso Query. che in questa applicazione designa i nomi dei parametry querystring. I nomi dei parametri querystring sono case-sensitive.

<data name="Query.comments" xml:space="preserve">
  <value>commenti</value>
</data>

Riscrittura dei percorsi localizzati

Avendo configurato una Dynamic Controller Route abbiamo indicato il tipo di un transformer che si occuperà di riscrivere i route values di action e controller, prima che la selezione dell'endpoint avvenga.

Tale transformer è implementato nel file Models/Localization/LocalizationTransformer.cs ed è stato registrato nel metodo ConfigureServices in Startup.cs per la dependency injection in questo modo:

services.AddSingleton<LocalizationTransformer>();

Questo transformer si occupa di creare una reverse map ovvero un dizionario per riconvertire i nomi di controller e action dal loro nome localizzato al loro nome originale. Cioè, grazie alla reverse map il nome localizzato Libro può essere ricondotto a Book, che è l'effettivo nome del controller Models/Controllers/BookController.cs.

Il servizio IStringLocalizer permette solo la localizzazione dal nome originale al nome localizzato e non viceversa.

La reverse map viene creata alla prima richiesta inviata da un utente e tenuta in cache per le successive richieste. Viene creata usando il servizio di ASP.NET Core IActionDescriptorCollectionProvider che restituisce i dettagli di tutte le action trovate nel progetto. Poi, vengono localizzati i loro nomi e i nomi dei loro controller, in ognuna delle Culture supportate dall'applicazione.

Come conseguenza di ciò, la prima richiesta potrebbe richiedere più tempo a completarsi, soprattutto se il progetto contiene parecchi Controller e parecchie Culture supportate. Dovremmo essere nell'ordine dei decimi di secondo ma vale la pena misurarlo con la classe Stopwatch.

Per realizzare la reverse map, viene usato il metodo WithCulture del servizio IStringLocalizer che è stato marcato da Microsoft come obsoleto. Non è ancora chiaro come verrà sostituito nella prossima release di ASP.NET Core. La discussione è aperta nella Issue #7756.

Todo

Questa demo NON copre:

  • Aree;
  • Razor Pages;
  • Navigazioni originate da form;
  • Generazione del tag canonical.

ATTENZIONE: Il codice dell'applicazione non è pronto per essere usato in produzione. Oltretutto, non è coperto da test automatici..

Link utili

aspnetcore-localization-demo's People

Contributors

brightsoul avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.