日常 - 后端响应数据国际化的解决方案

文章目录


环境

SpringBoot

Mybatis

Jackson

前言

因项目部署海外环境,现需将项目进行国际化,目前也就是将中文映射成英文。用户端根据环境进行本地化展示,而后台需要根据用户请求头将响应数据(如一些错误信息、部门、城市等)进行国际化返回。现该项目对外接口已超过一百,若方案不好,那工作量将是巨大的。

思路

在 Java 项目中,获取数据到处理,再将处理后的数据返回给用户,一般分为三层,也就是:

  1. DAO
  2. Service
  3. Controller

第一层 DAO(Data Access Object 数据存取对象) 就是直接和数据库打交道,若在这一层进行国际化处理,那相当于每张表都要加多个字段去保存国际化的数据。例如,用车订单表字段 city_name 有个数据为 深圳,那还要加多个字段名为 city_name_en 保存 Shenzhen,然后在 Mybatis 的 Mapper 文件根据传值判断最后要使用哪个字段。若后面要支持法语、德语、俄语...想想就可怕,所以这个肯定要 pass 掉。

既然靠近数据库的这么麻烦,那就看看最远的第三层 Controller。在 Controller 一般会有一个通用对象:

java 复制代码
@Data
public class Response {

    private Integer code;
    private String  message;
}

Service 接口响应成功则将数据赋值 datacode 约定成功赋值 0,序列化 json 后返回给用户,用户端可以根据响应 code 去处理数据或提示错误消息 message

看到序列化 json 我就想到 SpringBoot 里默认使用 Jackson 库,那么它在写入数据时能不能提供接口或工具对数据进行处理呢?

答案是有的,那就是继承 StdSerializer 重写其 #serialize 方法。

解决方案

写一个注解,用于国际化某个字符串:

java 复制代码
@JacksonAnnotationsInside
@JsonSerialize(using = I18NSer.class)
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface I18N {

}

编写实现 I18NSer:

java 复制代码
public class I18NSer extends StdSerializer<String> {
    protected I18NSer() {
        super(String.class);
    }

    @Override
    public void serialize(String value, JsonGenerator gen, SerializerProvider provider) throws IOException {
        if (value == null) {
            return;
        }

        // 国际化映射
        // 方法1:使用 ResourceBundle
        String message = I18NUtil.getMessage(value);
        gen.writeString(message);

        // 方法2:使用数据库保存国际化数据
        // (1) 在数据库创建国际化表,表需要保存原语言与目标语言两个字段,以及对应的文本
        // (2) 从数据库查询后保存到分布式缓存(如 redis),但要设置过期时间,避免不经常使用的占用内存,这样短时间内可以使用分布式缓存而不是数据库
        // (3) 最后保存最常用的数据到本地缓存,数据量大不了多少,例如 100 条,这样可以减少与分布式缓存的网络 I/O 时间
    }
}

Response 添加注解:

java 复制代码
@Data
public class Response {

    private Integer code;
    @I18N
    private String  message;
}

最后测试:

java 复制代码
@Test
public void testI18N() {
    Response response = new Response();
    response.setCode(0);
    response.setMessage("成功");

    Assertions.assertEquals("""
            {"code":0,"message":"成功"}""", JacksonUtil.toJson(response));

    I18NUtil.setHolder(Locale.ENGLISH);
    Assertions.assertEquals("""
            {"code":0,"message":"success"}""", JacksonUtil.toJson(response));
}

成功~

资源

i18n

相关推荐
FQNmxDG4S1 天前
Java多线程编程:Thread与Runnable的并发控制
java·开发语言
虹科网络安全1 天前
艾体宝干货|数据复制详解:类型、原理与适用场景
java·开发语言·数据库
axng pmje1 天前
Java语法进阶
java·开发语言·jvm
rKWP8gKv71 天前
Java微服务性能监控:Prometheus与Grafana集成方案
java·微服务·prometheus
老前端的功夫1 天前
【Java从入门到入土】28:Stream API:告别for循环的新时代
java·开发语言·python
qq_435287921 天前
第9章 夸父逐日与后羿射日:死循环与进程终止?十个太阳同时值班的并行冲突
java·开发语言·git·死循环·进程终止·并行冲突·夸父逐日
小江的记录本1 天前
【Kafka核心】架构模型:Producer、Broker、Consumer、Consumer Group、Topic、Partition、Replica
java·数据库·分布式·后端·搜索引擎·架构·kafka
yaoxin5211231 天前
397. Java 文件操作基础 - 创建常规文件与临时文件
java·开发语言·python
极客先躯1 天前
高级java每日一道面试题-2025年11月24日-容器与虚拟化题[Dockerj]-runc 的作用是什么?
java·oci 的命令行工具·最小可用·无守护进程·完全标准·创建容器的核心流程·runc 核心职责思维导图
用户60648767188961 天前
AI 抢不走的技能:用 Claude API 构建自动化工作流实战
java