Skip to main content

ASP.NET Core ローカライズ完全ガイド

IStringLocalizer の導入から本番運用まで、ASP.NET Core でリソースベースのローカライズを設定し、AI で翻訳を自動化します。

1

ローカライズサービスの有効化

Program.cs で AddLocalization() を使用してローカライズサービスを登録し、対応カルチャーを設定して、リクエストローカライズミドルウェアを追加します。これで、ASP.NET Core アプリケーションのローカライズパイプライン全体が構成されます。

AddLocalization() は、DI コンテナーに IStringLocalizer と IStringLocalizerFactory を登録します。ResourcesPath は .resx ファイルの場所をフレームワークに伝えます。AddViewLocalization() は Razor ビューの IViewLocalizer を、AddDataAnnotationsLocalization() はローカライズされた検証メッセージを有効にします。
Program.cs
using Microsoft.AspNetCore.Localization;
using System.Globalization;

var builder = WebApplication.CreateBuilder(args);

// 1. Register localization services
builder.Services.AddLocalization(o => o.ResourcesPath = "Resources");

// 2. Add MVC with view/data-annotation localization
builder.Services.AddControllersWithViews()
    .AddViewLocalization()
    .AddDataAnnotationsLocalization();

var app = builder.Build();

// 3. Configure supported cultures
var supportedCultures = new[] { "en", "de", "ja", "es", "pt-BR" }
    .Select(c => new CultureInfo(c)).ToArray();

app.UseRequestLocalization(new RequestLocalizationOptions
{
    DefaultRequestCulture = new RequestCulture("en"),
    SupportedCultures = supportedCultures,
    SupportedUICultures = supportedCultures,
});

app.UseStaticFiles();
app.UseRouting();
app.MapControllers();
app.Run();
2

RESX リソースファイルの作成

ASP.NET Core は、翻訳に RESX(XML リソース)ファイルを使用します。カルチャーおよびクラスごとに HomeController.en.resx、HomeController.de.resx などのファイルを 1 つ作成します。フレームワークは現在のリクエストカルチャーに基づいて正しいファイルを解決します。

Resources/Controllers/HomeController.{culture}.resx
<!-- Resources/Controllers/HomeController.en.resx -->
<?xml version="1.0" encoding="utf-8"?>
<root>
  <data name="Welcome" xml:space="preserve">
    <value>Welcome to our application</value>
  </data>
  <data name="Greeting" xml:space="preserve">
    <value>Hello, {0}!</value>
  </data>
</root>

<!-- Resources/Controllers/HomeController.de.resx -->
<?xml version="1.0" encoding="utf-8"?>
<root>
  <data name="Welcome" xml:space="preserve">
    <value>Willkommen in unserer Anwendung</value>
  </data>
  <data name="Greeting" xml:space="preserve">
    <value>Hallo, {0}!</value>
  </data>
</root>
コントローラーとビューで共有する文字列には、専用の RESX ファイルを持つ SharedResource クラスを使用してください。ボタンラベル、ナビゲーション項目、共通の検証メッセージなどに適しています。多数のコントローラー別 RESX ファイルでキーが重複するのを防げます。
Shared resources for cross-cutting strings
<!-- Resources/SharedResource.en.resx — shared across controllers -->
<?xml version="1.0" encoding="utf-8"?>
<root>
  <data name="AppName" xml:space="preserve">
    <value>My Application</value>
  </data>
  <data name="Save" xml:space="preserve"><value>Save</value></data>
  <data name="Cancel" xml:space="preserve"><value>Cancel</value></data>
</root>

// Marker class (empty — only used for type lookup)
namespace MyApp;
public class SharedResource { }
3

コントローラーとサービスでの IStringLocalizer の使用

依存性注入を使用し、任意のコントローラー、サービス、ミドルウェアへ IStringLocalizer&lt;T&gt; を注入します。ジェネリック型パラメーター T が、読み込む RESX ファイルを決定します。翻訳文字列を取得するには、オプションの書式パラメーターとともに localizer["Key"] という角括弧構文を使用します。

Controllers/HomeController.cs
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;

