
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.
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.
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();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.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><!-- 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 { }IStringLocalizer in Controllern und Diensten verwenden
Injizieren Sie IStringLocalizer<T> 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.
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 });
}
}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.
// 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: 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);
}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.
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"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.
// 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 {# عنصر}}";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.
@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>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.
# 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Übersetzungsqualität automatisieren
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.
dotnet add package I18nAgent.LocaleChainusing 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
RESX-Datei nicht gefunden
Falsche Middleware-Reihenfolge
Hintergrundthread verwendet falsche Kultur
Empfohlene Projektstruktur
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.csproji18n Agent jetzt testen
Legen Sie Ihre Übersetzungsdatei hier ab
JSON, YAML, PO, XML, CSV, Markdown, Properties
oder zum Auswählen klicken
Zielsprachen
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.
dotnet add package I18nAgent.LocaleChainusing 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 →