本地化是将应用程序的资源转换为应用程序支持的每个区域性的本地化版本的过程。 只有在完成 本地化评审 步骤后,才应继续执行本地化步骤,以验证全球化应用程序是否已准备好进行本地化。
准备好进行本地化的应用程序分为两个概念块:一个块,其中包含所有用户界面元素和一个包含可执行代码的块。 用户界面块仅包含可本地化的用户界面元素,如字符串、错误消息、对话框、菜单、嵌入的对象资源等区域性中性元素。 代码块仅包含所有受支持文化使用的应用程序代码。 公共语言运行时支持附属程序集资源模型,该模型将应用程序的可执行代码与其资源分开。 有关实现此模型的详细信息,请参阅 .NET 中的资源。
对于应用程序的每个本地化版本,请添加一个新的卫星程序集,其中包含本地化的用户界面模块,翻译成适合目标文化的语言。 所有文化的代码块应保持不变。 用户界面块的本地化版本与代码块的组合将生成应用程序的本地化版本。
本文介绍如何使用 IStringLocalizer
资源文件
用于隔离可本地化字符串的主要机制是资源 文件。 资源文件是扩展名 为 .resx 的 XML 文件。 资源文件在执行使用应用程序之前被转换;换句话说,它们表示静态的已转换内容。 资源文件名最常包含区域设置标识符,并采用以下格式:
地点:
此
可选 <.Locale> 表示资源文件内容的区域设置。
指定区域设置
区域设置至少应该定义语言,但也可以定义区域性(区域语言),甚至是国家或地区。 这些段通常由 - 字符分隔。 通过添加区域性特异性,在为最佳匹配项设置优先级的地方应用“区域性回退”规则。 区域设置应该映射到已知语言标记。 有关详细信息,请参阅 CultureInfo.Name。
区域性回退场景
假设你的本地化应用支持不同的塞尔维亚区域设置,并且其 MessageService 具有以下资源文件:
文件
区域语言
国家/地区代码
MessageService.sr-Cyrl-RS.resx
(塞尔维亚西里尔文)
RS
MessageService.sr-Cyrl.resx
西里尔文
MessageService.sr-Latn-BA.resx
(拉丁语、波斯尼亚-黑塞哥维那)
BA
MessageService.sr-Latn-ME.resx
(拉丁语、黑山)
我
MessageService.sr-Latn-RS.resx
(拉丁语、塞尔维亚)
RS
MessageService.sr-Latn.resx
拉丁语
MessageService.sr.resx
† 拉丁语
MessageService.resx
† 语言的默认区域语言。
当应用运行时,将 CultureInfo.CurrentCulture 设置为 "sr-Cyrl-RS" 本地化的区域性,尝试按以下顺序解析文件:
MessageService.sr-Cyrl-RS.resx
MessageService.sr-Cyrl.resx
MessageService.sr.resx
MessageService.resx
但是,如果应用运行时,将 CultureInfo.CurrentCulture 设置为 "sr-Latn-BA" 本地化的区域性,则尝试按以下顺序解析文件:
MessageService.sr-Latn-BA.resx
MessageService.sr-Latn.resx
MessageService.sr.resx
MessageService.resx
如果没有相应的匹配项,“区域性回退”规则将忽略区域设置,这意味着如果找不到匹配项,则选择资源文件编号 4。 如果区域性设置为 "fr-FR",本地化最终会下降到 MessageService.resx 文件,这可能会造成问题。 有关详细信息,请参阅 资源回退过程。
资源查找
资源文件在查找例程中被自动解析。 如果项目文件名不同于项目的根命名空间,则程序集名称可能有所不同。 这可能会阻止资源查找成功。 若要解决这种不匹配问题,请使用 RootNamespaceAttribute 向本地化服务提供提示。 提供后,它将在资源查找期间使用。
示例项目命名为 example.csproj,用于创建 example.dll 和 example.exe,并且使用 Localization.Example 命名空间。 应用 assembly 级别属性来更正这种不匹配问题:
[assembly: RootNamespace("Localization.Example")]
注册本地化服务
若要注册本地化服务,在配置服务期间调用其中 AddLocalization 一种扩展方法。 这将启用以下类型的依赖项注入(DI):
Microsoft.Extensions.Localization.IStringLocalizer
Microsoft.Extensions.Localization.IStringLocalizerFactory
配置本地化选项
AddLocalization(IServiceCollection, Action
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddLocalization(options =>
{
options.ResourcesPath = "Resources";
});
// Omitted for brevity.
资源文件可以位于项目中的任意位置,但有一些已证明是成功的常见做法。 往往最低阻力的路径被选择。 上述 C# 代码:
创建默认主机应用生成器。
在服务集合上调用AddLocalization ,将LocalizationOptions.ResourcesPath指定为"Resources".
这将导致本地化服务在 Resources 目录中查找资源文件。
ResourcesPath 和共享资源类
配置 ResourcesPath 后,IStringLocalizerFactory.Create(Type) 通过计算类型的相对名称(相对于根命名空间)并在资源文件路径前添加 ResourcesPath 来解析资源文件路径。 这意味着共享资源类相对于项目根命名空间的位置很重要。
例如,请考虑用于跨多个组件合并字符串的共享资源类。 如果ResourcesPath = "Resources"已设置,并且共享资源类SharedResource位于命名空间MyApp.Resources的Resources文件夹中(其完全限定的类型名称为MyApp.Resources.SharedResource),那么工厂会将基名称解析为Resources.SharedResource(相对于根命名空间MyApp),然后前缀添加ResourcesPath,从而生成一个路径:Resources/Resources/SharedResource.resx,这与实际的文件位置不符。
有两个选项可以避免这种情况:
将共享资源类保留在根命名空间中。 放置在 SharedResource.cs 项目根目录(命名空间 MyApp),而不是位于 Resources 文件夹中。 然后,工厂将基名称解析为 SharedResource,通过 ResourcesPath = "Resources",它正确查找 Resources/SharedResource.resx。
使用 factory.Create(string baseName, string location)。 明确提供基础名称和程序集名称,以直接控制资源文件的解析。
var assemblyName = typeof(SharedResource).Assembly.GetName().Name;
IStringLocalizer localizer = factory.Create("SharedResource", assemblyName);
无论SharedResource.cs在项目的哪个位置,此方法都有效。
注释
当与内部使用IStringLocalizerFactory的框架集成时,同样需要考虑这一点。 例如,ASP.NET Core 将此 AddDataAnnotationsLocalization 工厂与共享资源类一起使用。 如果 ResourcesPath 已配置,请使用 factory.Create(string, string) 重载。 或者,请确保共享资源类位于根命名空间中。
使用 IStringLocalizer
完成注册(并根据需要配置)本地化服务后,您可以将以下类型与依赖注入(DI)配合使用:
IStringLocalizer
IStringLocalizerFactory
若要创建能够返回本地化字符串的消息服务,请考虑以下事项 MessageService:
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Localization;
namespace Localization.Example;
public sealed class MessageService(IStringLocalizer
{
[return: NotNullIfNotNull(nameof(localizer))]
public string? GetGreetingMessage()
{
LocalizedString localizedString = localizer["GreetingMessage"];
return localizedString;
}
}
在前述 C# 代码中:
声明 IStringLocalizer
主构造函数定义参数 IStringLocalizer
使用GetGreetingMessage 方法调用IStringLocalizer.Item[String],并将"GreetingMessage"作为参数传递。
IStringLocalizer 还支持参数化字符串资源,请考虑使用以下 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;
}
}
在前述 C# 代码中:
声明 IStringLocalizer _localizer 字段。
主构造函数采用参数IStringLocalizerFactory,该参数用于从IStringLocalizer类型创建ParameterizedMessageService,并将其分配给_localizer字段。
该方法 GetFormattedMessage 调用 IStringLocalizer.Item[String, Object[]]、传递 "DinnerPriceFormat"、 dateTime 对象和 dinnerPrice 作为参数。
重要
IStringLocalizerFactory不需要。 而使用服务最好要求使用 IStringLocalizer
两个 IStringLocalizer.Item[] 索引器都返回 LocalizedString,它们具有向 的string?。
把所有的东西放在一起
若要使用消息服务以及本地化和资源文件来演示应用,请考虑以下 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
builder.Services.AddTransient
builder.Logging.SetMinimumLevel(LogLevel.Warning);
using IHost host = builder.Build();
IServiceProvider services = host.Services;
ILogger logger =
services.GetRequiredService
.CreateLogger("Localization.Example");
MessageService messageService =
services.GetRequiredService
logger.LogWarning(
"{Msg}",
messageService.GetGreetingMessage());
ParameterizedMessageService parameterizedMessageService =
services.GetRequiredService
logger.LogWarning(
"{Msg}",
parameterizedMessageService.GetFormattedMessage(
DateTime.Today.AddDays(-3), 37.63));
await host.RunAsync();
在前述 C# 代码中:
RootNamespaceAttribute 将 "Localization.Example" 设置为根命名空间。
Console.OutputEncoding 被分配给 Encoding.Unicode。
单个参数传递给 args 时,CultureInfo.CurrentCulture 和 CultureInfo.CurrentUICulture 分配有 CultureInfo.GetCultureInfo(String)给定的 arg[0] 的结果。
Host是使用默认值创建的。
本地化服务 MessageService 和 ParameterizedMessageService 注册到 DI 的 IServiceCollection。
若要消除干扰,日志记录配置为忽略任何低于警告的日志级别。
从 MessageService 实例解析 IServiceProvider ,并记录其生成的消息。
从 ParameterizedMessageService 实例解析 IServiceProvider ,并记录其生成的格式化消息。
*MessageService每个类定义一组 .resx 文件,每个文件都有一个条目。 下面是从 MessageService 开始的资源文件的示例内容:
MessageService.sr-Cyrl-RS.resx:
MessageService.sr-Latn.resx:
下面是资源文件的示例内容 ParameterizedMessageService ,从 ParameterizedMessageService.resx 开始:
ParameterizedMessageService.sr-Cyrl-RS.resx:
ParameterizedMessageService.sr-Latn.resx:
小窍门
为了简洁起见,有意省略所有资源文件 XML 注释、架构和
示例运行
以下示例运行显示了给定目标区域设置的各种本地化输出。
以 "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 ¤.
当省略运行项目的 .NET CLI 参数时,使用默认系统区域性,在本例中为 "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.
传递 "sr-Cryl-RS"时,找到正确的相应资源文件并应用本地化:
dotnet run --project .\example\example.csproj sr-Cryl-RS
warn: Localization.Example[0]
Здраво пријатељи, ".NЕТ" девелопер заједница је узбуђена што вас види овде!
warn: Localization.Example[0]
У уторак, 03. август 2021. моја вечера је коштала 38 RSD.
示例应用程序不提供 "fr-CA" 的资源文件,但在使用该区域性调用时,将使用非本地化的资源文件。
警告
由于已找到区域性,但未找到正确的资源文件,因此应用格式设置时,最终会实现部分本地化:
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 应用中打包和部署资源
Microsoft.Extensions.Localization
.NET 中的依赖关系注入
.NET 中的日志记录
ASP.NET 核心本地化