public class HomeController(
    IStringLocalizer<HomeController> localizer,
    IStringLocalizer<SharedResource> shared) : Controller
{
    public IActionResult Index()
    {
        ViewData["Welcome"] = localizer["Welcome"];
        ViewData["AppName"] = shared["AppName"];

        // String interpolation with format parameters
        var greeting = localizer["Greeting", User.Identity?.Name ?? "Guest"];
        return View(new HomeViewModel { Greeting = greeting });
    }
}
IStringLocalizer が翻訳値ではなくキー名を返す場合は、1)RESX ファイル名がクラスの名前空間と一致していること、2)AddLocalization() の ResourcesPath が正しいフォルダーを指していること、3)Visual Studio で RESX ファイルの Build Action が Embedded Resource に設定されていることを確認してください。
4

リクエストカルチャーミドルウェアの設定

ASP.NET Core は、クエリ文字列、Cookie、Accept-Language ヘッダーの順に、プロバイダーチェーンを使用してリクエストカルチャーを決定します。/de/home のような URL ルートセグメントからカルチャーを読み取る独自プロバイダーも追加できます。

Custom RouteDataRequestCultureProvider
// Culture resolved in order: QueryString, Cookie, Accept-Language
// Custom provider: read culture from URL route segment /de/home
public class RouteDataRequestCultureProvider : RequestCultureProvider
{
    public override Task<ProviderCultureResult?> DetermineProviderCultureResult(
        HttpContext httpContext)
    {
        var culture = httpContext.GetRouteValue("culture")?.ToString();
        if (string.IsNullOrEmpty(culture))
            return NullProviderCultureResult;
        return Task.FromResult<ProviderCultureResult?>(
            new ProviderCultureResult(culture));
    }
}

// Register in Program.cs (route provider first = highest priority):
app.UseRequestLocalization(new RequestLocalizationOptions
{
    DefaultRequestCulture = new RequestCulture("en"),
    SupportedCultures = supportedCultures,
    SupportedUICultures = supportedCultures,
    RequestCultureProviders = new List<IRequestCultureProvider>
    {
        new RouteDataRequestCultureProvider(),
        new QueryStringRequestCultureProvider(),
        new CookieRequestCultureProvider(),
        new AcceptLanguageHeaderRequestCultureProvider(),
    }
});
Language Switcher Action
// Language switcher: persist choice in cookie
[HttpPost]
public IActionResult SetLanguage(string culture, string returnUrl)
{
    Response.Cookies.Append(
        CookieRequestCultureProvider.DefaultCookieName,
        CookieRequestCultureProvider.MakeCookieValue(new RequestCulture(culture)),
        new CookieOptions { Expires = DateTimeOffset.UtcNow.AddYears(1) });
    return LocalRedirect(returnUrl);
}
ミドルウェアの順序は重要です。UseRequestLocalization() は UseRouting() の後、UseEndpoints() または MapControllers() の前に呼び出す必要があります。遅すぎる位置に置くと、コントローラーの実行時にカルチャーが設定されません。
5

データアノテーションのローカライズ

[Required]、[StringLength]、[Display] などの検証属性は、ErrorMessage または Name プロパティに RESX キー名を指定してローカライズできます。Program.cs で AddDataAnnotationsLocalization() を呼び出して有効にします。

ViewModels/RegisterViewModel.cs
using System.ComponentModel.DataAnnotations;

public class RegisterViewModel
{
    [Required(ErrorMessage = "NameRequired")]
    [Display(Name = "FullName")]
    [StringLength(100, ErrorMessage = "NameLength", MinimumLength = 2)]
    public string Name { get; set; } = string.Empty;

    [Required(ErrorMessage = "EmailRequired")]
    [EmailAddress(ErrorMessage = "EmailInvalid")]
    [Display(Name = "EmailAddress")]
    public string Email { get; set; } = string.Empty;

    [Required(ErrorMessage = "PasswordRequired")]
    [StringLength(100, ErrorMessage = "PasswordLength", MinimumLength = 8)]
    [Display(Name = "Password")]
    public string Password { get; set; } = string.Empty;
}
// RESX keys map to ErrorMessage/Name values:
// RegisterViewModel.de.resx: NameRequired = "Name ist erforderlich"
// RegisterViewModel.de.resx: FullName = "Vollständiger Name"
データアノテーションのローカライズでは、RESX ファイルの検索に Controller ではなく ViewModel のクラス名を使用します。RegisterViewModel の場合、フレームワークは Resources/ViewModels/RegisterViewModel.de.resx を検索します。RESX ファイルがコントローラーにちなんだ名前の場合、検証メッセージはローカライズされません。
6

複数形と ICU メッセージの処理

