Partilhar via


Localização em .NET

Localização é o processo de traduzir os recursos de um aplicativo em versões localizadas para cada cultura que o aplicativo suportará. Você deve prosseguir para a etapa de localização somente depois de concluir a etapa de revisão de Localizabilidade para verificar se o aplicativo globalizado está pronto para localização.

Um aplicativo que está pronto para localização é separado em dois blocos conceituais: um bloco que contém todos os elementos da interface do usuário e um bloco que contém código executável. O bloco de interface do usuário contém apenas elementos de interface do usuário localizáveis, como cadeias de caracteres, mensagens de erro, caixas de diálogo, menus, recursos de objeto incorporado e assim por diante para a cultura neutra. O bloco de código contém apenas o código do aplicativo a ser usado por todas as culturas suportadas. O common language runtime suporta um modelo de recurso de assembly satélite que separa o código executável de um aplicativo de seus recursos. Para obter mais informações sobre como implementar esse modelo, consulte Recursos no .NET.

Para cada versão localizada do seu aplicativo, adicione um novo assembly satélite que contenha o bloco de interface do usuário localizado traduzido para o idioma apropriado para a cultura de destino. O bloco de código para todas as culturas deve permanecer o mesmo. A combinação de uma versão localizada do bloco de interface do usuário com o bloco de código produz uma versão localizada do seu aplicativo.

Neste artigo, você aprenderá como usar as IStringLocalizer<T> implementações e IStringLocalizerFactory . Todo o código-fonte de exemplo neste artigo depende dos Microsoft.Extensions.Localization pacotes e Microsoft.Extensions.Hosting NuGet. Para obter mais informações sobre hospedagem, consulte .NET Generic Host.

Ficheiros de recursos

O principal mecanismo para isolar cadeias de caracteres localizáveis é com arquivos de recursos. Um arquivo de recurso é um arquivo XML com a extensão de arquivo .resx . Os arquivos de recursos são traduzidos antes da execução do aplicativo consumidor, ou seja, representam o conteúdo traduzido em repouso. Um nome de arquivo de recurso geralmente contém um identificador de localidade e assume a seguinte forma:

<FullTypeName><.Locale>.resx

Em que:

  • O <FullTypeName> representa recursos localizáveis para um tipo específico.
  • O opcional <.Locale> representa a localidade do conteúdo do arquivo de recurso.

Especificando localidades

A localidade deve definir o idioma, no mínimo, mas também pode definir a cultura (idioma regional), e até mesmo o país ou região. Esses segmentos são comumente delimitados pelo - personagem. Com a especificidade adicional de uma cultura, as regras de "fallback cultural" são aplicadas onde as melhores correspondências são priorizadas. A localidade deve ser mapeada para uma marca de idioma conhecida. Para obter mais informações, veja CultureInfo.Name.

Cenários de fallback cultural

Imagine que seu aplicativo localizado suporta várias localidades sérvias e tem os seguintes arquivos de recursos para o seu MessageService:

Ficheiro Língua regional Indicativo de País
MessageService.sr-Cyrl-RS.resx (cirílico, Sérvia) RS
MessageService.sr-Cyrl.resx Cirílico
MessageService.sr-Latn-BA.resx (Latim, Bósnia e Herzegovina) BA
MessageService.sr-Latn-ME.resx (Latim, Montenegro) ME
MessageService.sr-Latn-RS.resx (latim, Sérvia) RS
MessageService.sr-Latn.resx Latim
MessageService.sr.resx † Latim
MessageService.resx

† O idioma regional padrão para o idioma.

Quando seu aplicativo está sendo executado com o conjunto para uma cultura de "sr-Cyrl-RS" localização, CultureInfo.CurrentCulture tenta resolver arquivos na seguinte ordem:

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

No entanto, se o seu aplicativo estava sendo executado com o CultureInfo.CurrentCulture conjunto para uma cultura de "sr-Latn-BA" localização, tenta resolver arquivos na seguinte ordem:

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

A regra "culture fallback" ignorará as localidades quando não houver correspondências correspondentes, o que significa que o arquivo de recurso número quatro será selecionado se não for possível encontrar uma correspondência. Se a cultura fosse definida como "fr-FR", a localização acabaria caindo para o arquivo MessageService.resx, o que pode ser problemático. Para obter mais informações, consulte O processo de fallback de recursos.

Pesquisa de recursos

Os arquivos de recursos são resolvidos automaticamente como parte de uma rotina de pesquisa. Se o nome do arquivo de projeto for diferente do namespace raiz do projeto, o nome do assembly poderá ser diferente. Isso pode impedir que a pesquisa de recursos seja bem-sucedida. Para resolver essa incompatibilidade, use o RootNamespaceAttribute para fornecer uma dica para os serviços de localização. Quando fornecido, ele é usado durante a pesquisa de recursos.

