public class LocalizedValidationMetadataProvider : IValidationMetadataProvider
{
public LocalizedValidationMetadataProvider()
{
}
public void CreateValidationMetadata(ValidationMetadataProviderContext context)
{
var propertyName = context.Key.Name;
if (string.IsNullOrEmpty(propertyName))
return;
if (context.Key.ModelType.GetTypeInfo().IsValueType && Nullable.GetUnderlyingType(context.Key.ModelType.GetTypeInfo()) == null && context.ValidationMetadata.ValidatorMetadata.Where(m => m.GetType() == typeof(RequiredAttribute)).Count() == 0)
context.ValidationMetadata.ValidatorMetadata.Add(new RequiredAttribute());
foreach (var attribute in context.ValidationMetadata.ValidatorMetadata)
{
var tAttr = attribute as ValidationAttribute;
if (tAttr != null)
{
var errorName = tAttr.GetType().Name;
var fallbackName = errorName + "_ValidationError";
var name = tAttr.ErrorMessage ?? fallbackName;
var localized = Localizer.GetLocalization(name);
var text = localized;
tAttr.ErrorMessage = text;
tAttr.ErrorMessageResourceName = "";
}
}
}
}
此代码在运行应用程序时仅返回一次验证 如果我更改语言,验证不会发生任何变化 连displayName都改成功了
你可以按照我的样本检查一下哪里出了问题。
- 我的控制器
//The index and SetLanguage is necessary
using Microsoft.AspNetCore.Localization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Localization;
using System.Diagnostics;
using TestDelete.Models;
namespace TestDelete.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly IStringLocalizer<HomeController> _localizer;
public HomeController(ILogger<HomeController> logger, IStringLocalizer<HomeController> localizer)
{
_logger = logger;
_localizer = localizer;
}
public IActionResult Index()
{
ViewData["MyTitle"] = _localizer["The localised title of my app!"];
return View(new HomeViewModel());
}
[HttpPost]
public IActionResult Index(HomeViewModel model)
{
if (!ModelState.IsValid)
{
return View(model);
}
ViewData["Result"] = _localizer["Success!"];
return View(model);
}
[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);
}
public IActionResult Privacy()
{
return View();
}
[ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
public IActionResult Error()
{
return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
}
}
}
- 我的主页ViewModel
using System.ComponentModel.DataAnnotations;
namespace TestDelete.Models
{
public class HomeViewModel
{
[Required(ErrorMessage = "Required")]
[EmailAddress(ErrorMessage = "The Email field is not a valid e-mail address")]
[Display(Name = "Your Email")]
public string? Email { get; set; }
}
}
- 我的索引视图
@using Microsoft.AspNetCore.Mvc.Localization
@model TestDelete.Models.HomeViewModel
@{
ViewData["Title"] = "Home Page";
}
<h2>@ViewData["Title"]</h2>
<div class="row">
<div class="col-md-8">
<section>
<form asp-controller="Home" asp-action="Index" method="post" class="form-horizontal" novalidate>
<h4>@(ViewData["Result"] == null ? "Enter details" : ViewData["Result"])</h4>
<hr />
<div class="form-group">
<label asp-for="Email" class="col-md-2 control-label"></label>
<div class="col-md-10">
<input asp-for="Email" class="form-control" />
<span asp-validation-for="Email" class="text-danger"></span>
</div>
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<button type="submit" class="btn btn-default">Test</button>
</div>
</div>
</form>
</section>
</div>
</div>
- 在Views/Shared下设置_layout的footer来渲染设置语言的部分视图
<footer class="border-top footer text-muted">
…
<div class="col-sm-6 text-right">
@await Html.PartialAsync("_SelectLanguagePartial")
</div>
</div>
</footer>
- 同目录下的_SelectLanguagePartial
@using System.Threading.Tasks
@using Microsoft.AspNetCore.Builder
@using Microsoft.AspNetCore.Localization
@using Microsoft.AspNetCore.Mvc.Localization
@using Microsoft.Extensions.Options
@inject IViewLocalizer Localizer
@inject IOptions<RequestLocalizationOptions> LocOptions
@{
var requestCulture = Context.Features.Get<IRequestCultureFeature>();
var cultureItems = LocOptions.Value.SupportedUICultures
.Select(c => new SelectListItem { Value = c.Name, Text = c.DisplayName })
.ToList();
}
<div title="@Localizer["Request culture provider:"] @requestCulture?.Provider?.GetType().Name">
<form id="selectLanguage" asp-controller="Home"
asp-action="SetLanguage" asp-route-returnUrl="@Context.Request.Path"
method="post" class="form-horizontal" role="form">
@Localizer["Language:"] <select name="culture"
asp-for="@requestCulture.RequestCulture.UICulture.Name" asp-items="cultureItems"></select>
<button type="submit" class="btn btn-default btn-xs">Save</button>
</form>
</div>
- 您的 LocalizedValidationMetadataProvider 类
public class LocalizedValidationMetadataProvider : IValidationMetadataProvider{
...
}
- 自定义定位器类
using System.Globalization;
using System.Resources;
public class Localizer
{
// Create a ResourceManager instance that points to your resource file
//TestDelete is my namespace, place the resource file in the correct location
private static ResourceManager _resourceManager = new ResourceManager("TestDelete.HomeViewModel", typeof(Localizer).Assembly);
// Create a method that returns the localized string for a given name
public static string GetLocalization(string name)
{
// Get the current culture of the application
var culture = CultureInfo.CurrentCulture;
// Get the localized string for the name and culture
var localized = _resourceManager.GetString(name, culture);
// Return the localized string
return localized;
}
}
- 程序.cs
…
builder.Services.AddControllersWithViews();
builder.Services.AddLocalization(opts => { opts.ResourcesPath = "Resources"; });
builder.Services.AddMvc(options =>
{
options.ModelMetadataDetailsProviders.Add(new LocalizedValidationMetadataProvider());
})
.AddViewLocalization(
LanguageViewLocationExpanderFormat.Suffix,
opts => { opts.ResourcesPath = "Resources";
})
.AddDataAnnotationsLocalization();builder.Services.Configure<RequestLocalizationOptions>(options =>
{
var supportedCultures = new List<CultureInfo>
{
new CultureInfo("en-GB"),
new CultureInfo("en-US"),
new CultureInfo("en"),
new CultureInfo("fr-FR"),
new CultureInfo("fr"),
};
options.DefaultRequestCulture = new RequestCulture("en-GB");
// Formatting numbers, dates, etc.
options.SupportedCultures = supportedCultures;
// UI strings that we have localized.
options.SupportedUICultures = supportedCultures;
});
var app = builder.Build();
…
app.UseHttpsRedirection();
app.UseStaticFiles();
var options = ((IApplicationBuilder)app).ApplicationServices.GetRequiredService<IOptions<RequestLocalizationOptions>>();
app.UseRequestLocalization(options.Value);
app.UseRouting();
app.UseAuthorization();
app.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
app.Run();
- 我的英语和法语资源文件
如果 resx 文件“Build Action = Embedded”,请记住设置属性 资源”
如果我有什么理解错误的地方,请告诉我。