Lokalizacja na platformie .NET

Lokalizacja to proces tłumaczenia zasobów aplikacji na zlokalizowane wersje dla każdej kultury obsługiwanej przez aplikację. Należy przejść do kroku lokalizacji dopiero po wykonaniu kroku Przegląd możliwości lokalizacji, aby sprawdzić, czy zglobalizowana aplikacja jest gotowa do lokalizacji.

Aplikacja gotowa do lokalizacji jest podzielona na dwa bloki koncepcyjne: blok zawierający wszystkie elementy interfejsu użytkownika i blok zawierający kod wykonywalny. Blok interfejsu użytkownika zawiera tylko lokalizowalne elementy interfejsu użytkownika, takie jak ciągi, komunikaty o błędach, okna dialogowe, menu, zasoby obiektów osadzonych itd. dla kultury neutralnej. Blok kodu zawiera tylko kod aplikacji, który ma być używany przez wszystkie obsługiwane kultury. Środowisko uruchomieniowe języka wspólnego obsługuje model zasobów zestawu satelitarnego, który oddziela kod wykonywalny aplikacji od zasobów. Aby uzyskać więcej informacji na temat implementowania tego modelu, zobacz Zasoby na platformie .NET.

Dla każdej zlokalizowanej wersji aplikacji dodaj nowy zestaw satelitarny zawierający zlokalizowany blok interfejsu użytkownika przetłumaczony na odpowiedni język kultury docelowej. Blok kodu dla wszystkich kultur powinien pozostać taki sam. Kombinacja zlokalizowanej wersji bloku interfejsu użytkownika z blokiem kodu tworzy zlokalizowaną wersję aplikacji.

W tym artykule dowiesz się, jak używać IStringLocalizer<T> implementacji i IStringLocalizerFactory . Cały przykładowy kod źródłowy w tym artykule opiera się na pakietach Microsoft.Extensions.Localization NuGet i Microsoft.Extensions.Hosting . Aby uzyskać więcej informacji na temat hostingu, zobacz Host ogólny platformy .NET.

Pliki zasobów

Podstawowym mechanizmem izolowania ciągów lokalizowalnych jest użycie plików zasobów. Plik zasobu to plik XML z rozszerzeniem pliku resx . Pliki zasobów są tłumaczone przed wykonaniem aplikacji zużywanej — innymi słowy reprezentują przetłumaczoną zawartość w spoczynku. Nazwa pliku zasobu najczęściej zawiera identyfikator ustawień regionalnych i przyjmuje następujący formularz:

<FullTypeName><.Locale>.resx

Gdzie:

  • Reprezentuje <FullTypeName> zasoby lokalizowalne dla określonego typu.
  • Opcjonalnie <.Locale> reprezentuje ustawienia regionalne zawartości pliku zasobu.

Określanie ustawień regionalnych

Ustawienia regionalne powinny definiować język na minimalnym poziomie, ale może również definiować kulturę (język regionalny), a nawet kraj lub region. Te segmenty są często rozdzielane znakiem - . Dzięki dodatkowej specyfiki kultury reguły "powrotu kultury" są stosowane w przypadku określania priorytetów najlepszych dopasowań. Ustawienia regionalne powinny być mapowanie na dobrze znany tag języka. Aby uzyskać więcej informacji, zobacz CultureInfo.Name.

Scenariusze rezerwowe dotyczące kultury

Załóżmy, że zlokalizowana aplikacja obsługuje różne serbskie ustawienia regionalne i ma następujące pliki zasobów dla tej MessageServiceaplikacji:

Plik Język regionalny Kod kraju
MessageService.sr-Cyrl-RS.resx (Cyrylica, Serbia) OS
MessageService.sr-Cyrl.resx Cyrylica
MessageService.sr-Latn-BA.resx (Łaciński, Bośnia i Hercegowina) BA
MessageService.sr-Latn-ME.resx (Łaciński, Czarnogóra) ME
MessageService.sr-Latn-RS.resx (Łaciński, Serbia) OS
MessageService.sr-Latn.resx Łacińskiej
MessageService.sr.resx † łaciński
MessageService.resx

† Domyślny język regionalny dla języka.

Gdy aplikacja jest uruchomiona z CultureInfo.CurrentCulture zestawem do kultury "sr-Cyrl-RS" lokalizacji próbuje rozpoznać pliki w następującej kolejności:

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

Jeśli jednak aplikacja była uruchomiona z CultureInfo.CurrentCulture zestawem na kulturę "sr-Latn-BA" lokalizacji, próbuje rozpoznać pliki w następującej kolejności:

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

Reguła rezerwowa "kultury" będzie ignorować ustawienia regionalne, jeśli nie ma odpowiednich dopasowań, co oznacza, że plik zasobu numer cztery jest wybierany, jeśli nie można odnaleźć dopasowania. Jeśli dla kultury ustawiono "fr-FR"wartość , lokalizacja zakończy się na pliku MessageService.resx , który może być problematyczny. Aby uzyskać więcej informacji, zobacz Proces rezerwowy zasobu.

Wyszukiwanie zasobów

Pliki zasobów są automatycznie rozpoznawane jako część procedury wyszukiwania. Jeśli nazwa pliku projektu różni się od głównej przestrzeni nazw projektu, nazwa zestawu może się różnić. Może to uniemożliwić pomyślne wyszukiwanie zasobów. Aby rozwiązać ten problem, użyj elementu , RootNamespaceAttribute aby podać wskazówkę dla usług lokalizacji. Po podaniu jest on używany podczas wyszukiwania zasobów.

