Localisation dans .NET

La localisation correspond au processus de traduction des ressources d’une application dans des versions localisées pour chaque culture prise en charge par l’application. Vous devez passer à l’étape de localisation uniquement après avoir effectué l’étape Révision de l’adaptabilité pour vérifier que l’application globalisée est prête pour la localisation.

Une application prête pour la localisation est divisée en deux blocs conceptuels, un bloc qui contient tous les éléments d’interface utilisateur et un bloc qui contient le code exécutable. Le bloc d’interface utilisateur contient uniquement les éléments d’interface utilisateur localisables, comme les chaînes, les messages d’erreur, les boîtes de dialogue, les menus, les ressources d’objet incorporées, etc. relatifs à la culture neutre. Le bloc de code contient uniquement le code d’application à utiliser par toutes les cultures prises en charge. Le CLR prend en charge un modèle de ressource d’assembly satellite qui sépare le code exécutable d’une application de ses ressources. Pour plus d’informations sur l’implémentation de ce modèle, consultez Ressources dans .NET.

Pour chaque version localisée de votre application, ajoutez un nouvel assembly satellite contenant le bloc d’interface utilisateur localisée traduit dans la langue appropriée pour la culture cible. Le bloc de code pour toutes les cultures doit rester le même. La combinaison d’une version localisée du bloc d’interface utilisateur et du bloc de code produit une version localisée de votre application.

Dans cet article, vous allez apprendre à utiliser les implémentations IStringLocalizer<T> et IStringLocalizerFactory. Tous les exemples de code source de cet article s’appuie sur les packages NuGet Microsoft.Extensions.Localization et Microsoft.Extensions.Hosting. Pour plus d’informations sur l’hébergement, consultez Hôte générique .NET.

Fichiers de ressources

Le mécanisme principal pour isoler les chaînes localisables consiste à utiliser des fichiers de ressources. Un fichier de ressources est un fichier XML avec l’extension de fichier .resx. Les fichiers de ressources sont traduits avant l’exécution de l’application consommatrice. En d’autres termes, ils représentent le contenu traduit au repos. Un nom de fichier de ressources contient généralement un identificateur de paramètres régionaux et prend la forme suivante :

<FullTypeName><.Locale>.resx

Où :

  • Le <FullTypeName> représente des ressources localisables pour un type spécifique.
  • L’argument facultatif <.Locale> représente les paramètres régionaux du contenu du fichier de ressources.

Spécification des paramètres régionaux

Les paramètres régionaux doivent définir la langue, au minimum, mais elles peuvent également définir la culture (langue régionale) et même le pays ou la région. Ces segments sont généralement délimités par le caractère -. Avec la spécificité supplémentaire d’une culture, les règles de « secours pour la culture » sont appliquées où les meilleures correspondances sont hiérarchisées. Les paramètres régionaux doivent être mappés à une balise de langue connue. Pour plus d’informations, consultez CultureInfo.Name.

Scénarios de secours pour la culture

Imagine que votre application localisée prend en charge différents paramètres régionaux serbe et dispose des fichiers de ressources suivants pour son MessageService :

Fichier Langue régionale Indicatif de pays
MessageService.sr-Cyrl-RS.resx (Cyrillique, Serbie) RS
MessageService.sr-Cyrl.resx Cyrillique
MessageService.sr-Latn-BA.resx (Latin, Bosnie-Herzégovine) BA
MessageService.sr-Latn-ME.resx (Latin, Monténégro) ME
MessageService.sr-Latn-RS.resx (Latin, Serbie) RS
MessageService.sr-Latn.resx Latin
MessageService.sr.resx Latin
MessageService.resx

La langue régionale par défaut pour la langue.

Lorsque votre application s’exécute avec le CultureInfo.CurrentCulture défini sur une culture de "sr-Cyrl-RS" localisation tente de résoudre les fichiers dans l’ordre suivant :

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

Toutefois, si votre application s’exécutait avec le CultureInfo.CurrentCulture défini sur une culture de "sr-Latn-BA" localisation tente de résoudre les fichiers dans l’ordre suivant :

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

La règle de « secours pour la culture » ignore les paramètres régionaux lorsqu’il n’existe aucune correspondance correspondante, ce qui signifie que le numéro de fichier de ressource quatre est sélectionné s’il ne parvient pas à trouver une correspondance. Si la culture était définie sur "fr-FR", la localisation finirait par tomber dans le fichier MessageService.resx, ce qui peut être problématique. Pour plus d’informations, consultez Le processus de secours pour les ressources.

Recherche de ressources

Les fichiers de ressources sont automatiquement résolus dans le cadre d’une routine de recherche. Si le nom de votre fichier de projet est différent de l’espace de noms racine de votre projet, le nom de l’assembly peut différer. Cela peut empêcher la recherche de ressources de réussir autrement. Pour résoudre ce problème, utilisez le RootNamespaceAttribute pour fournir un indicateur aux services de localisation. Lorsqu’il est fourni, il est utilisé lors de la recherche de ressources.

L’exemple de projet est nommé example.csproj, qui crée un example.dll et un example.exe. Toutefois, l’espace de noms Localization.Example est utilisé. Appliquez un attribut assembly de niveau pour corriger cette incompatibilité :

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

