Rendere localizzabile il contenuto di un'app ASP.NET Core

Di Hisham Bin Ateya, Damien Bowden, Bart Calixto e Nadeem Afana

Un'attività per la localizzazione di un'app consiste nel eseguire il wrapping di contenuto localizzabile con codice che facilita la sostituzione del contenuto per impostazioni cultura diverse.

IStringLocalizer

IStringLocalizer e IStringLocalizer<T> sono stati progettato per migliorare la produttività durante lo sviluppo di app localizzate. IStringLocalizer usa e ResourceManagerResourceReader per fornire risorse specifiche delle impostazioni cultura in fase di esecuzione. L'interfaccia ha un indicizzatore e un oggetto IEnumerable per la restituzione di stringhe localizzate. IStringLocalizer non richiede l'archiviazione delle stringhe di lingua predefinite in un file di risorse. È possibile sviluppare un'app per la localizzazione senza creare file di risorse nelle prime fasi di sviluppo.

Nell'esempio di codice seguente viene illustrato come eseguire il wrapping della stringa "About Title" per la localizzazione.

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;

namespace Localization.Controllers;

[Route("api/[controller]")]
public class AboutController : Controller
{
    private readonly IStringLocalizer<AboutController> _localizer;

    public AboutController(IStringLocalizer<AboutController> localizer)
    {
        _localizer = localizer;
    }

    [HttpGet]
    public string Get()
    {
        return _localizer["About Title"];
    }
}

Nel codice precedente l'implementazione deriva dall'inserimento IStringLocalizer<T>delle dipendenze. Se non viene trovato il valore localizzato di "About Title", viene restituita la chiave dell'indicizzatore, ovvero la stringa "About Title".

È possibile lasciare le stringhe letterali della lingua predefinita nell'app ed eseguirne il wrapping nel localizzatore per potersi concentrare sullo sviluppo dell'app. Si sviluppa un'app con la lingua predefinita e la si prepara per il passaggio di localizzazione senza prima creare un file di risorse predefinito.

In alternativa, è possibile usare l'approccio tradizionale e fornire una chiave per recuperare la stringa della lingua predefinita. Per molti sviluppatori, il nuovo flusso di lavoro di non avere un file resx di linguaggio predefinito e il semplice wrapping dei valori letterali stringa può ridurre il sovraccarico di localizzazione di un'app. Altri sviluppatori preferiscono il flusso di lavoro tradizionale in quanto possono essere più facili da usare con valori letterali stringa lunghi e più facile aggiornare stringhe localizzate.

IHtmlLocalizer

Usare l'implementazione IHtmlLocalizer<TResource> per le risorse che contengono HTML. IHtmlLocalizer Codifica HTML gli argomenti formattati nella stringa di risorsa, ma non la codifica HTML della stringa di risorsa stessa. Nel codice evidenziato seguente, solo il valore del name parametro è codificato in HTML.

using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Localization;

namespace Localization.Controllers;

public class BookController : Controller
{
    private readonly IHtmlLocalizer<BookController> _localizer;

    public BookController(IHtmlLocalizer<BookController> localizer)
    {
        _localizer = localizer;
    }

    public IActionResult Hello(string name)
    {
        ViewData["Message"] = _localizer["<b>Hello</b><i> {0}</i>", name];

        return View();
    }

NOTA: in genere, solo localizzare il testo, non HTML.

IStringLocalizerFactory

Al livello più basso, IStringLocalizerFactory può essere recuperato da Dependency Injection:

public class TestController : Controller
{
    private readonly IStringLocalizer _localizer;
    private readonly IStringLocalizer _localizer2;

    public TestController(IStringLocalizerFactory factory)
    {
        var type = typeof(SharedResource);
        var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
        _localizer = factory.Create(type);
        _localizer2 = factory.Create("SharedResource", assemblyName.Name);
    }       

