• 【微软技术栈】C#.NET 中的本地化


    本文内容

    1. 资源文件
    2. 注册本地化服务
    3. 使用 IStringLocalizer 和 IStringLocalizerFactory
    4. 将其放在一起

    本地化是针对应用支持的每个区域性,将应用资源转换为本地化版本的过程。 只有在完成本地化评审步骤,以验证全球化应用是否做好本地化准备后,才应继续执行本地化步骤。

    可以开始进行本地化的应用程序分为两个概念块:一个是包含所有用户界面元素的块,另一个是包含可执行代码的块。 用户界面块仅包含可本地化的用户界面元素,如字符串、错误消息、对话框、菜单、嵌入的对象资源等区域性中性元素。 代码块仅包含所有支持的区域性要使用的应用代码。 公共语言运行时支持附属程序集资源模型,用于将应用的可执行代码与资源分隔开来。 

    对于应用的每个本地化版本,请添加新的附属程序集,其中包含转换为目标区域性的相应语言的本地化用户界面块。 所有区域性的代码块应保持不变。 用户界面块的本地化版本和代码块组合生成了应用的本地化版本。

    在本文中,你将了解如何使用 IStringLocalizer 和 IStringLocalizerFactory 实现。 本文中的所有示例源代码都依赖于 Microsoft.Extensions.Localization 和 Microsoft.Extensions.Hosting NuGet 包。 

    1、资源文件

    隔离可本地化字符串的主要机制是使用资源文件。 资源文件是具有 .resx 文件扩展名的 XML 文件。 资源文件在执行使用应用程序之前被转换,换句话说,它们表示静态的已转换内容。 资源文件名通常包含区域设置标识符,并采用以下格式:

    <.Locale>.resx

    其中:

    •  表示特定类型的可本地化资源。
    • 可选 <.Locale> 表示资源文件内容的区域设置。

    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" 本地化的区域性,尝试按以下顺序解析文件:

    1. MessageService.sr-Cyrl-RS.resx
    2. MessageService.sr-Cyrl.resx
    3. MessageService.sr.resx
    4. MessageService.resx

    但是,如果应用运行时,将 CultureInfo.CurrentCulture 设置为 "sr-Latn-BA" 本地化的区域性,则尝试按以下顺序解析文件:

    1. MessageService.sr-Latn-BA.resx
    2. MessageService.sr-Latn.resx
    3. MessageService.sr.resx
    4. 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):

    2.1 配置本地化选项

    AddLocalization(IServiceCollection, Action) 重载接受类型为 Action 的 setupAction 参数。 这使你可以配置本地化选项。

    1. HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
    2. builder.Services.AddLocalization(options =>
    3. {
    4. options.ResourcesPath = "Resources";
    5. });
    6. // Omitted for brevity.

    资源文件可以存在于项目中的任何位置,但是有些常见做法已经被证明是成功的。 通常情况下,会选择阻力最小的路径。 上述 C# 代码:

    这会使本地化服务在“Resources”目录中查找资源文件。

    3、使用 IStringLocalizer 和 IStringLocalizerFactory

    注册(并选择性配置)本地化服务后,可以将以下类型和 DI 结合使用:

    要创建能够返回本地化字符串的消息服务,请考虑使用以下 MessageService

    1. using System.Diagnostics.CodeAnalysis;
    2. using Microsoft.Extensions.Localization;
    3. namespace Localization.Example;
    4. public sealed class MessageService
    5. {
    6. private readonly IStringLocalizer<MessageService> _localizer = null!;
    7. public MessageService(IStringLocalizer<MessageService> localizer) =>
    8. _localizer = localizer;
    9. [return: NotNullIfNotNull(nameof(_localizer))]
    10. public string? GetGreetingMessage()
    11. {
    12. LocalizedString localizedString = _localizer["GreetingMessage"];
    13. return localizedString;
    14. }
    15. }

    在前述 C# 代码中:

    • 声明 IStringLocalizer _localizer 字段。
    • 构造函数采用 IStringLocalizer 参数并将其分配给 _localizer 字段。
    • GetGreetingMessage 方法调用 IStringLocalizer.Item[String],将 "GreetingMessage" 作为参数传递。

    IStringLocalizer 还支持参数化字符串资源,请考虑使用以下 ParameterizedMessageService

    1. using System.Diagnostics.CodeAnalysis;
    2. using Microsoft.Extensions.Localization;
    3. namespace Localization.Example;
    4. public class ParameterizedMessageService
    5. {
    6. private readonly IStringLocalizer _localizer = null!;
    7. public ParameterizedMessageService(IStringLocalizerFactory factory) =>
    8. _localizer = factory.Create(typeof(ParameterizedMessageService));
    9. [return: NotNullIfNotNull(nameof(_localizer))]
    10. public string? GetFormattedMessage(DateTime dateTime, double dinnerPrice)
    11. {
    12. LocalizedString localizedString = _localizer["DinnerPriceFormat", dateTime, dinnerPrice];
    13. return localizedString;
    14. }
    15. }

    在前述 C# 代码中:

    • 声明 IStringLocalizer _localizer 字段。
    • 构造函数采用 IStringLocalizerFactory 参数,该参数用于从 ParameterizedMessageService 类型创建 IStringLocalizer,并将其分配给 _localizer 字段。
    • GetFormattedMessage 方法调用 IStringLocalizer.Item[String, Object[]],将 "DinnerPriceFormat"(一种 dateTime 对象)和 dinnerPrice 作为参数传递。

     重要

    IStringLocalizerFactory 不是必需的。 而使用服务最好要求使用 IStringLocalizer

    两个 IStringLocalizer.Item[] 索引器都返回 LocalizedString,它们具有向 string? 的隐式转换

    4、将其放在一起

    若要举例说明使用两种消息服务以及本地化和资源文件的应用,请考虑使用以下 Program.cs 文件:

    1. using System.Globalization;
    2. using Localization.Example;
    3. using Microsoft.Extensions.DependencyInjection;
    4. using Microsoft.Extensions.Hosting;
    5. using Microsoft.Extensions.Localization;
    6. using Microsoft.Extensions.Logging;
    7. using static System.Console;
    8. using static System.Text.Encoding;
    9. [assembly: RootNamespace("Localization.Example")]
    10. OutputEncoding = Unicode;
    11. if (args is { Length: 1 })
    12. {
    13. CultureInfo.CurrentCulture =
    14. CultureInfo.CurrentUICulture =
    15. CultureInfo.GetCultureInfo(args[0]);
    16. }
    17. HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
    18. builder.Services.AddLocalization();
    19. builder.Services.AddTransient<MessageService>();
    20. builder.Services.AddTransient<ParameterizedMessageService>();
    21. builder.Logging.SetMinimumLevel(LogLevel.Warning);
    22. using IHost host = builder.Build();
    23. IServiceProvider services = host.Services;
    24. ILogger logger =
    25. services.GetRequiredService<ILoggerFactory>()
    26. .CreateLogger("Localization.Example");
    27. MessageService messageService =
    28. services.GetRequiredService<MessageService>();
    29. logger.LogWarning(
    30. "{Msg}",
    31. messageService.GetGreetingMessage());
    32. ParameterizedMessageService parameterizedMessageService =
    33. services.GetRequiredService<ParameterizedMessageService>();
    34. logger.LogWarning(
    35. "{Msg}",
    36. parameterizedMessageService.GetFormattedMessage(
    37. DateTime.Today.AddDays(-3), 37.63));
    38. await host.RunAsync();

    在前述 C# 代码中:

    每个 *MessageService 类都定义一组 .resx 文件,其中每个文件都有一个条目。 下面是 MessageService 资源文件的示例内容,从 MessageService.resx 开始:

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <root>
    3. <data name="GreetingMessage" xml:space="preserve">
    4. <value>Hi friends, the ".NET" developer community is excited to see you here!</value>
    5. </data>
    6. </root>

    MessageService.sr-Cyrl-RS.resx:

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <root>
    3. <data name="GreetingMessage" xml:space="preserve">
    4. <value>Здраво пријатељи, ".NЕТ" девелопер заједница је узбуђена што вас види овде!</value>
    5. </data>
    6. </root>

    MessageService.sr-Latn.resx:

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <root>
    3. <data name="GreetingMessage" xml:space="preserve">
    4. <value>Zdravo prijatelji, ".NET" developer zajednica je uzbuđena što vas vidi ovde!</value>
    5. </data>
    6. </root>

    下面是 ParameterizedMessageService 资源文件的示例内容,从 ParameterizedMessageService.resx 开始:

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <root>
    3. <data name="DinnerPriceFormat" xml:space="preserve">
    4. <value>On {0:D} my dinner cost {1:C}.</value>
    5. </data>
    6. </root>

    ParameterizedMessageService.sr-Cyrl-RS.resx:

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <root>
    3. <data name="DinnerPriceFormat" xml:space="preserve">
    4. <value>У {0:D} моја вечера је коштала {1:C}.</value>
    5. </data>
    6. </root>

    ParameterizedMessageService.sr-Latn.resx:

    1. <?xml version="1.0" encoding="utf-8"?>
    2. <root>
    3. <data name="DinnerPriceFormat" xml:space="preserve">
    4. <value>U {0:D} moja večera je koštala {1:C}.</value>
    5. </data>
    6. </root>

     提示

    为简洁起见,有意省略所有资源文件 XML 注释、架构和  元素。

    4.1 示例运行

    以下示例运行显示给定目标区域设置的各种本地化输出。

    以 "sr-Latn" 为例:

    1. dotnet run --project .\example\example.csproj sr-Latn
    2. warn: Localization.Example[0]
    3. Zdravo prijatelji, ".NET" developer zajednica je uzbuđena što vas vidi ovde!
    4. warn: Localization.Example[0]
    5. U utorak, 03. avgust 2021. moja večera je koštala 37,63 ¤.

    当省略运行项目的 .NET CLI 参数时,使用默认系统区域性,在本例中为 "en-US"

    1. dotnet run --project .\example\example.csproj
    2. warn: Localization.Example[0]
    3. Hi friends, the ".NET" developer community is excited to see you here!
    4. warn: Localization.Example[0]
    5. On Tuesday, August 3, 2021 my dinner cost $37.63.

    传递 "sr-Cryl-RS" 时,将找到正确的相应资源文件并应用本地化:

    1. dotnet run --project .\example\example.csproj sr-Cryl-RS
    2. warn: Localization.Example[0]
    3. Здраво пријатељи, ".NЕТ" девелопер заједница је узбуђена што вас види овде!
    4. warn: Localization.Example[0]
    5. У уторак, 03. август 2021. моја вечера је коштала 38 RSD.

    示例应用程序不提供 "fr-CA" 的资源文件,但在使用该区域性调用时,将使用非本地化的资源文件。

     警告

    由于已找到区域性,但未找到正确的资源文件,因此应用格式设置时,最终会实现部分本地化:

    1. dotnet run --project .\example\example.csproj fr-CA
    2. warn: Localization.Example[0]
    3. Hi friends, the ".NET" developer community is excited to see you here!
    4. warn: Localization.Example[0]
    5. On mardi 3 août 2021 my dinner cost 37,63 $.
  • 相关阅读:
    linux环境下,一步步教你命令行搭建自己的git服务器和客户端
    【新学期、新Flag】快来参与活动、获取丰厚的奖励吧
    消费品行业报告:化妆品容器市场现状研究分析与发展前景预测
    给我一块画布,我可以造一个全新的跨端UI
    GUI horizontalSlider 高度设置
    nginx.conf配置
    【LGR114C】地地铁铁
    kafka基础原理
    Vue(六)——使用脚手架(3)
    webpack5 基本配置
  • 原文地址:https://blog.csdn.net/m0_51887793/article/details/134235045