
Ο πλήρης οδηγός τοπικής προσαρμογής του ASP.NET Core
Από το IStringLocalizer έως την παραγωγή: ρυθμίστε τοπική προσαρμογή με πόρους στο ASP.NET Core και αυτοματοποιήστε τις μεταφράσεις με AI.
Ενεργοποιήστε τις υπηρεσίες τοπικής προσαρμογής
Καταχωρίστε τις υπηρεσίες τοπικής προσαρμογής στο Program.cs με το AddLocalization(), διαμορφώστε τις υποστηριζόμενες γλώσσες και προσθέστε το middleware τοπικής προσαρμογής αιτημάτων. Έτσι ενεργοποιείται ολόκληρη η διοχέτευση τοπικής προσαρμογής για την εφαρμογή ASP.NET Core.
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();Δημιουργήστε αρχεία πόρων RESX
Το ASP.NET Core χρησιμοποιεί αρχεία RESX (πόροι XML) για τις μεταφράσεις. Δημιουργήστε ένα αρχείο ανά γλώσσα και ανά κλάση: HomeController.en.resx, HomeController.de.resx κ.λπ. Το framework επιλέγει το σωστό αρχείο βάσει της τρέχουσας γλώσσας του αιτήματος.
<!-- 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><!-- 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 { }Χρησιμοποιήστε το IStringLocalizer σε controllers και υπηρεσίες
Εισαγάγετε το IStringLocalizer<T> σε οποιονδήποτε controller, υπηρεσία ή middleware μέσω dependency injection. Η γενική παράμετρος τύπου T καθορίζει ποιο αρχείο RESX θα φορτωθεί. Χρησιμοποιήστε τη σύνταξη αγκυλών localizer["Key"] για να ανακτήσετε μεταφρασμένες συμβολοσειρές, προαιρετικά με παραμέτρους μορφοποίησης.
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 });
}
}Διαμορφώστε το middleware γλώσσας αιτήματος
Το ASP.NET Core προσδιορίζει τη γλώσσα του αιτήματος μέσω μιας ακολουθίας παρόχων: συμβολοσειρά ερωτήματος, cookie και κεφαλίδα Accept-Language, με αυτή τη σειρά. Μπορείτε να προσθέσετε προσαρμοσμένους παρόχους — για παράδειγμα, να διαβάζετε τη γλώσσα από ένα τμήμα διαδρομής URL όπως το /de/home.
// 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: 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);
}Μεταφράστε τα data annotations
Χαρακτηριστικά επικύρωσης όπως τα [Required], [StringLength] και [Display] μπορούν να μεταφραστούν, αν ορίσετε τις ιδιότητες ErrorMessage ή Name σε ονόματα κλειδιών RESX. Καλέστε το AddDataAnnotationsLocalization() στο Program.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"Χειριστείτε πληθυντικούς και μηνύματα ICU
Το .NET δεν διαθέτει ενσωματωμένη υποστήριξη κανόνων πληθυντικού όπως το ICU. Για απλές περιπτώσεις, χρησιμοποιήστε ξεχωριστά κλειδιά RESX (ItemCount_One, ItemCount_Other) με επιλογή μέσω κώδικα. Για πλήρη υποστήριξη του ICU MessageFormat σε όλες τις κατηγορίες πληθυντικού CLDR, χρησιμοποιήστε τη βιβλιοθήκη MessageFormat.NET.
// 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 {# عنصر}}";Μεταφράστε τις προβολές Razor
Χρησιμοποιήστε το IViewLocalizer στις προβολές Razor μέσω του @inject. Επιλέγει αρχεία RESX βάσει της διαδρομής αρχείου της προβολής. Για συμβολοσειρές με σήμανση που είναι ασφαλής για HTML, χρησιμοποιήστε το IHtmlLocalizer. Τα Tag Helpers, όπως τα asp-for και asp-validation-for, χρησιμοποιούν αυτόματα τα μεταφρασμένα χαρακτηριστικά Display και ErrorMessage.
@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>Αυτοματοποιήστε τις μεταφράσεις RESX
Αφού ολοκληρώσετε τη ρύθμιση της τοπικής προσαρμογής, μεταφράστε τα αρχεία RESX με AI. Ζητήστε από τον βοηθό AI στο IDE σας να μεταφράσει το αρχικό RESX ή χρησιμοποιήστε το i18n Agent CLI στη διοχέτευση CI/CD για να διατηρείτε συγχρονισμένες τις μεταφράσεις.
# 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Αυτοματοποιήστε τον έλεγχο ποιότητας των μεταφράσεων
Διορθώστε την εναλλακτική επιλογή γλώσσας με το LocaleChain.NET
Η ενσωματωμένη ιεραρχία CultureInfo.Parent του .NET χρησιμοποιεί μόνο περικοπή BCP 47: για το pt-BR χρησιμοποιείται εναλλακτικά το pt και έπειτα το InvariantCulture, παραλείποντας το pt-PT. Το LocaleChain.NET παρέχει διαμορφώσιμες αλυσίδες εναλλακτικής επιλογής ανά γλώσσα για ολόκληρο το οικοσύστημα .NET.
dotnet add package I18nAgent.LocaleChainusing 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<>));Συνηθισμένες παγίδες
Η γλώσσα δεν έχει οριστεί στο αίτημα
Το αρχείο RESX δεν βρέθηκε
Λανθασμένη σειρά middleware
Το νήμα παρασκηνίου χρησιμοποιεί λανθασμένη γλώσσα
Προτεινόμενη δομή έργου
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Δοκιμάστε τώρα το i18n Agent
Αφήστε εδώ το αρχείο μετάφρασής σας
JSON, YAML, PO, XML, CSV, Markdown, Properties
ή κάντε κλικ για να επιλέξετε αρχείο
Γλώσσες-στόχοι
Εναλλακτική επιλογή γλώσσας με το I18nAgent.LocaleChain
Όταν λείπει ένα κλειδί μετάφρασης σε μια περιφερειακή γλώσσα όπως το pt-BR, το .NET μεταβαίνει απευθείας στην invariant culture αντί να ελέγξει πρώτα τη γονική γλώσσα pt.
dotnet add package I18nAgent.LocaleChainusing I18nAgent.LocaleChain;
LocaleChain.Configure(new Dictionary<string, string[]>
{
["pt-BR"] = new[] {"pt", "en"},
["zh-Hant-HK"] = new[] {"zh-Hant", "zh", "en"},
});Δείτε τον οδηγό μας για την εναλλακτική επιλογή γλώσσας, με την πλήρη λίστα των υποστηριζόμενων framework και 75 ενσωματωμένων αλυσίδων. Learn more →