次の方法で共有


ASP.NET Core Blazor のグローバリゼーションおよびローカライズ

注意

これは、この記事の最新バージョンではありません。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。

警告

このバージョンの ASP.NET Core はサポート対象から除外されました。 詳細については、 .NET および .NET Core サポート ポリシーを参照してください。 現在のリリースについては、この記事の .NET 9 バージョンを参照してください。

重要

この情報はリリース前の製品に関する事項であり、正式版がリリースされるまでに大幅に変更される可能性があります。 Microsoft はここに示されている情報について、明示か黙示かを問わず、一切保証しません。

現在のリリースについては、この記事の .NET 9 バージョンを参照してください。

この記事では、異なるカルチャや言語のユーザーにグローバル化およびローカライズされたコンテンツをレンダリングする方法について説明します。

グローバライズとローカライズ

グローバリゼーションの場合、Blazor から数値と日付の書式設定が提供されます。 ローカライズの場合、Blazor により .NET リソース システムを使用したコンテンツのレンダリングが行われます。

サポートされている ASP.NET Core のローカライズ機能は限られています。

サポート対象:IStringLocalizerIStringLocalizer<T> は Blazor アプリでサポートされています。

サポート対象外:IHtmlLocalizerIViewLocalizer、およびデータ注釈のローカライズは ASP.NET Core MVC の機能であり、 アプリでは "サポートされていません"。Blazor

この記事では、次に基づいて Blazor のグローバリゼーションとローカライズ機能を使用する方法について説明します。

  • ブラウザーの設定でのユーザー言語設定に基づきブラウザーによって設定された Accept-Language ヘッダー
  • Accept-Language ヘッダーの値に基づいていない、アプリによって設定されたカルチャ。 設定は、すべてのユーザーに対して静的にするか、アプリ ロジックに基づいて動的にすることができます。 設定がユーザー設定に基づく場合、通常は今後のアクセス時に再読み込みするために設定が保存されます。

全般的な追加情報については、次のリソースを参照してください。

多くの場合、グローバリゼーションとローカライズの概念を扱う場合、''言語'' と ''カルチャ'' という用語は区別なく使用されます。

この記事の場合、言語とは、ユーザーがブラウザーの設定で行った選択を指します。 ユーザーの言語選択は、Accept-Language ヘッダーのブラウザー要求で送信されます。 ブラウザーの設定では、通常、UI で「言語」という単語が使用されます。

カルチャは、.NET と Blazor API のメンバーに関連します。 たとえば、ユーザーの要求には、ユーザーの観点からAccept-Languageを指定する ヘッダーを含めることができますが、アプリでは最終的に、ユーザーが要求した言語から CurrentCulture (「カルチャ」) プロパティが設定されます。 API では通常、メンバー名に "culture" という単語が使用されます。

この記事のガイダンスでは、アクセシビリティ ツールで使用されるページの HTML 言語属性 (<html lang="...">) の設定については説明しません。 lang タグの <html> 属性または JavaScript の document.documentElement.lang に言語を割り当てることで、値を静的に設定できます。 JSを使用して の値を動的に設定できます。

注意

この記事のコード例では、null 許容参照型 (NRT) と .NET コンパイラの null 状態スタティック分析を採用しています。これは、.NET 6 以降の ASP.NET Core でサポートされています。 ASP.NET Core 5.0 以前をターゲットとする場合は、この記事の例から null 型の指定 (?) を削除してください。

グローバリゼーション

@bind 属性ディレクティブでは、アプリによってサポートされるユーザーの第一優先言語に基づいて形式を適用し、表示向けの値を解析します。 @bind では、値の解析および書式設定のための @bind:culture を提供する System.Globalization.CultureInfo パラメーターをサポートしています。

現在のカルチャは、System.Globalization.CultureInfo.CurrentCulture プロパティからアクセスできます。

CultureInfo.InvariantCulture は、次のフィールド型 (<input type="{TYPE}" /> プレースホルダーが型の {TYPE}) に使用されます。

  • date
  • number

前のフィールドの型は次のようになります。

  • 適切なブラウザー ベースの書式ルールを使用して表示されます。
  • 自由形式のテキストを含めることはできません。
  • ブラウザーの実装に基づいてユーザー操作の特性を指定します。

Blazor には、現在のカルチャで値をレンダリングするための組み込みのサポートが用意されています。 したがって、@bind:culturedate フィールドタイプを使用する場合、number でカルチャを指定しないことをお勧めします。

次のフィールドの型には、特定の書式設定の要件がありますが、すべての主要なブラウザーでサポートされていないため、Blazor では現在サポートされていません。

  • datetime-local
  • month
  • week

前述の型の現在のブラウザー サポートについては、使用可能なものに関するページを参照してください。

.NET グローバリゼーションと International Components for Unicode (ICU) のサポート (Blazor WebAssembly)

Blazor WebAssembly は、縮小されたグローバリゼーション API と組み込みの International Components for Unicode (ICU) ロケールのセットを使います。 詳細については、「.NET グローバリゼーションと ICU: WebAssembly での ICU」を参照してください。

カスタム ICU データ ファイルを読み込んでアプリのロケールを制御するには、「WASM グローバリゼーション Icu」を参照してください。 現在は、カスタム ICU データ ファイルを手動で構築する必要があります。 ファイルの作成プロセスを容易にする .NET ツールは、2025 年 11 月に .NET 10 向けに計画されています。

Blazor WebAssembly は、縮小されたグローバリゼーション API と組み込みの International Components for Unicode (ICU) ロケールのセットを使います。 詳細については、「.NET グローバリゼーションと ICU: WebAssembly での ICU」を参照してください。

Blazor WebAssembly アプリでロケールのカスタム サブセットを読み込むことは、.NET 8 以降でサポートされています。 詳細については、この記事の 8.0 以降のバージョンに関するセクションにアクセスしてください。

インバリアント グローバリゼーション

このセクションは、クライアント側の Blazor シナリオにのみ適用されます。

アプリのローカライズを必要としない場合は、一般的に英語 (米国) (en-US) に基づくインバリアント カルチャをサポートするようにアプリを構成します。 インバリアント グローバリゼーションを使用すると、アプリのダウンロード サイズが小さくなり、アプリの起動が高速化されます。 アプリのプロジェクト ファイル (InvariantGlobalization) で true プロパティを .csproj に設定します。

<PropertyGroup>
  <InvariantGlobalization>true</InvariantGlobalization>
</PropertyGroup>

または、次の方法でインバリアント グローバリゼーションを構成します。

  • runtimeconfig.jsonの場合:

    {
      "runtimeOptions": {
        "configProperties": {
          "System.Globalization.Invariant": true
        }
      }
    }
    
  • 環境変数を使用:

    • キー: DOTNET_SYSTEM_GLOBALIZATION_INVARIANT
    • 値: true または 1

詳細については、「グローバリゼーションのランタイム構成オプション」 (.NET ドキュメント) を参照してください。

タイムゾーン情報

このセクションは、クライアント側の Blazor シナリオにのみ適用されます。

インバリアント グローバリゼーション を採用すると、ローカライズされていないタイムゾーン名のみが使用されることになります。 タイムゾーン コードとデータをトリミングしてアプリのダウンロード サイズを削減し、アプリの起動を高速化するには、アプリのプロジェクト ファイルで、<InvariantTimezone> の値を持つ true MSBuild プロパティを適用します。

<PropertyGroup>
  <InvariantTimezone>true</InvariantTimezone>
</PropertyGroup>

注意

<BlazorEnableTimeZoneSupport> は、以前の <InvariantTimezone> 設定をオーバーライドします。 <BlazorEnableTimeZoneSupport> 設定は削除することをお勧めします。

