Skip to main content

Täydellinen opas ASP.NET Core:n lokalisointiin

IStringLocalizer:ista tuotantoon: ota resurssipohjainen lokalisointi käyttöön ASP.NET Core:ssa ja automatisoi sitten käännökset tekoälyllä.

1

Ota lokalisointipalvelut käyttöön

Rekisteröi lokalisointipalvelut Program.cs-tiedostossa AddLocalization()-funktiolla, määritä tuetut kulttuurit ja lisää pyyntöjen lokalisoinnin middleware. Tämä kytkee ASP.NET Core -sovelluksesi koko lokalisointiputken.

AddLocalization() rekisteröi IStringLocalizer:in ja IStringLocalizerFactoryn DI-säilöön. ResourcesPath kertoo ohjelmistokehykselle, mistä .resx-tiedostot löytyvät. AddViewLocalization() ottaa IViewLocalizerin käyttöön Razor-näkymissä ja AddDataAnnotationsLocalization() lokalisoidut validointisanomat.
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

Luo RESX-resurssitiedostot

ASP.NET Core käyttää käännöksiin RESX (XML-resurssi) -tiedostoja. Luo yksi tiedosto kulttuuria ja luokkaa kohden: HomeController.en.resx, HomeController.de.resx ja niin edelleen. Ohjelmistokehys ratkaisee oikean tiedoston nykyisen pyynnön kulttuurin perusteella.

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>
Käytä omaa RESX-tiedostoa käyttävää SharedResource-luokkaa eri kontrollereiden ja näkymien yhteisille merkkijonoille, kuten painikkeiden nimikkeille, siirtymiskohteille ja tavallisille validointisanomille. Näin avaimia ei tarvitse kopioida kymmeniin kontrollerikohtaisiin RESX-tiedostoihin.
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

Käytä IStringLocalizer:ia kontrollereissa ja palveluissa

Injektoi IStringLocalizer&lt;T&gt; mihin tahansa kontrolleriin, palveluun tai middlewareen riippuvuusinjektiolla. Yleinen tyyppiparametri T määrittää ladattavan RESX-tiedoston. Hae käännetyt merkkijonot hakasulkusyntaksilla localizer["Key"] ja anna halutessasi muotoiluparametrit.

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 });
    }
}
Jos IStringLocalizer palauttaa käännetyn arvon sijaan avaimen nimen, tarkista kolme asiaa: 1) RESX-tiedoston nimi vastaa luokan nimiavaruutta, 2) AddLocalization()-funktion ResourcesPath osoittaa oikeaan kansioon ja 3) RESX-tiedoston Build Action -arvoksi on asetettu Visual Studio:ssa Embedded Resource.
4

Määritä pyyntökulttuurin middleware

ASP.NET Core ratkaisee pyynnön kulttuurin palveluntarjoajaketjulla: kyselymerkkijono, eväste ja Accept-Language-otsake tässä järjestyksessä. Voit lisätä mukautettuja palveluntarjoajia, jotka esimerkiksi lukevat kulttuurin URL-reittisegmentistä, kuten /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);
}
Middlewaren järjestyksellä on merkitystä. UseRequestLocalization()-funktiota on kutsuttava UseRouting()-funktion jälkeen mutta ennen UseEndpoints()- tai MapControllers()-funktiota. Jos se sijoitetaan liian myöhään, kulttuuria ei ole asetettu kontrollerien suorituksen aikana.
5

Lokalisoi data-annotaatiot

Validointiominaisuudet, kuten [Required], [StringLength] ja [Display], voidaan lokalisoida asettamalla niiden ErrorMessage- tai Name-ominaisuudeksi RESX-avaimen nimi. Ota tämä käyttöön kutsumalla Program.cs-tiedostossa AddDataAnnotationsLocalization()-funktiota.

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"
Data-annotaatioiden lokalisointi etsii RESX-tiedostot ViewModel-luokan, ei kontrollerin, nimen perusteella. RegisterViewModelia varten ohjelmistokehys etsii tiedostoa Resources/ViewModels/RegisterViewModel.de.resx. Jos RESX-tiedostosi on nimetty kontrollerin mukaan, validointisanomia ei lokalisoida.
6

Käsittele monikkomuodot ja ICU-sanomat

