Lokalisering i .NET
Lokalisering är processen att översätta ett programs resurser till lokaliserade versioner för varje kultur som programmet stöder. Du bör gå vidare till lokaliseringssteget först när du har slutfört lokaliseringsgranskningssteget för att kontrollera att det globaliserade programmet är redo för lokalisering.
Ett program som är redo för lokalisering är uppdelat i två konceptuella block: ett block som innehåller alla användargränssnittselement och ett block som innehåller körbar kod. Användargränssnittsblocket innehåller endast lokaliserade användargränssnittselement som strängar, felmeddelanden, dialogrutor, menyer, inbäddade objektresurser och så vidare för den neutrala kulturen. Kodblocket innehåller endast den programkod som ska användas av alla kulturer som stöds. Den vanliga språkkörningen stöder en resursmodell för satellitsammansättning som separerar ett programs körbara kod från dess resurser. Mer information om hur du implementerar den här modellen finns i Resurser i .NET.
För varje lokaliserad version av ditt program lägger du till en ny satellitsammansättning som innehåller det lokaliserade användargränssnittsblocket översatt till lämpligt språk för målkulturen. Kodblocket för alla kulturer bör förbli detsamma. Kombinationen av en lokaliserad version av användargränssnittsblocket med kodblocket genererar en lokaliserad version av ditt program.
I den här artikeln får du lära dig hur du använder implementeringarna IStringLocalizer<T> och IStringLocalizerFactory . All exempelkällkod i den här artikeln förlitar sig på NuGet-paketen Microsoft.Extensions.Localization
och Microsoft.Extensions.Hosting
. Mer information om värdtjänster finns i .NET Generic Host.
Resursfiler
Den primära mekanismen för att isolera lokala strängar är med resursfiler. En resursfil är en XML-fil med filnamnstillägget .resx . Resursfiler översätts före körningen av det förbrukande programmet, med andra ord representerar de översatt innehåll i vila. Ett resursfilnamn innehåller vanligtvis en språkidentifierare och får följande formulär:
<FullTypeName><.Locale>.resx
Där:
<FullTypeName>
Representerar lokaliserbara resurser för en viss typ.- Det valfria
<.Locale>
representerar språkvarianten för resursfilens innehåll.
Ange nationella inställningar
Språkvarianten bör definiera språket, som minst, men det kan också definiera kulturen (regionalt språk) och till och med landet eller regionen. Dessa segment avgränsas ofta av -
tecknet. Med den extra specificiteten i en kultur tillämpas reglerna för "kulturåterställning" där bästa matchningar prioriteras. Språkvarianten bör mappas till en välkänd språktagg. Mer information finns i CultureInfo.Name.
Scenarier för kulturåterställning
Anta att din lokaliserade app stöder olika serbiska språk och har följande resursfiler för dess MessageService
:
Fil | Regionalt språk | Landskod |
---|---|---|
MessageService.sr-Cyrl-RS.resx | (Kyrillisk, Serbien) | RS |
MessageService.sr-Cyrl.resx | Kyrilliska | |
MessageService.sr-Latn-BA.resx | (Latin, Bosnien och Hercegovina) | BA |
MessageService.sr-Latn-ME.resx | (Latinsk, Montenegro) | ME |
MessageService.sr-Latn-RS.resx | (Latin, Serbien) | RS |
MessageService.sr-Latn.resx | Latin | |
MessageService.sr.resx | † latin | |
MessageService.resx |
† Standard regionalt språk för språket.
När din app körs med CultureInfo.CurrentCulture inställningen till en lokaliseringskultur "sr-Cyrl-RS"
försöker den matcha filer i följande ordning:
- MessageService.sr-Cyrl-RS.resx
- MessageService.sr-Cyrl.resx
- MessageService.sr.resx
- MessageService.resx
Men om din app kördes med CultureInfo.CurrentCulture uppsättningen till en kultur av "sr-Latn-BA"
lokalisering försöker lösa filer i följande ordning:
- MessageService.sr-Latn-BA.resx
- MessageService.sr-Latn.resx
- MessageService.sr.resx
- MessageService.resx
Regeln "kulturåterställning" ignorerar nationella inställningar när det inte finns några motsvarande matchningar, vilket innebär att resursfil nummer fyra väljs om den inte kan hitta en matchning. Om kulturen var inställd "fr-FR"
på skulle lokaliseringen hamna i filen MessageService.resx , vilket kan vara problematiskt. Mer information finns i Återställningsprocessen för resurser.
Resurssökning
Resursfiler löses automatiskt som en del av en sökningsrutin. Om projektfilens namn skiljer sig från projektets rotnamnområde kan sammansättningsnamnet skilja sig åt. Detta kan förhindra att resurssökningen annars lyckas. För att åtgärda det här matchningsfelet RootNamespaceAttribute använder du för att ge en ledtråd till lokaliseringstjänsterna. När den tillhandahålls används den under resurssökningen.
Exempelprojektet heter example.csproj, som skapar en example.dll och example.exe– men Localization.Example
namnområdet används. Använd ett assembly
nivåattribut för att korrigera det här matchningsfelet:
[assembly: RootNamespace("Localization.Example")]
Registrera lokaliseringstjänster
Om du vill registrera lokaliseringstjänster anropar du någon av tilläggsmetoderna AddLocalization under konfigurationen av tjänster. Detta aktiverar beroendeinmatning (DI) av följande typer:
- Microsoft.Extensions.Localization.IStringLocalizer<T>
- Microsoft.Extensions.Localization.IStringLocalizerFactory
Konfigurera lokaliseringsalternativ
Överlagringen AddLocalization(IServiceCollection, Action<LocalizationOptions>) accepterar en setupAction
parameter av typen Action<LocalizationOptions>
. På så sätt kan du konfigurera lokaliseringsalternativ.
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddLocalization(options =>
{
options.ResourcesPath = "Resources";
});
// Omitted for brevity.
Resursfiler kan finnas var som helst i ett projekt, men det finns vanliga metoder som har visat sig vara framgångsrika. Oftast följs vägen för minst motstånd. Föregående C#-kod:
- Skapar standardverktyget för värdappen.
- Anropar
AddLocalization
i tjänstsamlingen och LocalizationOptions.ResourcesPath anger som"Resources"
.
Detta skulle göra att lokaliseringstjänsterna söker efter resursfiler i katalogen Resurser .
Använda IStringLocalizer<T>
och IStringLocalizerFactory
När du har registrerat (och eventuellt konfigurerat) lokaliseringstjänsterna kan du använda följande typer med DI:
Om du vill skapa en meddelandetjänst som kan returnera lokaliserade strängar bör du tänka på följande 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;
}
}
I föregående C#-kod:
- Ett
IStringLocalizer<MessageService> localizer
fält deklareras. - Den primära konstruktorn definierar en
IStringLocalizer<MessageService>
parameter och avbildar den som ettlocalizer
argument. - Metoden
GetGreetingMessage
anropar överföringen IStringLocalizer.Item[String]"GreetingMessage"
som ett argument.
Har IStringLocalizer
även stöd för parametriserade strängresurser, tänk på följande 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;
}
}
I föregående C#-kod:
- Ett
IStringLocalizer _localizer
fält deklareras. - Den primära konstruktorn tar en
IStringLocalizerFactory
parameter som används för att skapa enIStringLocalizer
frånParameterizedMessageService
typen och tilldelar den till fältet_localizer
. - Metoden
GetFormattedMessage
anropar IStringLocalizer.Item[String, Object[]], skickar"DinnerPriceFormat"
, ettdateTime
objekt ochdinnerPrice
som argument.
Viktigt!
Det IStringLocalizerFactory
krävs inte. I stället är det bättre att använda tjänster för att kräva IStringLocalizer<T>.
Båda IStringLocalizer.Item[] indexerarna returnerar en LocalizedString, som har implicita konverteringar till string?
.
Färdigställa allt
Om du vill exemplifiera en app med både meddelandetjänster och lokaliserings- och resursfiler bör du överväga följande Program.cs fil:
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();
I föregående C#-kod:
- Anger RootNamespaceAttribute
"Localization.Example"
som rotnamnområdet. - Console.OutputEncoding är tilldelad till Encoding.Unicode.
- När ett enda argument skickas till
args
CultureInfo.CurrentCulture tilldelas och CultureInfo.CurrentUICulture resultatet av CultureInfo.GetCultureInfo(String) givetarg[0]
. - Host Skapas med standardvärden.
- Lokaliseringstjänsterna ,
MessageService
ochParameterizedMessageService
är registrerade iIServiceCollection
för DI. - Om du vill ta bort bruset är loggning konfigurerad för att ignorera alla loggnivåer som är lägre än en varning.
MessageService
Löses från instansenIServiceProvider
och dess resulterande meddelande loggas.ParameterizedMessageService
Löses från instansenIServiceProvider
och dess resulterande formaterade meddelande loggas.
Var och en av klasserna *MessageService
definierar en uppsättning .resx-filer , var och en med en enda post. Här är exempelinnehållet för resursfilerna MessageService
, som börjar med 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>
Här är exempelinnehållet för resursfilerna ParameterizedMessageService
, som börjar med 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>
Dricks
Alla XML-kommentarer, scheman och <resheader>
element i resursfilen utelämnas avsiktligt för korthet.
Exempelkörningar
Följande exempelkörningar visar de olika lokaliserade utdata, givet målspråk.
Överväg "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 ¤.
När du utelämnar ett argument till .NET CLI för att köra projektet används standardsystemkulturen – i det här fallet "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.
När du skickar "sr-Cryl-RS"
hittas rätt motsvarande resursfiler och lokaliseringen tillämpas:
dotnet run --project .\example\example.csproj sr-Cryl-RS
warn: Localization.Example[0]
Здраво пријатељи, ".NЕТ" девелопер заједница је узбуђена што вас види овде!
warn: Localization.Example[0]
У уторак, 03. август 2021. моја вечера је коштала 38 RSD.
Exempelprogrammet tillhandahåller inte resursfiler för "fr-CA"
, men när det anropas med den kulturen används de icke-lokaliserade resursfilerna.
Varning
Eftersom kulturen hittas men rätt resursfiler inte är det, får du delvis lokalisering när formateringen används:
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 $.