データ ファイルは、タイムゾーン情報を正しく保つために含まれています。 アプリでこの機能を必要としない場合は、アプリのプロジェクト ファイル内の <BlazorEnableTimeZoneSupport> MSBuild プロパティを false に設定して無効にすることを検討してください。

<PropertyGroup>
  <BlazorEnableTimeZoneSupport>false</BlazorEnableTimeZoneSupport>
</PropertyGroup>

デモンストレーション コンポーネント

次の CultureExample1 コンポーネントを使用して、この記事で説明する Blazor のグローバリゼーションとローカライズの概念を示せます。

CultureExample1.razor:

@page "/culture-example-1"
@using System.Globalization

<h1>Culture Example 1</h1>

<ul>
    <li><b>CurrentCulture</b>: @CultureInfo.CurrentCulture</li>
    <li><b>CurrentUICulture</b>: @CultureInfo.CurrentUICulture</li>
</ul>

<h2>Rendered values</h2>

<ul>
    <li><b>Date</b>: @dt</li>
    <li><b>Number</b>: @number.ToString("N2")</li>
</ul>

<h2><code>&lt;input&gt;</code> elements that don't set a <code>type</code></h2>

<p>
    The following <code>&lt;input&gt;</code> elements use
    <code>CultureInfo.CurrentCulture</code>.
</p>

<ul>
    <li><label><b>Date:</b> <input @bind="dt" /></label></li>
    <li><label><b>Number:</b> <input @bind="number" /></label></li>
</ul>

<h2><code>&lt;input&gt;</code> elements that set a <code>type</code></h2>

<p>
    The following <code>&lt;input&gt;</code> elements use
    <code>CultureInfo.InvariantCulture</code>.
</p>

<ul>
    <li><label><b>Date:</b> <input type="date" @bind="dt" /></label></li>
    <li><label><b>Number:</b> <input type="number" @bind="number" /></label></li>
</ul>

@code {
    private DateTime dt = DateTime.Now;
    private double number = 1999.69;
}

前の例 (N2) の数値文字列形式 (.ToString("N2")) は、標準の .NET 数値書式指定子です。 この N2 形式は、すべての数値型でサポートされ、グループ区切り記号を含み、小数点以下 2 桁まで表示されます。

必要に応じて、NavMenu コンポーネントの NavMenu.razor コンポーネント (CultureExample1) のナビゲーションにメニュー項目を追加します。

Accept-Language ヘッダーからカルチャを動的に設定する

アプリに Microsoft.Extensions.Localization パッケージを追加します。

Accept-Language ヘッダーはブラウザーによって設定され、ブラウザー設定でのユーザーの言語設定によって制御されます。 ブラウザー設定では、ユーザーは優先順に 1 つ以上の優先言語を設定します。 設定の順序は、ヘッダー内の言語ごとに品質値 (q、0-1) を設定するためにブラウザーによって使用されます。 次の例では、英語 (米国) または英語を優先して、英語 (米国)、英語、およびスペイン語 (コスタリカ) を指定しています。

Accept-Language: en-US,en;q=0.9,es-CR;q=0.8

アプリのカルチャは、アプリのサポートされているカルチャに一致する最初に要求された言語を照合することで設定されます。

"クライアント側の開発" では、クライアント側アプリのプロジェクト ファイル ( ) で BlazorWebAssemblyLoadAllGlobalizationData プロパティを true に設定します。.csproj

<PropertyGroup>
  <BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
</PropertyGroup>

"クライアント側の開発" では、 ヘッダーからカルチャを動的に設定することはできません。Accept-Language

注意

アプリの仕様で、サポートされているカルチャを明示的な一覧に制限する必要がある場合は、この記事の「ユーザー設定によってクライアント側のカルチャを動的に設定する」セクションを参照してください。

アプリはローカライズ ミドルウェアを使用してローカライズされます。 AddLocalization を使用して、ローカライズ サービスをアプリに追加します。

サービスが追加される Program ファイルに次の行を追加します。

builder.Services.AddLocalization();

サーバー側の開発では要求カルチャをチェックするミドルウェアの前に、アプリでサポートされているカルチャを指定します。 一般に、 MapRazorComponentsを呼び出す直前に、要求ローカリゼーション ミドルウェアを配置します。 次の例では、英語 (米国) とスペイン語 (コスタリカ) でサポートされるカルチャを構成しています。

サーバー側の開発ではルーティング ミドルウェア (UseRouting) が処理パイプラインに追加された直後に、アプリでサポートされているカルチャを指定します。 次の例では、英語 (米国) とスペイン語 (コスタリカ) でサポートされるカルチャを構成しています。

app.UseRequestLocalization(new RequestLocalizationOptions()
    .AddSupportedCultures(new[] { "en-US", "es-CR" })
    .AddSupportedUICultures(new[] { "en-US", "es-CR" }));

Program ファイルのミドルウェア パイプラインでのローカライズ ミドルウェアの順序付けについては、「ASP.NET Core のミドルウェア」を参照してください。

CultureExample1」セクションに示されている コンポーネントを使用して、グローバリゼーションのしくみを確認します。 英語 (米国) (en-US) で要求を発行します。 ブラウザーの言語設定で、スペイン語 (コスタリカ) (es-CR) に切り替えます。 Web ページを再度要求します。

注意

一部のブラウザーでは、要求とブラウザー独自の UI 設定の両方に既定の言語設定を使用する必要があります。 これにより、すべての設定 UI 画面が読むことができない言語になる可能性があるため、理解できる言語に戻すのが困難になる可能性があります。 Opera などのブラウザーは、Web ページ要求に既定の言語を設定しても、ブラウザーの設定 UI は使用言語のままにしてよいため、テストに最適です。

カルチャが英語 (米国) (en-US) の場合、表示されるコンポーネントでは、月/日の日付書式設定 (6/7)、12 時間制の時刻 (AM/PM)、および数値でコンマ、小数点値で点の区切り記号 (1,999.69) が使用されます。

  • 日付: 6/7/2021 6:45:22 AM
  • 数値: 1,999.69

カルチャがスペイン語 (コスタリカ) (es-CR) の場合、レンダリングされるコンポーネントでは、日/月の日付書式設定 (7/6)、24 時間制の時刻、および数値でピリオド、小数点値でコンマの区切り記号 (1.999,69) が使用されます。

  • 日付: 7/6/2021 6:49:38
  • 数値: 1.999,69

クライアント側のカルチャを静的に設定する

アプリのプロジェクト ファイル (BlazorWebAssemblyLoadAllGlobalizationData) で true プロパティを .csproj に設定します。

<PropertyGroup>
  <BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
</PropertyGroup>

クライアント側のレンダリングに対する中間言語 (IL) リンカーの構成によって、明示的に要求されたロケールを除き、国際化情報が削除されます。 詳しくは、「ASP.NET Core Blazor 用のリンカーを構成する」をご覧ください。

アプリのカルチャは、Blazor が applicationCultureBlazor スタート オプションで始まるときに JavaScript で設定できます。 次の例では、英語 (米国) (en-US) カルチャを使用して構成しています。

Blazorに <script> を追加して の自動開始を防止します。

<script src="{BLAZOR SCRIPT}" autostart="false"></script>

前の例の {BLAZOR SCRIPT} プレースホルダーは、Blazor スクリプトのパスとファイル名です。 スクリプトの場所については、「ASP.NET Core Blazor プロジェクトの構造」を参照してください。

<script>の後、 終了タグの前に、次の </body> ブロックを追加します。

Blazor Web App:

<script>
  Blazor.start({
    webAssembly: {
      applicationCulture: 'en-US'
    }
  });
</script>

スタンドアロン Blazor WebAssembly:

<script>
  Blazor.start({
    applicationCulture: 'en-US'
  });
</script>

applicationCulture の値は、BCP-47 言語タグ形式に準拠している必要があります。 Blazor の起動について詳しくは、「ASP.NET Core Blazor の起動」をご覧ください。

カルチャ Blazor のスタート オプションを設定する代わりに、C# コードでカルチャを設定することもできます。 CultureInfo.DefaultThreadCurrentCulture ファイルの CultureInfo.DefaultThreadCurrentUICultureProgram を同じカルチャに設定します。

System.Globalization 名前空間を Program ファイルに追加します。

using System.Globalization;

WebAssemblyHostBuilder (await builder.Build().RunAsync();) をビルドして実行する行の前にカルチャ設定を追加します。

CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("en-US");

注意

現在、 Blazor WebAssembly アプリは DefaultThreadCurrentCulture に基づいてリソースのみを読み込みます。 詳細については、Blazor WASM が現在のカルチャのみに依存する (現在の UI カルチャは尊重されない) (dotnet/aspnetcore #56824) に関する記事をご覧ください。

CultureExample1」セクションに示されている コンポーネントを使用して、グローバリゼーションのしくみを確認します。 英語 (米国) (en-US) で要求を発行します。 ブラウザーの言語設定で、スペイン語 (コスタリカ) (es-CR) に切り替えます。 Web ページを再度要求します。 要求された言語がスペイン語 (コスタリカ) である場合、アプリのカルチャは英語 (米国) (en-US) のままです。

サーバー側のカルチャを静的に設定する

サーバー側のアプリはローカライズ ミドルウェアを使用してローカライズされます。 AddLocalization を使用して、ローカライズ サービスをアプリに追加します。

Program ファイルで次のように指定します。

builder.Services.AddLocalization();

要求カルチャを確認する可能性のあるミドルウェアの前に、 Program ファイルで静的カルチャを指定します。 一般に、要求ローカリゼーション ミドルウェアは、 MapRazorComponentsの直前に配置します。 次の例では、英語 (米国) を構成しています。

ルーティング ミドルウェア (Program) が処理パイプラインに追加された直後に、UseRouting ファイルに静的カルチャを指定します。 次の例では、英語 (米国) を構成しています。

app.UseRequestLocalization("en-US");

UseRequestLocalization のカルチャ値は、BCP-47 言語タグ形式に準拠している必要があります。

Program ファイルのミドルウェア パイプラインでのローカライズ ミドルウェアの順序付けについては、「ASP.NET Core のミドルウェア」を参照してください。

サーバー側のアプリはローカライズ ミドルウェアを使用してローカライズされます。 AddLocalization を使用して、ローカライズ サービスをアプリに追加します。

Startup.ConfigureServices (Startup.cs):

services.AddLocalization();

ルーティング ミドルウェアが処理パイプラインに追加された直後に、Startup.Configure (Startup.cs) で静的カルチャを指定します。 次の例では、英語 (米国) を構成しています。

app.UseRequestLocalization("en-US");

UseRequestLocalization のカルチャ値は、BCP-47 言語タグ形式に準拠している必要があります。

Startup.Configure のミドルウェア パイプラインでのローカライズ ミドルウェアの順序付けについては、「ASP.NET Core のミドルウェア」を参照してください。

CultureExample1」セクションに示されている コンポーネントを使用して、グローバリゼーションのしくみを確認します。 英語 (米国) (en-US) で要求を発行します。 ブラウザーの言語設定で、スペイン語 (コスタリカ) (es-CR) に切り替えます。 Web ページを再度要求します。 要求された言語がスペイン語 (コスタリカ) である場合、アプリのカルチャは英語 (米国) (en-US) のままです。

ユーザー設定によってクライアント側のカルチャを動的に設定する

アプリでユーザーの設定が格納される可能性がある場所の例としては、ブラウザーのローカル ストレージ (クライアント側のシナリオで一般的)、ローカライズ用の cookie またはデータベース (サーバー側のシナリオで一般的)、外部データベースに接続され、Web API によってアクセスされる外部サービスがあります。 次の例は、ブラウザーのローカル ストレージを使用する方法を示しています。

アプリに Microsoft.Extensions.Localization パッケージを追加します。

注意

.NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。

プロジェクト ファイルで BlazorWebAssemblyLoadAllGlobalizationData プロパティを true に設定します。

<PropertyGroup>
  <BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
</PropertyGroup>

クライアント側レンダリング用のアプリのカルチャは、Blazor フレームワークの API を使用して設定されます。 ユーザーのカルチャの選択は、ブラウザーのローカル ストレージに保存できます。

<script>の後に 関数を指定して、ユーザーが選択したカルチャをブラウザーのローカル ストレージで取得および設定します。

<script>
  window.blazorCulture = {
    get: () => window.localStorage['BlazorCulture'],
    set: (value) => window.localStorage['BlazorCulture'] = value
  };
</script>

注意

前の例では、クライアントがグローバル関数で汚染されます。 実稼働アプリでのより適切なアプローチについては、「JavaScript モジュール内の JavaScript 分離」を参照してください。

System.GlobalizationMicrosoft.JSInterop の名前空間を、Program ファイルの先頭に追加します。

using System.Globalization;
using Microsoft.JSInterop;

次の行を削除します。

- await builder.Build().RunAsync();

前の行を、次のコードで置き換えます。 このコードを使用すると、Blazor のローカライズ サービスが AddLocalization を含むアプリのサービス コレクションに追加されたり、JS 相互運用が JS への呼び出しに使用されたりすることに加え、ローカル ストレージからユーザーが選択したカルチャが取得されます。 ローカル ストレージにユーザーのカルチャが含まれていない場合、コードは英語 (米国) (en-US) の既定値を設定します。

builder.Services.AddLocalization();

var host = builder.Build();

const string defaultCulture = "en-US";

var js = host.Services.GetRequiredService<IJSRuntime>();
var result = await js.InvokeAsync<string>("blazorCulture.get");
var culture = CultureInfo.GetCultureInfo(result ?? defaultCulture);

if (result == null)
{
    await js.InvokeVoidAsync("blazorCulture.set", defaultCulture);
}

CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;

await host.RunAsync();

注意

現在、 Blazor WebAssembly アプリは DefaultThreadCurrentCulture に基づいてリソースのみを読み込みます。 詳細については、Blazor WASM が現在のカルチャのみに依存する (現在の UI カルチャは尊重されない) (dotnet/aspnetcore #56824) に関する記事をご覧ください。

次の CultureSelector コンポーネントは、次の操作を実行する方法を示しています。

  • JS 相互運用を使用して、ユーザーが選択したカルチャをブラウザーのローカル ストレージに設定します。
  • 更新されたカルチャを使用する、要求したコンポーネント (forceLoad: true) を再読み込みします。

CultureSelector.razor:

@using System.Globalization
@inject IJSRuntime JS
@inject NavigationManager Navigation

<p>
    <label>
        Select your locale:
        <select @bind="selectedCulture" @bind:after="ApplySelectedCultureAsync">
            @foreach (var culture in supportedCultures)
            {
                <option value="@culture">@culture.DisplayName</option>
            }
        </select>
    </label>
</p>

@code
{
    private CultureInfo[] supportedCultures = new[]
    {
        new CultureInfo("en-US"),
        new CultureInfo("es-CR"),
    };

    private CultureInfo? selectedCulture;

    protected override void OnInitialized()
    {
        selectedCulture = CultureInfo.CurrentCulture;
    }

    private async Task ApplySelectedCultureAsync()
    {
        if (CultureInfo.CurrentCulture != selectedCulture)
        {
            await JS.InvokeVoidAsync("blazorCulture.set", selectedCulture!.Name);

            Navigation.NavigateTo(Navigation.Uri, forceLoad: true);
        }
    }
}
@using System.Globalization
@inject IJSRuntime JS
@inject NavigationManager Navigation

<p>
    <label>
        Select your locale:
        <select value="@selectedCulture" @onchange="HandleSelectedCultureChanged">
            @foreach (var culture in supportedCultures)
            {
                <option value="@culture">@culture.DisplayName</option>
            }
        </select>
    </label>
</p>

@code
{
    private CultureInfo[] supportedCultures = new[]
    {
        new CultureInfo("en-US"),
        new CultureInfo("es-CR"),
    };

    private CultureInfo? selectedCulture;

    protected override void OnInitialized()
    {
        selectedCulture = CultureInfo.CurrentCulture;
    }

    private async Task HandleSelectedCultureChanged(ChangeEventArgs args)
    {
        selectedCulture = CultureInfo.GetCultureInfo((string)args.Value!);

        if (CultureInfo.CurrentCulture != selectedCulture)
        {
            await JS.InvokeVoidAsync("blazorCulture.set", selectedCulture!.Name);

            Navigation.NavigateTo(Navigation.Uri, forceLoad: true);
        }
    }
}

注意

IJSInProcessRuntime の詳細については、「ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出す」を参照してください。

</main> コンポーネント (MainLayout) の MainLayout.razor 要素の終了タグ内に、CultureSelector コンポーネントを追加します。

<article class="bottom-row px-4">
    <CultureSelector />
</article>

CultureExample1」セクションに示されている コンポーネントを使用して、前述の例のしくみを確認します。

ユーザー設定によってサーバー側のカルチャを動的に設定する

アプリでユーザーの設定が格納される可能性がある場所の例としては、ブラウザーのローカル ストレージ (クライアント側のシナリオで一般的)、ローカライズ用の cookie またはデータベース (サーバー側のシナリオで一般的)、外部データベースに接続され、Web API によってアクセスされる外部サービスがあります。 以下の例では、ローカライズ cookie の使用方法を示しています。

注意

次の例では、 コンポーネント () の Routes コンポーネントで対話型サーバー側レンダリング (対話型 SSR) を指定することで、アプリで "App" インタラクティビティが採用されていることを前提としています。Components/App.razor

<Routes @rendermode="InteractiveServer" />

アプリでページ/コンポーネント単位のインタラクティビティが採用されている場合、このセクションの終わりにある注釈を見て、例のコンポーネントのレンダー モードを変更してください。

アプリに Microsoft.Extensions.Localization パッケージを追加します。

注意

.NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。

サーバー側のアプリはローカライズ ミドルウェアを使用してローカライズされます。 AddLocalization を使用して、ローカライズ サービスをアプリに追加します。

Program ファイルで次のように指定します。

builder.Services.AddLocalization();

アプリの既定とサポートされるカルチャを RequestLocalizationOptions に設定します。

要求処理パイプラインで MapRazorComponents を呼び出す前に、次のコードを置換します。

ルーティング ミドルウェア (UseRouting) が要求処理パイプラインに追加されたら、次のコードを配置します。

var supportedCultures = new[] { "en-US", "es-CR" };
var localizationOptions = new RequestLocalizationOptions()
    .SetDefaultCulture(supportedCultures[0])
    .AddSupportedCultures(supportedCultures)
    .AddSupportedUICultures(supportedCultures);

app.UseRequestLocalization(localizationOptions);

ミドルウェア パイプラインでのローカライズ ミドルウェアの順序付けについては、「ASP.NET Core のミドルウェア」を参照してください。

次の例では、ローカライズ ミドルウェアによって読み取ることができる cookie で現在のカルチャを設定する方法を示します。

App コンポーネントには次の名前空間が必要です。

App コンポーネント ファイル (Components/App.razor) の先頭に次を追加します。

@using System.Globalization
@using Microsoft.AspNetCore.Localization

@code コンポーネント ファイルの末尾に次の App ブロックを追加します。

@code {
    [CascadingParameter]
    public HttpContext? HttpContext { get; set; }

    protected override void OnInitialized()
    {
        HttpContext?.Response.Cookies.Append(
            CookieRequestCultureProvider.DefaultCookieName,
            CookieRequestCultureProvider.MakeCookieValue(
                new RequestCulture(
                    CultureInfo.CurrentCulture,
                    CultureInfo.CurrentUICulture)));
    }
}

Pages/_Host.cshtml ファイルを変更するには、次の名前空間が必要です。

ファイルに次のコードを追加します。

@using System.Globalization
@using Microsoft.AspNetCore.Localization
@{
    this.HttpContext.Response.Cookies.Append(
        CookieRequestCultureProvider.DefaultCookieName,
        CookieRequestCultureProvider.MakeCookieValue(
            new RequestCulture(
                CultureInfo.CurrentCulture,
                CultureInfo.CurrentUICulture)));
}

ミドルウェア パイプラインでのローカライズ ミドルウェアの順序付けについては、「ASP.NET Core のミドルウェア」を参照してください。

アプリがコントローラー アクションを処理するように構成されていない場合:

  • AddControllers ファイルでサービス コレクションに対して Program を呼び出して、MVC サービスを追加します。

    builder.Services.AddControllers();
    
  • Program (MapControllers) で IEndpointRouteBuilder を呼び出し、app ファイルにコントローラー エンドポイント ルーティングを追加します。

    app.MapControllers();
    

ユーザーがカルチャを選択できるように UI を提供するには、ローカライズ でのcookieをお勧めします。 アプリでは、コントローラーへのリダイレクトによって、ユーザーが選択したカルチャが保持されます。 コントローラーによって、ユーザーが選択したカルチャが cookie に設定され、ユーザーは元の URI にリダイレクトされます。 このプロセスは、ユーザーがセキュリティで保護されたリソースにアクセスしようとしたときに、Web アプリで発生する処理に似ています。ユーザーがサインイン ページにリダイレクトされ、元のリソースに戻されます。

Controllers/CultureController.cs:

using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;

[Route("[controller]/[action]")]
public class CultureController : Controller
{
    public IActionResult Set(string culture, string redirectUri)
    {
        if (culture != null)
        {
            HttpContext.Response.Cookies.Append(
                CookieRequestCultureProvider.DefaultCookieName,
                CookieRequestCultureProvider.MakeCookieValue(
                    new RequestCulture(culture, culture)));
        }

        return LocalRedirect(redirectUri);
    }
}

警告

前の例に示すように、LocalRedirect アクションの結果を使って、オープン リダイレクト攻撃を防ぎます。 詳細については、「ASP.NET Core でオープン リダイレクト攻撃を防止する」を参照してください。

次の CultureSelector コンポーネントは、新しいカルチャで SetCultureController メソッドを呼び出す方法を示しています。 コンポーネントは、アプリ全体で使用するために Shared フォルダーに配置されます。

CultureSelector.razor:

@using System.Globalization
@inject IJSRuntime JS
@inject NavigationManager Navigation

<p>
    <label>
        Select your locale:
        <select @bind="selectedCulture" @bind:after="ApplySelectedCultureAsync">
            @foreach (var culture in supportedCultures)
            {
                <option value="@culture">@culture.DisplayName</option>
            }
        </select>
    </label>
</p>

@code
{
    private CultureInfo[] supportedCultures = new[]
    {
        new CultureInfo("en-US"),
        new CultureInfo("es-CR"),
    };

    private CultureInfo? selectedCulture;

    protected override void OnInitialized()
    {
        selectedCulture = CultureInfo.CurrentCulture;
    }

    private async Task ApplySelectedCultureAsync()
    {
        if (CultureInfo.CurrentCulture != selectedCulture)
        {
            var uri = new Uri(Navigation.Uri)
                .GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped);
            var cultureEscaped = Uri.EscapeDataString(selectedCulture.Name);
            var uriEscaped = Uri.EscapeDataString(uri);

            Navigation.NavigateTo(
                $"Culture/Set?culture={cultureEscaped}&redirectUri={uriEscaped}",
                forceLoad: true);
        }
    }
}
@using System.Globalization
@inject IJSRuntime JS
@inject NavigationManager Navigation

<p>
    <label>
        Select your locale:
        <select value="@selectedCulture" @onchange="HandleSelectedCultureChanged">
            @foreach (var culture in supportedCultures)
            {
                <option value="@culture">@culture.DisplayName</option>
            }
        </select>
    </label>
</p>

@code
{
    private CultureInfo[] supportedCultures = new[]
    {
        new CultureInfo("en-US"),
        new CultureInfo("es-CR"),
    };

    private CultureInfo? selectedCulture;

    protected override void OnInitialized()
    {
        selectedCulture = CultureInfo.CurrentCulture;
    }

    private async Task HandleSelectedCultureChanged(ChangeEventArgs args)
    {
        selectedCulture = CultureInfo.GetCultureInfo((string)args.Value!);

        if (CultureInfo.CurrentCulture != selectedCulture)
        {
            var uri = new Uri(Navigation.Uri)
                .GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped);
            var cultureEscaped = Uri.EscapeDataString(selectedCulture.Name);
            var uriEscaped = Uri.EscapeDataString(uri);

            Navigation.NavigateTo(
                $"Culture/Set?culture={cultureEscaped}&redirectUri={uriEscaped}",
                forceLoad: true);
        }
    }
}

