美文网首页
接口多语言方案

接口多语言方案

作者: 千年的心 | 来源:发表于2020-07-04 12:50 被阅读0次

需求

最近接到一个官网需求,官网需要动态展示一些企业信息,并且需要中英文切换。客户会在后台录入中文和英文的数据,由于项目是前后端分离,我这边只负责后端部分,根据需求转换为接口的需求就是:1.未登陆匿名用户,那么接口需要返回用户选择的语言,2.登陆用户(系统只有一个账号来维护信息),那么需要返回中英文两个信息。

思路

自定义对象的序列化规则。

实现

1. 创建存储值对象

1.1 基础值对象

package com.yugioh.api.business.common.model;

import com.yugioh.api.business.common.enums.LanguageEnum;
import com.yugioh.api.business.util.RequestUtil;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.springframework.data.annotation.Transient;

import java.util.Objects;


/**
 * 公用多语言值对象
 *
 * @author lieber
 */
@Data
public class BaseValueObj<T> {

    /**
     * 中文
     */
    @ApiModelProperty("中文")
    T zh;

    /**
     * 英文
     */
    @ApiModelProperty("英文")
    T en;

    // 如果有其他的依次加

    /**
     * 前端使用值,前端展示一次只使用一个对象,不存入数据库,使用get方法判断
     */
    @ApiModelProperty(value = "展示端使用值-增加修改入参时不需要传入")
    @Transient
    T val;

    public T getVal() {
        // 根据当前语言环境判断使用哪个值返回
        if (Objects.equals(RequestUtil.getRequest().getHeader(LanguageEnum.HEADER), LanguageEnum.EN.code())) {
            return en;
        }
        return zh;
    }
}

1.2 返回当前所选语言的对象

package com.yugioh.api.business.common.model;

import com.fasterxml.jackson.annotation.JsonIgnore;

/**
 * 前端值对象,只返回当前语言的
 *
 * @author lieber
 */
public class ValueObj<T> extends BaseValueObj<T> {

    @Override
    @JsonIgnore
    public T getZh() {
        return null;
    }

    @Override
    @JsonIgnore
    public T getEn() {
        return null;
    }

}

1.3 返回所有语言的对象

package com.yugioh.api.business.common.model;

import com.fasterxml.jackson.annotation.JsonIgnore;

/**
 * 所有语言对象
 *
 * @author lieber
 */
public class ValueObjAll<T> extends BaseValueObj<T> {

    @Override
    @JsonIgnore
    public T getVal() {
        return null;
    }
}

2. 增加值对象序列化规则

2.1 新建规则

package com.yugioh.api.business.common.model;

import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.yugioh.api.business.util.RequestUtil;
import lombok.extern.slf4j.Slf4j;

import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.*;


/**
 * 公用多语言值对象序列化
 *
 * @author lieber
 */
@Slf4j
public class BaseValueObjSerializer extends JsonSerializer<BaseValueObj> {

    private BaseValueObjSerializer() {
    }

    public final static BaseValueObjSerializer instance = new BaseValueObjSerializer();


    @Override
    public void serialize(BaseValueObj value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        if (value == null) {
            gen.writeNull();
            return;
        }
        Map<String, Object> fieldMap;

        if (this.logon()) {
            fieldMap = this.getWriteInfo(ValueObjAll.class, value);
        } else {
            fieldMap = this.getWriteInfo(ValueObj.class, value);
        }
        gen.writeStartObject();
        for (Map.Entry<String, Object> entry : fieldMap.entrySet()) {
            gen.writeObjectField(entry.getKey(), entry.getValue());
        }
        gen.writeEndObject();
    }

    /**
     * 获取输出的字段和值
     *
     * @param clazz 输出的类
     * @param value 值
     * @return 映射
     */
    private Map<String, Object> getWriteInfo(Class<? extends BaseValueObj> clazz, BaseValueObj value) {
        List<Field> fields = new ArrayList<>(4);
        Class tempClass = clazz;
        while (tempClass != null) {
            fields.addAll(Arrays.asList(tempClass.getDeclaredFields()));
            tempClass = tempClass.getSuperclass();
        }
        Map<String, Object> map = new LinkedHashMap<>(fields.size());
        // 此处可优化,字段很少,可以直接填充而不需要使用反射从而提高效率。使用反射只是提高通用性和扩展性
        try {
            for (Field field : fields) {
                if (field.isAnnotationPresent(JsonIgnore.class)) {
                    continue;
                }
                PropertyDescriptor propertyDescriptor = new PropertyDescriptor(field.getName(), clazz);
                Method readMethod = propertyDescriptor.getReadMethod();
                if (readMethod.isAnnotationPresent(JsonIgnore.class)) {
                    continue;
                }
                map.put(field.getName(), readMethod.invoke(value));
            }
        } catch (Exception e) {
            log.error("获取可返回数据出现异常:", e);
        }
        return map;
    }
    
    // 判断是否登录
    private boolean logon() {
        return Objects.equals(RequestUtil.getRequest().getAttribute(RequestUtil.LOGIN_KEY), true);
    }
}

2.2 注册规则

package com.yugioh.api.common.core.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import com.yugioh.api.business.common.model.BaseValueObj;
import com.yugioh.api.business.common.model.BaseValueObjSerializer;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
 * 
 * @author lieber
 */
@Configuration
@Component
public class WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addResourceHandlers(ResourceHandlerRegistry registry) {
        // 配置模板资源路径
        registry.addResourceHandler("/templates/**").addResourceLocations("classpath:/templates/");
        registry.addResourceHandler("/static/**").addResourceLocations("classpath:/static/");
        registry.addResourceHandler("swagger-ui.html").addResourceLocations("classpath:/META-INF/resources/");
        registry.addResourceHandler("/webjars/**").addResourceLocations("classpath:/META-INF/resources/webjars/");
    }


    /**
     * 注册序列化规则
     */
    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
        MappingJackson2HttpMessageConverter jackson2HttpMessageConverter = new MappingJackson2HttpMessageConverter();
        ObjectMapper objectMapper = new ObjectMapper();
        SimpleModule simpleModule = new SimpleModule();
        // 配置BaseValueObj类的序列化规则
        simpleModule.addSerializer(BaseValueObj.class, BaseValueObjSerializer.instance);
        // 序列换成json时,将所有的long变成string
        // 因为js中得数字类型不能包含所有的java long值
        simpleModule.addSerializer(Long.class, ToStringSerializer.instance);
        simpleModule.addSerializer(Long.TYPE, ToStringSerializer.instance);
        objectMapper.registerModule(simpleModule);
        jackson2HttpMessageConverter.setObjectMapper(objectMapper);
        converters.add(jackson2HttpMessageConverter);
    }

}

3. 其它使用到的

package com.yugioh.api.business.common.enums;

/**
 * 语言枚举
 *
 * @author lieber
 */
public enum LanguageEnum {

    /**
     * 中文
     */
    ZH("zh-cn"),

    /**
     * 英文
     */
    EN("en-us");

    private String code;

    LanguageEnum(String code) {
        this.code = code;
    }

    public String code() {
        return code;
    }

    public final static String HEADER = "language";
}

4. 使用拓展

如果需要增加语言,那么需要做的事情是:
1.BaseValueObj增加对应字段,并在getVal方法中指定规则
2.ValueObj增加对应getter方法并加上@JsonIgnore
3.LanguageEnum增加对应语言

相关文章

网友评论

      本文标题:接口多语言方案

      本文链接:https://www.haomeiwen.com/subject/rjdhqktx.html