Inscrire les services de localisation

Pour inscrire les services de localisation, appelez l’une des méthodes d’extension AddLocalization lors de la configuration des services. Cela permet l’injection de dépendances (DI) des types suivants :

Configurer les options de localisation

La surcharge AddLocalization(IServiceCollection, Action<LocalizationOptions>) accepte un paramètre setupAction de type Action<LocalizationOptions>. Cela vous permet de configurer les options de localisation.

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

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

// Omitted for brevity.

Les fichiers de ressources peuvent se trouver n’importe où dans un projet, mais il existe des pratiques courantes qui se sont avérées efficaces. Le plus souvent, la voie de la moindre résistance est suivie. Le code C# précédent :

  • Crée le générateur d’applications hôtes par défaut.
  • Appelle AddLocalization sur la collection de services, en spécifiant LocalizationOptions.ResourcesPath en tant que "Resources".

Cela entraînerait la recherche de fichiers de ressources par les services de localisation dans le répertoire Ressources.

Utiliser IStringLocalizer<T> et IStringLocalizerFactory

Une fois que vous avez inscrit (et éventuellement configuré) les services de localisation, vous pouvez utiliser les types suivants avec DI :

Pour créer un service de message capable de retourner des chaînes localisées, tenez compte des points suivants 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;
    }
}

Dans le code C# précédent :

  • Un champ IStringLocalizer<MessageService> localizer est déclaré.
  • Le constructeur principal définit un paramètre IStringLocalizer<MessageService> et le capture en tant qu’argument localizer.
  • La méthode GetGreetingMessage appelle IStringLocalizer.Item[String] transférant "GreetingMessage" en tant qu’argument.

IStringLocalizer prend également en charge les ressources de chaîne paramétrable, à prendre en compte le ParameterizedMessageService suivant :

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;
    }
}

Dans le code C# précédent :

  • Un champ IStringLocalizer _localizer est déclaré.
  • Le constructeur principal prend un paramètre IStringLocalizerFactory, qui est utilisé pour créer un IStringLocalizer à partir du type ParameterizedMessageService et l’affecte au champ _localizer.
  • La méthode GetFormattedMessage appelle IStringLocalizer.Item[String, Object[]], en transférant "DinnerPriceFormat", un objet dateTime et dinnerPrice comme arguments.

Important

Le IStringLocalizerFactory n’est pas obligatoire. Au lieu de cela, il est préférable que les services consommatrices requièrent le IStringLocalizer<T>.

Les deux indexeurs IStringLocalizer.Item[] retournent un LocalizedString, qui a des conversions implicites en string?.

Assemblage

Pour illustrer une application utilisant à la fois les services de message, ainsi que les fichiers de localisation et de ressources, considérez le fichier Program. cs suivant :

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();

Dans le code C# précédent :

  • Le RootNamespaceAttribute définit "Localization.Example" en tant qu’espace de noms racine.
  • Le Console.OutputEncoding est affecté à Encoding.Unicode.
  • Lorsqu’un argument unique est passé à args, le CultureInfo.CurrentCulture et le CultureInfo.CurrentUICulture se voient attribuer le résultat de CultureInfo.GetCultureInfo(String) compte tenu le arg[0].
  • Le Host est créé avec les valeurs par défaut.
  • Les services de localisation, MessageServiceet ParameterizedMessageService sont inscrits auprès de IServiceCollection pour l’injection de dépendances.
  • Pour supprimer le bruit, la journalisation est configurée pour ignorer tout niveau de journal inférieur à un avertissement.
  • Le MessageService est résolu à partir de l’instance IServiceProvider et son message résultant est journalisé.
  • Le ParameterizedMessageService est résolu à partir de l’instance IServiceProvider et son message mis en forme résultant est journalisé.

Chacune des classes *MessageService définit un ensemble de fichiers .resx, chacun avec une seule entrée. Voici l’exemple de contenu des fichiers de ressources MessageService, en commençant par 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>

Voici l’exemple de contenu des fichiers de ressources ParameterizedMessageService, en commençant par 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>

Conseil

Tous les commentaires, schémas et éléments XML <resheader> du fichier de ressources sont intentionnellement omis par souci de concision.

Exemples d’exécutions

L’exemple d’exécution suivant montre les différentes sorties localisées, en fonction des paramètres régionaux ciblés.

Prenez le cas de "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 ¤.

Lorsque vous omettez un argument dans l’interface de ligne de commande .NET pour exécuter le projet, la culture système par défaut est utilisée, dans ce cas "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.

Lors du transfert de "sr-Cryl-RS", les fichiers de ressources correspondants appropriés sont trouvés et la localisation est appliquée :

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

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

L’exemple d’application ne fournit pas de fichiers de ressources pour "fr-CA", mais lorsqu’il est appelé avec cette culture, les fichiers de ressources non localisés sont utilisés.

Avertissement

Puisque la culture est trouvée, mais que les fichiers de ressources corrects ne le sont pas, lorsque la mise en forme est appliquée, vous vous retrouvez avec une localisation partielle :

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 $.

Voir aussi