Skip to main content

Panduan Lengkap Penyetempatan ASP.NET Core

Daripada IStringLocalizer hingga pengeluaran: sediakan penyetempatan berasaskan sumber dalam ASP.NET Core, kemudian automatikkan terjemahan dengan AI.

1

Dayakan Perkhidmatan Penyetempatan

Daftarkan perkhidmatan penyetempatan dalam Program.cs dengan AddLocalization(), konfigurasikan budaya yang disokong, dan tambahkan perisian tengah penyetempatan permintaan. Tindakan ini menyediakan seluruh saluran penyetempatan untuk aplikasi ASP.NET Core anda.

AddLocalization() mendaftarkan IStringLocalizer dan IStringLocalizerFactory dalam bekas DI. ResourcesPath memberitahu rangka kerja tempat untuk mencari fail .resx anda. AddViewLocalization() mendayakan IViewLocalizer dalam paparan Razor, dan AddDataAnnotationsLocalization() mendayakan mesej pengesahan yang disetempatkan.
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

Cipta Fail Sumber RESX

ASP.NET Core menggunakan fail RESX (sumber XML) untuk terjemahan. Cipta satu fail bagi setiap budaya untuk setiap kelas: HomeController.en.resx, HomeController.de.resx, dan seterusnya. Rangka kerja memilih fail yang betul berdasarkan budaya permintaan semasa.

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>
Gunakan kelas SharedResource bersama fail RESX-nya sendiri untuk rentetan yang dikongsi merentas pengawal dan paparan — label butang, item navigasi, dan mesej pengesahan umum. Cara ini mengelakkan penduaan kekunci merentas berpuluh-puluh fail RESX khusus pengawal.
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

Gunakan IStringLocalizer dalam Pengawal dan Perkhidmatan

Suntik IStringLocalizer&lt;T&gt; ke dalam mana-mana pengawal, perkhidmatan, atau perisian tengah melalui suntikan kebergantungan. Parameter jenis generik T menentukan fail RESX yang akan dimuatkan. Gunakan sintaks kurungan siku localizer["Key"] untuk mendapatkan rentetan terjemahan, dengan parameter format pilihan.

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 });
    }
}
Jika IStringLocalizer mengembalikan nama kekunci dan bukannya nilai terjemahan, semak tiga perkara: (1) penamaan fail RESX sepadan dengan ruang nama kelas, (2) ResourcesPath dalam AddLocalization() menunjuk ke folder yang betul, dan (3) Build Action fail RESX ditetapkan kepada Embedded Resource dalam Visual Studio.
4

Konfigurasikan Perisian Tengah Budaya Permintaan

ASP.NET Core menentukan budaya permintaan menggunakan rantaian penyedia: rentetan pertanyaan, kuki, dan pengepala Accept-Language (mengikut urutan tersebut). Anda boleh menambahkan penyedia tersuai — contohnya, membaca budaya daripada segmen laluan URL seperti /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);
}
Urutan perisian tengah adalah penting. UseRequestLocalization() mesti dipanggil selepas UseRouting(), tetapi sebelum UseEndpoints() atau MapControllers(). Jika diletakkan terlalu lewat, budaya belum ditetapkan apabila pengawal anda dijalankan.
5

Setempatkan Anotasi Data

Atribut pengesahan seperti [Required], [StringLength], dan [Display] boleh disetempatkan dengan menetapkan sifat ErrorMessage atau Name kepada nama kekunci RESX. Panggil AddDataAnnotationsLocalization() dalam Program.cs untuk mendayakannya.

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"
Penyetempatan anotasi data menggunakan nama kelas ViewModel untuk mencari fail RESX, bukan Controller. Untuk RegisterViewModel, rangka kerja mencari Resources/ViewModels/RegisterViewModel.de.resx. Jika fail RESX anda dinamakan sempena pengawal, mesej pengesahan tidak akan disetempatkan.
6

Kendalikan Bentuk Jamak dan Mesej ICU

