SpringBoot專案國際化

讓蛋蛋飛發表於2019-01-28

目錄

1. 建立國際化檔案Resource Bundle

專案結構圖:

springboot專案工程詳細結構

國際化檔案結構圖:

springboot國際化檔案

在IntelliJ IDEA中建立國際化檔案:

新增en_US的英文國際化檔案

新增zh_CN的中文國際化檔案

最終國際化新增完成的介面

2. 國際化配置類InternationalConfig

springboot國際化配置類

程式碼:

@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

springboot國際化處理類

程式碼:

@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的問題

如圖:

中文國際化出現亂碼

中文國際化出現\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官網的文件地址: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,見下截圖:

IntelliJ官網截圖

總結:輸入漢字(非ASCII碼),在IntelliJ資源編譯器中顯示轉義的Unicode碼(\uXXXX(X大寫)),勾上"Transparent native-to-ascii conversion",則在資源編譯器中轉換顯示為漢字,其實際儲存為轉義的Unicode碼。

解決方法:

IntelliJ的編碼設定

有關編碼的文章,可參考:www.ruanyifeng.com/blog/2007/1…

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中配置國際化檔案路徑。

自定義與預設的messages國際化檔案

國際化處理類見上面 4. 國際化處理類MessageSourceHandler,國際化使用是一樣的。

擴充套件學習

  1. 簡書上一篇“SpringBoot - Web開發國際化”的文章:www.jianshu.com/p/01e0c7251…

  2. 阮老師一篇關於編碼的文章:www.ruanyifeng.com/blog/2007/1…

回到目錄

相關文章