Przykładowy projekt nosi nazwę example.csproj, który tworzy example.dll i example.exe — jednak Localization.Example używana jest przestrzeń nazw. assembly Zastosuj atrybut poziomu, aby poprawić tę niezgodność:

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

Rejestrowanie usług lokalizacji

Aby zarejestrować usługi lokalizacyjne, należy wywołać jedną z AddLocalization metod rozszerzenia podczas konfiguracji usług. Umożliwi to wstrzyknięcie zależności (DI) następujących typów:

Konfigurowanie opcji lokalizacji

Przeciążenie AddLocalization(IServiceCollection, Action<LocalizationOptions>) akceptuje setupAction parametr typu Action<LocalizationOptions>. Umożliwia to skonfigurowanie opcji lokalizacji.

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

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

// Omitted for brevity.

Pliki zasobów mogą działać w dowolnym miejscu w projekcie, ale istnieją typowe rozwiązania, które okazały się skuteczne. Częściej niż nie następuje ścieżka najmniejszego oporu. Poprzedni kod języka C#:

Spowoduje to, że usługi lokalizacyjne będą szukać w katalogu Resources dla plików zasobów.

Używanie i IStringLocalizer<T>IStringLocalizerFactory

Po zarejestrowaniu (i opcjonalnie skonfigurowanych) usługach lokalizacji można użyć następujących typów z di:

Aby utworzyć usługę komunikatów, która może zwracać zlokalizowane ciągi, rozważ następujące kwestie 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;
    }
}

W poprzednim kodzie języka C#:

  • IStringLocalizer<MessageService> localizer Zadeklarowane jest pole.
  • Podstawowy konstruktor definiuje IStringLocalizer<MessageService> parametr i przechwytuje go jako localizer argument.
  • Metoda GetGreetingMessage wywołuje IStringLocalizer.Item[String] przekazywanie "GreetingMessage" jako argument.

Obiekt IStringLocalizer obsługuje również sparametryzowane zasoby ciągów, należy wziąć pod uwagę następujące ParameterizedMessageServicekwestie:

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

W poprzednim kodzie języka C#:

  • IStringLocalizer _localizer Zadeklarowane jest pole.
  • Podstawowy konstruktor przyjmuje IStringLocalizerFactory parametr , który jest używany do utworzenia elementu IStringLocalizer na podstawie ParameterizedMessageService typu i przypisuje go do _localizer pola.
  • Metoda GetFormattedMessage wywołuje metodę IStringLocalizer.Item[String, Object[]]dateTime , przekazując "DinnerPriceFormat"obiekt i dinnerPrice jako argumenty.

Ważne

Element IStringLocalizerFactory nie jest wymagany. Zamiast tego preferowane jest korzystanie z usług, aby wymagać .IStringLocalizer<T>

Oba IStringLocalizer.Item[] indeksatory zwracają element LocalizedString, który ma niejawne konwersje na string?.

Zebranie wszystkich elementów

Aby uzyskać przykład aplikacji przy użyciu obu usług komunikatów, wraz z lokalizacjami i plikami zasobów, rozważ następujący plik Program.cs :

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

W poprzednim kodzie języka C#:

  • Zestawy RootNamespaceAttribute"Localization.Example" są ustawiane jako główna przestrzeń nazw.
  • Element Console.OutputEncoding jest przypisany do Encoding.Unicode.
  • Po przekazaniu pojedynczego argumentu do argselementu CultureInfo.CurrentCulture i CultureInfo.CurrentUICulture zostaną przypisane wyniki podanego CultureInfo.GetCultureInfo(String) elementu arg[0].
  • Zostanie Host utworzony z wartościami domyślnymi.
  • Usługi lokalizacyjne , MessageServicei ParameterizedMessageService są zarejestrowane w usłudze IServiceCollection di.
  • Aby usunąć szum, rejestrowanie jest skonfigurowane do ignorowania dowolnego poziomu dziennika niższego niż ostrzeżenie.
  • Element MessageService jest rozpoznawany z IServiceProvider wystąpienia, a jego wynikowy komunikat jest rejestrowany.
  • Element ParameterizedMessageService jest rozpoznawany z IServiceProvider wystąpienia, a wynikowy sformatowany komunikat jest rejestrowany.

Każda z *MessageService klas definiuje zestaw plików resx z jednym wpisem. Oto przykładowa zawartość MessageService plików zasobów rozpoczynająca się od 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>

Oto przykładowa zawartość ParameterizedMessageService plików zasobów, począwszy od parametrizedMessageService.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>

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

Napiwek

Wszystkie komentarze, schemat i <resheader> elementy pliku zasobów są celowo pomijane w celu zwięzłości.

Przykładowe uruchomienia

W poniższym przykładzie pokazano różne zlokalizowane dane wyjściowe, biorąc pod uwagę docelowe ustawienia regionalne.

Rozważ następujące kwestie "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 ¤.

W przypadku pominięcia argumentu do interfejsu wiersza polecenia platformy .NET w celu uruchomienia projektu używana jest domyślna kultura systemowa — w tym przypadku "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.

Podczas przekazywania "sr-Cryl-RS"polecenia zostaną znalezione odpowiednie pliki zasobów i zastosowana lokalizacja:

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

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

Przykładowa aplikacja nie udostępnia plików zasobów dla "fr-CA"programu , ale gdy jest wywoływana z tej kultury, używane są nielokalizowane pliki zasobów.

Ostrzeżenie

Ponieważ kultura jest znaleziona, ale poprawne pliki zasobów nie są, podczas formatowania jest stosowane, kończy się częściową lokalizacją:

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

Zobacz też