Skip to main content

Išsamus ASP.NET Core lokalizavimo vadovas

Nuo IStringLocalizer iki gamybinės aplinkos: sukonfigūruokite ištekliais pagrįstą lokalizavimą ASP.NET Core, tada automatizuokite vertimus naudodami DI.

1

Įjungti lokalizavimo paslaugas

Užregistruokite lokalizavimo paslaugas Program.cs su AddLocalization(), sukonfigūruokite palaikomas kultūras ir pridėkite užklausos lokalizavimo tarpinę programinę įrangą. Taip prijungiamas visas ASP.NET Core programos lokalizavimo konvejeris.

AddLocalization() užregistruoja IStringLocalizer ir IStringLocalizerFactory DI talpykloje. ResourcesPath nurodo sistemai, kur rasti .resx failus. AddViewLocalization() įjungia IViewLocalizer Razor rodiniuose, o AddDataAnnotationsLocalization() – lokalizuotus tikrinimo pranešimus.
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

Sukurti RESX išteklių failus

ASP.NET Core vertimams naudoja RESX (XML išteklių) failus. Sukurkite po vieną kiekvienos kultūros failą kiekvienai klasei: HomeController.en.resx, HomeController.de.resx ir t. t. Sistema išsprendžia tinkamą failą pagal dabartinės užklausos kultūrą.

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>
Bendroms valdiklių ir rodinių eilutėms – mygtukų etiketėms, naršymo elementams ir bendriems tikrinimo pranešimams – naudokite SharedResource klasę su jos RESX failais. Taip išvengsite raktų dubliavimo dešimtyse konkretiems valdikliams skirtų RESX failų.
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

Naudoti IStringLocalizer valdikliuose ir paslaugose

Per priklausomybių įterpimą įterpkite IStringLocalizer&lt;T&gt; į bet kurį valdiklį, paslaugą ar tarpinę programinę įrangą. Bendrasis tipo parametras T nustato, kurį RESX failą įkelti. Išverstoms eilutėms gauti naudokite skliaustų sintaksę localizer["Key"] ir, jei reikia, formato parametrus.

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 });
    }
}
Jei IStringLocalizer grąžina rakto pavadinimą vietoje išverstos reikšmės, patikrinkite tris dalykus: 1) RESX failo pavadinimas atitinka klasės vardų sritį; 2) AddLocalization() ResourcesPath nurodo tinkamą aplanką; 3) Visual Studio RESX failo Build Action nustatyta į Embedded Resource.
4

Sukonfigūruoti užklausos kultūros tarpinę programinę įrangą

ASP.NET Core nustato užklausos kultūrą naudodamas teikėjų grandinę: užklausos eilutę, slapuką ir antraštę Accept-Language (tokia tvarka). Galite pridėti pasirinktinių teikėjų, pavyzdžiui, kultūrai nuskaityti iš URL maršruto segmento /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);
}
Tarpinės programinės įrangos tvarka svarbi. UseRequestLocalization() reikia iškviesti po UseRouting(), bet prieš UseEndpoints() arba MapControllers(). Jei įdėta per vėlai, valdikliams vykdantis kultūra nebus nustatyta.
5

Lokalizuoti duomenų anotacijas

Tikrinimo atributus, pavyzdžiui, [Required], [StringLength] ir [Display], galima lokalizuoti nustatant jų ypatybes ErrorMessage arba Name į RESX raktų pavadinimus. Program.cs iškvieskite AddDataAnnotationsLocalization(), kad tai įjungtumėte.

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"
Duomenų anotacijų lokalizavimas RESX failų ieško pagal ViewModel klasės, o ne valdiklio pavadinimą. RegisterViewModel atveju sistema ieško Resources/ViewModels/RegisterViewModel.de.resx. Jei RESX failai pavadinti pagal valdiklį, tikrinimo pranešimai nebus lokalizuoti.
6

Apdoroti daugiskaitą ir ICU pranešimus

