记录基于若依SpringCloud版本的I8n国际化子模块开发
1.在ruoyi-common模块下添加子模块ruoyi-common-i18n
2.先看下国际化包的pom.xml
因为后续考虑会将国际化语言配置文件内容加载到redis中去,所以此处预留引入了redis相关的依赖,
注意:这是属于子模块,不作为单独项目运行,独立运行的模块都是基于SpringMVC相关的拦截来处理。如自定义验证表演,和自定义标签,都需要响应的处理。
所以这里会引入spring-boot-starter-web依赖,虽然不会重复依赖,为了使结构清晰,在引用国际化模块时,我会排除国际化包引入的spring-boot-starter-web依赖。后续会截图说明。
3.工程目录结构
4.因为ruoyi-springCloud版本是基于Nacos读取配置文件,所以在在配置类编写时,读取国际化配置文件,可以相对比较灵活。以下是介绍下比较关键的几个类
4.1 I18nUtil,国际化工具类,主要用于根据key和语言进行国际化语言转换
package com.ruoyi.common.i18n.utils; import com.ruoyi.common.constant.Constants; import com.ruoyi.common.redis.service.RedisService; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.utils.TokenUtil; import org.springframework.context.MessageSource; import org.springframework.context.i18n.LocaleContextHolder; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.util.Locale; @Component public class I18nUtil { /** * 国际化语言前缀 */ private static String localeAttributeName = Constants.LOCALE_SESSION_ATTRIBUTE_NAME; private static MessageSource messageSource; private static RedisService redisService; /** * 构建读取配置对象用于注入 * @param messageSource */ public I18nUtil(MessageSource messageSource,RedisService redisService) { I18nUtil.messageSource = messageSource; I18nUtil.redisService=redisService; } /** * 获取配置文件中key所对应的国际化语言 * @param msgKey * @return */ public static String getMessage(String msgKey) { return getMessage(msgKey, null); } /** * 获取配置文件中key所对应的国际化信息,存在占位符通过args数组进行数据填充 * @param msgKey * @param args * @return */ public static String getMessage(String msgKey,Object[] args) { try { return messageSource.getMessage(msgKey, args, getLocaleFromRedis()); } catch (Exception e) { return msgKey; } } /** * 从redis中获取当前语言 * @return */ public static Locale getLocaleFromRedis(){ String token =""; try { token = TokenUtil.getToken(getRequest()); } catch (Exception e) { return LocaleContextHolder.getLocale(); } String lan=redisService.getCacheObject(localeAttributeName + token); Locale locale=null; if(StringUtils.isNotBlank(lan)){ String[] lans=lan.split("_"); locale = new Locale(lans[0], lans[1]); }else{ locale=LocaleContextHolder.getLocale();//获取浏览器请求 } return locale; } /** * 获取请求属性 * @return */ public static ServletRequestAttributes getRequestAttributes() { RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); return (ServletRequestAttributes) attributes; } /** * 获取请求request * @return */ public static HttpServletRequest getRequest() { return getRequestAttributes().getRequest(); } }
4.2 国际化包配置类
package com.ruoyi.common.i18n.configure; import com.ruoyi.common.i18n.interceptor.I18nLocaleChangeInterceptor; import com.ruoyi.common.i18n.messagesource.RedisMessageSource; import com.ruoyi.common.i18n.resolver.RedisLocaleResolver; import com.ruoyi.common.i18n.utils.I18nUtil; import com.ruoyi.common.redis.service.RedisService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.cache.annotation.EnableCaching; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.validation.Validator; import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean; import org.springframework.web.servlet.LocaleResolver; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; import org.springframework.web.servlet.i18n.LocaleChangeInterceptor; import java.util.List; import java.util.Locale; /** * 国际化配置 */ @Configuration @EnableCaching public class LocaleConfig{ /** * 配置需要进行加载的配置文件 */ @Value("#{'${i18n_msg.baseName}'.split(',')}") private List<String> basenames; @Autowired private RedisService redisService; /** * 添加注入配置 * * @return */ @Bean public I18nUtil i18nUtil() { return new I18nUtil(messageSource(),redisService); } /** * 设置配置国际化配置文件所在位置 * * @return */ @Bean public RedisMessageSource messageSource() { // Locale.setDefault(Locale.CHINESE); RedisMessageSource source = new RedisMessageSource(); source.setBasenames(basenames.toArray(new String[basenames.size()])); // name of the resource bundle // source.setUseCodeAsDefaultMessage(true); source.setDefaultEncoding("UTF-8"); return source; } /** * 验证标签 * * @return * @throws Exception */ @Bean public Validator getValidator() { LocalValidatorFactoryBean validator = new LocalValidatorFactoryBean(); validator.setValidationMessageSource(messageSource()); return validator; } /** * 默认解析器 其中locale表示默认语言 */ @Bean(name = "langLocaleResolver") public LocaleResolver localeResolver() { RedisLocaleResolver localeResolver = new RedisLocaleResolver(); localeResolver.setDefaultLocale(Locale.SIMPLIFIED_CHINESE); return localeResolver; } /* * * 默认拦截器 其中lang表示切换语言的参数名*/ @Bean public WebMvcConfigurer localeInterceptor() { return new WebMvcConfigurer() { @Override public void addInterceptors(InterceptorRegistry registry) { I18nLocaleChangeInterceptor localeInterceptor = new I18nLocaleChangeInterceptor(localeResolver()); localeInterceptor.setParamName("lang"); registry.addInterceptor(localeInterceptor); } }; } }
其实比较关键就是这两个,所以重点代码都贴出来哈哈。
5.system模块引用pom说明,将18n国际化依赖的包排除掉,只是为了看起来更合理点,也可以不排除
具体调用示例,正常转换只需要调用I18nUtil.getMessage(key)即可。重点说下自定义校验标签,若@NotBlank @Length,因为在国际化包中配置类中在Validator中将国际化注入进去
因为ruoyi-system模块中主要校验都是通过GlobalExceptionHandler进行处理,此处讲述如何配置和处理
这里配置的时候,可以加上{}也可以不用,因为加了{}就会被自动解析成国际化后的语音,灵活性比较没那么高,我这里选择的是直接写key在上面
然后在全局异常处,统一处理如下
自此独立模块的国际化模块已经差不多完毕了。
以下是踩到的坑,
1.gateway模块时基于springwebFlux实现的所以在gateway中国际化切记不要饮用国际化包,直接在gateway模块中单独再实现一遍就好
2.语音切换的时候再i18n模块中的拦截器中,语音切换时,记得重写语言设置的类默认的那个类设置国际化语音时是直接抛出异常的
3.因为我这边已经加入了Redis支持。后续完善时再来补文章