1. 创建国际化文件Resource Bundle
项目结构图:

springboot项目工程详细结构
国际化文件结构图:

springboot国际化文件
在IntelliJ IDEA中创建国际化文件:

添加en_US的英文国际化文件

添加zh_CN的中文国际化文件

最终国际化添加完成的界面
2. 国际化配置类InternationalConfig

springboot国际化配置类
代码:
@Configurationpublic 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

springboot国际化处理类
代码:
@Componentpublic class MessageSourceHandler {@Autowiredprivate HttpServletRequest request;@Autowiredprivate MessageSource messageSource;public String getMessage(String messageKey) {String message = messageSource.getMessage(messageKey, null, RequestContextUtils.getLocale(request));return message;}}
注意:
- 如果是根据Request请求的语言来决定国际化:
@Autowiredprivate 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@RestControllerAdvicepublic class GlobalExceptionHandler {// 引入国际化处理类@Autowiredprivate 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的问题
如图:

中文国际化出现乱码

中文国际化出现\uXXXX
乱码问题根源:
<1> 创建国际化文件,IDEA默认工程的初始默认编码是GBK,如下图:

IDEA默认工程初始编码为GBK
<2> 当我添加了一些国际化内容时,此时意识到编码不是UTF-8,我修改了一下默认的工程编码和系统properties编码,改为UTF-8,如下图所示。
由于我是在GBK编码下加的中文国际化内容,后又把工程编码和properties编码改为了UTF-8,两边编码不一致,导致出现乱码。

修改了工程编码和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 valueWas ich nicht wei\u00df, macht mich nicht hei\u00dfthen the resource bundle editor will showWas 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,见下截图:

IntelliJ官网截图
总结:输入汉字(非ASCII码),在IntelliJ资源编译器中显示转义的Unicode码(\uXXXX(X大写)),勾上”Transparent native-to-ascii conversion”,则在资源编译器中转换显示为汉字,其实际存储为转义的Unicode码。
解决方法:

IntelliJ的编码设置
有关编码的文章,可参考: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)@EnableConfigurationPropertiespublic class MessageSourceAutoConfiguration {private static final Resource[] NO_RESOURCES = {};@Bean@ConfigurationProperties(prefix = "spring.messages")public MessageSourceProperties messageSourceProperties() {return new MessageSourceProperties();}// 配置了MessageSource的Bean,并装配了上面MessageSourceProperties的Bean@Beanpublic 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;}...}// 国际化资源文件配置类Propertiespublic 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中配置国际化文件路径。

自定义与默认的messages国际化文件
国际化处理类见上面(4. 国际化处理类MessageSourceHandler),国际化使用是一样的。
扩展学习
- 简书上一篇“SpringBoot - Web开发国际化”的文章:https://www.jianshu.com/p/01e0c7251d72
- 阮老师一篇关于编码的文章:http://www.ruanyifeng.com/blog/2007/10/ascii_unicode_and_utf-8.html
