Condividi tramite


Localizzazione in .NET

La localizzazione è il processo di conversione delle risorse di un'applicazione in versioni localizzate per ogni lingua supportata dall'applicazione. È consigliabile procedere al passaggio di localizzazione solo dopo aver completato il passaggio di revisione della localizzabilità per verificare che l'applicazione globalizzata sia pronta per la localizzazione.

Un'applicazione pronta per la localizzazione è separata in due blocchi concettuali: un blocco che contiene tutti gli elementi dell'interfaccia utente e un blocco che contiene codice eseguibile. Il blocco dell'interfaccia utente contiene solo elementi dell'interfaccia utente localizzabili, ad esempio stringhe, messaggi di errore, finestre di dialogo, menu, risorse di oggetti incorporate e così via per la cultura neutra. Il blocco di codice contiene solo il codice dell'applicazione da essere utilizzato da tutte le culture supportate. Il Common Language Runtime supporta un modello di risorse assembly satellite che separa il codice eseguibile di un'applicazione dalle sue risorse. Per altre informazioni sull'implementazione di questo modello, vedere Risorse in .NET.

Per ogni versione localizzata dell'applicazione, aggiungere un nuovo assembly satellite contenente il blocco dell'interfaccia utente per la versione localizzata tradotto nella lingua appropriata per la cultura di destinazione. Il blocco di codice per tutte le culture dovrebbe rimanere invariato. La combinazione di una versione localizzata del blocco dell'interfaccia utente con il blocco di codice produce una versione localizzata dell'applicazione.

In questo articolo, si apprenderà come utilizzare le implementazioni IStringLocalizer<T> e IStringLocalizerFactory. Tutto il codice sorgente di esempio in questo articolo si basa sui pacchetti NuGet Microsoft.Extensions.Localization e Microsoft.Extensions.Hosting. Per ulteriori informazioni sull'hosting, vedere Host Generico .NET.

File di risorse

Il meccanismo principale per isolare le stringhe localizzabili è costituito dai file di risorse. Un file di risorse è un file XML con estensione resx . I file di risorse vengono tradotti prima dell'esecuzione dell'applicazione che li utilizza, in altre parole, rappresentano il contenuto tradotto a riposo. Un nome di file di risorse contiene in genere un identificatore delle impostazioni locali e si presenta nel seguente modo:

<FullTypeName><.Locale>.resx

Dove:

  • Il <FullTypeName> rappresenta le risorse localizzabili per un tipo specifico.
  • L'oggetto facoltativo <.Locale> rappresenta le impostazioni locali del contenuto del file di risorse.

Specificare le localizzazioni

L'impostazione locale deve definire la lingua, almeno come requisito minimo, ma può anche definire la cultura (lingua regionale) e persino il paese o la regione. Questi segmenti sono comunemente delimitati dal - carattere . Con l'aggiunta della specificità di una cultura, vengono applicate le regole di fallback in cui le corrispondenze migliori sono prioritarie. Le località devono essere mappate a un tag di lingua conosciuto. Per altre informazioni, vedere CultureInfo.Name.

Scenari di fallback culturale

Immagina che la tua app localizzata supporti varie località serbe e abbia i seguenti file di risorse per il suo MessageService.

Documento Lingua regionale Codice paese
MessageService.sr-Cyrl-RS.resx (Cirillico, Serbia) RS
MessageService.sr-Cyrl.resx Cirillico
MessageService.sr-Latn-BA.resx (Alfabeto latino, Bosnia ed Erzegovina) BA
MessageService.sr-Latn-ME.resx (Alfabeto latino, Montenegro) me
MessageService.sr-Latn-RS.resx (Alfabeto latino, Serbia) RS
MessageService.sr-Latn.resx Latino
MessageService.sr.resx Latino
MessageService.resx

Lingua regionale predefinita per la lingua.

Quando l'app è in esecuzione con l'opzione CultureInfo.CurrentCulture impostata su una cultura specifica di "sr-Cyrl-RS", la localizzazione tenta di risolvere i file nel seguente ordine:

  1. MessageService.sr-Cyrl-RS.resx
  2. MessageService.sr-Cyrl.resx
  3. MessageService.sr.resx
  4. MessageService.resx

Tuttavia, se l'app è in esecuzione con CultureInfo.CurrentCulture impostato sulla cultura "sr-Latn-BA", la localizzazione tenta di risolvere i file nel seguente ordine:

  1. MessageService.sr-Latn-BA.resx
  2. MessageService.sr-Latn.resx
  3. MessageService.sr.resx
  4. MessageService.resx

La regola di fallback della cultura ignorerà le impostazioni locali quando non sono presenti corrispondenze, questo significa che il quarto file di risorse viene selezionato se non è in grado di trovare una corrispondenza. Se le impostazioni cultura erano impostate su "fr-FR", la localizzazione finirebbe per cadere nel file MessageService.resx che può essere problematico. Per altre informazioni, vedere Processo di fallback delle risorse.

Ricerca risorse

