Spring对国际化的支持

前言

i18n(其来源是英文单词 internationalization 的首末字符i和n,18为中间的字符数)是"国际化"的简称。对程序来说,可以在不修改内部代码的情况下,根据不同语言及地区显示不同的页面。

准备工作

IDEA中properties文件默认的编码为GBK,需要修改为UTF-8。

Java支持

java内部提供了对 i18n 的支持,使用ResourceBundle工具类。

配置资源文件

配置不同语言的资源文件,需要配置在classpath下,因为我的是SpringBoot项目,所以放在了resources目录下

en_US表示美国,

name=lisi

zh_CN表示中国

name=李四

没有后缀表示默认,设置为其他配置的父配置文件

name=lisi
age=23

代码处理

import java.util.Locale;
import java.util.ResourceBundle;

public class TestI18n {

  public static void main(String[] args) {
    //获取US(美国)的配置信息
    ResourceBundle resourceBundle = ResourceBundle.getBundle("i18n/messages", Locale.CHINA);
    for (String key : resourceBundle.keySet()) {
      System.out.println(resourceBundle.getObject(key));//lisi age
    }
    //测试默认的配置
    resourceBundle = ResourceBundle.getBundle("i18n/messages", Locale.ITALY);
    System.out.println(resourceBundle.keySet());//name,age
  }

}

先根据传入的Locale查询到配置文件,如果查询不到,再查询默认的配置文件。

Spring支持

Spring在Java的基础上做了封装,加了一层缓存,并且支持模板信息。

配置文件

默认配置文件有变动,其他不变

name=lisi
age={0}

代码处理

import java.util.Locale;
import org.springframework.context.support.ResourceBundleMessageSource;

public class TestSpringI18n {

  public static void main(String[] args) {
    ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
    //需要设置为UTF-8,默认为ISO-8859-1
    messageSource.setDefaultEncoding("UTF-8");
    messageSource.addBasenames("i18n/messages");
    System.out.println(messageSource.getMessage("name", null, Locale.CHINA));//李四
    System.out.println(messageSource.getMessage("age", new Object[]{"12"}, Locale.CHINA));//12
  }

}

模板消息是使用Java中的MessageFormat实现的,格式为{index}

Spring内部对ResourceBundle的缓存处理。

SpringBoot支持

SpringBoot在Spring的基础上添加了自动配置。这里我们简单实现一个功能,根据不同的语言返回不同的name。

配置路径

server:
  port: 8880
  servlet:
    context-path: /i18n

spring:
  messages:
    basename: i18n/messages

可以看到SpringBoot默认使用的就是ResourceBundleMessageSource。

配置自定义Locale解析器

import java.util.Locale;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Service;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.i18n.AbstractLocaleResolver;

/**
 * bean名称必须为localeResolver
 */
@Service(DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME)
public class CustomLocaleReslover extends AbstractLocaleResolver {

  @Override
  public Locale resolveLocale(HttpServletRequest request) {
    String language = request.getHeader("language");
    if ("zh".equals(language)) {
      return Locale.CHINA;
    }
    if ("en".equals(language)) {
      return Locale.US;
    }
    return Locale.CHINA;
  }

  @Override
  public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

  }
}

bean名称必须为localeResolver,因为DispatcherServlet中就是根据这个name来获取实例的。

配置Controller

import javax.servlet.http.HttpServletRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.servlet.support.RequestContext;

@RestController
@RequestMapping("/test")
public class TestController {

  @GetMapping("/testI18n")
  public String testI18n(HttpServletRequest request) {
    RequestContext requestContext = new RequestContext(request);
    return requestContext.getMessage("name");
  }
}

HibernateValidator支持

SpringBoot自动注入了HibernateValidator

但使用的是默认的ResourceBundleLocator,默认的ResourceBundle路径为ValidationMessages。我们需要自定义路径。

配置自定义消息处理器

@Configuration
public class HibernateValidatorConfig {

  @Bean
  @ConditionalOnMissingBean(Validator.class)
  public static LocalValidatorFactoryBean defaultValidator(MessageSource messageSource) {
    LocalValidatorFactoryBean factoryBean = new LocalValidatorFactoryBean();
    ResourceBundleMessageInterpolator messageInterpolator = new ResourceBundleMessageInterpolator(
        new MessageSourceResourceBundleLocator(messageSource));
    factoryBean.setMessageInterpolator(messageInterpolator);
    return factoryBean;
  }

}

实体类

import javax.validation.constraints.NotBlank;

public class TestRequest {

  @NotBlank(message = "{username.errormsg}")
  private String username;

  public void setUsername(String username) {
    this.username = username;
  }

  public String getUsername() {
    return username;
  }
}

配置文件

默认配置文件有变动,其他不变

name=lisi
username.errormsg=用户名称不能为空

配置Controller

@RestController
@RequestMapping("/test")
public class TestController {

  @GetMapping("/testI18n")
  public String testI18n(HttpServletRequest request, @Valid @RequestBody TestRequest testRequest,
      BindingResult bindingResult) {
    RequestContext requestContext = new RequestContext(request);
    if (bindingResult.hasErrors()) {
      return bindingResult.getAllErrors().get(0).getDefaultMessage();//用户名称不能为空
    }
    return requestContext.getMessage("name");
  }
}

如果没有传username,就会返回"用户名称不能为空"的错误信息。

posted @ 2022-04-05 13:32  strongmore  阅读(187)  评论(0编辑  收藏  举报