项目结构图: html
国际化文件结构图: spring
在IntelliJ IDEA中建立国际化文件: springboot
代码:bash
@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;
}
}
复制代码
spring:
profiles:
active: dev
# 配置国际化文件路径
messages:
basename: i18n/messages
---
spring:
profiles: dev
---
spring:
profiles: test
---
spring:
profiles: prod
复制代码
代码:服务器
@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;
}
}
复制代码
注意:app
@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;
}
复制代码
引入MessageSourceHandler类的对象messageSourceHandler,调用其messageSourceHandler.getMessage()方法便可。ide
@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());
}
}
复制代码
如图: 学习
乱码问题根源:ui
<1> 建立国际化文件,IDEA默认工程的初始默认编码是GBK,以下图: this
<2> 当我添加了一些国际化内容时,此时意识到编码不是UTF-8,我修改了一下默认的工程编码和系统properties编码,改成UTF-8,以下图所示。
因为我是在GBK编码下加的中文国际化内容,后又把工程编码和properties编码改成了UTF-8,两边编码不一致,致使出现乱码。
\uXXXX问题根源:
\uXXXX是Unicode的转义字符,和\n,\r同属于转义字符,看一下IntelliJ官网对此说明,以下:
IntelliJ官网的文档地址:www.jetbrains.com/help/idea/2…
## 在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码。
解决方法:
有关编码的文章,可参考:www.ruanyifeng.com/blog/2007/1…
有,固然有。
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开发国际化”的文章:www.jianshu.com/p/01e0c7251…
阮老师一篇关于编码的文章:www.ruanyifeng.com/blog/2007/1…