I file di risorse vengono risolti automaticamente come parte di una routine di ricerca. Se il nome del file di progetto è diverso dallo spazio dei nomi radice del progetto, il nome dell'assembly potrebbe essere diverso. Questo può impedire che la ricerca delle risorse abbia successo. Per risolvere questa mancata corrispondenza, usa il RootNamespaceAttribute per fornire un suggerimento ai servizi di localizzazione. Se specificato, viene usato durante la ricerca delle risorse.

Il progetto di esempio è denominato example.csproj, che crea un example.dll e example.exe, ma viene usato lo Localization.Example spazio dei nomi . Applicare un assembly attributo di livello per correggere questa mancata corrispondenza:

[assembly: RootNamespace("Localization.Example")]

Registrare i servizi di localizzazione

Per registrare i servizi di localizzazione, chiamare uno dei AddLocalization metodi di estensione durante la configurazione dei servizi. Consentirà l'iniezione delle dipendenze (DI) per i seguenti tipi:

Configurare le opzioni di localizzazione

L'overload AddLocalization(IServiceCollection, Action<LocalizationOptions>) accetta un setupAction parametro di tipo Action<LocalizationOptions>. In questo modo è possibile configurare le opzioni di localizzazione.

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

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

// Omitted for brevity.

I file di risorse possono vivere ovunque in un progetto, ma esistono procedure comuni che hanno dimostrato di avere successo. Più spesso di non, viene seguita la via della minima resistenza. Il codice C# precedente:

In questo modo i servizi di localizzazione cercherebbero nella directory Resources i file di risorse.

Usare IStringLocalizer<T> ed IStringLocalizerFactory

Dopo aver registrato (e facoltativamente configurato) i servizi di localizzazione, è possibile usare i tipi seguenti con l'inserimento delle dipendenze:

Per creare un servizio messaggi in grado di restituire stringhe localizzate, considerare quanto segue MessageService:

using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Localization;

namespace Localization.Example;

public sealed class MessageService(IStringLocalizer<MessageService> localizer)
{
    [return: NotNullIfNotNull(nameof(localizer))]
    public string? GetGreetingMessage()
    {
        LocalizedString localizedString = localizer["GreetingMessage"];

        return localizedString;
    }
}

Nel codice C# precedente:

  • Viene dichiarato un IStringLocalizer<MessageService> localizer campo.
  • Il costruttore primario definisce un IStringLocalizer<MessageService> parametro e lo acquisisce come localizer argomento.
  • Il metodo GetGreetingMessage invoca il IStringLocalizer.Item[String] passando "GreetingMessage" come argomento.

Il IStringLocalizer supporta anche le risorse stringa con parametri, considera quanto segue ParameterizedMessageService:

using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Localization;

namespace Localization.Example;

public class ParameterizedMessageService(IStringLocalizerFactory factory)
{
    private readonly IStringLocalizer _localizer =
        factory.Create(typeof(ParameterizedMessageService));

    [return: NotNullIfNotNull(nameof(_localizer))]
    public string? GetFormattedMessage(DateTime dateTime, double dinnerPrice)
    {
        LocalizedString localizedString = _localizer["DinnerPriceFormat", dateTime, dinnerPrice];

        return localizedString;
    }
}

Nel codice C# precedente:

  • Viene dichiarato un IStringLocalizer _localizer campo.
  • Il costruttore primario accetta un IStringLocalizerFactory parametro, che viene usato per creare un IStringLocalizer oggetto dal ParameterizedMessageService tipo e lo assegna al _localizer campo.
  • Il GetFormattedMessage metodo richiama IStringLocalizer.Item[String, Object[]], passando "DinnerPriceFormat", un dateTime oggetto e dinnerPrice come argomenti.

Importante

Non è obbligatorio IStringLocalizerFactory. È preferibile che i servizi utilizzati richiedano il IStringLocalizer<T>.

Entrambi IStringLocalizer.Item[] gli indicizzatori restituiscono un LocalizedString, con conversioni implicite in string?.

Combinare tutti gli elementi

Per esemplificare un'app usando entrambi i servizi di messaggistica, insieme alla localizzazione e ai file di risorse, considerare il file Program.cs seguente.

using System.Globalization;
using Localization.Example;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.Logging;
using static System.Console;
using static System.Text.Encoding;

[assembly: RootNamespace("Localization.Example")]

OutputEncoding = Unicode;

if (args is [var cultureName])
{
    CultureInfo.CurrentCulture =
        CultureInfo.CurrentUICulture =
            CultureInfo.GetCultureInfo(cultureName);
}

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddLocalization();
builder.Services.AddTransient<MessageService>();
builder.Services.AddTransient<ParameterizedMessageService>();
builder.Logging.SetMinimumLevel(LogLevel.Warning);

using IHost host = builder.Build();

IServiceProvider services = host.Services;

ILogger logger =
    services.GetRequiredService<ILoggerFactory>()
        .CreateLogger("Localization.Example");

MessageService messageService =
    services.GetRequiredService<MessageService>();
