Search code examples
c#asp.net-coreasp.net-core-mvcasp.net-core-2.1

How to translate the "ErrorMessage" from a "Custom Attribute"


I created a Custom Validation Attribute that only validates if a CPF property is a valid CPF, but when I Localize the application I noticed that my Custom Attribute was not having its messages localized by the Framework, unlike the Data Attribute Required that has its message located correctly:

Example of using attributes with Required being correctly localized.

[Required(ErrorMessage = "CPF Requerido")]
[CPF(ErrorMessage = "CPF Inválido")]
public string CPF { get; set; }

Setting the location in the Startup.cs file

services
    .AddMvc()
    .SetCompatibilityVersion(CompatibilityVersion.Version_2_1)
    .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
    .AddDataAnnotationsLocalization(options =>
    {
        options.DataAnnotationLocalizerProvider = (type, factory) =>
        {
             return factory.Create(typeof(SharedResource));
        };
    });

Custom validation class:

public class CPFAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext context)
    {
        //Omitted for not being part of the context
    }
}

Versions:

Microsoft.AspNetCore.App (2.1.1)

Microsoft.NETCore.App (2.1)


Solution

  • Implement the Attripute Adapter:

    public class CPFAttributeAdapter : AttributeAdapterBase<CPFAttribute>
    {
            public CPFAttributeAdapter(CPFAttributeattribute, IStringLocalizer stringLocalizer) : base(attribute, stringLocalizer) { }
    
        public override void AddValidation(ClientModelValidationContext context) { }
            public override string GetErrorMessage(ModelValidationContextBase validationContext)
            {
                return GetErrorMessage(validationContext.ModelMetadata, validationContext.ModelMetadata.GetDisplayName());
            }
        }
    

    And implement the Attripute Adapter Provider:

    public class CPFAttributeAdapterProvider : IValidationAttributeAdapterProvider
    {
        private readonly IValidationAttributeAdapterProvider _baseProvider = new ValidationAttributeAdapterProvider();
    
        public IAttributeAdapter GetAttributeAdapter(CPFAttribute attribute, IStringLocalizer stringLocalizer)
        {
            if (attribute is CPFAttribute)
                return new CPFAttributeAdapter(attribute as CPFAttribute, stringLocalizer);
            else
                return _baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
        }
    
        public IAttributeAdapter GetAttributeAdapter(ValidationAttribute attribute, IStringLocalizer stringLocalizer)
        {
            if (attribute is CPFAttribute) return
                    new CPFAttributeAdapter(attribute as CPFAttribute,
            stringLocalizer);
            else return _baseProvider.GetAttributeAdapter(attribute, stringLocalizer);
        }
    }
    

    And write this in Startup.cs:

        services.AddSingleton<IValidationAttributeAdapterProvider, CPFAttributeAdapterProvider>();