【问题标题】:How to localize model field names without [Display] attribute?如何本地化没有 [Display] 属性的模型字段名称?
【发布时间】:2019-11-21 10:40:18
【问题描述】:

我已经使用 IValidationMetadataProvider 方法实现了验证属性的本地化。

它在错误消息中使用模型的字段名称。

我想从资源字符串中翻译字段名称,就像我对消息所做的那样。但我不想在每个字段上都加上[Display("FieldName")] 属性。甚至不需要放置空的 [Display] 属性 - 这将是多余的样板代码。

理想情况下,我想以某种方式告诉 MVC 验证器,每次它需要验证消息的字段名称时,它应该询问一些自定义提供程序,以便我可以从 IStringLocalizer 实现返回值。

有什么方法可以将自定义字段名称输入 MVC 验证器,而不会到处乱扔[Display] 属性?

【问题讨论】:

    标签: validation localization asp.net-core-mvc


    【解决方案1】:

    经过大量的试验和错误以及寻找基于反射的 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;
            }
    
        }
    
    

    【讨论】:

      猜你喜欢
      • 2015-07-02
      • 2018-12-17
      • 2017-04-25
      • 2021-09-09
      • 1970-01-01
      • 2023-03-26
      • 1970-01-01
      • 2018-04-19
      • 1970-01-01
      相关资源
      最近更新 更多