1. 创建国际化文件Resource Bundle
项目结构图:
国际化文件结构图:
在IntelliJ IDEA中创建国际化文件:
2. 国际化配置类InternationalConfig
代码:
@Configuration
public class InternationalConfig {
@Value(value = "${spring.messages.basename}")
private String basename;
@Bean(name = "messageSource")
public ResourceBundleMessageSource getMessageResource() {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
messageSource.setBasename(basename);
return messageSource;
}
}
3. application.yml配置文件中配置国际化文件路径
spring:
profiles:
active: dev
# 配置国际化文件路径
messages:
basename: i18n/messages
---
spring:
profiles: dev
---
spring:
profiles: test
---
spring:
profiles: prod
4. 国际化处理类MessageSourceHandler
代码:
@Component
public class MessageSourceHandler {
@Autowired
private HttpServletRequest request;
@Autowired
private MessageSource messageSource;
public String getMessage(String messageKey) {
String message = messageSource.getMessage(messageKey, null, RequestContextUtils.getLocale(request));
return message;
}
}
注意:
- 如果是根据Request请求的语言来决定国际化:
@Autowired
private HttpServletRequest request;
public String getMessage(String messageKey) {
String message = messageSource.getMessage(messageKey, null, RequestContextUtils.getLocale(request));
return message;
}
- 如果是根据应用部署的服务器系统来决定国际化:
public String getMessage(String messageKey) {
String message = messageSource.getMessage(messageKey, null, LocaleContextHolder.getLocale());
return message;
}
5. 国际化使用
引入MessageSourceHandler类的对象messageSourceHandler,调用其messageSourceHandler.getMessage()方法即可。
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
// 引入国际化处理类
@Autowired
private MessageSourceHandler messageSourceHandler;
private String handleException(Exception e, String code) {
return handleException(e, code, null);
}
// 具体异常处理类
private String handleException(Exception e, String code, Object body) {
String msgKey = e.getMessage();
String msg = msgKey;
try {
msg = messageSourceHandler.getMessage(msgKey);
} catch (Exception ex) {
log.error(ex.getMessage(), ex);
}
if (StringUtils.isEmpty(msg)) {
if (StringUtils.isEmpty(msgKey)) {
msg = messageSourceHandler.getMessage(ErrorTypeEnum.INTERNAL_SERVER_ERROR.getMessage());
} else {
msg = msgKey;
}
}
log.error("Return Error Message : " + msg);
return msg;
}
// 请求错误异常处理
@ExceptionHandler(BadRequestException.class)
public String handleBadRequest(BadRequestException e) {
return handleException(e, e.getCode());
}
// 服务器内部异常处理
@ExceptionHandler(InternalServerException.class)
public String handleInternalServerError(InternalServerException e) {
return handleException(e, e.getCode());
}
// 调用其他服务异常处理
@ExceptionHandler(InvokeOtherServerException.class)
public String handleInvokeOtherServerError(InvokeOtherServerException e) {
return handleException(e, e.getCode(), e.getBody());
}
// DAO异常处理
@ExceptionHandler(DaoException.class)
public String handleDaoError(DaoException e) {
return handleException(e, e.getCode());
}
}
FAQ:
1. 中文国际化出现乱码或\uXXXX的问题
如图:
乱码问题根源:
<1> 创建国际化文件,IDEA默认工程的初始默认编码是GBK,如下图:
<2> 当我添加了一些国际化内容时,此时意识到编码不是UTF-8,我修改了一下默认的工程编码和系统properties编码,改为UTF-8,如下图所示。
由于我是在GBK编码下加的中文国际化内容,后又把工程编码和properties编码改为了UTF-8,两边编码不一致,导致出现乱码。
\uXXXX问题根源:
\uXXXX是Unicode的转义字符,和\n,\r同属于转义字符,看一下IntelliJ官网对此说明,如下:
IntelliJ官网的文档地址:https://www.jetbrains.com/help/idea/2017.1/editing-resource-bundle.html
## 在properties文件中格式为\uXXXX的所有转义字符,在资源编译器中被显示为未转义的Unicode字符
All escaped characters in the *.properties files in the format \uXXXX, are displayed in the resource bundle editor as un-escaped unicode literals.
## 反之亦然,如果在资源编译器中输入非ASCII字符,则它将反映在底层的properties文件中作为相应的格式为\uXXXX的转义字符
Vice versa, if a non-ASCII character is entered in the resource bundle editor, it is reflected in the underlying *.properties file as a corresponding escaped character in the format \uXXXX.
##下面是举了个例子
For example, if the *.properties file contains a property value
Was ich nicht wei\u00df, macht mich nicht hei\u00df
then the resource bundle editor will show
Was ich nicht weiß, macht mich nicht heiß
## 资源编译器本身不做任何转换。若要在属性文件中正确解析转义序列,请在“设置/首选项”对话框的“文件编码页”中选择“透明本机到ascii转换”复选框。
Resource bundle editor itself does not perform any conversion. To have escape sequences properly resolved in properties files, select the check box Transparent native-to-ascii conversion in the File Encoding page of the Settings/Preferences dialog.
## 可以使用大写和小写十六进制符号(例如'\u00E3'与'\u00e3')对非ascii符号进行编码。大写默认使用。要使用小写,请将bin/idea.properties文件(安装IntelliJ的文件夹)中的'idea.native2ascii.lowercase'属性设置为true。
It is possible to encode non-ascii symbols using both upper- and lower-case hex symbols (e.g. '\u00E3' vs '\u00e3'). Upper case is used by default. To use lower case, set 'idea.native2ascii.lowercase' property in the bin/idea.properties file to true.
Refer to the section Tuning IntelliJ IDEA for details.
继续跳转Tuning IntelliJ IDEA for details,见下截图:
总结:输入汉字(非ASCII码),在IntelliJ资源编译器中显示转义的Unicode码(\uXXXX(X大写)),勾上"Transparent native-to-ascii conversion",则在资源编译器中转换显示为汉字,其实际存储为转义的Unicode码。
解决方法:
有关编码的文章,可参考:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
2. SpringBoot有没有自带的国际化资源配置类?
有,当然有。
SpringBoot提供了自动配置类MessageSourceAutoConfiguration,
@Configuration
@ConditionalOnMissingBean(value = MessageSource.class, search = SearchStrategy.CURRENT)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(ResourceBundleCondition.class)
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {
private static final Resource[] NO_RESOURCES = {};
@Bean
@ConfigurationProperties(prefix = "spring.messages")
public MessageSourceProperties messageSourceProperties() {
return new MessageSourceProperties();
}
// 配置了MessageSource的Bean,并装配了上面MessageSourceProperties的Bean
@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
if (StringUtils.hasText(properties.getBasename())) {
messageSource.setBasenames(StringUtils.commaDelimitedListToStringArray(
StringUtils.trimAllWhitespace(properties.getBasename())));
}
if (properties.getEncoding() != null) {
messageSource.setDefaultEncoding(properties.getEncoding().name());
}
messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
Duration cacheDuration = properties.getCacheDuration();
if (cacheDuration != null) {
messageSource.setCacheMillis(cacheDuration.toMillis());
}
messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
return messageSource;
}
...
}
// 国际化资源文件配置类Properties
public class MessageSourceProperties {
/**
* Comma-separated list of basenames (essentially a fully-qualified classpath
* location), each following the ResourceBundle convention with relaxed support for
* slash based locations. If it doesn't contain a package qualifier (such as
* "org.mypackage"), it will be resolved from the classpath root.
*/
// 默认国际化资源文件名为messages,默认放在类路径下,在application.yml中不需要做任何国际化路径配置
private String basename = "messages";
/**
* Message bundles encoding.
*/
private Charset encoding = StandardCharsets.UTF_8;
/**
* Loaded resource bundle files cache duration. When not set, bundles are cached
* forever. If a duration suffix is not specified, seconds will be used.
*/
@DurationUnit(ChronoUnit.SECONDS)
private Duration cacheDuration;
/**
* Whether to fall back to the system Locale if no files for a specific Locale have
* been found. if this is turned off, the only fallback will be the default file (e.g.
* "messages.properties" for basename "messages").
*/
private boolean fallbackToSystemLocale = true;
/**
* Whether to always apply the MessageFormat rules, parsing even messages without
* arguments.
*/
private boolean alwaysUseMessageFormat = false;
/**
* Whether to use the message code as the default message instead of throwing a
* "NoSuchMessageException". Recommended during development only.
*/
private boolean useCodeAsDefaultMessage = false;
...
}
如果创建自定义的国际化资源(Resource Bundle)文件,例如:i18n/messages,则需要在application.yml中配置该自定义国际化文件的路径。
如果在resources文件夹路径下直接创建messages国际化资源文件(名字必须为messages),则不需要在applicaiton.yml中配置国际化文件路径。
国际化处理类见上面(4. 国际化处理类MessageSourceHandler),国际化使用是一样的。
扩展学习
上一篇“SpringBoot - Web开发国际化”的文章:https://www.jianshu.com/p/01e0c7251d72
阮老师一篇关于编码的文章:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html