Skip to main content

Den komplette guide til ASP.NET Core-lokalisering

Fra IStringLocalizer til produktion: Opsæt ressourcebaseret lokalisering i ASP.NET Core og automatiser derefter oversættelser med AI.

1

Aktivér lokaliseringstjenester

Registrer lokaliseringstjenester i Program.cs med AddLocalization(), konfigurer understøttede kulturer og tilføj middleware til lokalisering af anmodninger. Det forbinder hele lokaliseringsprocessen for din ASP.NET Core-applikation.

AddLocalization() registrerer IStringLocalizer og IStringLocalizerFactory i DI-containeren. ResourcesPath fortæller frameworket, hvor dine .resx-filer findes. AddViewLocalization() aktiverer IViewLocalizer i Razor-visninger og AddDataAnnotationsLocalization() aktiverer lokaliserede valideringsmeddelelser.
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

Opret RESX-ressourcefiler

ASP.NET Core bruger RESX-filer (XML-ressourcer) til oversættelser. Opret én fil pr. kultur pr. klasse: HomeController.en.resx, HomeController.de.resx osv. Frameworket finder den korrekte fil ud fra den aktuelle anmodningskultur.

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>
Brug en SharedResource-klasse med egne RESX-filer til strenge, der deles på tværs af controllere og visninger — knaptekster, navigationselementer og almindelige valideringsmeddelelser. Det undgår dublerede nøgler på tværs af snesevis af controllerspecifikke RESX-filer.
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

Brug IStringLocalizer i controllere og tjenester

Injicer IStringLocalizer&lt;T&gt; i enhver controller, tjeneste eller middleware via afhængighedsinjektion. Den generiske typeparameter T bestemmer, hvilken RESX-fil der indlæses. Brug kantparentessyntaksen localizer["Key"] til at hente oversatte strenge med valgfrie formatparametre.

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 });
    }
}
Hvis IStringLocalizer returnerer nøglenavnet i stedet for den oversatte værdi, skal du kontrollere tre ting: (1) RESX-filens navn matcher klassens namespace, (2) ResourcesPath i AddLocalization() peger på den korrekte mappe og (3) RESX-filens Build Action er angivet som Embedded Resource i Visual Studio.
4

Konfigurer middleware til anmodningskultur

ASP.NET Core bestemmer anmodningskulturen ved hjælp af en kæde af udbydere: forespørgselsstreng, cookie og Accept-Language-header (i den rækkefølge). Du kan tilføje brugerdefinerede udbydere — for eksempel ved at læse kulturen fra et URL-rutesegment som /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);
}
Rækkefølgen af middleware er vigtig. UseRequestLocalization() skal kaldes efter UseRouting(), men før UseEndpoints() eller MapControllers(). Hvis det placeres for sent, er kulturen ikke angivet, når dine controllere køres.
5

Lokaliser dataannoteringer

Valideringsattributter som [Required], [StringLength] og [Display] kan lokaliseres ved at angive RESX-nøglenavne som deres ErrorMessage- eller Name-egenskaber. Kald AddDataAnnotationsLocalization() i Program.cs for at aktivere dette.

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"
Lokalisering af dataannoteringer bruger ViewModel-klassens navn til at finde RESX-filer, ikke controllerens. For RegisterViewModel leder frameworket efter Resources/ViewModels/RegisterViewModel.de.resx. Hvis dine RESX-filer er navngivet efter controlleren, bliver valideringsmeddelelserne ikke lokaliseret.
6

Håndter flertalsformer og ICU-meddelelser

