前言
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,就会返回"用户名称不能为空"的错误信息。