How to localize standard error messages of validation attributes in ASP.NET Core
Asked Answered
A

2

7

How to localize standard error messages of validation attributes in ASP.NET Core (v2.2)? For Example, [Required] attribute has this error message "The xxx field is required."; [EmailAddress] has "The xxx field is not a valid e-mail address."; [Compare] has "'xxx' and 'yyy' do not match." and so on. In our project we use not English language and I want to find a way how to translate standard error messages without writing them directly in every attribute of every data-model class

Ance answered 11/12, 2019 at 10:45 Comment(1)
Did you find any solution?Mcgrath
S
3

If you just want to localize the error messages but not to build a multi-language site, you may try this: (the message strings may be in your language.)

  1. Add a custom IValidationMetadataProvider :
    public class MyModelMetadataProvider : IValidationMetadataProvider
    {
        public void CreateValidationMetadata(ValidationMetadataProviderContext context)
        {
            if (context == null)
            {
                throw new ArgumentNullException();
            }
            var validators = context.ValidationMetadata.ValidatorMetadata;

            // add [Required] for value-types (int/DateTime etc)
            // to set ErrorMessage before asp.net does it
            var theType = context.Key.ModelType;
            var underlyingType = Nullable.GetUnderlyingType(theType);

            if (theType.IsValueType &&
                underlyingType == null && // not nullable type
                validators.Where(m => m.GetType() == typeof(RequiredAttribute)).Count() == 0)
            {
                validators.Add(new RequiredAttribute());
            }
            foreach (var obj in validators)
            {
                if (!(obj is ValidationAttribute attribute))
                {
                    continue;
                }
                fillErrorMessage<RequiredAttribute>(attribute, 
                    "You must fill in '{0}'.");
                fillErrorMessage<MinLengthAttribute>(attribute, 
                    "Min length of '{0}' is {1}.");
                fillErrorMessage<MaxLengthAttribute>(attribute, 
                    "Max length of '{0}' is {1}.");
                fillErrorMessage<EmailAddressAttribute>(attribute, 
                    "Invalid email address.", true);
                // other attributes like RangeAttribute, CompareAttribute, etc
            }
        }
        private void fillErrorMessage<T>(object attribute, string errorMessage, 
            bool forceOverriding = false) 
            where T : ValidationAttribute
        {
            if (attribute is T validationAttribute)
            {
                if (forceOverriding ||
                    (validationAttribute.ErrorMessage == null 
                    && validationAttribute.ErrorMessageResourceName == null))
                {
                    validationAttribute.ErrorMessage = errorMessage;
                }
            }
        }
    }
  1. add some lines in Startup.cs :
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews()
                .AddMvcOptions(m => {
                    m.ModelMetadataDetailsProviders.Add(new MyModelMetadataProvider());

                    m.ModelBindingMessageProvider.SetValueMustBeANumberAccessor(
                        fieldName => string.Format("'{0}' must be a valid number.", fieldName));
                    // you may check the document of `DefaultModelBindingMessageProvider`
                    // and add more if needed

                })
                ;
        }

see the document of DefaultModelBindingMessageProvider

If you can read in Japanese, see this article for more details.

Strasbourg answered 18/2, 2021 at 10:26 Comment(1)
Thanks! Works fine in ASP.NET Core 7.0. BTW, defining a custom EmailAddressAttribute error message won't have any effect in all major browsers, because ASP.NET Core adds the type="email" to the input, which triggers the browser's validation, and if it doesn't pass, the browser displays its own error message (in the browser's current language) and the form isn't submittedShayn
B
1

This is spelled out in the docs. You can do either:

  1. Use the ResourcePath option on the attribute.

     [Required(ResourcePath = "Resources")]
    

Then, you'd add the localized message to Resources/Namespace.To.MyClass.[lang].resx.

  1. Use one resource file for all classes:

     public void ConfigureServices(IServiceCollection services)
     {
         services.AddMvc()
             .AddDataAnnotationsLocalization(options => {
                 options.DataAnnotationLocalizerProvider = (type, factory) =>
                     factory.Create(typeof(SharedResource));
             });
     }
    
Bahia answered 11/12, 2019 at 13:52 Comment(1)
looks like this approach expects ErrorMessage parameter for all attributes in all models to be translated. I want to avoid itAnce

© 2022 - 2024 — McMap. All rights reserved.