dotnet-localization について
.NET で多言語アプリを開発する人向けのローカライズスキルです。リソース形式、文字列取得、カルチャ別書式、右から左へのレイアウト、複数形、および各種 .NET UI フレームワークとの連携を解説します。
主な機能
- 01.resx と JSON リソースの利用
- 02IStringLocalizer による文字列ローカライズ
- 03カルチャ別の日付・数値・通貨書式
- 04右から左へのレイアウト対応
- 05ローカライズライブラリによる複数形処理
- 06各種 .NET UI フレームワークとの連携
活用シーン
- 01.NET アプリへの翻訳テキスト追加
- 02指定カルチャに応じた価格・日付の書式設定
- 03右から左に書く言語向けの画面準備
- 04複数ロケールでの複数形処理の選択
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ wshaddix/ dotnet-skills --skill dotnet-localizationインストール後、Claude Code で/dotnet-localizationを入力して読み込まれたか確認します。
詳細
dotnet-localization
包括的な .NET の国際化とローカライズ:.resx リソースファイルとサテライトアセンブリ、最新の代替手段(JSON リソース、AOT 向けソースジェネレーター)、IStringLocalizer のパターン、CultureInfo を使った日付・数値・通貨の書式設定、RTL レイアウトのサポート、複数形処理エンジン、Blazor、MAUI、Uno Platform、WPF におけるフレームワークごとのローカライズ統合。
バージョンの前提: .NET 8.0 以降を基準とします。IStringLocalizer は .NET Core 1.0 以降で安定しており、ローカライズ API は .NET 5 以降で安定しています。.NET 9 以降の機能には明示的に印を付けています。
対象範囲の境界: このスキルでは、リソース形式、IStringLocalizer、書式設定、RTL、複数形処理といった横断的なローカライズの課題をすべて扱います。UI フレームワークの各セクションでは、アーキテクチャの概要を示し、実装パターンの詳細についてはフレームワーク固有のスキルを参照します。
対象外: Blazor コンポーネントの詳細なパターンについては、[skill:dotnet-blazor-components] を参照してください。MAUI 開発の詳細なパターンについては、[skill:dotnet-maui-development] を参照してください。Uno Platform のプロジェクト構成と Extensions エコシステムについては、[skill:dotnet-uno-platform] を参照してください。WPF の Host ビルダーと MVVM パターンについては、[skill:dotnet-wpf-modern] を参照してください。ソースジェネレーターの作成(Roslyn API)については、[skill:dotnet-csharp-source-generators] を参照してください。
相互参照:Blazor コンポーネントのライフサイクルについては [skill:dotnet-blazor-components]、MAUI アプリの構成については [skill:dotnet-maui-development]、Uno Extensions と x:Uid については [skill:dotnet-uno-platform]、最新の .NET での WPF については [skill:dotnet-wpf-modern] を参照してください。
.resx リソースファイル
概要
リソースファイル(.resx)は、.NET の標準的なローカライズ形式です。これらはサテライトアセンブリにコンパイルされ、自動的なカルチャフォールバックを行う ResourceManager によって解決されます。
カルチャフォールバックチェーン
リソースは、より具体的なものから順に検索され、一致するものが見つかるまでフォールバックします。
sr-Cyrl-RS.resx -> sr-Cyrl.resx -> sr.resx -> Resources.resx (default/neutral)
既定の .resx ファイル(カルチャサフィックスなし)が唯一の信頼できる情報源です。翻訳ファイルには、既定のファイルに存在しないキーを含めてはいけません。
プロジェクトのセットアップ
<PropertyGroup>
<NeutralLanguage>en-US</NeutralLanguage>
</PropertyGroup>
<ItemGroup>
<EmbeddedResource Include="Resources\Messages.resx" />
<EmbeddedResource Include="Resources\Messages.fr-FR.resx" />
<EmbeddedResource Include="Resources\Messages.de-DE.resx" />
</ItemGroup>
リソースファイルの構造
<?xml version="1.0" encoding="utf-8"?>
<root>
<data name="Welcome" xml:space="preserve">
<value>Welcome to the application</value>
<comment>Shown on the home page</comment>
</data>
<data name="ItemCount" xml:space="preserve">
<value>You have {0} item(s)</value>
<comment>{0} = number of items</comment>
</data>
</root>
リソースへのアクセス
// Via generated strongly-typed class (ResXFileCodeGenerator custom tool)
string welcome = Messages.Welcome;
// Via ResourceManager directly
var rm = new ResourceManager("MyApp.Resources.Messages",
typeof(Messages).Assembly);
string welcome = rm.GetString("Welcome", CultureInfo.CurrentUICulture);
最新の代替手段
JSON ベースのリソース
構成にすでに JSON を使用しているプロジェクト向けの軽量な代替手段です。JSON ファイルをバックエンドとする IStringLocalizer 実装を提供するライブラリがあります。
// Resources/en-US.json
{
"Welcome": "Welcome to the application",
"ItemCount": "You have {0} item(s)"
}
ライブラリ:
Senlin.Mo.Localization— JSON ベースのIStringLocalizerEmbedded.Json.Localization— 埋め込み JSON リソース
JSON リソースは ASP.NET Core で広く使われていますが、.resx の組み込みツールサポート(Visual Studio デザイナー、サテライトアセンブリのコンパイル)がありません。
AOT 互換性のためのソースジェネレーター
従来の ResourceManager を使用する .resx は、実行時にリフレクションを使用するため、Native AOT やトリミングに適していません。ソースジェネレーターを使うと、コンパイル時に型安全なアクセサークラスを生成でき、実行時のリフレクションを排除できます。
推奨するソースジェネレーター:
| ジェネレーター | 説明 | AOT 対応 |
|---|---|---|
| ResXGenerator (ycanardeau) | IStringLocalizer のサポートと DI 登録を備えた型安全なクラス | はい |
| VocaDb.ResXFileCodeGenerator | 元祖の型安全な .resx ソースジェネレーター | はい |
組み込みの ResXFileCodeGenerator | Visual Studio のカスタムツール(Roslyn ソースジェネレーターではありません) | いいえ — 静的プロパティを生成しますが、引き続き ResourceManager を使用します |
<ItemGroup>
<PackageReference Include="ResXGenerator" Version="1.*"
PrivateAssets="all" />
</ItemGroup>
// Generated at compile time -- no runtime reflection
string welcome = Messages.Welcome;
// With DI registration (ResXGenerator)
services.AddResXLocalization();
推奨: リソース形式には、最も幅広いツールサポートを備えた .resx ファイルを使用し、AOT やトリミングが必要な場合はソースジェネレーターを組み合わせてください。JSON リソースは、軽量なプロジェクトや構成中心のプロジェクトに限って使用してください。
IStringLocalizer のパターン
登録
var builder = WebApplication.CreateBuilder(args);
// Register localization services
builder.Services.AddLocalization(options =>
options.ResourcesPath = "Resources");
var app = builder.Build();
// Configure request localization middleware
var supportedCultures = new[] { "en-US", "fr-FR", "de-DE", "ja-JP" };
app.UseRequestLocalization(options =>
{
options.SetDefaultCulture(supportedCultures[0])
.AddSupportedCultures(supportedCultures)
.AddSupportedUICultures(supportedCultures);
});
IStringLocalizer
主要なローカライズインターフェイスです。DI 経由で注入できます。サービス、コントローラー、Blazor コンポーネント、ミドルウェアなど、あらゆる場所で使用できます。
public class OrderService
{
private readonly IStringLocalizer<OrderService> _localizer;
public OrderService(IStringLocalizer<OrderService> localizer)
{
_localizer = localizer;
}
public string GetConfirmation(int orderId)
{
// Indexer returns LocalizedString with implicit string conversion
return _localizer["OrderConfirmed", orderId];
// Resolves: "Order {0} confirmed" with orderId substituted
}
public bool IsTranslated(string key)
{
LocalizedString result = _localizer[key];
return !result.ResourceNotFound;
}
}
IViewLocalizer(MVC Razor ビューのみ)
ビューのパスに一致するリソースファイルを自動的に解決します。Blazor ではサポートされていません。
@* Views/Home/Index.cshtml *@
@inject IViewLocalizer Localizer
<h1>@Localizer["Welcome"]</h1>
<p>@Localizer["ItemCount", Model.Count]</p>
リソースファイルの場所:Resources/Views/Home/Index.en-US.resx
IHtmlLocalizer(MVC のみ)
書式引数を HTML エンコードし、リソース文字列内の HTML はそのまま保持する、HTML 対応のバリアントです。Blazor ではサポートされていません。
@inject IHtmlLocalizer<SharedResource> HtmlLocalizer
@* Resource: "Read our <a href='/terms'>terms</a>, {0}" *@
@* {0} is HTML-encoded, the <a> tag is preserved *@
<p>@HtmlLocalizer["TermsNotice", Model.UserName]</p>
それぞれの使い分け
| インターフェイス | 対象範囲 | HTML セーフ | Blazor | MVC |
|---|---|---|---|---|
IStringLocalizer<T> | あらゆる場所 | いいえ(プレーンテキスト) | はい | はい |
IViewLocalizer | ビュー固有の文字列 | いいえ | いいえ | はい |
IHtmlLocalizer<T> | リソース内の HTML | はい | いいえ | はい |
名前空間の解決
リソースの検索に失敗した場合は、名前空間の整合性を確認してください。IStringLocalizer<T> は、ResourcesPath を基準とした T の完全修飾型名を使用してリソースを解決します。名前空間とアセンブリの不一致を修正するには、RootNamespaceAttribute を使用してください。
[assembly: RootNamespace("MyApp")]
日付、数値、通貨の書式設定
CultureInfo
CultureInfo は、カルチャ固有の書式設定を行う中心的なクラスです。動作を制御するプロパティは、次の2つです。
CultureInfo.CurrentCulture-- 書式設定(日付、数値、通貨)を制御しますCultureInfo.CurrentUICulture-- リソースの検索(使用する.resxファイル)を制御します
// Always pass explicit CultureInfo -- never rely on thread defaults in server code
var date = DateTime.Now.ToString("D", new CultureInfo("fr-FR"));
// "vendredi 14 fevrier 2026"
var price = 1234.56m.ToString("C", new CultureInfo("de-DE"));
// "1.234,56 EUR" (uses NumberFormatInfo.CurrencySymbol)
var number = 1234567.89.ToString("N2", new CultureInfo("ja-JP"));
// "1,234,567.89"
サーバーサイドのベストプラクティス
// Use useUserOverride: false in server scenarios to avoid
// picking up user-customized formats
var culture = new CultureInfo("en-US", useUserOverride: false);
// Set culture per-request (ASP.NET Core middleware handles this)
CultureInfo.CurrentCulture = culture;
CultureInfo.CurrentUICulture = culture;
書式指定子
| 書式指定子 | 型 | 例(en-US) | 例(de-DE) |
|---|---|---|---|
"d" | 短い日付 | 2/14/2026 | 14.02.2026 |
"D" | 長い日付 | Friday, February 14, 2026 | Freitag, 14. Februar 2026 |
"C" | 通貨 | $1,234.56 | 1.234,56 EUR |
"N2" | 数値 | 1,234.57 | 1.234,57 |
"P1" | パーセント | 85.5% | 85,5 % |
RTL サポート
RTL カルチャの検出
bool isRtl = CultureInfo.CurrentCulture.TextInfo.IsRightToLeft;
// true for: ar-*, he-*, fa-*, ur-*, etc.
フレームワークごとの RTL パターン
Blazor: ネイティブの FlowDirection はありません。CSS の dir 属性を使用してください。
// wwwroot/js/app.js
window.setDocumentDirection = (dir) => document.documentElement.dir = dir;
// Set via named JS function (avoid eval -- causes CSP unsafe-eval violations)
await JSRuntime.InvokeVoidAsync("setDocumentDirection",
isRtl ? "rtl" : "ltr");
Blazor コンポーネントの高度なパターンについては、[skill:dotnet-blazor-components] を参照してください。
MAUI: VisualElement と Window の FlowDirection プロパティを使用します。
// Set at window level -- cascades to all children
window.FlowDirection = isRtl
? FlowDirection.RightToLeft
: FlowDirection.LeftToRight;
Android では、AndroidManifest.xml に android:supportsRtl="true" が必要です(MAUI ではデフォルトで設定されています)。MAUI の高度なパターンについては、[skill:dotnet-maui-development] を参照してください。
Uno Platform: WinUI の FlowDirection モデルを継承します。
<Page FlowDirection="RightToLeft">
</Page>
Uno Extensions と x:Uid バインディングについては、[skill:dotnet-uno-platform] を参照してください。
WPF: FrameworkElement の FlowDirection プロパティを使用します。
<Window FlowDirection="RightToLeft">
</Window>
最新の .NET における WPF のパターンについては、[skill:dotnet-wpf-modern] を参照してください。
複数形処理
問題点
単純な文字列補間では、言語をまたいだ複数形処理に対応できません。
// WRONG: English-only, breaks in languages with complex plural rules
$"You have {count} item{(count != 1 ? "s" : "")}"
アラビア語のように、複数形が6種類(zero、one、two、few、many、other)ある言語もあります。ポーランド語では、数の範囲に応じて「few」と「many」を区別します。
ICU MessageFormat(MessageFormat.NET)
ICU の複数形カテゴリーを使用した、CLDR 準拠の複数形処理です。国際化を重視するプロジェクトにおすすめです。
// Package: jeffijoe/messageformat.net (v5.0+, ships CLDR pluralizers)
var formatter = new MessageFormatter();
string pattern = "{count, plural, " +
"=0 {No items}" +
"one {# item}" +
"other {# items}}";
formatter.Format(pattern, new { count = 0 }); // "No items"
formatter.Format(pattern, new { count = 1 }); // "1 item"
formatter.Format(pattern, new { count = 42 }); // "42 items"
SmartFormat.NET
複数形処理を組み込みで備えた、柔軟なテキストテンプレートです。柔軟性を最大限に高めたいプロジェクトに適しています。
// Package: axuno/SmartFormat (v3.6.1+)
using SmartFormat;
Smart.Format("{count:plural:No items|# item|# items}",
new { count = 0 }); // "No items"
Smart.Format("{count:plural:No items|# item|# items}",
new { count = 1 }); // "1 item"
Smart.Format("{count:plural:No items|# item|# items}",
new { count = 5 }); // "5 items"
複数形処理エンジンの選択
| エンジン | CLDR 準拠 | API スタイル | 最適な用途 |
|---|---|---|---|
| MessageFormat.NET | 完全(CLDR カテゴリー) | ICU パターン文字列 | 標準準拠が必要な複数ロケール対応アプリ |
| SmartFormat.NET | 部分的(拡張可能) | .NET 書式文字列の拡張 | 複数形処理に対応した柔軟なテンプレート |
| 手動の条件分岐 | なし | string.Format + 条件分岐 | 英語のみで単純な2形式を使う場合 |
UI フレームワークとの統合
Blazor のローカライズ
Blazor がサポートしているのは IStringLocalizer のみです。IHtmlLocalizer と IViewLocalizer は利用できません。
コンポーネントへのインジェクション:
@inject IStringLocalizer<MyComponent> Loc
<h1>@Loc["Welcome"]</h1>
<p>@Loc["ItemCount", items.Count]</p>
レンダリングモードごとのカルチャ設定:
| レンダリングモード | カルチャの取得元 |
|---|---|
| Server / SSR | RequestLocalizationMiddleware(サーバー側) |
| WebAssembly | CultureInfo.DefaultThreadCurrentCulture + Blazor の開始オプション applicationCulture |
| Auto | 両方。初期読み込みにはサーバーのミドルウェア、クライアント側には WASM のカルチャを使用 |
WASM のグローバリゼーションデータ:
<PropertyGroup>
<BlazorWebAssemblyLoadAllGlobalizationData>true</BlazorWebAssemblyLoadAllGlobalizationData>
</PropertyGroup>
このプロパティを設定しない場合、Blazor WASM で読み込まれるのは ICU データの一部のみです。ダウンロードサイズを最小限に抑えるには、InvariantGlobalization=true を使用します(ローカライズが完全に無効になります)。
カルチャの動的な切り替え:
// CultureSelector component pattern:
// 1. Store selected culture in browser local storage
// 2. Set culture cookie via controller redirect (server-side)
// 3. Read cookie in RequestLocalizationMiddleware
Blazor コンポーネントの詳細なパターン(ライフサイクル、状態管理、JS 相互運用)については、[skill:dotnet-blazor-components] を参照してください。
MAUI のローカライズ
MAUI では .resx ファイルと、強く型付けされた生成プロパティを使用します。
リソースの設定:
Resources/
Strings/
AppResources.resx # Default (neutral) culture
AppResources.fr-FR.resx # French
AppResources.ja-JP.resx # Japanese
XAML バインディング:
<ContentPage xmlns:strings="clr-namespace:MyApp.Resources.Strings">
<Label Text="{x:Static strings:AppResources.Welcome}" />
<Button Text="{x:Static strings:AppResources.LoginButton}" />
</ContentPage>
コードからのアクセス:
string welcome = AppResources.Welcome;
プラットフォームごとの要件:
- iOS/Mac Catalyst:
Info.plistにCFBundleLocalizationsを追加します - Windows:
Package.appxmanifestに<Resource Language="...">エントリを追加します - すべてのプラットフォーム:csproj で
<NeutralLanguage>en-US</NeutralLanguage>を設定します
MAUI 開発の詳細なパターン(コントロール、ナビゲーション、プラットフォーム API)については、[skill:dotnet-maui-development] を参照してください。
Uno Platform のローカライズ
Uno では .resw ファイル(Windows リソース形式)と x:Uid を使用し、XAML リソースを自動的にバインドします。
リソースの構成:
Strings/
en/Resources.resw
fr-FR/Resources.resw
ja-JP/Resources.resw
登録:
// In Host builder configuration
.UseLocalization()
x:Uid を使用した XAML バインディング:
<TextBlock x:Uid="MainPage_Title" />
<Button x:Uid="LoginButton" />
実行時のカルチャ切り替え:
var localizationService = serviceProvider
.GetRequiredService<ILocalizationService>();
await localizationService.SetCurrentCultureAsync(
new CultureInfo("fr-FR"));
// Note: XAML x:Uid bindings retain old culture until app restart
既知の制限事項: x:Uid ベースのローカライズでは、SetCurrentCultureAsync の呼び出し後もアプリを再起動するまで古いカルチャが維持されます。コードベースの IStringLocalizer ではすぐに更新されます。
Uno Extensions エコシステムの設定と MVUX パターンについては、[skill:dotnet-uno-platform] を参照してください。
WPF のローカライズ
.NET 8 以降で推奨される方法: 実行時のロケール切り替えには、DynamicResource バインディングを使用する .resx ファイルを推奨します。LocBaml は避けてください(.NET Framework でのみ動作します)。
リソースディクショナリを使用する方法:
<ResourceDictionary xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:sys="clr-namespace:System;assembly=System.Runtime">
<sys:String x:Key="Welcome">Welcome</sys:String>
<sys:String x:Key="LoginButton">Log In</sys:String>
</ResourceDictionary>
<TextBlock Text="{DynamicResource Welcome}" />
<Button Content="{DynamicResource LoginButton}" />
実行時のロケール切り替え:
// Swap resource dictionary at runtime
var dict = new ResourceDictionary
{
Source = new Uri($"Resources/Strings.{cultureName}.xaml",
UriKind.Relative)
};
Application.Current.Resources.MergedDictionaries.Clear();
Application.Current.Resources.MergedDictionaries.Add(dict);
ResX を使用する方法(より簡単で、すべての .NET バージョンで動作):
// Standard .resx with generated class
string welcome = Strings.Welcome;
// Runtime switch
Thread.CurrentThread.CurrentUICulture = new CultureInfo("fr-FR");
// Re-read after culture change
string welcomeFr = Strings.Welcome; // Now returns French
コミュニティ製の選択肢:
- WPF Localization Extensions — 宣言的なローカライズのための XAML マークアップ拡張機能を備えた RESX ファイル
- LocBamlCore(h3xds1nz)— .NET 9 をサポートする非公式の移植版。最新の .NET で BAML をローカライズできます
WPF のホストビルダー、MVVM Toolkit、テーマ設定のパターンについては、[skill:dotnet-wpf-modern] を参照してください。
エージェント利用時の注意事項
- Blazor で
IHtmlLocalizerやIViewLocalizerを使用しないでください。 これらは MVC 専用の機能です。Blazor コンポーネントではIStringLocalizer<T>を使用してください。 - サーバーコードで
CultureInfo.CurrentCultureのスレッド既定値に依存しないでください。 書式設定メソッドには、常に明示的なCultureInfoを渡してください。サーバーのスレッドカルチャは、リクエストのカルチャと一致しない場合があります。 - 複数形をハードコードしないでください。 英語の「単数形/複数形」の区別は、6つの形式があるアラビア語やポーランド語などの言語では通用しません。CLDR に準拠した適切な複数形処理には、MessageFormat.NET または SmartFormat.NET を使用してください。
- .NET 8 以降の WPF で LocBaml を使用しないでください。 LocBaml は .NET Framework 専用のサンプルツールです。最新の WPF では
.resxファイルまたはリソースディクショナリを使用してください。 - Blazor WASM で
BlazorWebAssemblyLoadAllGlobalizationDataを設定し忘れないでください。 設定しないと ICU データが一部しか読み込まれず、多くのカルチャで日付や数値の書式設定が正しく行われません。 - 既定の
.resxファイルに存在しない翻訳キーを追加しないでください。 既定のリソースを唯一の信頼できる情報源とし、サテライトアセンブリにはそのサブセットのみを含めてください。 - AOT/トリミングされたアプリで
ResourceManagerを直接使用しないでください。 リフレクションに依存します。コンパイル時にリソースへアクセスするには、ソースジェネレーター(ResXGenerator)を使用してください。 - MAUI ではプラットフォーム固有の設定を忘れないでください。 iOS/Mac Catalyst では
Info.plistのCFBundleLocalizationsが必要です。Windows ではResource Languageエントリが必要です。