    public IActionResult About()
    {
        ViewData["Message"] = _localizer["Your application description page."] 
            + " loc 2: " + _localizer2["Your application description page."];

        return View();
    }

Il codice precedente illustra ognuno dei due metodi di creazione factory.

Risorse condivise

È possibile partizionare le stringhe localizzate in base al controller o all'area oppure avere un solo contenitore. Nell'app di esempio viene usata una classe marcatore denominata SharedResource per le risorse condivise. La classe marcatore non viene mai chiamata:

// Dummy class to group shared resources

namespace Localization;

public class SharedResource
{
}

Nell'esempio seguente vengono usati i InfoController localizzatori e SharedResource :

public class InfoController : Controller
{
    private readonly IStringLocalizer<InfoController> _localizer;
    private readonly IStringLocalizer<SharedResource> _sharedLocalizer;

    public InfoController(IStringLocalizer<InfoController> localizer,
                   IStringLocalizer<SharedResource> sharedLocalizer)
    {
        _localizer = localizer;
        _sharedLocalizer = sharedLocalizer;
    }

    public string TestLoc()
    {
        string msg = "Shared resx: " + _sharedLocalizer["Hello!"] +
                     " Info resx " + _localizer["Hello!"];
        return msg;
    }

Localizzazione delle visualizzazioni

Il servizio IViewLocalizer fornisce le stringhe localizzate per una visualizzazione. La classe ViewLocalizer implementa questa interfaccia e individua la posizione della risorsa dal percorso del file della visualizzazione. Il codice seguente illustra come usare l'implementazione predefinita di IViewLocalizer:

@using Microsoft.AspNetCore.Mvc.Localization

@inject IViewLocalizer Localizer

@{
    ViewData["Title"] = Localizer["About"];
}
<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>

<p>@Localizer["Use this area to provide additional information."]</p>

L'implementazione predefinita di IViewLocalizer individua il file di risorse in base al nome file della visualizzazione. Non è disponibile alcuna opzione per l'uso di un file di risorse condivise globali. ViewLocalizer implementa il localizzatore usando IHtmlLocalizer, quindi Razor non codifica la stringa localizzata. È possibile parametrizzare le stringhe di risorse e IViewLocalizer codifica HTML i parametri, ma non la stringa di risorsa. Considerare il markup seguente Razor :

@Localizer["<i>Hello</i> <b>{0}!</b>", UserManager.GetUserName(User)]

Un file di risorse francese può contenere i valori seguenti:

Chiave valore
<i>Hello</i> <b>{0}!</b> <i>Bonjour</i> <b>{0} !</b>

La visualizzazione di cui è stato eseguito il rendering conterrà il markup HTML del file di risorse.

In genere, solo localizzare il testo, non HTML.

Per usare un file di risorse condivise in una visualizzazione, inserire IHtmlLocalizer<T>:

@using Microsoft.AspNetCore.Mvc.Localization
@using Localization.Services

@inject IViewLocalizer Localizer
@inject IHtmlLocalizer<SharedResource> SharedLocalizer

@{
    ViewData["Title"] = Localizer["About"];
}
<h2>@ViewData["Title"].</h2>

<h1>@SharedLocalizer["Hello!"]</h1>

Localizzazione di DataAnnotations

I messaggi di errore DataAnnotations vengono localizzati con IStringLocalizer<T>. Usando l'opzione ResourcesPath = "Resources" è possibile memorizzare i messaggi di errore in RegisterViewModel in uno dei percorsi seguenti:

  • Resources/ViewModels.Account.RegisterViewModel.fr.resx
  • Resources/ViewModels/Account/RegisterViewModel.fr.resx
using System.ComponentModel.DataAnnotations;

namespace Localization.ViewModels.Account;

public class RegisterViewModel
{
    [Required(ErrorMessage = "The Email field is required.")]
    [EmailAddress(ErrorMessage = "The Email field is not a valid email address.")]
    [Display(Name = "Email")]
    public string Email { get; set; }