.NET neturi integruoto daugiskaitos taisyklių palaikymo kaip ICU. Paprastais atvejais naudokite atskirus RESX raktus (ItemCount_One, ItemCount_Other) ir kodo perjungiklį. Visapusiškam ICU MessageFormat palaikymui visose CLDR daugiskaitos kategorijose naudokite biblioteką 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 {# عنصر}}";
Vienaskaitos formoms aptikti niekada nenaudokite count == 1. Prancūzų kalboje 0 laikomas vienaskaita. Rusų kalba turi atskiras few ir many formas. Arabų kalboje yra šešios daugiskaitos kategorijos. Naudokite CLDR suprantančias daugiskaitos taisykles arba tokią biblioteką kaip MessageFormat.NET, kuri tai apdoroja tinkamai.
7

Lokalizuoti Razor rodinius

Razor rodiniuose naudokite IViewLocalizer per @inject. Jis išsprendžia RESX failus pagal rodinio failo kelią. HTML saugioms eilutėms su žymėjimu naudokite IHtmlLocalizer. Tokie žymų pagalbininkai kaip asp-for ir asp-validation-for automatiškai naudoja lokalizuotus Display ir ErrorMessage atributus.

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 išsprendžia RESX failus pagal rodinio kelią: Views/Home/Index.cshtml ieško Resources/Views/Home/Index.de.resx. Jei norite bendrinti eilutes tarp rodinių, atskirai įterpkite IStringLocalizer&lt;SharedResource&gt;.
8

Automatizuoti RESX vertimus

Baigę lokalizavimo sąranką išverskite RESX failus naudodami DI. IDE paprašykite DI asistento išversti šaltinio RESX arba naudokite i18n Agent CLI CI/CD konvejeryje, kad vertimai liktų sinchronizuoti.

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
Verskite palaipsniui – prie šaltinio RESX failo pridėję naujų raktų išverskite tik skirtumą, o ne generuokite visus failus iš naujo. Taip išsaugomi žmonių peržiūrėti vertimai ir sumažinamas nereikalingų pakeitimų kiekis.

Automatizuoti vertimo kokybę

Naudodami i18n-validate prieš išleidimą aptikite trūkstamus raktus ir sugadintus vietos rezervavimo ženklus. Kol dar nėra tikrų vertimų, patikrinkite UI su i18n-pseudo pseudoverstimais.

Ištaisykite atsarginę lokalę su LocaleChain.NET

Integruota .NET CultureInfo.Parent hierarchija naudoja tik BCP 47 trumpinimą: pt-BR grįžta prie pt, tada InvariantCulture, praleisdama pt-PT. LocaleChain.NET suteikia konfigūruojamas kiekvienos lokalės atsargines grandines visai .NET ekosistemai.

Be LocaleChain.NET pt-BR naudotojas, trūkstant portugališkos eilutės, mato anglų kalbą, net jei turite išsamų pt-PT vertimą. Ta pati problema veikia es-MX (praleidžia es-419), zh-Hant (praleidžia zh-Hans) ir dešimtis kitų regioninių variantų.
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<>));

Dažnos klaidos

Užklausoje nenustatyta kultūra

Vertimai visada rodomi numatytąja kalba. Patikrinkite, ar tarpinės programinės įrangos konvejeryje iškviestas UseRequestLocalization() ir sukonfigūruoti kultūros teikėjai. Patikrinkite, ar naršyklė siunčia Accept-Language antraštes. Išbandykite su ?culture=de užklausos eilutėje, kad patvirtintumėte tarpinės programinės įrangos veikimą.

RESX failas nerastas

IStringLocalizer grąžina rakto pavadinimą vietoje išverstos reikšmės. Dažniausia priežastis – RESX failo pavadinimas neatitinka viso klasės vardų srities kelio ResourcesPath atžvilgiu. Įjunkite Microsoft.Extensions.Localization derinimo žurnalus, kad matytumėte, kuriuose keliuose sistema ieško.

Netinkama tarpinės programinės įrangos tvarka

UseRequestLocalization() turi būti prieš UseEndpoints() ir MapControllers(). Jei įdėta po jų, valdikliams vykdantis užklausos kultūra nenustatyta. .NET 6+ minimaliame priegloboje iškvieskite ją prieš app.MapControllers().

Foninė gija naudoja netinkamą kultūrą

CultureInfo.CurrentCulture ir CurrentUICulture nustatomos kiekvienai gijai. Foninės užduotys (Task.Run, prieglobos paslaugos) paveldi gijų telkinio, o ne užklausos kultūrą. Siųsdami foninį darbą aiškiai užfiksuokite ir nustatykite kultūrą.

Rekomenduojama projekto struktūra

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

Išbandykite i18n Agent dabar

Nuvilkite vertimo failą čia

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

arba spustelėkite norėdami pasirinkti

Tikslinės kalbos

Registruotis nereikiaMomentinis įvertis

Atsarginė lokalė su I18nAgent.LocaleChain

Kai regioninėje lokalėje, pavyzdžiui, pt-BR, trūksta vertimo rakto, .NET iškart pereina prie invariantinės kultūros, užuot pirmiausia patikrinusi pirminę lokalę 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"},
});

Visą palaikomų sistemų sąrašą ir 75 integruotas grandines rasite mūsų atsarginių lokalių vadove. Learn more →

Dažnai užduodami klausimai