CultureSelector コンポーネントに MainLayout コンポーネントを追加します。 </main> ファイルの終了 Components/Layout/MainLayout.razor タグの中に次のマークアップを配置します。

CultureSelector コンポーネントに MainLayout コンポーネントを追加します。 </main> ファイルの終了 Shared/MainLayout.razor タグの中に次のマークアップを配置します。

<article class="bottom-row px-4">
    <CultureSelector />
</article>

CultureExample1」セクションに示されている コンポーネントを使用して、前述の例のしくみを確認します。

前の例では、 コンポーネント () の Routes コンポーネントで対話型サーバー レンダー モードを指定することで、アプリで "App" インタラクティビティが採用されていることを前提としています。Components/App.razor

<Routes @rendermode="InteractiveServer" />

アプリでページ/コンポーネント単位のインタラクティビティが採用されている場合、次の変更を行います。

  • CultureExample1 コンポーネント ファイル (Components/Pages/CultureExample1.razor) の先頭に対話型サーバー レンダリング モードを追加します。

    @rendermode InteractiveServer
    
  • アプリのメイン レイアウト (Components/Layout/MainLayout.razor) で、CultureSelector コンポーネントに対話型サーバー レンダリング モードを適用します。

    <CultureSelector @rendermode="InteractiveServer" />
    

