Skip to main content

Cjelovit vodič za lokalizaciju u ASP.NET Coreu

Od sučelja IStringLocalizer do produkcije: postavite lokalizaciju temeljenu na resursima u ASP.NET Coreu, a zatim automatizirajte prevođenje pomoću AI-ja.

1

Omogućite usluge lokalizacije

Registrirajte usluge lokalizacije u datoteci Program.cs pomoću AddLocalization(), konfigurirajte podržane kulture i dodajte međuverzje za lokalizaciju zahtjeva. Time povezujete cijeli tijek lokalizacije svoje ASP.NET Core aplikacije.

AddLocalization() registrira IStringLocalizer i IStringLocalizerFactory u DI spremniku za umetanje ovisnosti. ResourcesPath određuje gdje radni okvir traži .resx datoteke. AddViewLocalization() omogućuje IViewLocalizer u prikazima Razor, a AddDataAnnotationsLocalization() omogućuje lokalizaciju poruka provjere valjanosti.
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

Izradite datoteke resursa RESX

ASP.NET Core za prijevode koristi RESX datoteke (XML datoteke resursa). Izradite po jednu datoteku za svaku kulturu i klasu: HomeController.en.resx, HomeController.de.resx itd. Radni okvir odabire ispravnu datoteku prema kulturi trenutačnog zahtjeva.

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>
Za tekstove zajedničke kontrolerima i prikazima upotrijebite klasu SharedResource s vlastitim RESX datotekama — oznake gumba, navigacijske stavke i uobičajene poruke provjere valjanosti. Tako izbjegavate ponavljanje ključeva u desecima RESX datoteka za pojedine kontrolere.
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

Upotrebljavajte IStringLocalizer u kontrolerima i uslugama

Umetnite IStringLocalizer&lt;T&gt; u bilo koji kontroler, uslugu ili međuverzje putem umetanja ovisnosti. Generički parametar vrste T određuje koja se RESX datoteka učitava. Prevedene tekstove dohvatite sintaksom uglatih zagrada localizer["Key"], uz neobavezne parametre oblikovanja.

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 });
    }
}
Ako IStringLocalizer vraća naziv ključa umjesto prevedene vrijednosti, provjerite tri stavke: (1) odgovara li naziv RESX datoteke prostoru imena klase, (2) upućuje li ResourcesPath u AddLocalization() na ispravnu mapu i (3) je li svojstvo Build Action RESX datoteke u alatu Visual Studio postavljeno na Embedded Resource.
4

Konfigurirajte međuverzje za kulturu zahtjeva

ASP.NET Core određuje kulturu zahtjeva lancem pružatelja: iz parametra upita, kolačića i zaglavlja Accept-Language, tim redoslijedom. Možete dodati prilagođene pružatelje — primjerice, za čitanje kulture iz segmenta URL putanje kao što je /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);
}
Redoslijed međuverzja važan je. UseRequestLocalization() mora se pozvati nakon UseRouting(), ali prije UseEndpoints() ili MapControllers(). Ako ga postavite prekasno, kultura neće biti postavljena pri izvođenju kontrolera.
5

Lokalizirajte podatkovne anotacije

Atribute provjere valjanosti poput [Required], [StringLength] i [Display] možete lokalizirati tako da njihova svojstva ErrorMessage ili Name postavite na nazive RESX ključeva. To omogućite pozivom AddDataAnnotationsLocalization() u datoteci Program.cs.

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"
Lokalizacija podatkovnih anotacija RESX datoteke traži prema nazivu klase ViewModel, a ne kontrolera. Za RegisterViewModel radni okvir traži Resources/ViewModels/RegisterViewModel.de.resx. Ako su RESX datoteke nazvane prema kontroleru, poruke provjere valjanosti neće se lokalizirati.
6

Obradite množinu i ICU poruke