    [Required(ErrorMessage = "The Password field is required.")]
    [StringLength(8, ErrorMessage = "The {0} must be at least {2} characters long.",
                                                                 MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password", ErrorMessage =
                            "The password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }
}

Gli attributi di non convalida vengono localizzati.

Come usare una stringa di risorsa per più classi

Il codice seguente illustra come usare una sola stringa di risorsa per gli attributi di convalida con più classi:

    services.AddMvc()
        .AddDataAnnotationsLocalization(options => {
            options.DataAnnotationLocalizerProvider = (type, factory) =>
                factory.Create(typeof(SharedResource));
        });

Nel codice precedente è SharedResource la classe corrispondente al file resx in cui vengono archiviati i messaggi di convalida. Con questo approccio, DataAnnotations usa SharedResourcesolo , anziché la risorsa per ogni classe.

Configurare i servizi di localizzazione

I servizi di localizzazione sono configurati in Program.cs:

builder.Services.AddLocalization(options => options.ResourcesPath = "Resources");

builder.Services.AddMvc()
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
    .AddDataAnnotationsLocalization();
  • AddLocalization aggiunge i servizi di localizzazione al contenitore dei servizi, incluse le implementazioni per IStringLocalizer<T> e IStringLocalizerFactory. Il codice precedente imposta anche il percorso delle risorse su "Resources".

  • AddViewLocalization aggiunge il supporto per i file di visualizzazione localizzati. In questo esempio, la localizzazione delle visualizzazioni si basa sul suffisso del file di visualizzazione. Ad esempio , "fr" nel Index.fr.cshtml file .

  • AddDataAnnotationsLocalization aggiunge il supporto per i messaggi di convalida localizzati DataAnnotations tramite IStringLocalizer astrazioni.

Nota

È possibile che non si riesca a immettere virgole decimali nel campo. Per supportare la convalida jQuery per impostazioni locali diverse dall'inglese che usano la virgola (",") come separatore decimale e per formati di data diversi da quello dell'inglese degli Stati Uniti, è necessario eseguire alcuni passaggi per globalizzare l'app. Per istruzioni sull'aggiunta di una virgola decimale, vedere questo commento di GitHub 4076 .

Passaggi successivi

La localizzazione di un'app comporta anche le attività seguenti:

Risorse aggiuntive

Rick Anderson, Damien Bowden, Bart Calixto, Nadeem Afana e Hisham Bin Ateya

Un'attività per la localizzazione di un'app consiste nel eseguire il wrapping di contenuto localizzabile con codice che facilita la sostituzione del contenuto per impostazioni cultura diverse.

IStringLocalizer

IStringLocalizer e IStringLocalizer<T> sono stati progettato per migliorare la produttività durante lo sviluppo di app localizzate. IStringLocalizer usa e ResourceManagerResourceReader per fornire risorse specifiche delle impostazioni cultura in fase di esecuzione. L'interfaccia ha un indicizzatore e un oggetto IEnumerable per la restituzione di stringhe localizzate. IStringLocalizer non richiede l'archiviazione delle stringhe di lingua predefinite in un file di risorse. È possibile sviluppare un'app per la localizzazione senza creare file di risorse nelle prime fasi di sviluppo.

Nell'esempio di codice seguente viene illustrato come eseguire il wrapping della stringa "About Title" per la localizzazione.

using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;

namespace Localization.Controllers
{
    [Route("api/[controller]")]
    public class AboutController : Controller
    {
        private readonly IStringLocalizer<AboutController> _localizer;

        public AboutController(IStringLocalizer<AboutController> localizer)
        {
            _localizer = localizer;
        }

        [HttpGet]
        public string Get()
        {
            return _localizer["About Title"];
        }
    }
}

Nel codice precedente l'implementazione deriva dall'inserimento IStringLocalizer<T>delle dipendenze. Se non viene trovato il valore localizzato di "About Title", viene restituita la chiave dell'indicizzatore, ovvero la stringa "About Title".

È possibile lasciare le stringhe letterali della lingua predefinita nell'app ed eseguirne il wrapping nel localizzatore per potersi concentrare sullo sviluppo dell'app. Si sviluppa un'app con la lingua predefinita e la si prepara per il passaggio di localizzazione senza prima creare un file di risorse predefinito.

In alternativa, è possibile usare l'approccio tradizionale e fornire una chiave per recuperare la stringa della lingua predefinita. Per molti sviluppatori, il nuovo flusso di lavoro di non avere un file resx di linguaggio predefinito e il semplice wrapping dei valori letterali stringa può ridurre il sovraccarico di localizzazione di un'app. Altri sviluppatori preferiscono il flusso di lavoro tradizionale in quanto possono essere più facili da usare con valori letterali stringa lunghi e più facile aggiornare stringhe localizzate.

IHtmlLocalizer

Usare l'implementazione IHtmlLocalizer<T> per le risorse che contengono HTML. IHtmlLocalizer Codifica HTML gli argomenti formattati nella stringa di risorsa, ma non la codifica HTML della stringa di risorsa stessa. Nel codice evidenziato seguente, solo il valore del name parametro è codificato in HTML.

using System;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Localization;

namespace Localization.Controllers
{
    public class BookController : Controller
    {
        private readonly IHtmlLocalizer<BookController> _localizer;

        public BookController(IHtmlLocalizer<BookController> localizer)
        {
            _localizer = localizer;
        }

        public IActionResult Hello(string name)
        {
            ViewData["Message"] = _localizer["<b>Hello</b><i> {0}</i>", name];

            return View();
        }

Nota

In genere, solo localizzare il testo, non HTML.

IStringLocalizerFactory

Al livello più basso, è possibile ottenere IStringLocalizerFactory dall'inserimento delle dipendenze:

{
    public class TestController : Controller
    {
        private readonly IStringLocalizer _localizer;
        private readonly IStringLocalizer _localizer2;

        public TestController(IStringLocalizerFactory factory)
        {
            var type = typeof(SharedResource);
            var assemblyName = new AssemblyName(type.GetTypeInfo().Assembly.FullName);
            _localizer = factory.Create(type);
            _localizer2 = factory.Create("SharedResource", assemblyName.Name);
        }       

        public IActionResult About()
        {
            ViewData["Message"] = _localizer["Your application description page."] 
                + " loc 2: " + _localizer2["Your application description page."];

Il codice precedente illustra ognuno dei due metodi di creazione factory.

Risorse condivise

È possibile partizionare le stringhe localizzate in base al controller o all'area oppure avere un solo contenitore. Nell'app di esempio, viene usata una classe fittizia denominata SharedResource per le risorse condivise.

// Dummy class to group shared resources

namespace Localization
{
    public class SharedResource
    {
    }
}

Alcuni sviluppatori usano la classe Startup per contenere le stringhe globali o condivise. Nell'esempio seguente vengono usati i InfoController localizzatori e SharedResource :

public class InfoController : Controller
{
    private readonly IStringLocalizer<InfoController> _localizer;
    private readonly IStringLocalizer<SharedResource> _sharedLocalizer;

    public InfoController(IStringLocalizer<InfoController> localizer,
                   IStringLocalizer<SharedResource> sharedLocalizer)
    {
        _localizer = localizer;
        _sharedLocalizer = sharedLocalizer;
    }

    public string TestLoc()
    {
        string msg = "Shared resx: " + _sharedLocalizer["Hello!"] +
                     " Info resx " + _localizer["Hello!"];
        return msg;
    }

Localizzazione delle visualizzazioni

Il servizio IViewLocalizer fornisce le stringhe localizzate per una visualizzazione. La classe ViewLocalizer implementa questa interfaccia e individua la posizione della risorsa dal percorso del file della visualizzazione. Il codice seguente illustra come usare l'implementazione predefinita di IViewLocalizer:

@using Microsoft.AspNetCore.Mvc.Localization

@inject IViewLocalizer Localizer

@{
    ViewData["Title"] = Localizer["About"];
}
<h2>@ViewData["Title"].</h2>
<h3>@ViewData["Message"]</h3>

<p>@Localizer["Use this area to provide additional information."]</p>

L'implementazione predefinita di IViewLocalizer individua il file di risorse in base al nome file della visualizzazione. Non è disponibile alcuna opzione per l'uso di un file di risorse condivise globali. ViewLocalizer implementa il localizzatore usando IHtmlLocalizer, quindi Razor non codifica la stringa localizzata. È possibile parametrizzare le stringhe di risorse e IViewLocalizer codifica HTML i parametri, ma non la stringa di risorsa. Considerare il markup seguente Razor :

@Localizer["<i>Hello</i> <b>{0}!</b>", UserManager.GetUserName(User)]

Un file di risorse francese può contenere i valori seguenti:

Chiave valore
<i>Hello</i> <b>{0}!</b> <i>Bonjour</i> <b>{0} !</b>

La visualizzazione di cui è stato eseguito il rendering conterrà il markup HTML del file di risorse.

Nota

In genere, solo localizzare il testo, non HTML.

Per usare un file di risorse condivise in una visualizzazione, inserire IHtmlLocalizer<T>:

@using Microsoft.AspNetCore.Mvc.Localization
@using Localization.Services

@inject IViewLocalizer Localizer
@inject IHtmlLocalizer<SharedResource> SharedLocalizer

@{
    ViewData["Title"] = Localizer["About"];
}
<h2>@ViewData["Title"].</h2>

<h1>@SharedLocalizer["Hello!"]</h1>

Localizzazione di DataAnnotations

I messaggi di errore DataAnnotations vengono localizzati con IStringLocalizer<T>. Usando l'opzione ResourcesPath = "Resources" è possibile memorizzare i messaggi di errore in RegisterViewModel in uno dei percorsi seguenti:

  • Resources/ViewModels.Account.RegisterViewModel.fr.resx
  • Resources/ViewModels/Account/RegisterViewModel.fr.resx
public class RegisterViewModel
{
    [Required(ErrorMessage = "The Email field is required.")]
    [EmailAddress(ErrorMessage = "The Email field is not a valid email address.")]
    [Display(Name = "Email")]
    public string Email { get; set; }

    [Required(ErrorMessage = "The Password field is required.")]
    [StringLength(8, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
    public string ConfirmPassword { get; set; }
}

In ASP.NET Core MVC 1.1.0 e versioni successive, gli attributi di non convalida vengono localizzati.

Come usare una stringa di risorsa per più classi

Il codice seguente illustra come usare una sola stringa di risorsa per gli attributi di convalida con più classi:

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

Nel codice precedente è SharedResource la classe corrispondente al file resx in cui vengono archiviati i messaggi di convalida. Con questo approccio, DataAnnotations usa SharedResourcesolo , anziché la risorsa per ogni classe.

Configurare i servizi di localizzazione

I servizi di localizzazione vengono configurati nel Startup.ConfigureServices metodo :

services.AddLocalization(options => options.ResourcesPath = "Resources");

services.AddMvc()
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
    .AddDataAnnotationsLocalization();
  • AddLocalization aggiunge i servizi di localizzazione al contenitore dei servizi, incluse le implementazioni per IStringLocalizer<T> e IStringLocalizerFactory. Il codice precedente imposta anche il percorso delle risorse su "Resources".

  • AddViewLocalization aggiunge il supporto per i file di visualizzazione localizzati. In questo esempio, la localizzazione delle visualizzazioni si basa sul suffisso del file di visualizzazione. Ad esempio , "fr" nel Index.fr.cshtml file .

  • AddDataAnnotationsLocalization aggiunge il supporto per i messaggi di convalida localizzati DataAnnotations tramite IStringLocalizer astrazioni.

Nota

È possibile che non si riesca a immettere virgole decimali nel campo. Per supportare la convalida jQuery per impostazioni locali diverse dall'inglese che usano la virgola (",") come separatore decimale e per formati di data diversi da quello dell'inglese degli Stati Uniti, è necessario eseguire alcuni passaggi per globalizzare l'app. Per istruzioni sull'aggiunta di una virgola decimale, vedere questo commento di GitHub 4076 .

Passaggi successivi

La localizzazione di un'app comporta anche le attività seguenti:

Risorse aggiuntive