ユーザー設定に応じて、Blazor Web App にカルチャを動的に設定する

このセクションは、自動 (サーバーおよび WebAssembly) 対話機能を採用する Blazor Web App に適用されます。

アプリでユーザーの設定を保存する場所の例として、ブラウザーのローカル ストレージ (クライアント側のシナリオで一般的)、ローカライズ cookie またはデータベース (サーバー側のシナリオで一般的)、ローカル ストレージとローカライズ cookie (サーバーと WebAssembly コンポーネントを備えたBlazor Web App)、または外部データベースにアタッチされ、Web API からアクセスされる外部サービスがあります。 次の例は、クライアント側レンダリング (CSR) コンポーネントにブラウザーのローカル ストレージを使い、サーバー側でレンダリング (SSR) コンポーネントにローカライズ cookie を使う方法を示しています。

.Client プロジェクトの更新

Microsoft.Extensions.Localization パッケージを .Client プロジェクトに追加します。

注意

.NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。

BlazorWebAssemblyLoadAllGlobalizationData プロジェクト ファイルで true プロパティを .Client に設定します。

<PropertyGroup>
  <BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
</PropertyGroup>

System.GlobalizationMicrosoft.JSInterop の名前空間を、.Client プロジェクトの Program ファイルの先頭に追加します。

using System.Globalization;
using Microsoft.JSInterop;

次の行を削除します。

- await builder.Build().RunAsync();

前の行を、次のコードで置き換えます。 このコードを使用すると、Blazor のローカライズ サービスが AddLocalization を含むアプリのサービス コレクションに追加されたり、JS 相互運用が JS への呼び出しに使用されたりすることに加え、ローカル ストレージからユーザーが選択したカルチャが取得されます。 ローカル ストレージにユーザーのカルチャが含まれていない場合、コードは英語 (米国) (en-US) の既定値を設定します。

builder.Services.AddLocalization();

var host = builder.Build();

const string defaultCulture = "en-US";

var js = host.Services.GetRequiredService<IJSRuntime>();
var result = await js.InvokeAsync<string>("blazorCulture.get");
var culture = CultureInfo.GetCultureInfo(result ?? defaultCulture);

if (result == null)
{
    await js.InvokeVoidAsync("blazorCulture.set", defaultCulture);
}

CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;

await host.RunAsync();

注意

