Skip to main content

Der vollständige Leitfaden zur ASP.NET-Core-Lokalisierung

Von IStringLocalizer bis zur Produktion: Richten Sie ressourcenbasierte Lokalisierung in ASP.NET Core ein und automatisieren Sie anschließend Übersetzungen mit KI.

1

Lokalisierungsdienste aktivieren

Registrieren Sie Lokalisierungsdienste in Program.cs mit AddLocalization(), konfigurieren Sie unterstützte Kulturen und fügen Sie die Middleware zur Anforderungslokalisierung hinzu. Dadurch wird die gesamte Lokalisierungspipeline Ihrer ASP.NET-Core-Anwendung eingerichtet.

AddLocalization() registriert IStringLocalizer und IStringLocalizerFactory im DI-Container. ResourcesPath teilt dem Framework den Speicherort Ihrer .resx-Dateien mit. AddViewLocalization() aktiviert IViewLocalizer in Razor-Ansichten und AddDataAnnotationsLocalization() lokalisierte Validierungsmeldungen.
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-Ressourcendateien erstellen

ASP.NET Core verwendet RESX-Dateien (XML-Ressourcen) für Übersetzungen. Erstellen Sie eine Datei pro Kultur und Klasse: HomeController.en.resx, HomeController.de.resx usw. Das Framework löst anhand der aktuellen Anforderungskultur die richtige Datei auf.

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>
Verwenden Sie für controller- und ansichtsübergreifende Zeichenfolgen – Schaltflächenbezeichnungen, Navigationselemente und häufige Validierungsmeldungen – eine SharedResource-Klasse mit eigenen RESX-Dateien. So vermeiden Sie doppelte Schlüssel in Dutzenden Controller-spezifischer RESX-Dateien.
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 in Controllern und Diensten verwenden

Injizieren Sie IStringLocalizer&lt;T&gt; per Abhängigkeitsinjektion in beliebige Controller, Dienste oder Middleware. Der generische Typparameter T bestimmt die zu ladende RESX-Datei. Rufen Sie übersetzte Zeichenfolgen mit der Klammersyntax localizer["Key"] und optionalen Formatparametern ab.

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 });
    }
}
Wenn IStringLocalizer den Schlüsselnamen statt des übersetzten Werts zurückgibt, prüfen Sie drei Punkte: (1) Die RESX-Benennung entspricht dem Klassennamensraum, (2) ResourcesPath in AddLocalization() verweist auf den richtigen Ordner und (3) die Build Action der RESX-Datei ist in Visual Studio auf Embedded Resource gesetzt.
4

Middleware für die Anforderungskultur konfigurieren

ASP.NET Core ermittelt die Anforderungskultur über eine Kette von Providern: Abfragezeichenfolge, Cookie und Accept-Language-Header, in dieser Reihenfolge. Sie können benutzerdefinierte Provider ergänzen, etwa zum Lesen der Kultur aus einem URL-Routensegment wie /de/home.

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);
}
Die Reihenfolge der Middleware ist entscheidend. UseRequestLocalization() muss nach UseRouting(), aber vor UseEndpoints() oder MapControllers() aufgerufen werden. Steht der Aufruf zu spät, ist die Kultur beim Ausführen Ihrer Controller nicht gesetzt.
5

Datenannotationen lokalisieren

Validierungsattribute wie [Required], [StringLength] und [Display] lassen sich lokalisieren, indem ihre Eigenschaften ErrorMessage oder Name auf RESX-Schlüsselnamen gesetzt werden. Aktivieren Sie dies mit AddDataAnnotationsLocalization() in Program.cs.

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"
Die Lokalisierung von Datenannotationen verwendet zum Auffinden von RESX-Dateien den Namen der ViewModel-Klasse, nicht den Controller. Für RegisterViewModel sucht das Framework Resources/ViewModels/RegisterViewModel.de.resx. Sind Ihre RESX-Dateien nach dem Controller benannt, werden Validierungsmeldungen nicht lokalisiert.
6

Pluralformen und ICU-Nachrichten verarbeiten

