-
背景
项目已经上线,因业务需要进行国际化。要求原业务和接口不变,通过注解的方式实现,主要是在返回属性上使用注解的方式实现。
-
实现方式1 通过实现ResponseBodyAdvice接口进行属性国际化
注解:
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ReflectI18n {
String type();
}
数据库配置的多语言信息,项目启动加载到redis,通过xxljob进行redis数据更新(代码略),主要通过反射方法,获取到每个类,属性进行判断zhuanh具体处理注解代码如下:
//@ControllerAdvice
public class I18nResponseBodyAdvice implements ResponseBodyAdvice {
@Autowired
private RedisUtil i18nRedisUtil;
@Autowired
private RedisTemplate<String, String> redisTemplate;
@Override
public boolean supports(MethodParameter methodParameter, Class aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
//取到header头信息中的多语言参数字段
String languageCode = RequestHolder.getDataLanguageHeaderValue();
if (null == languageCode || "".equals(languageCode)) {
return body;
}
if (languageCode.equals("zh")) { // 处理默认语言的问题
return body;
}
if (body != null) {
// 对message进行处理
if (body instanceof Result) {
Result bodyResult = (Result) body;
if (bodyResult.getMessage() != null) {
// 通过返回码处理多语言,根据编码+多语言:得到消息
if (bodyResult.getErrorCode() != null) { // 根据errorcode去缓存中获取多语言的字段
}else{
// 如果errorCode为空,则不转换多语言,默认系统返回的语言。接口中强制返回errorCode字段
if (i18nRedisUtil.hasKey(I18nConstants.MESSAGE_PRE + bodyResult.getMessage())) {
if (i18nRedisUtil.hasKey(I18nConstants.I18N_PRE + i18nRedisUtil.get(I18nConstants.MESSAGE_PRE + bodyResult.getMessage()) + languageCode)) {
// bodyResult.set(bodyResult.getData(), i18nRedisUtil.get(I18nConstants.I18N_PRE + i18nRedisUtil.get(I18nConstants.MESSAGE_PRE + filedValue) + languageCode)); // 给变量赋值
bodyResult.setMessage((String)i18nRedisUtil.get(I18nConstants.I18N_PRE + i18nRedisUtil.get(I18nConstants.MESSAGE_PRE + bodyResult.getMessage()) + languageCode));
}
}
}
}
if (null != bodyResult.getData()) {
Class<?> cls = bodyResult.getData().getClass();
if (List.class.isAssignableFrom(cls)) {
List lists = (ArrayList) bodyResult.getData();
if (null == lists) {
return body;
}
if (lists.size() <= 0) {
return body;
}
// 如果list不为空,就遍历list
setMultiLanguageByList(lists, languageCode);
} else {// 不是list,则按照对象进行处理
setMultiLanguageByObject(cls, bodyResult, languageCode);
}
}
}
if (body instanceof ListResult) {
ListResult bodyResult = (ListResult) body;
List list = bodyResult.getData().getList();
if (null == list) {
return body;
}
if (list.size() <= 0) {
return body;
}
setMultiLanguageByList(list, languageCode);
}
}
return body;
}
/**
* 处理结果是list的返回数据
*
* @param list
*/
private void setMultiLanguageByList(List<?> list, String languageCode) {
for (int i = 0; i < list.size(); i++) {
Class<?> dtoClass = list.get(i).getClass(); // 获取list中的对象
Field[] fields = dtoClass.getDeclaredFields();// 获取他的字段数组
for (Field field : fields) {
field.setAccessible(true); // 设置字段可访问
//获取带有 i18n 注解的字段
ReflectI18n languageAnnotation = field.getDeclaredAnnotation(ReflectI18n.class);
if (languageAnnotation != null) {
try {
String type = languageAnnotation.type();
// field.set(list.get(i), "哈哈哈哈哈哈--test,后期屏蔽"); // 给变量赋值-test
String value = (String) field.get(list.get(i));
// 先判断message的key,根据message的key那id的code+language进行替换
if (i18nRedisUtil.hasKey(I18nConstants.MESSAGE_PRE +languageAnnotation.type() + value)) {
if (i18nRedisUtil.hasKey(I18nConstants.I18N_PRE +languageAnnotation.type() + i18nRedisUtil.get(I18nConstants.MESSAGE_PRE +languageAnnotation.type() + value) + languageCode)) {
field.set(list.get(i), i18nRedisUtil.get(I18nConstants.I18N_PRE +languageAnnotation.type() + i18nRedisUtil.get(I18nConstants.MESSAGE_PRE +languageAnnotation.type() + value) + languageCode)); // 给变量赋值
}
}
} catch (IllegalAccessException e) {
log.error("变量赋值出错" + e.getMessage(), e);
e.printStackTrace();
}
}
}
}
}
/**
* 处理结果是对象的方法
*
* @param cls
* @param bodyResult
*/
private void setMultiLanguageByObject(Class<?> cls, Result bodyResult, String languageCode) {
Field[] fields = cls.getDeclaredFields();// 获取他的字段数组
for (Field field : fields) {
field.setAccessible(true); // 设置字段可访问
//获取带有 i18n 注解的字段
ReflectI18n languageAnnotation = field.getDeclaredAnnotation(ReflectI18n.class);
if (languageAnnotation != null) {
try {
String filedValue = (String) field.get(bodyResult.getData());
// 先判断message的key,根据message的key那id的code+language进行替换
if (i18nRedisUtil.hasKey(I18nConstants.MESSAGE_PRE +languageAnnotation.type()+ filedValue)) {
if (i18nRedisUtil.hasKey(I18nConstants.I18N_PRE + i18nRedisUtil.get(I18nConstants.MESSAGE_PRE+languageAnnotation.type() + filedValue) + languageCode)) {
field.set(bodyResult.getData(), i18nRedisUtil.get(I18nConstants.I18N_PRE+languageAnnotation.type() + i18nRedisUtil.get(I18nConstants.MESSAGE_PRE +languageAnnotation.type()+ filedValue) + languageCode)); // 给变量赋值
}
}
} catch (IllegalAccessException e) {
log.error("变量赋值出错" + e.getMessage(), e);
e.printStackTrace();
}
}
}
}
}
使用方式
@ReflectI18n(type="1")
private String title;
-
实现方式2:通过序列化方式进行多语言转换
注解代码:
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.TYPE, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@JacksonAnnotationsInside
@JsonSerialize(using = TypeJsonI18nSerializer.class)
public @interface TypeJsonI18n {
String type() ;
}
业务实现:通过继承 JsonSerializer进行处理,代码如下:
主要逻辑是从redis中获取多语言匹配的值,进行处理。
jsonGenerator.writeString((String) i18nRedisUtil.get(I18nConstants.I18N_PRE + jsonI18nType + i18nRedisUtil.get(I18nConstants.MESSAGE_PRE + jsonI18nType + s) + languageCode));
@Component
public class TypeJsonI18nSerializer extends JsonSerializer<String> implements ContextualSerializer {
@Resource
private RedisUtil i18nRedisUtil;
private String jsonI18nType;
private boolean flag = false;
public TypeJsonI18nSerializer() {
flag = false;
}
public TypeJsonI18nSerializer(RedisUtil i18nRedisUtil) {
flag = false;
this.i18nRedisUtil = i18nRedisUtil;
}
public TypeJsonI18nSerializer(String jsonI18nType, boolean flag,RedisUtil i18nRedisUtil) {
this.i18nRedisUtil = i18nRedisUtil;
this.jsonI18nType = jsonI18nType;
this.flag = flag;
}
@Override
public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
if (flag) {
String languageCode = RequestHolder.getDataLanguageHeaderValue();
if (i18nRedisUtil.hasKey(I18nConstants.MESSAGE_PRE + jsonI18nType + s)) {
if (i18nRedisUtil.hasKey(I18nConstants.I18N_PRE + jsonI18nType + i18nRedisUtil.get(I18nConstants.MESSAGE_PRE + jsonI18nType + s) + languageCode)) {
jsonGenerator.writeString((String) i18nRedisUtil.get(I18nConstants.I18N_PRE + jsonI18nType + i18nRedisUtil.get(I18nConstants.MESSAGE_PRE + jsonI18nType + s) + languageCode));
} else {
jsonGenerator.writeString(s);
}
} else {
jsonGenerator.writeString(s);
}
} else {
jsonGenerator.writeString(s);
}
}
@Override
public JsonSerializer<?> createContextual(SerializerProvider serializerProvider, BeanProperty beanProperty) throws JsonMappingException {
TypeJsonI18n annotation = beanProperty.getAnnotation(TypeJsonI18n.class);
if (annotation == null) {
return new TypeJsonI18nSerializer(i18nRedisUtil);
}
return new TypeJsonI18nSerializer(annotation.type(),true,i18nRedisUtil);
}
}
使用方式:
@TypeJsonI18n(type="1")
private String title;
-
总结
项目进行国际化的过程中,使用spring的ResponseBodyAdvice,使用序列化方式进行多语言的转换。在此做个记录