Skip to main content

Den kompletta guiden till lokalisering i ASP.NET Core

Från IStringLocalizer till produktion: konfigurera resursbaserad lokalisering i ASP.NET Core och automatisera sedan översättningar med AI.

1

Aktivera lokaliseringstjänster

Registrera lokaliseringstjänster i Program.cs med AddLocalization(), konfigurera kulturer som stöds och lägg till mellanprogrammet för lokalisering av begäranden. Detta kopplar samman hela lokaliseringsflödet för din ASP.NET Core-applikation.

AddLocalization() registrerar IStringLocalizer och IStringLocalizerFactory i DI-containern. ResourcesPath anger var ramverket ska leta efter dina .resx-filer. AddViewLocalization() aktiverar IViewLocalizer i Razor-vyer och AddDataAnnotationsLocalization() aktiverar lokaliserade valideringsmeddelanden.
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

Skapa RESX-resursfiler

ASP.NET Core använder RESX-filer (XML-resurser) för översättningar. Skapa en fil per kultur och klass: HomeController.en.resx, HomeController.de.resx och så vidare. Ramverket väljer rätt fil utifrån den aktuella begärandekulturen.

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>
Använd en SharedResource-klass med egna RESX-filer för strängar som delas mellan kontrollrar och vyer – knapptexter, navigeringsobjekt och vanliga valideringsmeddelanden. Då slipper du duplicera nycklar i dussintals kontrollspecifika 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

Använd IStringLocalizer i kontrollrar och tjänster

Mata in IStringLocalizer&lt;T&gt; i valfri kontroller, tjänst eller valfritt mellanprogram via beroendeinjektion. Den generiska typparametern T avgör vilken RESX-fil som ska läsas in. Använd hakparentessyntaxen localizer["Key"] för att hämta översatta strängar, med valfria formatparametrar.

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 });
    }
}
Om IStringLocalizer returnerar nyckelnamnet i stället för det översatta värdet ska du kontrollera tre saker: (1) att RESX-filens namn matchar klassens namnrymd, (2) att ResourcesPath i AddLocalization() pekar på rätt mapp och (3) att RESX-filens Build Action är inställd på Embedded Resource i Visual Studio.
4

Konfigurera mellanprogram för begärandekultur

ASP.NET Core fastställer begärandekulturen med en kedja av leverantörer: frågesträng, cookie och Accept-Language-rubrik (i den ordningen). Du kan lägga till anpassade leverantörer – till exempel en som läser kulturen från ett URL-ruttsegment 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);
}
Ordningen på mellanprogrammen spelar roll. UseRequestLocalization() måste anropas efter UseRouting(), men före UseEndpoints() eller MapControllers(). Om anropet placeras för sent är kulturen inte inställd när dina kontrollrar körs.
5

Lokalisera dataannoteringar

Valideringsattribut som [Required], [StringLength] och [Display] kan lokaliseras genom att deras ErrorMessage- eller Name-egenskaper anges som RESX-nyckelnamn. Anropa AddDataAnnotationsLocalization() i Program.cs för att aktivera detta.

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 dataannoteringar använder ViewModel-klassens namn för att hitta RESX-filer, inte kontrollerns namn. För RegisterViewModel letar ramverket efter Resources/ViewModels/RegisterViewModel.de.resx. Om dina RESX-filer har namngetts efter kontrollern lokaliseras inte valideringsmeddelandena.
6

Hantera pluralformer och ICU-meddelanden

