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:
- MessageService.sr-Cyrl-RS.resx
- MessageService.sr-Cyrl.resx
- MessageService.sr.resx
- 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:
- MessageService.sr-Latn-BA.resx
- MessageService.sr-Latn.resx
- MessageService.sr.resx
- 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:
- Microsoft.Extensions.Localization.IStringLocalizer<T>
- Microsoft.Extensions.Localization.IStringLocalizerFactory
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:
- Cria o construtor de aplicativos host padrão.
- Chama
AddLocalization
a coleção de serviços, especificando LocalizationOptions.ResourcesPath como"Resources"
.
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 umlocalizer
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 umIStringLocalizer
a partir doParameterizedMessageService
tipo, e o atribui ao_localizer
campo. - O
GetFormattedMessage
método invoca IStringLocalizer.Item[String, Object[]], passando"DinnerPriceFormat"
, umdateTime
objeto edinnerPrice
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 oarg[0]
. - O Host é criado com padrões.
- Os serviços de localização,
MessageService
eParameterizedMessageService
estão registrados noIServiceCollection
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 daIServiceProvider
instância e sua mensagem resultante é registrada. - O
ParameterizedMessageService
é resolvido a partir daIServiceProvider
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 $.