.NET nema ugrađenu podršku za pravila množine poput ICU-a. U jednostavnim slučajevima upotrijebite zasebne RESX ključeve (ItemCount_One, ItemCount_Other) i naredbu switch u kodu. Za potpunu podršku za format ICU MessageFormat u svim CLDR kategorijama množine upotrijebite biblioteku 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 {# عنصر}}";
Nikada ne utvrđujte jedninu izrazom count == 1. Francuski tretira 0 kao jedninu, ruski ima zasebne oblike za 'few' i 'many', a arapski šest kategorija množine. Upotrijebite pravila množine usklađena s CLDR-om ili biblioteku poput MessageFormat.NET koja ih ispravno primjenjuje.
7

Lokalizirajte prikaze Razor

U prikazima Razor upotrebljavajte IViewLocalizer putem @inject. On odabire RESX datoteke prema putanji datoteke prikaza. Za tekstove s oznakama koji su sigurni za HTML upotrijebite IHtmlLocalizer. Pomagači oznaka poput asp-for i asp-validation-for automatski upotrebljavaju lokalizirane atribute Display i ErrorMessage.

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 odabire RESX datoteke prema putanji prikaza: Views/Home/Index.cshtml traži Resources/Views/Home/Index.de.resx. Želite li dijeliti tekstove među prikazima, zasebno umetnite IStringLocalizer&lt;SharedResource&gt;.
8

Automatizirajte prevođenje RESX datoteka

Nakon postavljanja lokalizacije prevedite RESX datoteke pomoću AI-ja. U svojem IDE-u zatražite od AI pomoćnika da prevede izvornu RESX datoteku ili upotrijebite i18n Agent CLI u CI/CD procesu kako bi prijevodi ostali usklađeni.

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
Prevodite postupno — kada u izvornu RESX datoteku dodate nove ključeve, prevedite samo razliku umjesto ponovnog stvaranja svih datoteka. Tako čuvate prijevode koje su pregledali ljudi i svodite nepotrebne izmjene na najmanju mjeru.

Automatizirajte provjeru kvalitete prijevoda

Alatom i18n-validate otkrijte ključeve koji nedostaju i neispravna rezervirana mjesta prije objave. Korisničko sučelje testirajte pseudoprijevodima iz alata i18n-pseudo prije nego što stignu stvarni prijevodi.

Ispravite pričuvni odabir jezika uz LocaleChain.NET

Ugrađena hijerarhija CultureInfo.Parent u .NET-u upotrebljava samo skraćivanje oznaka prema standardu BCP 47: pt-BR prelazi na pt, a zatim na InvariantCulture, preskačući pt-PT. LocaleChain.NET pruža prilagodljive lance pričuvnog odabira za svaku jezičnu inačicu u cijelom .NET ekosustavu.

Bez LocaleChain.NET-a korisniku s jezičnom oznakom pt-BR prikazuje se engleski kada nedostaje portugalski tekst — čak i ako imate cjelovit prijevod za pt-PT. Isti problem pogađa es-MX (preskače es-419), zh-Hant (preskače zh-Hans) i desetke drugih regionalnih inačica.
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<>));

Česte zamke

Kultura zahtjeva nije postavljena

Prijevodi se uvijek prikazuju na zadanom jeziku. Provjerite poziva li se UseRequestLocalization() u tijeku međuverzja i jesu li pružatelji kulture konfigurirani. Provjerite šalje li preglednik zaglavlja Accept-Language. Ispitajte rad međuverzja pomoću ?culture=de u parametrima upita.

RESX datoteka nije pronađena

IStringLocalizer vraća naziv ključa umjesto prevedene vrijednosti. Najčešći je uzrok naziv RESX datoteke koji ne odgovara punoj putanji prostora imena klase u odnosu na ResourcesPath. Omogućite zapisivanje poruka za otklanjanje pogrešaka komponente Microsoft.Extensions.Localization kako biste vidjeli koje putanje radni okvir pretražuje.

Pogrešan redoslijed međuverzja

UseRequestLocalization() mora se nalaziti prije UseEndpoints() i MapControllers(). Ako ga postavite iza njih, kultura zahtjeva neće biti postavljena pri izvođenju kontrolera. U minimalnom modelu hostiranja za .NET 6+ pozovite ga prije app.MapControllers().

Pozadinska nit upotrebljava pogrešnu kulturu

CultureInfo.CurrentCulture i CurrentUICulture svojstveni su pojedinoj niti. Pozadinski zadaci (Task.Run, hostirane usluge) nasljeđuju kulturu skupa niti, a ne kulturu zahtjeva. Pri pokretanju pozadinskog posla izričito zabilježite i postavite kulturu.

Preporučena struktura projekta

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

Isprobajte i18n Agent odmah

Povucite datoteku za prijevod ovdje

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

ili kliknite za odabir

Ciljni jezici

Registracija nije potrebnaProcjena odmah

Pričuvni odabir jezika uz I18nAgent.LocaleChain

Kada ključ prijevoda nedostaje u regionalnoj jezičnoj inačici poput pt-BR, .NET odmah prelazi na nepromjenjivu kulturu umjesto da najprije provjeri nadređenu jezičnu oznaku 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"},
});

U našem vodiču za pričuvni odabir jezika pogledajte cjelovit popis podržanih radnih okvira i 75 ugrađenih lanaca. Learn more →

Česta pitanja