9

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

1
  • Did you find any solution? Commented Dec 24, 2020 at 8:22

2 Answers 2

4

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.

Sign up to request clarification or add additional context in comments.

1 Comment

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 submitted
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));
             });
     }
    

2 Comments

looks like this approach expects ErrorMessage parameter for all attributes in all models to be translated. I want to avoid it
Such a let-down from how it was handled in .Net Framework, which was providing globalization out of the box for this. It baffles me all core projects have to translate and globalize default validation logic themselves instead of having it out of the box. But all issues I found about that were closed without being done. I have tried opening a new one.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.