.NET saknar inbyggt stöd för pluralregler av samma typ som ICU. Använd separata RESX-nycklar (ItemCount_One, ItemCount_Other) med en switch-sats i koden för enkla fall. Använd biblioteket MessageFormat.NET för fullständigt stöd för ICU MessageFormat i alla CLDR-pluralkategorier.

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 {# عنصر}}";
Använd aldrig count == 1 för att identifiera singularformer. Franska behandlar 0 som singular. Ryska har separata former för ”few” och ”many”. Arabiska har sex pluralkategorier. Använd CLDR-medvetna pluralregler eller ett bibliotek som MessageFormat.NET som hanterar detta korrekt.
7

Lokalisera Razor-vyer

Använd IViewLocalizer i Razor-vyer via @inject. Den väljer RESX-filer utifrån vyns filsökväg. Använd IHtmlLocalizer för HTML-säkra strängar med kod. Tag Helpers som asp-for och asp-validation-for använder automatiskt lokaliserade Display- och ErrorMessage-attribut.

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 väljer RESX-filer utifrån vyns sökväg: Views/Home/Index.cshtml letar efter Resources/Views/Home/Index.de.resx. Om du vill dela strängar mellan vyer matar du in IStringLocalizer&lt;SharedResource&gt; separat.
8

Automatisera RESX-översättningar

När lokaliseringskonfigurationen är klar kan du översätta dina RESX-filer med AI. Be din AI-assistent att översätta din RESX-källfil direkt i utvecklingsmiljön eller använd i18n Agent CLI i ditt CI/CD-flöde för att hålla översättningarna synkroniserade.

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
Översätt stegvis – när du lägger till nya nycklar i RESX-källfilen översätter du bara skillnaden i stället för att generera om alla filer. Då bevaras översättningar som har granskats av människor och onödiga ändringar minimeras.

Automatisera översättningskvaliteten

Upptäck saknade nycklar och trasiga platshållare med i18n-validate innan de når produktion. Testa gränssnittet med pseudoöversättningar via i18n-pseudo innan de riktiga översättningarna är klara.

Åtgärda språkreserv med LocaleChain.NET

.NET:s inbyggda CultureInfo.Parent-hierarki använder endast BCP 47-trunkering: pt-BR går tillbaka till pt och sedan InvariantCulture, men hoppar över pt-PT. LocaleChain.NET tillhandahåller konfigurerbara reservkedjor per språkvariant för hela .NET-ekosystemet.

Utan LocaleChain.NET ser en användare med pt-BR engelska när en portugisisk sträng saknas – även om du har en komplett pt-PT-översättning. Samma problem påverkar es-MX (hoppar över es-419), zh-Hant (hoppar över zh-Hans) och dussintals andra regionala 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<>));

Vanliga fallgropar

Kulturen är inte inställd för begäran

Översättningarna visas alltid på standardspråket. Kontrollera att UseRequestLocalization() anropas i mellanprogramsflödet och att kulturleverantörerna är konfigurerade. Kontrollera att webbläsaren skickar Accept-Language-rubriker. Testa med ?culture=de i frågesträngen för att bekräfta att mellanprogrammet fungerar.

RESX-filen hittades inte

IStringLocalizer returnerar nyckelnamnet i stället för det översatta värdet. Den vanligaste orsaken är att RESX-filnamnet inte matchar klassens fullständiga namnrymdssökväg i förhållande till ResourcesPath. Aktivera felsökningsloggning för Microsoft.Extensions.Localization för att se vilka sökvägar ramverket söker igenom.

Fel ordning på mellanprogrammen

UseRequestLocalization() måste stå före UseEndpoints() och MapControllers(). Om anropet placeras efter dem är begärandekulturen inte inställd när kontrollrarna körs. Med minimal värdkonfiguration i .NET 6+ ska du anropa det före app.MapControllers().

Bakgrundstråden använder fel kultur

CultureInfo.CurrentCulture och CurrentUICulture gäller per tråd. Bakgrundsuppgifter (Task.Run, värdbaserade tjänster) ärver trådpoolens kultur, inte begärandekulturen. Samla uttryckligen in och ställ in kulturen när bakgrundsarbete skickas iväg.

Rekommenderad 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

Prova i18n Agent nu

Släpp din översättningsfil här

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

eller klicka för att välja en fil

Målspråk

Ingen registrering krävsPrisuppskattning direkt

Språkreserv med I18nAgent.LocaleChain

När en översättningsnyckel saknas i en regional språkvariant som pt-BR går .NET direkt till den invarianta kulturen i stället för att först kontrollera den överordnade 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"},
});

I vår guide till språkreserver finns en fullständig lista över ramverk som stöds och 75 inbyggda kedjor. Learn more →

Vanliga frågor