O projeto de exemplo é chamado example.csproj, que cria um example.dll e example.exe — no entanto, o Localization.Example namespace é usado. Aplique um assembly atributo level para corrigir essa incompatibilidade:

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

Registrar serviços de localização

Para registrar serviços de localização, chame um dos AddLocalization métodos de extensão durante a configuração dos serviços. Isso permitirá a injeção de dependência (DI) dos seguintes tipos:

Configurar opções de localização

A AddLocalization(IServiceCollection, Action<LocalizationOptions>) sobrecarga aceita um setupAction parâmetro do tipo Action<LocalizationOptions>. Isso permite que você configure opções de localização.

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

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

// Omitted for brevity.

Os arquivos de recursos podem viver em qualquer lugar em um projeto, mas há práticas comuns em vigor que provaram ser bem-sucedidas. Na maioria das vezes, segue-se o caminho de menor resistência. O código C# anterior:

Isso faria com que os serviços de localização procurassem arquivos de recursos no diretório Resources .

Utilização IStringLocalizer<T> e IStringLocalizerFactory

Depois de registrar (e, opcionalmente, configurar) os serviços de localização, você pode usar os seguintes tipos com DI:

Para criar um serviço de mensagem capaz de retornar cadeias de caracteres localizadas, considere o seguinte 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;
    }
}

No código C# anterior:

  • Um IStringLocalizer<MessageService> localizer campo é declarado.
  • O construtor primário define um IStringLocalizer<MessageService> parâmetro e o captura como um localizer argumento.
  • O GetGreetingMessage método invoca a IStringLocalizer.Item[String] passagem "GreetingMessage" como um argumento.

O IStringLocalizer também suporta recursos de cadeia de caracteres parametrizados, considere o seguinte 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;
    }
}

No código C# anterior:

  • Um IStringLocalizer _localizer campo é declarado.
  • O construtor primário pega um IStringLocalizerFactory parâmetro, que é usado para criar um IStringLocalizer a partir do ParameterizedMessageService tipo, e o atribui ao _localizer campo.
  • O GetFormattedMessage método invoca IStringLocalizer.Item[String, Object[]], passando "DinnerPriceFormat", um dateTime objeto e dinnerPrice como argumentos.

Importante

O IStringLocalizerFactory não é obrigatório. Em vez disso, é preferível que os serviços de consumo exijam o IStringLocalizer<T>.

Ambos os IStringLocalizer.Item[] indexadores retornam um LocalizedString, que tem conversões implícitas para string?.

Juntar tudo

Para exemplificar um aplicativo usando ambos os serviços de mensagem, juntamente com arquivos de localização e recursos, considere o seguinte arquivo 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();

No código C# anterior:

  • O RootNamespaceAttribute define "Localization.Example" como o namespace raiz.
  • O Console.OutputEncoding é atribuído a Encoding.Unicode.
  • Quando um único argumento é passado para args, o CultureInfo.CurrentCulture e CultureInfo.CurrentUICulture são atribuídos o resultado de CultureInfo.GetCultureInfo(String) dado o arg[0].
  • O Host é criado com padrões.
  • Os serviços de localização, MessageServicee ParameterizedMessageService estão registrados no IServiceCollection para DI.
  • Para remover o ruído, o registro em log é configurado para ignorar qualquer nível de log inferior a um aviso.
  • O MessageService é resolvido a partir da IServiceProvider instância e sua mensagem resultante é registrada.
  • O ParameterizedMessageService é resolvido a partir da IServiceProvider instância e sua mensagem formatada resultante é registrada.

Cada uma das *MessageService classes define um conjunto de arquivos .resx , cada um com uma única entrada. Aqui está o conteúdo de exemplo para os MessageService arquivos de recurso, começando com 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>

Aqui está o conteúdo de exemplo para os ParameterizedMessageService arquivos de recurso, começando com 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>

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

Gorjeta

Todos os comentários, esquema e <resheader> elementos XML do arquivo de recurso são intencionalmente omitidos para brevidade.

Exemplos de execuções

As execuções de exemplo a seguir mostram as várias saídas localizadas, dadas as localidades de destino.

Considere "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 ¤.

Ao omitir um argumento para a CLI do .NET para executar o projeto, a cultura do sistema padrão é usada — neste 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.

Ao passar "sr-Cryl-RS", os arquivos de recursos correspondentes corretos são encontrados e a localização aplicada:

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

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

O aplicativo de exemplo não fornece arquivos de recurso para "fr-CA"o , mas quando chamado com essa cultura, os arquivos de recurso não localizados são usados.

Aviso

Como a cultura é encontrada, mas os arquivos de recursos corretos não são, quando a formatação é aplicada, você acaba com a localização parcial:

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

Consulte também