.NET besitzt keine integrierte Unterstützung für Pluralregeln wie ICU. Verwenden Sie in einfachen Fällen getrennte RESX-Schlüssel (ItemCount_One, ItemCount_Other) mit einer Verzweigung im Code. Nutzen Sie für vollständige ICU-MessageFormat-Unterstützung aller CLDR-Pluralkategorien die Bibliothek 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 {# عنصر}}";
Verwenden Sie niemals count == 1 zur Erkennung von Singularformen. Französisch behandelt 0 als Singular. Russisch besitzt getrennte Formen für few und many, Arabisch sechs Pluralkategorien. Verwenden Sie CLDR-fähige Pluralregeln oder eine Bibliothek wie MessageFormat.NET, die dies korrekt verarbeitet.
7

Razor-Ansichten lokalisieren

Verwenden Sie IViewLocalizer in Razor-Ansichten über @inject. Er löst RESX-Dateien anhand des Dateipfads der Ansicht auf. Verwenden Sie IHtmlLocalizer für HTML-sichere Zeichenfolgen mit Markup. Tag Helpers wie asp-for und asp-validation-for nutzen automatisch lokalisierte Display- und ErrorMessage-Attribute.

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 löst RESX-Dateien anhand des Ansichtspfads auf: Views/Home/Index.cshtml sucht Resources/Views/Home/Index.de.resx. Wenn Sie Zeichenfolgen ansichtsübergreifend teilen möchten, injizieren Sie zusätzlich IStringLocalizer&lt;SharedResource&gt;.
8

RESX-Übersetzungen automatisieren

Wenn Ihre Lokalisierung eingerichtet ist, übersetzen Sie Ihre RESX-Dateien mit KI. Bitten Sie Ihren KI-Assistenten in Ihrer IDE, Ihre Ausgangs-RESX-Datei zu übersetzen, oder halten Sie Übersetzungen mit der CLI von i18n Agent in Ihrer CI/CD-Pipeline synchron.

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
Übersetzen Sie schrittweise: Wenn Sie Ihrer RESX-Ausgangsdatei neue Schlüssel hinzufügen, übersetzen Sie nur die Änderungen, statt sämtliche Dateien neu zu erzeugen. So bleiben von Menschen geprüfte Übersetzungen erhalten und unnötige Änderungen werden minimiert.

Übersetzungsqualität automatisieren

Erkennen Sie mit i18n-validate fehlende Schlüssel und beschädigte Platzhalter vor der Veröffentlichung. Testen Sie Ihre Benutzeroberfläche mit i18n-pseudo und Pseudoübersetzungen, bevor echte Übersetzungen vorliegen.

Locale-Fallback mit LocaleChain.NET korrigieren

Die integrierte Hierarchie CultureInfo.Parent von .NET verwendet nur BCP-47-Verkürzung: pt-BR fällt auf pt und dann InvariantCulture zurück und überspringt pt-PT. LocaleChain.NET bietet für das gesamte .NET-Ökosystem konfigurierbare Fallback-Ketten pro Locale.

Ohne LocaleChain.NET sieht eine Person mit pt-BR Englisch, wenn eine portugiesische Zeichenfolge fehlt – selbst bei einer vollständigen pt-PT-Übersetzung. Dasselbe Problem betrifft es-MX (überspringt es-419), zh-Hant (überspringt zh-Hans) und Dutzende weitere regionale Varianten.
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<>));

Häufige Fallstricke

Kultur für die Anfrage nicht gesetzt

Übersetzungen zeigen stets die Standardsprache. Prüfen Sie, ob UseRequestLocalization() in der Middleware-Pipeline aufgerufen wird und die Kulturprovider konfiguriert sind. Stellen Sie sicher, dass der Browser Accept-Language-Header sendet. Testen Sie mit ?culture=de in der Abfragezeichenfolge, ob die Middleware funktioniert.

RESX-Datei nicht gefunden

IStringLocalizer gibt den Schlüsselnamen statt des übersetzten Werts zurück. Meist entspricht der RESX-Dateiname nicht dem vollständigen Namensraumpfad der Klasse relativ zu ResourcesPath. Aktivieren Sie Debug-Protokollierung für Microsoft.Extensions.Localization, um die vom Framework durchsuchten Pfade zu sehen.

Falsche Middleware-Reihenfolge

UseRequestLocalization() muss vor UseEndpoints() und MapControllers() stehen. Andernfalls ist die Anforderungskultur beim Ausführen der Controller nicht gesetzt. Rufen Sie die Funktion beim minimalen Hosting in .NET 6+ vor app.MapControllers() auf.

Hintergrundthread verwendet falsche Kultur

CultureInfo.CurrentCulture und CurrentUICulture gelten pro Thread. Hintergrundaufgaben (Task.Run, gehostete Dienste) erben die Kultur des Threadpools, nicht die Anforderungskultur. Erfassen und setzen Sie die Kultur ausdrücklich, wenn Sie Hintergrundarbeit starten.

Empfohlene Projektstruktur

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 jetzt testen

Legen Sie Ihre Übersetzungsdatei hier ab

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

oder zum Auswählen klicken

Zielsprachen

Keine Registrierung erforderlichSofortiges Angebot

Locale-Fallback mit I18nAgent.LocaleChain

Fehlt ein Übersetzungsschlüssel in einer regionalen Locale wie pt-BR, wechselt .NET direkt zur invarianten Kultur, statt zuerst die übergeordnete Locale pt zu prüfen.

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"},
});

In unserem Leitfaden zu Locale-Fallbacks finden Sie die vollständige Liste unterstützter Frameworks und 75 integrierter Ketten. Learn more →

Häufig gestellte Fragen