Skip to main content

Kompletní průvodce lokalizací v ASP.NET Core

Od IStringLocalizer po produkci: nastavte lokalizaci založenou na resources v ASP.NET Core a poté automatizujte překlady pomocí AI.

1

Povolte lokalizační služby

Zaregistrujte lokalizační služby v Program.cs pomocí AddLocalization(), nakonfigurujte podporované kultury a přidejte request localization middleware. Tím zapojíte celou lokalizační pipeline pro Vaši aplikaci ASP.NET Core.

AddLocalization() zaregistruje IStringLocalizer a IStringLocalizerFactory do DI kontejneru. ResourcesPath říká frameworku, kde hledat Vaše .resx soubory. AddViewLocalization() povolí IViewLocalizer v Razor views a AddDataAnnotationsLocalization() povolí lokalizované validační zprávy.
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

Vytvořte RESX resource soubory

ASP.NET Core používá pro překlady RESX (XML resource) soubory. Vytvořte jeden soubor na kulturu pro každou třídu: HomeController.en.resx, HomeController.de.resx atd. Framework vybere správný soubor podle aktuální request culture.

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>
Použijte třídu SharedResource s vlastními RESX soubory pro řetězce sdílené napříč controllery a views — popisky tlačítek, navigační položky a běžné validační zprávy. Tím se vyhnete duplikaci klíčů napříč desítkami RESX souborů specifických pro jednotlivé controllery.
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

Použijte IStringLocalizer v controllerech a servisech

Pomocí dependency injection vložte IStringLocalizer&lt;T&gt; do libovolného controlleru, servisu nebo middleware. Generický parametr typu T určuje, který RESX soubor se má načíst. Přeložené řetězce získáte pomocí syntaxe localizer["Key"] s volitelnými formátovacími parametry.

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 });
    }
}
Pokud IStringLocalizer vrací název klíče místo přeložené hodnoty, zkontrolujte tři věci: (1) pojmenování RESX souboru odpovídá namespace třídy, (2) ResourcesPath v AddLocalization() ukazuje na správnou složku a (3) Build Action u RESX souboru je ve Visual Studiu nastavena na Embedded Resource.
4

Nakonfigurujte request culture middleware

ASP.NET Core určuje request culture pomocí řetězce providerů: query string, cookie a hlavička Accept-Language (v tomto pořadí). Můžete přidat vlastní providery — například načítat culture z URL segmentu routy, jako 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);
}
Pořadí middleware je důležité. UseRequestLocalization() musíte zavolat po UseRouting(), ale před UseEndpoints() nebo MapControllers(). Pokud ho umístíte příliš pozdě, culture nebude nastavena ve chvíli, kdy se vykonávají Vaše controllery.
5

Lokalizujte data annotations

Validační atributy jako [Required], [StringLength] a [Display] lze lokalizovat tak, že jejich vlastnosti ErrorMessage nebo Name nastavíte na názvy klíčů v RESX. Povolte to zavoláním AddDataAnnotationsLocalization() v 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"
Lokalizace data annotations používá pro hledání RESX souborů název třídy ViewModel, ne Controller. Pro RegisterViewModel framework hledá Resources/ViewModels/RegisterViewModel.de.resx. Pokud jsou Vaše RESX soubory pojmenované podle controlleru, validační zprávy se lokalizovat nebudou.
6

Řešte pluralizaci a ICU zprávy