.NET:issä ei ole ICU:n kaltaista sisäänrakennettua monikkosääntöjen tukea. Käytä yksinkertaisissa tapauksissa erillisiä RESX-avaimia (ItemCount_One, ItemCount_Other) ja koodin switch-lausetta. Käytä MessageFormat.NET-kirjastoa kaikkien CLDR-monikkoluokkien kattavaan ICU MessageFormat -tukeen.

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 {# عنصر}}";
Älä koskaan tunnista yksikkömuotoa ehdolla count == 1. Ranska käsittelee 0:n yksikkönä. Venäjässä on erilliset few- ja many-muodot. Arabiassa on kuusi monikkoluokkaa. Käytä CLDR:n huomioivia monikkosääntöjä tai MessageFormat.NET:in kaltaista kirjastoa, joka käsittelee tämän oikein.
7

Lokalisoi Razor-näkymät

Käytä Razor-näkymissä IViewLocalizeria @inject-direktiivillä. Se ratkaisee RESX-tiedostot näkymän tiedostopolun perusteella. Käytä HTML-turvallisiin merkintää sisältäviin merkkijonoihin IHtmlLocalizeria. asp-for- ja asp-validation-for-tyyppiset Tag Helperit käyttävät lokalisoituja Display- ja ErrorMessage-ominaisuuksia automaattisesti.

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 ratkaisee RESX-tiedostot näkymäpolun mukaan: Views/Home/Index.cshtml etsii tiedostoa Resources/Views/Home/Index.de.resx. Jos haluat jakaa merkkijonoja näkymien välillä, injektoi erikseen IStringLocalizer&lt;SharedResource&gt;.
8

Automatisoi RESX-käännökset

Kun lokalisointi on otettu käyttöön, käännä RESX-tiedostosi tekoälyllä. Pyydä IDE-ympäristössäsi tekoälyavustajaasi kääntämään RESX-lähdetiedosto tai pidä käännökset synkronoituina i18n Agent:in CLI:llä CI/CD-putkessasi.

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
Käännä vaiheittain — kun lisäät uusia avaimia RESX-lähdetiedostoon, käännä vain diff äläkä luo kaikkia tiedostoja uudelleen. Näin ihmisten tarkistamat käännökset säilyvät ja tarpeettomat muutokset vähenevät.

Automatisoi käännöslaatu

Löydä puuttuvat avaimet ja rikkoutuneet paikkamerkit i18n-validate:lla ennen julkaisua. Testaa käyttöliittymää pseudokäännöksillä i18n-pseudo:n avulla ennen oikeiden käännösten valmistumista.

Korjaa varakielet LocaleChain.NET:illä

.NET:in sisäänrakennettu CultureInfo.Parent-hierarkia käyttää vain BCP 47 -lyhennystä: pt-BR siirtyy pt:hen ja sitten InvariantCultureen ohittaen pt-PT:n. LocaleChain.NET tarjoaa määritettävät kieliversiokohtaiset varakieliketjut koko .NET-ekosysteemille.

Ilman LocaleChain.NET:iä pt-BR-käyttäjä näkee portugalinkielisen merkkijonon puuttuessa englannin, vaikka täydellinen pt-PT-käännös olisi saatavilla. Sama ongelma koskee es-MX:ää (ohittaa es-419:n), zh-Hantia (ohittaa zh-Hansin) ja kymmeniä muita alueellisia muotoja.
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<>));

Tavalliset sudenkuopat

Kulttuuria ei ole asetettu pyynnölle

Käännökset näkyvät aina oletuskielellä. Tarkista, että UseRequestLocalization()-funktiota kutsutaan middleware-putkessa ja kulttuuripalveluntarjoajat on määritetty. Varmista, että selain lähettää Accept-Language-otsakkeet. Testaa kyselymerkkijonon arvolla ?culture=de varmistaaksesi middlewaren toiminnan.

RESX-tiedostoa ei löydy

IStringLocalizer palauttaa käännetyn arvon sijaan avaimen nimen. Tavallisin syy on RESX-tiedoston nimi, joka ei vastaa luokan täydellistä nimiavaruuspolkua suhteessa ResourcesPathiin. Ota Microsoft.Extensions.Localizationin virheenjäljitysloki käyttöön nähdäksesi ohjelmistokehyksen tarkistamat polut.

Middlewaren järjestys on virheellinen

UseRequestLocalization()-funktion on oltava ennen UseEndpoints()- ja MapControllers()-funktioita. Jos se sijoitetaan niiden jälkeen, pyyntökulttuuria ei ole asetettu kontrollereiden suorituksen aikana. Kutsu sitä .NET 6+ -version suppeassa isännöinnissä ennen app.MapControllers()-funktiota.

Taustasäie käyttää väärää kulttuuria

CultureInfo.CurrentCulture ja CurrentUICulture ovat säiekohtaisia. Taustatehtävät (Task.Run, isännöidyt palvelut) perivät säievarannon kulttuurin, eivät pyynnön kulttuuria. Tallenna ja aseta kulttuuri eksplisiittisesti, kun lähetät työn taustalle.

Suositeltu projektirakenne

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

Kokeile i18n Agent:ia nyt

Pudota käännöstiedostosi tähän

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

tai valitse napsauttamalla

Kohdekielet

Rekisteröitymistä ei tarvitaVälitön arvio

Varakieliketju I18nAgent.LocaleChain:illa

Kun alueellisesta kieliversiosta, kuten pt-BR:stä, puuttuu käännösavain, .NET siirtyy suoraan muuttumattomaan kulttuuriin eikä tarkista ensin pääkieliversiota 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"},
});

Katso varakielioppaastamme kaikki tuetut ohjelmistokehykset ja 75 sisäänrakennettua ketjua. Learn more →

Usein kysytyt kysymykset