Skip to main content

Den komplette guiden til lokalisering i ASP.NET Core

Fra IStringLocalizer til produksjon: sett opp ressursbasert lokalisering i ASP.NET Core, og automatiser deretter oversettelser med AI.

1

Aktiver lokaliseringstjenester

Registrer lokaliseringstjenester i Program.cs med AddLocalization(), konfigurer støttede kulturer, og legg til middlewaren for forespørselslokalisering. Dette kobler sammen hele lokaliseringspipelinen for ASP.NET Core-applikasjonen din.

AddLocalization() registrerer IStringLocalizer og IStringLocalizerFactory i DI-containeren. ResourcesPath forteller rammeverket hvor det skal finne .resx-filene dine. AddViewLocalization() aktiverer IViewLocalizer i Razor-visninger, og AddDataAnnotationsLocalization() aktiverer lokaliserte valideringsmeldinger.
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

Opprett RESX-ressursfiler

ASP.NET Core bruker RESX-filer (XML-ressurser) til oversettelser. Opprett én fil per kultur per klasse: HomeController.en.resx, HomeController.de.resx osv. Rammeverket finner riktig fil basert på den aktuelle forespørselskulturen.

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>
Bruk en SharedResource-klasse med sine egne RESX-filer for strenger som deles mellom kontrollere og visninger, som knappetekster, navigasjonselementer og vanlige valideringsmeldinger. Dette unngår at nøkler dupliseres over dusinvis av kontrollerspesifikke 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

Bruk IStringLocalizer i kontrollere og tjenester

Injiser IStringLocalizer&lt;T&gt; i en kontroller, tjeneste eller middleware via dependency injection. Den generiske typeparameteren T bestemmer hvilken RESX-fil som lastes. Bruk hakeparentessyntaksen localizer["Key"] for å hente oversatte strenger, med valgfrie formatparametere.

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økkelnavnet i stedet for den oversatte verdien, kontroller tre ting: (1) at navnet på RESX-filen matcher klassens navnerom, (2) at ResourcesPath i AddLocalization() peker til riktig mappe, og (3) at RESX-filens Build Action er satt til Embedded Resource i Visual Studio.
4

Konfigurer middleware for forespørselskultur

ASP.NET Core avgjør forespørselskulturen ved hjelp av en kjede av providere: query string, cookie og Accept-Language-header (i den rekkefølgen). Du kan legge til egendefinerte providere, for eksempel ved å lese 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);
}
Rekkefølgen på middleware har betydning. UseRequestLocalization() må kalles etter UseRouting(), men før UseEndpoints() eller MapControllers(). Hvis den plasseres for sent, blir ikke kulturen satt når kontrollerne dine kjører.
5

Lokaliser dataannotasjoner

Valideringsattributter som [Required], [StringLength] og [Display] kan lokaliseres ved å sette ErrorMessage- eller Name-egenskapene deres til RESX-nøkkelnavn. Kall AddDataAnnotationsLocalization() i Program.cs for å 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 av dataannotasjoner bruker klassenavnet til ViewModel for å finne RESX-filer, ikke kontrolleren. For RegisterViewModel ser rammeverket etter Resources/ViewModels/RegisterViewModel.de.resx. Hvis RESX-filene dine er navngitt etter kontrolleren, blir ikke valideringsmeldingene lokalisert.
6

Håndter flertallsformer og ICU-meldinger