.NET har ikke indbygget understøttelse af flertalsregler som ICU. I enkle tilfælde kan du bruge separate RESX-nøgler (ItemCount_One, ItemCount_Other) med en kodebaseret switch. Brug biblioteket MessageFormat.NET for fuld understøttelse af ICU MessageFormat på tværs af alle CLDR-flertalskategorier.

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 {# عنصر}}";
Brug aldrig count == 1 til at registrere entalsformer. Fransk behandler 0 som ental. Russisk har separate former for 'few' og 'many'. Arabisk har seks flertalskategorier. Brug flertalsregler med CLDR-understøttelse eller et bibliotek som MessageFormat.NET, der håndterer dette korrekt.
7

Lokaliser Razor-visninger

Brug IViewLocalizer i Razor-visninger via @inject. Den finder RESX-filer ud fra visningens filsti. Brug IHtmlLocalizer til HTML-sikre strenge med markup. Tag Helpers som asp-for og asp-validation-for bruger automatisk lokaliserede Display- og ErrorMessage-attributter.

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 finder RESX-filer ud fra visningsstien: Views/Home/Index.cshtml leder efter Resources/Views/Home/Index.de.resx. Hvis du vil dele strenge på tværs af visninger, skal du injicere IStringLocalizer&lt;SharedResource&gt; separat.
8

Automatiser RESX-oversættelser

Når din lokaliseringsopsætning er færdig, kan du oversætte dine RESX-filer med AI. Bed din AI-assistent i dit IDE om at oversætte din kilde-RESX eller brug i18n Agent CLI i din CI/CD-pipeline til at holde oversættelserne synkroniseret.

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
Oversæt trinvist — når du føjer nye nøgler til din RESX-kildefil, skal du kun oversætte forskellen i stedet for at regenerere alle filer. Dermed bevares oversættelser, som mennesker har gennemgået, mens unødvendige ændringer minimeres.

Automatiser oversættelseskvaliteten

Find manglende nøgler og ødelagte pladsholdere med i18n-validate, før de udgives. Test din brugergrænseflade med pseudooversættelser ved hjælp af i18n-pseudo, før de rigtige oversættelser er klar.

Ret sprogtilbagefald med LocaleChain.NET

.NET's indbyggede CultureInfo.Parent-hierarki bruger kun BCP 47-afkortning: pt-BR falder tilbage til pt og derefter InvariantCulture og springer pt-PT over. LocaleChain.NET leverer konfigurerbare tilbagefaldskæder for hvert sprog til hele .NET-økosystemet.

Uden LocaleChain.NET ser en pt-BR-bruger engelsk, når en portugisisk streng mangler — selv hvis du har en komplet pt-PT-oversættelse. Det samme problem påvirker es-MX (springer es-419 over), zh-Hant (springer zh-Hans over) og snesevis af andre regionale varianter.
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<>));

Almindelige faldgruber

Kulturen er ikke angivet for anmodningen

Oversættelser viser altid standardsproget. Kontrollér, at UseRequestLocalization() kaldes i middlewareprocessen og at kulturudbyderne er konfigureret. Bekræft, at browseren sender Accept-Language-headere. Test med ?culture=de i forespørgselsstrengen for at bekræfte, at middleware fungerer.

RESX-filen blev ikke fundet

IStringLocalizer returnerer nøglenavnet i stedet for den oversatte værdi. Den mest almindelige årsag er et RESX-filnavn, der ikke matcher klassens fulde namespace-sti i forhold til ResourcesPath. Aktivér fejlfindingslogning for Microsoft.Extensions.Localization for at se, hvilke stier frameworket søger i.

Forkert rækkefølge af middleware

UseRequestLocalization() skal stå før UseEndpoints() og MapControllers(). Hvis det placeres efter dem, er anmodningskulturen ikke angivet, når controllerne køres. Ved minimal hosting i .NET 6+ skal du kalde det før app.MapControllers().

Baggrundstråden bruger den forkerte kultur

CultureInfo.CurrentCulture og CurrentUICulture gælder pr. tråd. Baggrundsopgaver (Task.Run, hostede tjenester) arver trådpuljens kultur, ikke anmodningskulturen. Registrer og angiv kulturen eksplicit, når du sender arbejde til baggrundsbehandling.

Anbefalet 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

Prøv i18n Agent nu

Slip din oversættelsesfil her

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

eller klik for at vælge en fil

Målsprog

Kræver ingen tilmeldingEstimat med det samme

Sprogtilbagefald med I18nAgent.LocaleChain

Når en oversættelsesnøgle mangler i en regional kultur som pt-BR, går .NET direkte til den invariante kultur i stedet for først at kontrollere den overordnede kultur 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"},
});

Se vores guide til sprogtilbagefald for at få den komplette liste over understøttede frameworks og 75 indbyggede kæder. Learn more →

Ofte stillede spørgsmål