spring boot parameter check internationalization exception information

scene

If the validator check parameter is not valid, save the error information to BindingResult, and finally use i18n to display the corresponding exception information according to different countries and regions. You do not need to add any additional code to your application to do this once you have configured it.

Today spring boot is so popular that request parameters are checked with spring boot's validator and exception information is thrown by BindingResult, a solution that can be seen everywhere. But here's the pit: spring boot's validator doesn't support internationalization of exception information, so hibernate validator is used to implement i18n.

Bad configuration

My initial configuration was:

import org.springframework.validation.Validator;
@Configuration
public class I18DemoConfig {  
    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasename("ValidationMessages");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }

    @Bean
    public Validator getValidator() {
        LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
        bean.setValidationMessageSource(messageSource());
        return bean;
    }
}

ValidationMessages_zh_CN.properties:

vo.UserVo.AGE_MUST_GREATERTHAN_18=Age cannot be less than 18 years

The parameters of the check are:

@Min(value = 18, message = "{vo.UserVo.AGE_MUST_GREATERTHAN_18}")
private int age;

You can get the defaultMessage from BindingResult always: "{vo.UserVo.AGE_MUST_GREATERTHAN_18}". But execute:

 String result = messageSource.getMessage("vo.UserVo.EMAIL_NOT_EMPTY", null, Locale.CHINA)

You can get the result you want: result = can't be younger than 18.

This means that the i18n configuration itself is valid, but i18n is not validator effective. At first I thought it was a problem with the spring boot validation version, which is used in a lot of data. Why do I not work with this configuration alone? Finally, I found out in the corner cubes that the validator configuration was successful.

import javax.validation.Validator
  
@Bean(name = "validator")
public Validator localValidatorFactoryBean(){
  MessageSourceResourceBundleLocator messageSourceResourceBundleLocator = new MessageSourceResourceBundleLocator(messageSource());
  ResourceBundleMessageInterpolator resourceBundleMessageInterpolator = new ResourceBundleMessageInterpolator(messageSourceResourceBundleLocator);
  return Validation.byDefaultProvider().configure()
    .messageInterpolator(resourceBundleMessageInterpolator)
    .buildValidatorFactory()
    .getValidator();
}

When I was still lucky to be able to guess, I suddenly found these two differences: org.springframework.validation.Validator VS javax.validation.Validator. The former i18n is not effective, the latter is.

Continue to check the reason...

Find a place at last

The developer of the spring framework said that the spring boot validation does not support inserting information. The spring framework does this by using Hibernate validator.

Correct configuration

import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver;
import javax.validation.Validator;  // To use this
import java.util.Locale;
import static java.util.Arrays.asList;

@Configuration
public class I18DemoConfig {
    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        //Specify an internationalized Reurce Bundle address
        messageSource.setBasename("ValidationMessages");
        //Specify default encoding for Internationalization
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }

    @Bean
    public Validator getValidator() {
        LocalValidatorFactoryBean bean = new LocalValidatorFactoryBean();
        bean.setValidationMessageSource(messageSource());
        return bean;
    }

    @Bean
    public LocaleResolver localeResolver() {
        AcceptHeaderLocaleResolver resolver = new AcceptHeaderLocaleResolver();
        resolver.setSupportedLocales(asList(Locale.CHINA, Locale.US));
        resolver.setDefaultLocale(Locale.CHINA);
        return resolver;
    }
}

Configuration files under resource:

  • Profile content:

    # ValidationMessages_en_US.properties
    vo.UserVo.AGE_MUST_GREATERTHAN_18=Must be at least 18 years old
    
    # ValidationMessages_zh_CN.properties
    vo.UserVo.AGE_MUST_GREATERTHAN_18=Age cannot be less than 18 years
    
  • messageSource() configures the file prefix for international information storage and uses utf8 encoding, otherwise the man is shown to be garbled at the front. en_US and zh_ CNs are abbreviations of American English and Simplified Chinese respectively. Other language abbreviations are here.

  • Valuator cannot use the spring framework or i18n will not work.

  • LocaleResolver is used to determine the current country and region and render the information in the corresponding language. The AcceptHeaderLocaleResolver identifies the country to be determined from the Accept-Language field in the request header, but there are other ways to make a decision, such as getting the specified field from the middle session or rquestParam, which can be checked if needed. Two languages are supported here: Locale.CHINA, Locale.US uses the former by default.

test

English:

Chinese:

Keywords: Spring Validator i18n

Added by alanzhao on Fri, 04 Feb 2022 19:25:38 +0200