.NET tidak mempunyai sokongan terbina dalam untuk peraturan bentuk jamak seperti ICU. Untuk kes mudah, gunakan kekunci RESX berasingan (ItemCount_One, ItemCount_Other) dengan percabangan kod. Untuk sokongan penuh ICU MessageFormat merentas semua kategori bentuk jamak CLDR, gunakan pustaka 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 {# عنصر}}";
Jangan sekali-kali menggunakan count == 1 untuk mengesan bentuk tunggal. Bahasa Perancis menganggap 0 sebagai bentuk tunggal. Bahasa Rusia mempunyai bentuk berasingan untuk 'sedikit' dan 'banyak'. Bahasa Arab mempunyai enam kategori bentuk jamak. Gunakan peraturan bentuk jamak yang memahami CLDR atau pustaka seperti MessageFormat.NET yang mengendalikannya dengan betul.
7

Setempatkan Paparan Razor

Gunakan IViewLocalizer dalam paparan Razor melalui @inject. IViewLocalizer memilih fail RESX berdasarkan laluan fail paparan. Untuk rentetan yang selamat bagi HTML dan mempunyai penanda, gunakan IHtmlLocalizer. Tag Helper seperti asp-for dan asp-validation-for menggunakan atribut Display dan ErrorMessage yang disetempatkan secara automatik.

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 memilih fail RESX berdasarkan laluan paparan: Views/Home/Index.cshtml mencari Resources/Views/Home/Index.de.resx. Jika anda mahu berkongsi rentetan merentas paparan, suntik IStringLocalizer&lt;SharedResource&gt; secara berasingan.
8

Automatikkan Terjemahan RESX

Selepas persediaan penyetempatan selesai, terjemahkan fail RESX anda menggunakan AI. Dalam IDE, minta pembantu AI menterjemahkan RESX sumber anda, atau gunakan CLI i18n Agent dalam saluran CI/CD agar terjemahan kekal disegerakkan.

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
Terjemahkan secara berperingkat — apabila anda menambahkan kekunci baharu pada fail RESX sumber, terjemahkan hanya perbezaannya dan bukannya menjana semula semua fail. Cara ini mengekalkan terjemahan yang telah disemak manusia dan meminimumkan perubahan yang tidak perlu.

Automatikkan Kualiti Terjemahan

Kesan kekunci hilang dan ruang letak rosak sebelum dikeluarkan dengan i18n-validate. Uji UI dengan terjemahan pseudo menggunakan i18n-pseudo sebelum terjemahan sebenar tersedia.

Baiki Sandaran Lokal dengan LocaleChain.NET

Hierarki CultureInfo.Parent terbina dalam .NET hanya menggunakan pemangkasan BCP 47: pt-BR bersandar kepada pt kemudian InvariantCulture, sambil melangkaui pt-PT. LocaleChain.NET menyediakan rantaian sandaran setiap lokal yang boleh dikonfigurasikan untuk seluruh ekosistem .NET.

Tanpa LocaleChain.NET, pengguna pt-BR melihat bahasa Inggeris apabila rentetan bahasa Portugis tiada — walaupun anda mempunyai terjemahan pt-PT yang lengkap. Masalah yang sama menjejaskan es-MX (melangkaui es-419), zh-Hant (melangkaui zh-Hans), dan berpuluh-puluh varian serantau lain.
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<>));

Kesilapan Umum

Budaya Tidak Ditetapkan pada Permintaan

Terjemahan sentiasa memaparkan bahasa lalai. Semak bahawa UseRequestLocalization() dipanggil dalam saluran perisian tengah dan penyedia budaya telah dikonfigurasikan. Pastikan pelayar menghantar pengepala Accept-Language. Uji dengan ?culture=de dalam rentetan pertanyaan untuk mengesahkan bahawa perisian tengah berfungsi.

Fail RESX Tidak Ditemui

IStringLocalizer mengembalikan nama kekunci dan bukannya nilai terjemahan. Punca paling umum ialah nama fail RESX yang tidak sepadan dengan laluan ruang nama penuh kelas relatif kepada ResourcesPath. Dayakan pengelogan nyahpepijat untuk Microsoft.Extensions.Localization bagi melihat laluan yang dicari oleh rangka kerja.

Urutan Perisian Tengah Salah

UseRequestLocalization() mesti muncul sebelum UseEndpoints() dan MapControllers(). Jika diletakkan selepasnya, budaya permintaan belum ditetapkan apabila pengawal dijalankan. Dalam pengehosan minimum .NET 6+, panggil fungsi ini sebelum app.MapControllers().

Bebenang Latar Belakang Menggunakan Budaya yang Salah

CultureInfo.CurrentCulture dan CurrentUICulture adalah bagi setiap bebenang. Tugas latar belakang (Task.Run, perkhidmatan yang dihoskan) mewarisi budaya kumpulan bebenang, bukan budaya permintaan. Tangkap dan tetapkan budaya secara jelas apabila menghantar kerja latar belakang.

Struktur Projek yang Disyorkan

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

Cuba i18n Agent Sekarang

Lepaskan fail terjemahan anda di sini

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

atau klik untuk semak imbas

Bahasa sasaran

Tidak perlu mendaftarAnggaran serta-merta

Sandaran Lokal dengan I18nAgent.LocaleChain

Apabila kekunci terjemahan tiada dalam lokal serantau seperti pt-BR, .NET terus beralih kepada budaya invarian dan bukannya menyemak lokal induk pt terlebih dahulu.

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"},
});

Lihat Panduan Sandaran Bahasa kami untuk senarai lengkap rangka kerja yang disokong dan 75 rantaian terbina dalam. Learn more →

Soalan Lazim