.NET har ikke innebygd støtte for flertallsregler slik ICU har. For enkle tilfeller bruker du separate RESX-nøkler (ItemCount_One, ItemCount_Other) med en switch i koden. For full støtte for ICU MessageFormat over alle CLDR-flertallskategorier bruker du biblioteket 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 {# عنصر}}";
Bruk aldri count == 1 for å oppdage entallsformer. Fransk behandler 0 som entall. Russisk har egne former for «few» og «many». Arabisk har seks flertallskategorier. Bruk CLDR-bevisste flertallsregler eller et bibliotek som MessageFormat.NET som håndterer dette korrekt.
7

Lokaliser Razor-visninger

Bruk IViewLocalizer i Razor-visninger via @inject. Den finner RESX-filer basert på visningens filbane. For HTML-trygge strenger med markering bruker du IHtmlLocalizer. Tag Helpers som asp-for og asp-validation-for bruker automatisk lokaliserte 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 finner RESX-filer basert på visningsbanen: Views/Home/Index.cshtml ser etter Resources/Views/Home/Index.de.resx. Hvis du vil dele strenger mellom visninger, injiserer du IStringLocalizer&lt;SharedResource&gt; separat.
8

Automatiser RESX-oversettelser

Når lokaliseringsoppsettet er ferdig, kan du oversette RESX-filene dine med AI. I IDE-en kan du be AI-assistenten om å oversette RESX-kildefilen, eller bruke i18n Agent CLI i CI/CD-pipelinen for å holde oversettelsene synkronisert.

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
Oversett inkrementelt, når du legger til nye nøkler i kilde-RESX-filen, oversetter du bare diffen i stedet for å regenerere alle filene. Dette bevarer eventuelle menneskelig gjennomgåtte oversettelser og minimerer unødvendige endringer.

Automatiser oversettelseskvalitet

Fang opp manglende nøkler og ødelagte plassholdere før de driftsettes med i18n-validate. Test brukergrensesnittet med pseudo-oversettelser ved hjelp av i18n-pseudo før de virkelige oversettelsene kommer.

Fiks reserveløsning for språkvarianter med LocaleChain.NET

.NETs innebygde CultureInfo.Parent-hierarki bruker bare BCP 47-avkorting: pt-BR faller tilbake til pt og deretter InvariantCulture, og hopper over pt-PT. LocaleChain.NET gir konfigurerbare reservekjeder per språkvariant for hele .NET-økosystemet.

Uten LocaleChain.NET ser en pt-BR-bruker engelsk når en portugisisk streng mangler, selv om du har en komplett pt-PT-oversettelse. Samme problem gjelder es-MX (hopper over es-419), zh-Hant (hopper over zh-Hans) og dusinvis av 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<>));

Vanlige fallgruver

Kultur ikke satt på forespørselen

Oversettelser vises alltid på standardspråket. Kontroller at UseRequestLocalization() kalles i middleware-pipelinen, og at kulturproviderne er konfigurert. Bekreft at nettleseren sender Accept-Language-headere. Test med ?culture=de i spørringsstrengen for å bekrefte at middlewaren fungerer.

RESX-fil ikke funnet

IStringLocalizer returnerer nøkkelnavnet i stedet for den oversatte verdien. Den vanligste årsaken er at RESX-filnavnet ikke matcher klassens fullstendige navneromsbane relativt til ResourcesPath. Aktiver feilsøkingslogging for Microsoft.Extensions.Localization for å se hvilke baner rammeverket søker i.

Feil rekkefølge på middleware

UseRequestLocalization() må komme før UseEndpoints() og MapControllers(). Hvis den plasseres etter, blir ikke forespørselskulturen satt når kontrollerne kjører. I minimal hosting i .NET 6+ kaller du den før app.MapControllers().

Bakgrunnstråd bruker feil kultur

CultureInfo.CurrentCulture og CurrentUICulture er per tråd. Bakgrunnsoppgaver (Task.Run, hostede tjenester) arver kulturen fra trådpoolen, ikke forespørselskulturen. Fang opp og sett kulturen eksplisitt når du starter bakgrunnsarbeid.

Anbefalt prosjektstruktur

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 nå

Slipp oversettelsesfilen din her

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

eller klikk for å bla gjennom

Målspråk

Ingen registrering krevesUmiddelbart estimat

Reserveløsning for språkvarianter med I18nAgent.LocaleChain

Når en oversettelsesnøkkel mangler i en regional språkvariant som pt-BR, går .NET rett til den invariante kulturen i stedet for først å sjekke den overordnede språkvarianten 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 guiden vår om reserveløsning for språkvarianter for hele listen over støttede rammeverk og 75 innebygde kjeder. Learn more →

Ofte stilte spørsmål