國際化是產(chǎn)品、應(yīng)用程序或文檔內(nèi)容的設(shè)計和開發(fā),它可以為不同文化、地區(qū)或語言的目標受眾輕松實現(xiàn)本地化。國際化(Internationalization)通常用英文寫成 i18n,其中 18 是英文單詞中 i 和 n 之間的字母數(shù)。
任何一個面向全世界的軟件都會面臨多語言國際化的問題,尤其是跨境電商行業(yè),在做自研系統(tǒng)必不可少的要實現(xiàn)多語言功能,就是在數(shù)據(jù)展示給用戶之前,替換成用戶可識別的語言,這里以spring i18n 國際化來介紹一下。
SpringBoot下Application.yml
basename:以逗號分隔的基名列表(本質(zhì)上是一個完全限定的類路徑位置),每個基名都遵循 ResourceBundle 約定,對基于斜杠的位置提供寬松的支持,它將從類路徑根目錄解析。
cache-duration:加載的資源包文件緩存的持續(xù)時間。如果沒有設(shè)置,捆綁包將被永久緩存。如果沒有指定持續(xù)時間后綴,將使用秒。
spring:
# 資源信息
messages:
encoding: utf-8
# 國際化資源文件路徑(配置文件路徑),多模塊也可以用逗號分隔
basename: i18n/messages
將異常信息寫入i18n資源文件
messages.properties
exception.name.can.not.null=exception name can not null,the name is {0}
exception.insert=exception insert
messages_zh_CN.properties
exception.name.can.not.null=用戶名不能為空,用戶名為:{0}
exception.insert=新增異常
工具類 I18nUtils
@Component
@Slf4j
public class I18nUtils {
// 如果當前bean不加@Component注解,則messageSource無法注入,始終為null
private static MessageSource messageSource;
@Autowired
public void setMessageSource(MessageSource messageSource) {
I18nUtils.messageSource = messageSource;
}
/**
* 解析code對應(yīng)的信息進行返回,如果對應(yīng)的code不能被解析則拋出異常NoSuchMessageException
*
* @param code 需要進行解析的code,對應(yīng)資源文件中的一個屬性名
* @param args 當對應(yīng)code對應(yīng)的信息不存在時需要返回的默認值
* @return 國際化翻譯值
*/
public static String i18n(String code, Object... args) {
return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
}
/**
* 解析code對應(yīng)的信息進行返回,如果對應(yīng)的code不能被解析則返回默認信息defaultMessage。
*
* @param code 需要進行解析的code,對應(yīng)資源文件中的一個屬性名
* @param defaultMessage 當對應(yīng)code對應(yīng)的信息不存在時需要返回的默認值
* @param args 需要用來替換code對應(yīng)的信息中包含參數(shù)的內(nèi)容,如:{0},{1,date},{2,time}
* @return 對應(yīng)的Locale
*/
public static String i18nOrDefault(String code, String defaultMessage, Object... args) {
return messageSource.getMessage(code, args, defaultMessage, LocaleContextHolder.getLocale());
}
/**
* 因為i18n方法如果獲取不到對應(yīng)的鍵值,會拋異常NoSuchMessageException
* 本方法是對i18n方法的封裝。當報錯時并不拋出異常,而是返回source
*
* @param source 模板
* @param args 參數(shù)
* @return 返回I18n(正常結(jié)束)或者source(拋出異常)
* @see #i18n(String, Object...)
*/
@NonNull
public static String tryI18n(@NonNull String source, @NonNull Object... args) {
String res;
try {
res = i18n(source, args);
} catch (Exception ignored) {
res = source;
}
return res;
}
}
自定義異常
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {
@ExceptionHandler(CommonException.class)
public ApiResponse<Object> handleCommonException(CommonException e) {
log.error(e.getMessage(), e);
return new ApiResponse<>(-1,"error",e.getMessage());
}
}
應(yīng)用
@Service
public class UserService {
@Resource
private UserDao userDao;
public void insertUser(UserQo userQo){
UserEntity userEntity = userDao.findByName(userQo.getName());
if(Objects.nonNull(userEntity)){
// i18n帶有參數(shù)
String name = userQo.getName();
throw new CommonException("exception.name.can.not.null",name);
}
userEntity = new UserEntity();
BeanUtils.copyProperties(userQo,userEntity);
userEntity.setCreateTime(new Date());
int count = userDao.insert(userEntity);
if(count==1){
// i18n不帶有參數(shù)
throw new CommonException("exception.insert");
}
}
}
結(jié)論
這里只是簡單的演示了 spring i18n 的功能,可以滿足一些簡單場景的需求,可以適當擴展,比如:如果使用了 nacos 等配置中心,則需要去注冊中心手動拉取 i18n 的 properties 文件內(nèi)容,并加載到應(yīng)用程序的內(nèi)存里。