.NET nemá vestavěnou podporu pravidel pluralizace jako ICU. Pro jednoduché případy použijte samostatné RESX klíče (ItemCount_One, ItemCount_Other) s přepínačem v kódu. Pro plnou podporu ICU MessageFormat napříč všemi plural kategoriemi CLDR použijte knihovnu 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 {# عنصر}}";
Nikdy nepoužívejte count == 1 k detekci singuláru. Francouzština považuje 0 za singulár. Ruština má samostatné tvary pro „few“ a „many“. Arabština má šest plural kategorií. Použijte pravidla pluralizace podle CLDR nebo knihovnu, jako je MessageFormat.NET, která to řeší správně.
7

Lokalizujte Razor views

V Razor views použijte IViewLocalizer přes @inject. RESX soubory se vyhodnocují podle cesty k view souboru. Pro řetězce s markupem bezpečné pro HTML použijte IHtmlLocalizer. Tag Helpers jako asp-for a asp-validation-for automaticky používají lokalizované atributy Display a 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 vyhodnocuje RESX soubory podle cesty k view: Views/Home/Index.cshtml hledá Resources/Views/Home/Index.de.resx. Pokud chcete sdílet řetězce napříč views, injektujte zvlášť IStringLocalizer&lt;SharedResource&gt;.
8

Automatizujte RESX překlady

S dokončeným lokalizačním nastavením přeložte své RESX soubory pomocí AI. V IDE požádejte svého AI asistenta o překlad zdrojového RESX, nebo použijte i18n Agent CLI ve Vaší CI/CD pipeline, aby překlady zůstaly synchronizované.

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
Překládejte inkrementálně — když přidáte nové klíče do zdrojového RESX souboru, přeložte pouze diff místo regenerování všech souborů. Tím zachováte lidsky revidované překlady a minimalizujete churn.

Automatizujte kvalitu překladu

Zachyťte chybějící klíče a rozbité zástupné symboly dříve, než se nasadí, pomocí i18n-validate. Otestujte své UI pomocí pseudo-překladů v i18n-pseudo ještě předtím, než dorazí skutečné překlady.

Opravte fallback locale pomocí LocaleChain.NET

.NET má ve vestavěné hierarchii CultureInfo.Parent pouze zkracování podle BCP 47: pt-BR fallbackuje na pt a pak na InvariantCulture, čímž přeskakuje pt-PT. LocaleChain.NET poskytuje konfigurovatelné fallback řetězce pro jednotlivá locale napříč celým ekosystémem .NET.

Bez LocaleChain.NET uživatel pt-BR uvidí angličtinu, když chybí portugalský řetězec — i když máte kompletní překlad pt-PT. Stejný problém postihuje es-MX (přeskakuje es-419), zh-Hant (přeskakuje zh-Hans) a desítky dalších regionálních 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<>));

Běžné chyby

Kultura není nastavena pro požadavek

Překlady se vždy zobrazují ve výchozím jazyce. Zkontrolujte, že je UseRequestLocalization() zavoláno v middleware pipeline a že jsou nakonfigurovaní provideři kultury. Ověřte, že prohlížeč odesílá hlavičky Accept-Language. Otestujte to pomocí ?culture=de v query stringu, abyste potvrdili, že middleware funguje.

RESX soubor nebyl nalezen

IStringLocalizer vrací název klíče místo přeložené hodnoty. Nejčastější příčinou je název RESX souboru, který neodpovídá úplné cestě namespace třídy vzhledem k ResourcesPath. Zapněte debug logování pro Microsoft.Extensions.Localization, abyste viděli, které cesty framework prohledává.

Pořadí middleware je nesprávné

UseRequestLocalization() musí být před UseEndpoints() a MapControllers(). Pokud je umístěno až za nimi, request culture není nastavena ve chvíli, kdy se controllery vykonávají. V .NET 6+ minimal hostingu ho zavolejte před app.MapControllers().

Vlákno na pozadí používá nesprávnou kulturu

CultureInfo.CurrentCulture a CurrentUICulture jsou per-thread. Úlohy na pozadí (Task.Run, hosted services) dědí kulturu thread poolu, ne request culture. Při spouštění práce na pozadí kulturu výslovně zachyťte a nastavte.

Doporučená struktura projektu

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

Vyzkoušejte i18n Agent nyní

Sem přetáhněte svůj překladový soubor

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

nebo klikněte a vyberte soubor

Cílové jazyky

Bez registraceOkamžitý odhad

Fallback lokál s I18nAgent.LocaleChain

Když v regionální lokále, jako je pt-BR, chybí překladový klíč, .NET skočí rovnou na invariantní kulturu místo toho, aby nejdřív zkontroloval nadřazenou lokálu 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"},
});

Podívejte se na náš Průvodce fallbackem lokál, kde najdete úplný seznam podporovaných frameworků a 75 vestavěných řetězců. Learn more →

Často kladené otázky