本文内容
- 资源文件
- 注册本地化服务
- 使用 IStringLocalizer 和 IStringLocalizerFactory
- 将其放在一起
本地化是针对应用支持的每个区域性,将应用资源转换为本地化版本的过程。 只有在完成本地化评审步骤,以验证全球化应用是否做好本地化准备后,才应继续执行本地化步骤。
可以开始进行本地化的应用程序分为两个概念块:一个是包含所有用户界面元素的块,另一个是包含可执行代码的块。 用户界面块仅包含可本地化的用户界面元素,如字符串、错误消息、对话框、菜单、嵌入的对象资源等区域性中性元素。 代码块仅包含所有支持的区域性要使用的应用代码。 公共语言运行时支持附属程序集资源模型,用于将应用的可执行代码与资源分隔开来。
对于应用的每个本地化版本,请添加新的附属程序集,其中包含转换为目标区域性的相应语言的本地化用户界面块。 所有区域性的代码块应保持不变。 用户界面块的本地化版本和代码块组合生成了应用的本地化版本。
在本文中,你将了解如何使用”>IStringLocalizer和IStringLocalizerFactory实现。 本文中的所有示例源代码都依赖于Microsoft.Extensions.Localization和Microsoft.Extensions.HostingNuGet 包。
1、资源文件
隔离可本地化字符串的主要机制是使用资源文件。 资源文件是具有 .resx 文件扩展名的 XML 文件。 资源文件在执行使用应用程序之前被转换,换句话说,它们表示静态的已转换内容。 资源文件名通常包含区域设置标识符,并采用以下格式:
.resx
其中:
表示特定类型的可本地化资源。
- 可选
表示资源文件内容的区域设置。
1.1 指定区域设置
区域设置至少应该定义语言,但也可以定义区域性(区域语言),甚至是国家或地区。 这些段通常由-
字符分隔。 通过添加区域性特异性,在为最佳匹配项设置优先级的地方应用“区域性回退”规则。 区域设置应该映射到已知语言标记。
1.2 区域性回退场景
假设你的本地化应用支持不同的塞尔维亚区域设置,并且其MessageService
具有以下资源文件:
文件 | 区域语言 | 国家/地区代码 |
---|---|---|
MessageService.sr-Cyrl-RS.resx | (西里尔语,塞尔维亚) | RS |
MessageService.sr-Cyrl.resx | 西里尔语 | |
MessageService.sr-Latn-BA.resx | (拉丁语,波斯尼亚和黑塞哥维那) | BA |
MessageService.sr-Latn-ME.resx | (拉丁语,黑山共和国) | ME |
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 文件,这会造成问题。
1.3 资源查找
资源文件会在查找例程中自动解析。 如果项目文件名不同于项目的根命名空间,则程序集名称可能不同。 这可能会阻止资源查找成功。 要解决这种不匹配问题,请使用RootNamespaceAttribute向本地化服务提供提示。 提供以后,它将在资源查找期间使用。
示例项目名为 example.csproj,它会创建 example.dll 和 example.exe ,但是会使用Localization.Example
命名空间。 应用assembly
级别属性来更正这种不匹配问题:
[assembly: RootNamespace("Localization.Example")]
2、注册本地化服务
要注册本地化服务,请在服务配置期间调用其中一个AddLocalization扩展方法。 这将启用以下类型的依赖关系注入 (DI):
- “>Microsoft.Extensions.Localization.IStringLocalizer
- Microsoft.Extensions.Localization.IStringLocalizerFactory
2.1 配置本地化选项
)”>AddLocalization(IServiceCollection, Action)重载接受类型为Action
的setupAction
参数。 这使你可以配置本地化选项。
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);builder.Services.AddLocalization(options =>{options.ResourcesPath = "Resources";});// Omitted for brevity.
资源文件可以存在于项目中的任何位置,但是有些常见做法已经被证明是成功的。 通常情况下,会选择阻力最小的路径。 上述 C# 代码:
- 创建默认主机应用生成器。
- 对服务集合调用
AddLocalization
,并将LocalizationOptions.ResourcesPath指定为"Resources"
。
这会使本地化服务在“Resources”目录中查找资源文件。
3、使用IStringLocalizer
和IStringLocalizerFactory
在注册(并选择性配置)本地化服务后,可以将以下类型和 DI 结合使用:
- “>IStringLocalizer
- IStringLocalizerFactory
要创建能够返回本地化字符串的消息服务,请考虑使用以下MessageService
:
using System.Diagnostics.CodeAnalysis;using Microsoft.Extensions.Localization;namespace Localization.Example;public sealed class MessageService{private readonly IStringLocalizer _localizer = null!;public MessageService(IStringLocalizer localizer) =>_localizer = localizer;[return: NotNullIfNotNull(nameof(_localizer))]public string? GetGreetingMessage(){LocalizedString localizedString = _localizer["GreetingMessage"];return localizedString;}}
在前述 C# 代码中:
- 声明
IStringLocalizer _localizer
字段。 - 构造函数采用
IStringLocalizer
参数并将其分配给_localizer
字段。 GetGreetingMessage
方法调用IStringLocalizer.Item[String],将"GreetingMessage"
作为参数传递。
IStringLocalizer
还支持参数化字符串资源,请考虑使用以下ParameterizedMessageService
:
using System.Diagnostics.CodeAnalysis;using Microsoft.Extensions.Localization;namespace Localization.Example;public class ParameterizedMessageService{private readonly IStringLocalizer _localizer = null!;public ParameterizedMessageService(IStringLocalizerFactory factory) =>_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
参数,该参数用于从ParameterizedMessageService
类型创建IStringLocalizer
,并将其分配给_localizer
字段。 GetFormattedMessage
方法调用IStringLocalizer.Item[String, Object[]],将"DinnerPriceFormat"
(一种dateTime
对象)和dinnerPrice
作为参数传递。
重要
IStringLocalizerFactory
不是必需的。 而使用服务最好要求使用”>IStringLocalizer。
两个IStringLocalizer.Item[]索引器都返回LocalizedString,它们具有向string?
的隐式转换。
4、将其放在一起
若要举例说明使用两种消息服务以及本地化和资源文件的应用,请考虑使用以下 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 { Length: 1 }){CultureInfo.CurrentCulture =CultureInfo.CurrentUICulture =CultureInfo.GetCultureInfo(args[0]);}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分配有arg[0]
给定的CultureInfo.GetCultureInfo(String)的结果。 - Host使用默认值创建。
- 本地化服务
MessageService
和ParameterizedMessageService
注册到 DI 的IServiceCollection
。 - 为了消除干扰,日志记录被配置为忽略低于警告的任何日志级别。
MessageService
是从IServiceProvider
实例解析的,其生成的消息被记录。ParameterizedMessageService
是从IServiceProvider
实例解析的,其生成的已设置格式的消息被记录。
每个*MessageService
类都定义一组 .resx 文件,其中每个文件都有一个条目。 下面是MessageService
资源文件的示例内容,从 MessageService.resx 开始:
Hi friends, the ".NET" developer community is excited to see you here!
MessageService.sr-Cyrl-RS.resx:
Здраво пријатељи, ".NЕТ" девелопер заједница је узбуђена што вас види овде!
MessageService.sr-Latn.resx:
Zdravo prijatelji, ".NET" developer zajednica je uzbuđena što vas vidi ovde!
下面是ParameterizedMessageService
资源文件的示例内容,从 ParameterizedMessageService.resx 开始:
On {0:D} my dinner cost {1:C}.
ParameterizedMessageService.sr-Cyrl-RS.resx:
У {0:D} моја вечера је коштала {1:C}.
ParameterizedMessageService.sr-Latn.resx:
U {0:D} moja večera je koštala {1:C}.
提示
为简洁起见,有意省略所有资源文件 XML 注释、架构和元素。
4.1 示例运行
以下示例运行显示给定目标区域设置的各种本地化输出。
以"sr-Latn"
为例:
dotnet run --project .\example\example.csproj sr-Latnwarn: 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.csprojwarn: 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-RSwarn: Localization.Example[0]Здраво пријатељи, ".NЕТ" девелопер заједница је узбуђена што вас види овде!warn: Localization.Example[0]У уторак, 03. август 2021. моја вечера је коштала 38 RSD.
示例应用程序不提供"fr-CA"
的资源文件,但在使用该区域性调用时,将使用非本地化的资源文件。
警告
由于已找到区域性,但未找到正确的资源文件,因此应用格式设置时,最终会实现部分本地化:
dotnet run --project .\example\example.csproj fr-CAwarn: 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 $.