現在、 Blazor WebAssembly アプリは DefaultThreadCurrentCulture に基づいてリソースのみを読み込みます。 詳細については、Blazor WASM が現在のカルチャのみに依存する (現在の UI カルチャは尊重されない) (dotnet/aspnetcore #56824) に関する記事をご覧ください。

次の CultureSelector コンポーネントを .Client プロジェクトに追加します。

このコンポーネントは、SSR または CSR コンポーネントのいずれかで機能する次のアプローチを採用しています。

  • クライアント側のグローバリゼーション データには、サーバー側のグローバリゼーション データが提供するカルチャ表示名のローカライズされたテキストが含まれるため、ドロップダウン リストで使用できる各カルチャの表示名はディクショナリによって提供されます。 たとえば、サーバー側ローカライズには、English (United States) がカルチャである場合は en-US、別のカルチャが使われている場合は Ingles () が表示されます。 カルチャ表示名のローカライズは Blazor WebAssembly グローバリゼーションでは使用できないため、読み込まれたカルチャに対するクライアント上の米国英語の表示名は単に en-US になります。 カスタム ディクショナリを使うと、コンポーネントは少なくとも完全な英語のカルチャ名を表示できます。
  • ユーザーがカルチャを変更すると、JS 相互運用機能によってローカル ブラウザー ストレージにカルチャが設定され、コントローラー アクションによってそのカルチャでローカライズ cookie が更新されます。 コントローラーは、後述する「サーバー プロジェクトの更新」セクションでアプリに追加されます。

Pages/CultureSelector.razor:

@using System.Globalization
@inject IJSRuntime JS
@inject NavigationManager Navigation

<p>
    <label>
        Select your locale:
        <select @bind="@selectedCulture" @bind:after="ApplySelectedCultureAsync">
            @foreach (var culture in supportedCultures)
            {
                <option value="@culture">@cultureDict[culture.Name]</option>
            }
        </select>
    </label>
</p>

@code
{
    private Dictionary<string, string> cultureDict = 
        new()
        {
            { "en-US", "English (United States)" },
            { "es-CR", "Spanish (Costa Rica)" }
        };

    private CultureInfo[] supportedCultures = new[]
    {
        new CultureInfo("en-US"),
        new CultureInfo("es-CR"),
    };

    private CultureInfo? selectedCulture;

    protected override void OnInitialized()
    {
        selectedCulture = CultureInfo.CurrentCulture;
    }

    private async Task ApplySelectedCultureAsync()
    {
        if (CultureInfo.CurrentCulture != selectedCulture)
        {
            await JS.InvokeVoidAsync("blazorCulture.set", selectedCulture!.Name);

            var uri = new Uri(Navigation.Uri)
                .GetComponents(UriComponents.PathAndQuery, UriFormat.Unescaped);
            var cultureEscaped = Uri.EscapeDataString(selectedCulture.Name);
            var uriEscaped = Uri.EscapeDataString(uri);

            Navigation.NavigateTo(
                $"Culture/Set?culture={cultureEscaped}&redirectUri={uriEscaped}",
                forceLoad: true);
        }
    }
}

注意

IJSInProcessRuntime の詳細については、「ASP.NET Core Blazor で .NET メソッドから JavaScript 関数を呼び出す」を参照してください。

.Client プロジェクトに次の CultureClient コンポーネントを配置して、CSR コンポーネントのグローバリゼーションがどのように機能するかを調べます。

Pages/CultureClient.razor:

@page "/culture-client"
@rendermode InteractiveWebAssembly
@using System.Globalization

<PageTitle>Culture Client</PageTitle>

<h1>Culture Client</h1>

<ul>
    <li><b>CurrentCulture</b>: @CultureInfo.CurrentCulture</li>
    <li><b>CurrentUICulture</b>: @CultureInfo.CurrentUICulture</li>
</ul>

<h2>Rendered values</h2>

<ul>
    <li><b>Date</b>: @dt</li>
    <li><b>Number</b>: @number.ToString("N2")</li>
</ul>

<h2><code>&lt;input&gt;</code> elements that don't set a <code>type</code></h2>

<p>
    The following <code>&lt;input&gt;</code> elements use
    <code>CultureInfo.CurrentCulture</code>.
</p>

<ul>
    <li><label><b>Date:</b> <input @bind="dt" /></label></li>
    <li><label><b>Number:</b> <input @bind="number" /></label></li>
</ul>

<h2><code>&lt;input&gt;</code> elements that set a <code>type</code></h2>

<p>
    The following <code>&lt;input&gt;</code> elements use
    <code>CultureInfo.InvariantCulture</code>.
</p>

<ul>
    <li><label><b>Date:</b> <input type="date" @bind="dt" /></label></li>
    <li><label><b>Number:</b> <input type="number" @bind="number" /></label></li>
</ul>

@code {
    private DateTime dt = DateTime.Now;
    private double number = 1999.69;
}

サーバー プロジェクトの更新

サーバー プロジェクトに Microsoft.Extensions.Localization パッケージを追加します。

注意

.NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。

サーバー側のアプリはローカライズ ミドルウェアを使用してローカライズされます。 AddLocalization を使用して、ローカライズ サービスをアプリに追加します。

サービスが登録されているサーバー プロジェクトの Program ファイル内。

builder.Services.AddLocalization();

アプリの既定とサポートされるカルチャを RequestLocalizationOptions に設定します。

要求処理パイプラインで MapRazorComponents を呼び出す前に、次のコードを置換します。

var supportedCultures = new[] { "en-US", "es-CR" };
var localizationOptions = new RequestLocalizationOptions()
    .SetDefaultCulture(supportedCultures[0])
    .AddSupportedCultures(supportedCultures)
    .AddSupportedUICultures(supportedCultures);

app.UseRequestLocalization(localizationOptions);

次の例では、ローカライズ ミドルウェアによって読み取ることができる cookie で現在のカルチャを設定する方法を示します。

App コンポーネントには次の名前空間が必要です。

App コンポーネント ファイル (Components/App.razor) の先頭に次を追加します。

@using System.Globalization
@using Microsoft.AspNetCore.Localization

クライアント側レンダリング用のアプリのカルチャは、Blazor フレームワークの API を使用して設定されます。 ユーザーのカルチャ選択は、CSR コンポーネントのブラウザーのローカル ストレージに保存できます。

Blazor の <script> タグの後に、ブラウザーのローカル ストレージを使ってユーザーのカルチャ選択を取得および設定する JS 関数を指定します。

<script>
  window.blazorCulture = {
    get: () => window.localStorage['BlazorCulture'],
    set: (value) => window.localStorage['BlazorCulture'] = value
  };
</script>

注意

前の例では、クライアントがグローバル関数で汚染されます。 実稼働アプリでのより適切なアプローチについては、「JavaScript モジュール内の JavaScript 分離」を参照してください。

@code コンポーネント ファイルの末尾に次の App ブロックを追加します。

@code {
    [CascadingParameter]
    public HttpContext? HttpContext { get; set; }

    protected override void OnInitialized()
    {
        HttpContext?.Response.Cookies.Append(
            CookieRequestCultureProvider.DefaultCookieName,
            CookieRequestCultureProvider.MakeCookieValue(
                new RequestCulture(
                    CultureInfo.CurrentCulture,
                    CultureInfo.CurrentUICulture)));
    }
}

サーバー プロジェクトがコントローラー アクションを処理するように構成されていない場合:

  • AddControllers ファイルでサービス コレクションに対して Program を呼び出して、MVC サービスを追加します。

    builder.Services.AddControllers();
    
  • Program (MapControllers) で IEndpointRouteBuilder を呼び出し、app ファイルにコントローラー エンドポイント ルーティングを追加します。

    app.MapControllers();
    

ユーザーが SSR コンポーネントのカルチャを選択できるようにするには、ローカライズ による "リダイレクトベースのアプローチ" を使います。cookie アプリでは、コントローラーへのリダイレクトによって、ユーザーが選択したカルチャが保持されます。 コントローラーによって、ユーザーが選択したカルチャが cookie に設定され、ユーザーは元の URI にリダイレクトされます。 このプロセスは、ユーザーがセキュリティで保護されたリソースにアクセスしようとしたときに、Web アプリで発生する処理に似ています。ユーザーがサインイン ページにリダイレクトされ、元のリソースに戻されます。

Controllers/CultureController.cs:

using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;

[Route("[controller]/[action]")]
public class CultureController : Controller
{
    public IActionResult Set(string culture, string redirectUri)
    {
        if (culture != null)
        {
            HttpContext.Response.Cookies.Append(
                CookieRequestCultureProvider.DefaultCookieName,
                CookieRequestCultureProvider.MakeCookieValue(
                    new RequestCulture(culture, culture)));
        }

        return LocalRedirect(redirectUri);
    }
}

警告

前の例に示すように、LocalRedirect アクションの結果を使って、オープン リダイレクト攻撃を防ぎます。 詳細については、「ASP.NET Core でオープン リダイレクト攻撃を防止する」を参照してください。

CultureSelector コンポーネントに MainLayout コンポーネントを追加します。 </main> ファイルの終了 Components/Layout/MainLayout.razor タグの中に次のマークアップを配置します。

<article class="bottom-row px-4">
    <CultureSelector @rendermode="InteractiveAuto" />
</article>

CultureExample1」セクションに示されている コンポーネントを使用して、前述の例のしくみを確認します。

サーバー プロジェクトに次の CultureServer コンポーネントを配置して、SSR コンポーネントのグローバリゼーションがどのように機能するかを調べます。

Components/Pages/CultureServer.razor:

@page "/culture-server"
@rendermode InteractiveServer
@using System.Globalization

<PageTitle>Culture Server</PageTitle>

<h1>Culture Server</h1>

<ul>
    <li><b>CurrentCulture</b>: @CultureInfo.CurrentCulture</li>
    <li><b>CurrentUICulture</b>: @CultureInfo.CurrentUICulture</li>
</ul>

<h2>Rendered values</h2>

<ul>
    <li><b>Date</b>: @dt</li>
    <li><b>Number</b>: @number.ToString("N2")</li>
</ul>

<h2><code>&lt;input&gt;</code> elements that don't set a <code>type</code></h2>

<p>
    The following <code>&lt;input&gt;</code> elements use
    <code>CultureInfo.CurrentCulture</code>.
</p>

<ul>
    <li><label><b>Date:</b> <input @bind="dt" /></label></li>
    <li><label><b>Number:</b> <input @bind="number" /></label></li>
</ul>

<h2><code>&lt;input&gt;</code> elements that set a <code>type</code></h2>

<p>
    The following <code>&lt;input&gt;</code> elements use
    <code>CultureInfo.InvariantCulture</code>.
</p>

<ul>
    <li><label><b>Date:</b> <input type="date" @bind="dt" /></label></li>
    <li><label><b>Number:</b> <input type="number" @bind="number" /></label></li>
</ul>

@code {
    private DateTime dt = DateTime.Now;
    private double number = 1999.69;
}

CultureClientCultureServer の両コンポーネントを Components/Layout/NavMenu.razor のサイドバー ナビゲーションに追加します。

<div class="nav-item px-3">
    <NavLink class="nav-link" href="culture-server">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Culture (Server)
    </NavLink>
</div>
<div class="nav-item px-3">
    <NavLink class="nav-link" href="culture-client">
        <span class="bi bi-list-nested-nav-menu" aria-hidden="true"></span> Culture (Client)
    </NavLink>
</div>

対話型自動コンポーネント

このセクションのガイダンスは、対話型自動レンダリング モードを採用するコンポーネントにも適用できます。

@rendermode InteractiveAuto

ローカリゼーション

アプリで動的カルチャの選択がまだサポートされていない場合は、Microsoft.Extensions.Localization パッケージをアプリに追加します。

注意

.NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。

クライアント側のローカライズ

アプリのプロジェクト ファイル (BlazorWebAssemblyLoadAllGlobalizationData) で true プロパティを .csproj に設定します。

<PropertyGroup>
  <BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
</PropertyGroup>

Program ファイルで、System.Globalization の名前空間をファイルの先頭に追加します。

using System.Globalization;

Blazor を使用して、アプリのサービスコレクションに AddLocalization のローカライズ サービスを追加します。

builder.Services.AddLocalization();

サーバー側のローカライズ

ローカライズ ミドルウェアを使用して、アプリのカルチャを設定します。

アプリで動的カルチャの選択がまだサポートされていない場合は、次のようにします。

  • AddLocalization を使用して、ローカライズ サービスをアプリに追加します。
  • アプリの既定のカルチャとサポートされるカルチャを Program ファイルで指定します。 次の例では、英語 (米国) とスペイン語 (コスタリカ) でサポートされるカルチャを構成しています。
builder.Services.AddLocalization();

要求カルチャを確認する可能性があるミドルウェアの前に、要求ローカライズ ミドルウェアを配置します。 一般に、ミドルウェアは、 MapRazorComponentsを呼び出す直前に配置します。

ルーティング ミドルウェア (UseRouting) が処理パイプラインに追加された直後:

var supportedCultures = new[] { "en-US", "es-CR" };
var localizationOptions = new RequestLocalizationOptions()
    .SetDefaultCulture(supportedCultures[0])
    .AddSupportedCultures(supportedCultures)
    .AddSupportedUICultures(supportedCultures);

app.UseRequestLocalization(localizationOptions);

ミドルウェア パイプラインでのローカライズ ミドルウェアの順序付けについては、「ASP.NET Core のミドルウェア」を参照してください。

  • AddLocalization を使用して、ローカライズ サービスをアプリに追加します。
  • アプリの既定とサポートされるカルチャを Startup.Configure (Startup.cs) で指定します。 次の例では、英語 (米国) とスペイン語 (コスタリカ) でサポートされるカルチャを構成しています。

Startup.ConfigureServices (Startup.cs):

services.AddLocalization();

ルーティング ミドルウェア (Startup.Configure) が処理パイプラインに追加された直後のUseRouting:

var supportedCultures = new[] { "en-US", "es-CR" };
var localizationOptions = new RequestLocalizationOptions()
    .SetDefaultCulture(supportedCultures[0])
    .AddSupportedCultures(supportedCultures)
    .AddSupportedUICultures(supportedCultures);

app.UseRequestLocalization(localizationOptions);

Startup.Configure のミドルウェア パイプラインでのローカライズ ミドルウェアの順序付けについては、「ASP.NET Core のミドルウェア」を参照してください。

アプリでユーザーのカルチャ設定の格納に基づきリソースをローカライズする必要がある場合は、ローカライズ カルチャ cookie を使用します。 cookie を使用すると、WebSocket 接続によってカルチャを正しく伝達できることを確実にします。 ローカライズ スキームが URL パスまたはクエリ文字列に基づいている場合は、スキームが WebSockets を使用できない可能性があるため、カルチャを保持できません。 したがって、ローカライズ カルチャ cookie の使用をお勧めします。 この記事の「ユーザー設定によってサーバー側のカルチャを動的に設定する」セクションを参照して、ユーザーが選択したカルチャを保持する Razor 式の例を参照してください。

ローカライズされたリソースの例

このセクションのローカライズされたリソースの例は、この記事の前の例と連動しています。アプリのサポートされているカルチャは、既定のロケールとして英語 (en)、ユーザー選択可能またはブラウザー指定の代替ロケールとしてスペイン語 (es) になっています。

それぞれのロケールごとのリソース ファイルを作成します。 次の例では、英語とスペイン語の Greeting 文字列のリソースが作成されます。

  • 英語 (en): Hello, World!
  • スペイン語 (es): ¡Hola, Mundo!

注意

Pages フォルダーを右クリックし、[追加]>[新しい項目]>[リソースファイル] の順に選択することで、次のリソース ファイルを Visual Studio に追加できます。 そのファイルに CultureExample2.resx という名前を付けます。 エディターが表示されたら、新しいエントリのデータを指定します。 名前Greeting に設定し、Hello, World! に設定します。 ファイルを保存します。

Visual Studio Code を使用している場合は、Tim Heuer の ResX Viewer and Editor をインストールすることをお勧めします。 CultureExample2.resx フォルダーに空の Pages ファイルを追加します。 この拡張機能は、UI でファイル管理を自動的に引き継ぎます。 [新しいリソースの追加] ボタン選択します。 指示に沿って、Greeting (キー)、Hello, World! (値)、None (コメント) のエントリを追加します。 ファイルを保存します。 ファイルを閉じてから再度開くと、Greeting リソースが表示されます。

Tim Heuer の ResX Viewer and Editor は Microsoft によって所有または保守管理されておらず、Microsoft サポート契約またはライセンスの対象でもありません。

次に、一般的なリソース ファイルを示します。 Visual Studio の組み込みリソース ファイル エディターや、リソース ファイルの作成および編集を行うための拡張機能を備えた Visual Studio Code など、統合開発環境 (IDE) に組み込まれているツールを使用したくない場合は、手動でリソース ファイルをアプリの Pages フォルダーに配置することができます。

Pages/CultureExample2.resx:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" use="required" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <data name="Greeting" xml:space="preserve">
    <value>Hello, World!</value>
  </data>
</root>

注意

Pages フォルダーを右クリックし、[追加]>[新しい項目]>[リソースファイル] の順に選択することで、次のリソース ファイルを Visual Studio に追加できます。 そのファイルに CultureExample2.es.resx という名前を付けます。 エディターが表示されたら、新しいエントリのデータを指定します。 名前Greeting に設定し、¡Hola, Mundo! に設定します。 ファイルを保存します。

Visual Studio Code を使用している場合は、Tim Heuer の ResX Viewer and Editor をインストールすることをお勧めします。 CultureExample2.resx フォルダーに空の Pages ファイルを追加します。 この拡張機能は、UI でファイル管理を自動的に引き継ぎます。 [新しいリソースの追加] ボタン選択します。 指示に沿って、Greeting (キー)、¡Hola, Mundo! (値)、None (コメント) のエントリを追加します。 ファイルを保存します。 ファイルを閉じてから再度開くと、Greeting リソースが表示されます。

次に、一般的なリソース ファイルを示します。 Visual Studio の組み込みリソース ファイル エディターや、リソース ファイルの作成および編集を行うための拡張機能を備えた Visual Studio Code など、統合開発環境 (IDE) に組み込まれているツールを使用したくない場合は、手動でリソース ファイルをアプリの Pages フォルダーに配置することができます。

Pages/CultureExample2.es.resx:

<?xml version="1.0" encoding="utf-8"?>
<root>
  <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
    <xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
    <xsd:element name="root" msdata:IsDataSet="true">
      <xsd:complexType>
        <xsd:choice maxOccurs="unbounded">
          <xsd:element name="metadata">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" />
              </xsd:sequence>
              <xsd:attribute name="name" use="required" type="xsd:string" />
              <xsd:attribute name="type" type="xsd:string" />
              <xsd:attribute name="mimetype" type="xsd:string" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="assembly">
            <xsd:complexType>
              <xsd:attribute name="alias" type="xsd:string" />
              <xsd:attribute name="name" type="xsd:string" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="data">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
                <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
              <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
              <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
              <xsd:attribute ref="xml:space" />
            </xsd:complexType>
          </xsd:element>
          <xsd:element name="resheader">
            <xsd:complexType>
              <xsd:sequence>
                <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
              </xsd:sequence>
              <xsd:attribute name="name" type="xsd:string" use="required" />
            </xsd:complexType>
          </xsd:element>
        </xsd:choice>
      </xsd:complexType>
    </xsd:element>
  </xsd:schema>
  <resheader name="resmimetype">
    <value>text/microsoft-resx</value>
  </resheader>
  <resheader name="version">
    <value>2.0</value>
  </resheader>
  <resheader name="reader">
    <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <resheader name="writer">
    <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
  </resheader>
  <data name="Greeting" xml:space="preserve">
    <value>¡Hola, Mundo!</value>
  </data>
</root>

次のコンポーネントは、Greeting でローカライズされた IStringLocalizer<T> 文字列を使用する方法を示しています。 次の例の Razor マークアップ @Loc["Greeting"] では、前のリソース ファイルで設定されている Greeting 値にキーを付けた文字列をローカライズします。

Microsoft.Extensions.Localization の名前空間をアプリの _Imports.razor ファイルに追加します。

@using Microsoft.Extensions.Localization

CultureExample2.razor:

@page "/culture-example-2"
@using System.Globalization
@inject IStringLocalizer<CultureExample2> Loc

<h1>Culture Example 2</h1>

<ul>
    <li><b>CurrentCulture</b>: @CultureInfo.CurrentCulture</li>
    <li><b>CurrentUICulture</b>: @CultureInfo.CurrentUICulture</li>
</ul>

<h2>Greeting</h2>

<p>
    @Loc["Greeting"]
</p>

<p>
    @greeting
</p>

@code {
    private string? greeting;

    protected override void OnInitialized()
    {
        greeting = Loc["Greeting"];
    }
}

必要に応じて、CultureExample2 コンポーネントのメニュー項目を NavMenu コンポーネント (NavMenu.razor) のナビゲーションに追加します。

WebAssembly カルチャ プロバイダーの参照ソース

Blazor フレームワークによるローカライズの処理方法について詳しくは、ASP.NET Core 参照ソースで WebAssemblyCultureProvider クラスを参照してください。

注意

通常、.NET 参照ソースへのドキュメント リンクを使用すると、リポジトリの既定のブランチが読み込まれます。このブランチは、.NET の次回リリースに向けて行われている現在の開発を表します。 特定のリリースのタグを選択するには、[Switch branches or tags](ブランチまたはタグの切り替え) ドロップダウン リストを使います。 詳細については、「ASP.NET Core ソース コードのバージョン タグを選択する方法」 (dotnet/AspNetCore.Docs #26205) を参照してください。

共有リソース

ローカライズ共有リソースを作成するには、次の方法を使用します。

  • Microsoft.Extensions.Localization パッケージがプロジェクトによって参照されていることを確認します。

    注意

    .NET アプリへのパッケージの追加に関するガイダンスについては、「パッケージ利用のワークフロー」 (NuGet ドキュメント) の "パッケージのインストールと管理" に関する記事を参照してください。 NuGet.org で正しいパッケージ バージョンを確認します。

  • プロジェクトの _Imports ファイル内のエントリを使用して、Microsoft.Extensions.Localization 名前空間がプロジェクトの Razor コンポーネントで使用可能であることを確認します。

    @using Microsoft.Extensions.Localization
    
  • 任意のクラス名を持つダミーのクラスを作成します。 次に例を示します。

    • アプリは BlazorSample 名前空間を使用し、ローカライズ資産は BlazorSample.Localization 名前空間を使用します。
    • ダミー クラスの名前は SharedResource です。
    • クラス ファイルは、アプリのルートにある Localization フォルダーに配置されます。

    注意

    自動生成されたデザイナー ファイル (たとえば、SharedResources.Designer.cs) を使用しないでください。 ダミー クラスは、共有リソース クラスとして機能することを目的とします。 デザイナー ファイルが存在すると、名前空間の競合が発生します。

    Localization/SharedResource.cs:

    namespace BlazorSample.Localization;
    
    public class SharedResource
    {
    }
    
  • Embedded resourceを使用して共有リソース ファイルを作成します。 次に例を示します。

    • ファイルは、ダミー Localization クラス (SharedResource) により Localization/SharedResource.cs フォルダーに配置されます。

    • ダミー クラスの名前と一致するようにリソース ファイルに名前を付けます。 次のサンプル ファイルには、既定のローカライズ ファイルとスペイン語 (es) ローカライズ用のファイルが含まれます。

    • Localization/SharedResource.resx

    • Localization/SharedResource.es.resx

    警告

    このセクションのアプローチに従うときは、 LocalizationOptions.ResourcesPath を同時に設定し、IStringLocalizerFactory.Create を使用してリソースを読み込むことはできません。

  • IStringLocalizer<T> コンポーネントの挿入された Razor のダミー クラスを参照するには、ローカライズ名前空間の @using ディレクティブを配置するか、ダミー クラス参照にローカライズ名前空間を含めます。 次の例で以下を実行します。

    • 最初の例では、Localization ディレクティブを使用して SharedResource ダミー クラスの @using 名前空間を指定します。
    • 2 番目の例では、SharedResource ダミー クラスの名前空間を明示的に指定します。

    Razor コンポーネントでは、次の "いずれか" の方法を使用します。

    @using Localization
    @inject IStringLocalizer<SharedResource> Loc
    
    @inject IStringLocalizer<Localization.SharedResource> Loc
    

その他のガイダンスについては、「ASP.NET Core でのグローバリゼーションとローカリゼーション」を参照してください。

開発者ツールの [センサー] ウィンドウを使用した場所のオーバーライド

Google Chrome または Microsoft Edge 開発者ツールの [センサー] ペインを使用して場所のオーバーライドを使用すると、フォールバック言語はプリレンダリング後にリセットされます。 テスト時に [センサー] ペインを使用して言語を設定しないでください。 言語はブラウザーの言語設定を使用して設定します。

詳細については、「Blazorローカライズが InteractiveServer で動作しない (dotnet/aspnetcore #53707)」を参照してください。

その他のリソース