項目結構圖: 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…