حدث
١٧ رمضان، ٩ م - ٢١ رمضان، ١٠ ص
انضم إلى سلسلة الاجتماعات لإنشاء حلول الذكاء الاصطناعي قابلة للتطوير استنادا إلى حالات الاستخدام في العالم الحقيقي مع المطورين والخبراء الآخرين.
تسجيل الآنلم يعد هذا المتصفح مدعومًا.
بادر بالترقية إلى Microsoft Edge للاستفادة من أحدث الميزات والتحديثات الأمنية والدعم الفني.
Localization is the process of translating an application's resources into localized versions for each culture that the application will support. You should proceed to the localization step only after completing the Localizability review step to verify that the globalized application is ready for localization.
An application that is ready for localization is separated into two conceptual blocks: a block that contains all user interface elements and a block that contains executable code. The user interface block contains only localizable user-interface elements such as strings, error messages, dialog boxes, menus, embedded object resources, and so on for the neutral culture. The code block contains only the application code to be used by all supported cultures. The common language runtime supports a satellite assembly resource model that separates an application's executable code from its resources. For more information about implementing this model, see Resources in .NET.
For each localized version of your application, add a new satellite assembly that contains the localized user interface block translated into the appropriate language for the target culture. The code block for all cultures should remain the same. The combination of a localized version of the user interface block with the code block produces a localized version of your application.
In this article, you will learn how to use the IStringLocalizer<T> and IStringLocalizerFactory implementations. All of the example source code in this article relies on the Microsoft.Extensions.Localization
and Microsoft.Extensions.Hosting
NuGet packages. For more information on hosting, see .NET Generic Host.
The primary mechanism for isolating localizable strings is with resource files. A resource file is an XML file with the .resx file extension. Resource files are translated prior to the execution of the consuming application—in other words, they represent translated content at rest. A resource file name most commonly contains a locale identifier, and takes on the following form:
<FullTypeName><.Locale>.resx
Where:
<FullTypeName>
represents localizable resources for a specific type.<.Locale>
represents the locale of the resource file contents.The locale should define the language, at a bare minimum, but it can also define the culture (regional language), and even the country or region. These segments are commonly delimited by the -
character. With the added specificity of a culture, the "culture fallback" rules are applied where best matches are prioritized. The locale should map to a well-known language tag. For more information, see CultureInfo.Name.
Imagine that your localized app supports various Serbian locales, and has the following resource files for its MessageService
:
File | Regional language | Country Code |
---|---|---|
MessageService.sr-Cyrl-RS.resx | (Cyrillic, Serbia) | RS |
MessageService.sr-Cyrl.resx | Cyrillic | |
MessageService.sr-Latn-BA.resx | (Latin, Bosnia & Herzegovina) | BA |
MessageService.sr-Latn-ME.resx | (Latin, Montenegro) | ME |
MessageService.sr-Latn-RS.resx | (Latin, Serbia) | RS |
MessageService.sr-Latn.resx | Latin | |
MessageService.sr.resx | † Latin | |
MessageService.resx |
† The default regional language for the language.
When your app is running with the CultureInfo.CurrentCulture set to a culture of "sr-Cyrl-RS"
localization attempts to resolve files in the following order:
However, if your app was running with the CultureInfo.CurrentCulture set to a culture of "sr-Latn-BA"
localization attempts to resolve files in the following order:
The "culture fallback" rule will ignore locales when there are no corresponding matches, meaning resource file number four is selected if it's unable to find a match. If the culture was set to "fr-FR"
, localization would end up falling to the MessageService.resx file which can be problematic. For more information, see The resource fallback process.
Resource files are automatically resolved as part of a lookup routine. If your project file name is different than the root namespace of your project, the assembly name might differ. This can prevent resource lookup from being otherwise successful. To address this mismatch, use the RootNamespaceAttribute to provide a hint to the localization services. When provided, it is used during resource lookup.
The example project is named example.csproj, which creates an example.dll and example.exe—however, the Localization.Example
namespace is used. Apply an assembly
level attribute to correct this mismatch:
[assembly: RootNamespace("Localization.Example")]
To register localization services, call one of the AddLocalization extension methods during the configuration of services. This will enable dependency injection (DI) of the following types:
The AddLocalization(IServiceCollection, Action<LocalizationOptions>) overload accepts a setupAction
parameter of type Action<LocalizationOptions>
. This allows you to configure localization options.
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddLocalization(options =>
{
options.ResourcesPath = "Resources";
});
// Omitted for brevity.
Resource files can live anywhere in a project, but there are common practices in place that have proven to be successful. More often than not, the path of least resistance is followed. The preceding C# code:
AddLocalization
on the service collection, specifying LocalizationOptions.ResourcesPath as "Resources"
.This would cause the localization services to look in the Resources directory for resource files.
After you've registered (and optionally configured) the localization services, you can use the following types with DI:
To create a message service that is capable of returning localized strings, consider the following 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;
}
}
In the preceding C# code:
IStringLocalizer<MessageService> localizer
field is declared.IStringLocalizer<MessageService>
parameter and captures it as a localizer
argument.GetGreetingMessage
method invokes the IStringLocalizer.Item[String] passing "GreetingMessage"
as an argument.The IStringLocalizer
also supports parameterized string resources, consider the following 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;
}
}
In the preceding C# code:
IStringLocalizer _localizer
field is declared.IStringLocalizerFactory
parameter, which is used to create an IStringLocalizer
from the ParameterizedMessageService
type, and assigns it to the _localizer
field.GetFormattedMessage
method invokes IStringLocalizer.Item[String, Object[]], passing "DinnerPriceFormat"
, a dateTime
object, and dinnerPrice
as arguments.هام
The IStringLocalizerFactory
isn't required. Instead, it is preferred for consuming services to require the IStringLocalizer<T>.
Both IStringLocalizer.Item[] indexers return a LocalizedString, which have implicit conversions to string?
.
To exemplify an app using both message services, along with localization and resource files, consider the following Program.cs file:
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();
In the preceding C# code:
"Localization.Example"
as the root namespace.args
, the CultureInfo.CurrentCulture and CultureInfo.CurrentUICulture are assigned the result of CultureInfo.GetCultureInfo(String) given the arg[0]
.MessageService
, and ParameterizedMessageService
are registered to the IServiceCollection
for DI.MessageService
is resolved from the IServiceProvider
instance and its resulting message is logged.ParameterizedMessageService
is resolved from the IServiceProvider
instance and its resulting formatted message is logged.Each of the *MessageService
classes defines a set of .resx files, each with a single entry. Here is the example content for the MessageService
resource files, starting with 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>
Here is the example content for the ParameterizedMessageService
resource files, starting with 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>
تلميح
All of the resource file XML comments, schema, and <resheader>
elements are intentionally omitted for brevity.
The following example runs show the various localized outputs, given targeted locales.
Consider "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 ¤.
When omitting an argument to the .NET CLI to run the project, the default system culture is used—in this case "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.
When passing "sr-Cryl-RS"
, the correct corresponding resource files are found and the localization applied:
dotnet run --project .\example\example.csproj sr-Cryl-RS
warn: Localization.Example[0]
Здраво пријатељи, ".NЕТ" девелопер заједница је узбуђена што вас види овде!
warn: Localization.Example[0]
У уторак, 03. август 2021. моја вечера је коштала 38 RSD.
The sample application does not provide resource files for "fr-CA"
, but when called with that culture, the non-localized resource files are used.
تحذير
Since the culture is found but the correct resource files are not, when formatting is applied you end up with partial localization:
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 $.
ملاحظات .NET
.NET هو مشروع مصدر مفتوح. حدد رابطًا لتقديم الملاحظات:
حدث
١٧ رمضان، ٩ م - ٢١ رمضان، ١٠ ص
انضم إلى سلسلة الاجتماعات لإنشاء حلول الذكاء الاصطناعي قابلة للتطوير استنادا إلى حالات الاستخدام في العالم الحقيقي مع المطورين والخبراء الآخرين.
تسجيل الآنالتدريب
الوحدة النمطية
تصميم صفحات .NET MAUI XAML متسقة باستخدام الموارد المشتركة - Training
تعرف على كيفية استخدام الموارد المشتركة الثابتة والديناميكية لإنشاء واجهة مستخدم .NET Multi-platform App UI (MAUI). وشاهد كيف يمكن للأنماط أن تجعل واجهة المستخدم متسقة ويمكن الوصول إليها.
الوثائق
Globalize and localize .NET applications - .NET
Learn how to develop a world-ready application. Read about globalization, localizability review, and localization in .NET.
Learn more about: Globalization
Provide localized resources for languages and cultures in an ASP.NET Core app
Learn how to provide localized resources for localizing content of an ASP.NET Core app into different languages and cultures.