logger.LogWarning(
    "{Msg}",
    messageService.GetGreetingMessage());

ParameterizedMessageService parameterizedMessageService =
    services.GetRequiredService<ParameterizedMessageService>();
logger.LogWarning(
    "{Msg}",
    parameterizedMessageService.GetFormattedMessage(
        DateTime.Today.AddDays(-3), 37.63));

await host.RunAsync();

Nel codice C# precedente:

  • RootNamespaceAttribute imposta "Localization.Example" come spazio dei nomi radice.
  • Il Console.OutputEncoding viene assegnato a Encoding.Unicode.
  • Quando viene passato un singolo argomento a args, a CultureInfo.CurrentCulture e CultureInfo.CurrentUICulture viene assegnato il risultato di CultureInfo.GetCultureInfo(String) in base a arg[0].
  • Viene Host creato con predefiniti.
  • I servizi di localizzazione, MessageServicee ParameterizedMessageService vengono registrati nell'oggetto per l'inserimento IServiceCollection delle dipendenze.
  • Per rimuovere il rumore, la registrazione è configurata per ignorare qualsiasi livello di log inferiore a un avviso.
  • L'istanza MessageService risolve IServiceProvider e il messaggio risultante è registrato su registro.
  • L'oggetto ParameterizedMessageService viene risolto dall'istanza IServiceProvider e il messaggio formattato risultante viene registrato.

Ognuna delle *MessageService classi definisce un set di file con estensione resx , ognuno con una singola voce. Ecco il contenuto di esempio per i file di MessageService risorse, a partire da MessageService.resx:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <data name="GreetingMessage" xml:space="preserve">
    <value>Hi friends, the ".NET" developer community is excited to see you here!</value>
  </data>
</root>

MessageService.sr-Cyrl-RS.resx:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <data name="GreetingMessage" xml:space="preserve">
    <value>Здраво пријатељи, ".NЕТ" девелопер заједница је узбуђена што вас види овде!</value>
  </data>
</root>

MessageService.sr-Latn.resx:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <data name="GreetingMessage" xml:space="preserve">
    <value>Zdravo prijatelji, ".NET" developer zajednica je uzbuđena što vas vidi ovde!</value>
  </data>
</root>

Ecco il contenuto di esempio per i file di ParameterizedMessageService risorse, a partire da ParameterizedMessageService.resx:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <data name="DinnerPriceFormat" xml:space="preserve">
    <value>On {0:D} my dinner cost {1:C}.</value>
  </data>
</root>

ParameterizedMessageService.sr-Cyrl-RS.resx:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <data name="DinnerPriceFormat" xml:space="preserve">
    <value>У {0:D} моја вечера је коштала {1:C}.</value>
  </data>
</root>

ParameterizedMessageService.sr-Latn.resx:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <data name="DinnerPriceFormat" xml:space="preserve">
    <value>U {0:D} moja večera je koštala {1:C}.</value>
  </data>
</root>

Suggerimento

Tutti i commenti, gli schemi e <resheader> gli elementi XML del file di risorse vengono intenzionalmente omessi per brevità.

Esecuzioni di esempio

L'esempio seguente illustra i vari output localizzati, date le impostazioni locali di destinazione.

Prendere in considerazione "sr-Latn":

dotnet run --project .\example\example.csproj sr-Latn

warn: Localization.Example[0]
      Zdravo prijatelji, ".NET" developer zajednica je uzbuđena što vas vidi ovde!
warn: Localization.Example[0]
      U utorak, 03. avgust 2021. moja večera je koštala 37,63 ¤.

Quando si omette un argomento alla CLI .NET per eseguire il progetto, viene utilizzata la cultura di sistema predefinita, in questo caso "en-US":

dotnet run --project .\example\example.csproj

warn: Localization.Example[0]
      Hi friends, the ".NET" developer community is excited to see you here!
warn: Localization.Example[0]
      On Tuesday, August 3, 2021 my dinner cost $37.63.

Quando si passa "sr-Cryl-RS", vengono trovati i file di risorse corrispondenti corretti e la localizzazione applicata.

dotnet run --project .\example\example.csproj sr-Cryl-RS

warn: Localization.Example[0]
      Здраво пријатељи, ".NЕТ" девелопер заједница је узбуђена што вас види овде!
warn: Localization.Example[0]
      У уторак, 03. август 2021. моја вечера је коштала 38 RSD.

L'applicazione di esempio non fornisce i file di risorse per "fr-CA", ma quando viene richiamata con quella cultura, vengono usati i file di risorse non localizzati.

Avvertimento

Poiché la cultura viene trovata ma i file di risorse corretti non sono trovati, quando viene applicata la formattazione, si ottiene una localizzazione parziale.

dotnet run --project .\example\example.csproj fr-CA

warn: Localization.Example[0]
     Hi friends, the ".NET" developer community is excited to see you here!
warn: Localization.Example[0]
     On mardi 3 août 2021 my dinner cost 37,63 $.

Vedere anche