我已经使用IValidationMetadataProvider方法为验证属性实现了本地化。
它在错误消息中使用模型的字段名称。
我想翻译资源字符串中的字段名,就像处理消息一样。但是我不想在每个字段上都放置[Display("FieldName")]
属性。甚至不需要放置空的[Display]
属性-这将是多余的样板代码。
理想情况下,我想以某种方式告诉MVC验证器,每当它需要字段名称作为验证消息时,它都应该询问一些自定义提供程序,以便我可以从IStringLocalizer实现中返回该值。
有什么方法可以将自定义字段名称提供给MVC验证程序,而不会随处吐出[Display]
属性吗?
答案 0 :(得分:0)
经过反复的尝试和寻找基于反射的DisplayAttribute注入之后,我发现可以注入自己的IDisplayMetadataProvider。
因此,现在我可以完全省略[Display]属性,并且我的类将自动从资源中提取字段名称,并与模型属性名称匹配。额外的好处-它将确保匹配属性的名称一致,因为它将使用全局默认值,除非对特定属性应用了显式[Display(Name = "SomeOtherResourceKey")]
。
public class LocalizableInjectingDisplayNameProvider : IDisplayMetadataProvider
{
private IStringLocalizer _stringLocalizer;
private Type _injectableType;
public LocalizableInjectingDisplayNameProvider(IStringLocalizer stringLocalizer, Type injectableType)
{
_stringLocalizer = stringLocalizer;
_injectableType = injectableType;
}
public void CreateDisplayMetadata(DisplayMetadataProviderContext context)
{
// ignore non-properties and types that do not match some model base type
if (context.Key.ContainerType == null ||
!_injectableType.IsAssignableFrom(context.Key.ContainerType))
return;
// In the code below I assume that expected use of field name will be:
// 1 - [Display] or Name not set when it is ok to fill with the default translation from the resource file
// 2 - [Display(Name = x)]set to a specific key in the resources file to override my defaults
var propertyName = context.Key.Name;
var modelName = context.Key.ContainerType.Name;
// sanity check
if (string.IsNullOrEmpty(propertyName) || string.IsNullOrEmpty(modelName))
return;
var fallbackName = propertyName + "_FieldName";
// If explicit name is missing, will try to fall back to generic widely known field name,
// which should exist in resources (such as "Name_FieldName", "Id_FieldName", "Version_FieldName", "DateCreated_FieldName" ...)
var name = fallbackName;
// If Display attribute was given, use the last of it
// to extract the name to use as resource key
foreach (var attribute in context.PropertyAttributes)
{
var tAttr = attribute as DisplayAttribute;
if (tAttr != null)
{
// Treat Display.Name as resource name, if it's set,
// otherwise assume default.
name = tAttr.Name ?? fallbackName;
}
}
// At first, attempt to retrieve model specific text
var localized = _stringLocalizer[name];
// Final attempt - default name from property alone
if (localized.ResourceNotFound)
localized = _stringLocalizer[fallbackName];
// If not found yet, then give up, leave initially determined name as it is
var text = localized.ResourceNotFound ? name : localized;
context.DisplayMetadata.DisplayName = () => text;
}
}