.NET には、ICU のような複数形規則が組み込まれていません。単純な場合は個別の RESX キー(ItemCount_One、ItemCount_Other)をコードの switch とともに使用します。すべての CLDR 複数形カテゴリーに対応する ICU MessageFormat が必要な場合は、MessageFormat.NET ライブラリを使用してください。

Plural handling strategies
// Option 1: Separate RESX keys with code switch
// HomeController.en.resx: ItemCount_One = "You have {0} item"
// HomeController.en.resx: ItemCount_Other = "You have {0} items"
public string GetItemCount(int count)
{
    var key = count == 1 ? "ItemCount_One" : "ItemCount_Other";
    return _localizer[key, count];
}

// Option 2: ICU MessageFormat (dotnet add package MessageFormat.NET)
using Jeffijoe.MessageFormat;
var formatter = new MessageFormatter();

var pattern = "{count, plural, one {# item} other {# items}} in your cart";
var result = formatter.FormatMessage(pattern,
    new Dictionary<string, object> { { "count", 5 } });
// => "5 items in your cart"

// Arabic: 6 plural forms (zero, one, two, few, many, other)
var arPattern = @"{count, plural,
    zero {لا عناصر} one {عنصر واحد} two {عنصران}
    few {# عناصر} many {# عنصرًا} other {# عنصر}}";
単数形の判定に count == 1 を使用しないでください。フランス語では 0 を単数として扱い、ロシア語には「few」と「many」の別形式があり、アラビア語には 6 つの複数形カテゴリーがあります。CLDR 対応の複数形規則か、正しく処理する MessageFormat.NET のようなライブラリを使用してください。
7

Razor ビューのローカライズ

Razor ビューでは @inject を介して IViewLocalizer を使用します。ビューのファイルパスに基づいて RESX ファイルが解決されます。マークアップを含む HTML セーフな文字列には IHtmlLocalizer を使用してください。asp-for や asp-validation-for などの Tag Helper は、ローカライズされた Display 属性と ErrorMessage 属性を自動的に使用します。

Views/Home/Index.cshtml
@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@inject IHtmlLocalizer<SharedResource> SharedHtml

<h1>@Localizer["Welcome"]</h1>
<p>@Localizer["Greeting", User.Identity?.Name]</p>

@* IHtmlLocalizer: does NOT escape — use for RESX values with HTML *@
<p>@SharedHtml["TermsNotice"]</p>

@* Tag Helpers auto-localize Display/ErrorMessage attributes *@
<form asp-action="Register">
    <label asp-for="Name"></label>
    <input asp-for="Name" />
    <span asp-validation-for="Name"></span>
    <button type="submit">@Localizer["Submit"]</button>
</form>
IViewLocalizer はビューパスから RESX ファイルを解決します。Views/Home/Index.cshtml の場合、Resources/Views/Home/Index.de.resx を検索します。ビュー間で文字列を共有する場合は、IStringLocalizer&lt;SharedResource&gt; を別途注入してください。
8

RESX 翻訳の自動化

ローカライズの設定が完了したら、AI を使用して RESX ファイルを翻訳します。IDE で AI アシスタントにソース RESX の翻訳を依頼するか、CI/CD パイプラインで i18n Agent CLI を使用し、翻訳を同期できます。

Terminal
# In your IDE, ask your AI assistant:
> Translate Resources/Controllers/HomeController.en.resx to German, Japanese, Spanish

# HomeController.de.resx created (1.2s)
# HomeController.ja.resx created (1.5s)
# HomeController.es.resx created (1.1s)

# Or use the CLI in CI/CD:
npx i18n-agent translate Resources/Controllers/HomeController.en.resx --lang de,ja,es
差分単位で翻訳してください。ソース RESX ファイルに新しいキーを追加した場合は、すべてのファイルを再生成せず、差分だけを翻訳します。人が確認済みの翻訳を保持し、不要な変更を最小限にできます。

翻訳品質の自動管理

i18n-validate を使用すると、リリース前に欠落キーや壊れたプレースホルダーを検出できます。実際の翻訳が届く前に、i18n-pseudo の疑似翻訳で UI をテストしてください。

LocaleChain.NET によるロケールフォールバックの改善

.NET 組み込みの CultureInfo.Parent 階層は BCP 47 の切り詰めだけを使用します。pt-BR は pt、InvariantCulture の順にフォールバックし、pt-PT を飛ばします。LocaleChain.NET は .NET エコシステム全体に、ロケール別に設定可能なフォールバックチェーンを提供します。

LocaleChain.NET がない場合、pt-BR の翻訳に文字列が欠けていると、完全な pt-PT 翻訳があってもユーザーには英語が表示されます。同じ問題は es-MX(es-419 を飛ばす)、zh-Hant(zh-Hans を飛ばす)など、多数の地域バリエーションに影響します。
Terminal
dotnet add package I18nAgent.LocaleChain
Program.cs
using I18nAgent.LocaleChain;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddLocalization(o => o.ResourcesPath = "Resources");

// Zero-config: built-in chains (pt-BR -> pt-PT -> pt -> en, etc.)
LocaleChain.Configure();

// Or customize specific chains:
LocaleChain.Configure(new Dictionary<string, string[]>
{
    ["pt-BR"] = new[] { "pt-PT", "pt", "en" },
    ["es-MX"] = new[] { "es-419", "es", "en" },
});

// Register the chain-aware string localizer
builder.Services.AddSingleton(
    typeof(IStringLocalizer<>),
    typeof(LocaleChainStringLocalizer<>));

よくある問題

リクエストにカルチャーが設定されていない

翻訳が常にデフォルト言語で表示されます。ミドルウェアパイプラインで UseRequestLocalization() が呼び出され、カルチャープロバイダーが設定されていることを確認してください。ブラウザーが Accept-Language ヘッダーを送信していることも確認します。クエリ文字列に ?culture=de を指定して、ミドルウェアが機能するかテストしてください。

RESX ファイルが見つからない

IStringLocalizer が翻訳値ではなくキー名を返します。最も一般的な原因は、RESX ファイル名が ResourcesPath からの相対パスでクラスの完全な名前空間と一致していないことです。Microsoft.Extensions.Localization のデバッグログを有効にし、フレームワークが検索するパスを確認してください。

ミドルウェアの順序が正しくない

UseRequestLocalization() は UseEndpoints() と MapControllers() より前に配置する必要があります。後に配置すると、コントローラーの実行時にリクエストカルチャーが設定されません。.NET 6 以降の最小ホスティングでは、app.MapControllers() より前に呼び出してください。

バックグラウンドスレッドで誤ったカルチャーが使用される

CultureInfo.CurrentCulture と CurrentUICulture はスレッド単位です。バックグラウンドタスク(Task.Run、ホステッドサービス)はリクエストカルチャーではなく、スレッドプールのカルチャーを継承します。バックグラウンド処理をディスパッチするときに、カルチャーを明示的に取得して設定してください。

推奨プロジェクト構成

Project Structure
MyAspNetApp/
├── Controllers/
│   └── HomeController.cs
├── ViewModels/
│   └── RegisterViewModel.cs
├── Views/
│   └── Home/
│       └── Index.cshtml
├── Resources/
│   ├── Controllers/
│   │   ├── HomeController.en.resx     # English (source)
│   │   ├── HomeController.de.resx     # German
│   │   └── HomeController.ja.resx     # Japanese
│   ├── ViewModels/
│   │   ├── RegisterViewModel.en.resx
│   │   └── RegisterViewModel.de.resx
│   ├── Views/Home/
│   │   ├── Index.en.resx
│   │   └── Index.de.resx
│   └── SharedResource.en.resx
├── SharedResource.cs                  # Marker class
├── Program.cs
└── MyAspNetApp.csproj

i18n Agent を今すぐ試す

翻訳ファイルをここにドロップ

JSON, YAML, PO, XML, CSV, Markdown, Properties

またはクリックしてファイルを選択

翻訳先言語

登録不要すぐに見積もり

I18nAgent.LocaleChain によるロケールフォールバック

pt-BR のような地域ロケールに翻訳キーがない場合、.NET は親ロケール pt を先に確認せず、インバリアントカルチャーへ直接フォールバックします。

Terminal
dotnet add package I18nAgent.LocaleChain
Configuration
using I18nAgent.LocaleChain;

LocaleChain.Configure(new Dictionary<string, string[]>
{
    ["pt-BR"] = new[] {"pt", "en"},
    ["zh-Hant-HK"] = new[] {"zh-Hant", "zh", "en"},
});

対応フレームワークの全一覧と 75 の組み込みチェーンについては、ロケールフォールバックガイドをご覧ください。 Learn more →

よくある質問