.NET でのローカライズ

ローカリゼーションはアプリケーションのリソースを翻訳するプロセスであり、そのアプリケーションで対応するカルチャ別のバージョンが作られます。 ローカライズ化の確認手順で世界展開するアプリケーションがローカライズ可能であることを確認してから、ローカライズ手順に進んでください。

ローカライズ可能なアプリケーションは、概念的に 2 つのブロックに分けられます。すべてのユーザー インターフェイス要素を含むブロックと実行可能なコードを含むブロックです。 ユーザー インターフェイス ブロックには、ローカライズ可能なユーザー インターフェイス要素のみが含まれます。ニュートラル カルチャの場合、文字列、エラー メッセージ、ダイアログ ボックス、メニュー、埋め込みオブジェクト リソースなどです。 コード ブロックには、すべての対応カルチャで使用されるアプリケーション コードのみが含まれます。 共通言語ランタイムは、アプリケーションの実行可能コードをそのリソースから分離させるサテライト アセンブリ リソース モデルに対応しています。 このモデルの実装方法については、.NET のリソースに関する記事を参照してください。

アプリケーションのローカライズ バージョンごとに、ターゲット カルチャの言語に翻訳されたユーザー インターフェイス ブロックを含む新しいサテライト アセンブリを追加します。 すべてのカルチャのコード ブロックを同じにしてください。 ユーザー インターフェイス ブロックのローカライズされたバージョンとコード ブロックを組み合わせることで、アプリケーションのローカライズ バージョンが作られます。

この記事では、IStringLocalizer<T> および IStringLocalizerFactory 実装の使用方法について説明します。 この記事のすべてのソース コード例は、Microsoft.Extensions.Localization および Microsoft.Extensions.Hosting NuGet パッケージに依存しています。 ホスティングの詳細については、「.NET での汎用ホスト」を参照してください。

リソース ファイル

ローカライズ可能な文字列を分離するための主要なメカニズムは、リソース ファイルを使用することです。 リソース ファイルは、 .resx ファイル拡張子を持つ XML ファイルです。 リソース ファイルは、使用側アプリケーションを実行する前に翻訳されます。つまり、保存されている翻訳コンテンツを表します。 通常、リソース ファイル名にはロケール識別子が含まれ、次の形式が使用されます。

<FullTypeName><.Locale>.resx

この場合、

  • <FullTypeName> は、特定の型のローカライズ可能なリソースを表します。
  • 省略可能な <.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 (ラテン、モンテネグロ) 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 ファイルを処理することになります。 詳細については、「リソース フォールバック プロセス」を参照してください。

リソース検索

リソース ファイルは、検索ルーチンの一部として自動的に解決されます。 プロジェクト ファイル名がプロジェクトのルート名前空間と異なる場合、アセンブリ名が異なる可能性があります。 これにより、リソース検索が正常に行われない場合があります。 この不一致に対処するには、RootNamespaceAttribute を使用して、ローカライズ サービスにヒントを提供します。 提供された場合、それがリソース検索時に使用されます。

この例のプロジェクトには、example.csproj という名前が付けられており、example.dllexample.exe が作成されますが、Localization.Example 名前空間が使用されます。 assembly レベル属性を適用して、この不一致を修正します。

[assembly: RootNamespace("Localization.Example")]

ローカライズ サービスを登録する

ローカライズ サービスを登録するには、サービスの構成中に AddLocalization 拡張メソッドのいずれかを呼び出します。 これにより、次の型の依存関係の挿入 (DI) が有効になります。

ローカライズ オプションを構成する

AddLocalization(IServiceCollection, Action<LocalizationOptions>) オーバーロードでは、Action<LocalizationOptions> 型の setupAction パラメーターが受け入れられます。 これにより、ローカライズ オプションを構成できます。

HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);

builder.Services.AddLocalization(options =>
{
    options.ResourcesPath = "Resources";
});

// Omitted for brevity.

リソース ファイルはプロジェクト内のどこにでも配置できますが、正常に実行されることがわかっている一般的な方法があります。 多くの場合、最も問題の少ないパスに従います。 前述の C# コードでは、次のことが行われます。

  • 既定のホスト アプリ ビルダーを作成します。
  • LocalizationOptions.ResourcesPath"Resources" として指定し、サービス コレクション上で AddLocalization を呼び出します。

これにより、ローカライズ サービスでリソース ファイルの Resources ディレクトリが検索されるようになります。

IStringLocalizer<T>IStringLocalizerFactory を使用する

ローカライズ サービスを登録 (および必要に応じて構成) した後、DI で次の型を使用できます。

ローカライズされた文字列を返すことができるメッセージ サービスを作成するために、次の 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;
    }
}

前述の C# コードでは:

  • IStringLocalizer<MessageService> localizer フィールドが宣言されています。
  • プライマリ コンストラクターでは IStringLocalizer<MessageService> パラメーターを定義し、それを localizer 引数としてキャプチャします。
  • 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 フィールドが宣言されています。
  • プライマリ コンストラクターでは、ParameterizedMessageService 型から IStringLocalizer を作成するために使用される IStringLocalizerFactory パラメーターを受け取り、それを _localizer フィールドに割り当てます。
  • GetFormattedMessage メソッドでは IStringLocalizer.Item[String, Object[]] を呼び出して、"DinnerPriceFormat"dateTime オブジェクト、および dinnerPrice を引数として渡します。

重要

IStringLocalizerFactory は必要ありません。 代わりに、IStringLocalizer<T> を要求するサービスを使用することをお勧めします。

両方の 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<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();

前述の C# コードでは:

  • RootNamespaceAttribute では、ルート名前空間として "Localization.Example" を設定します。
  • Console.OutputEncodingEncoding.Unicode に割り当てられます。
  • 1 つの引数が args に渡されると、CultureInfo.CurrentCultureCultureInfo.CurrentUICulture には、arg[0] が指定された CultureInfo.GetCultureInfo(String) の結果が割り当てられます。
  • Host既定値を使用して作成されます。
  • ローカライズ サービスである MessageServiceParameterizedMessageService は、DI 用の IServiceCollection に登録されます。
  • ノイズを除去するために、ログは警告よりも低いログ レベルを無視するように構成されています。
  • MessageServiceIServiceProvider インスタンスから解決され、その結果のメッセージがログに記録されます。
  • ParameterizedMessageServiceIServiceProvider インスタンスから解決され、その結果の書式設定されたメッセージがログに記録されます。

*MessageService クラスでは、それぞれ 1 つのエントリを持つ .resx ファイルのセットが定義されます。 以下に、MessageService.resx から始まる MessageService リソース ファイルのコンテンツ例を示します。

<?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>

以下に、ParameterizedMessageService.resx から始まる ParameterizedMessageService リソース ファイルのコンテンツ例を示します。

<?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>

ヒント

すべてのリソース ファイルの XML コメント、スキーマ、および <resheader> 要素は、簡潔にするために意図的に省略されています。

実行例

次の実行例では、対象となるロケールを指定して、ローカライズされたさまざまな出力を示します。

"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 $.

こちらもご覧ください