轻松实现Springboot国际化动态配置,有点干

上篇文章 我们介绍了Springboot静态国际化的使用,实际工作中使用静态配置文件的形式的国际化不够灵活,扩展起来也相对麻烦,如果需要修改其中的国际化信息就要重新加载对应的配置文件或者是重启项目,很不友好,所以我们需要实现动态配置国际化信息。 最直接的方式就是通过数据库将国际化信息进行持久化,可以随时更新国际化信息。本文将介绍如何动态配置国际化信息。

动态配置国际化信息

引入依赖

为了方便,这里使用JPA、MySQL进行数据库方面的操作,引入相关依赖:

xml 复制代码
<dependency>
  <groupId>org.springframework.boot</groupId>
   <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.23</version>
</dependency>

<dependency>
   <groupId>org.projectlombok</groupId>
   <artifactId>lombok</artifactId>
   <scope>provided</scope>
</dependency>

表结构及数据

新建一张表i18n_msg表结构如下:

sql 复制代码
CREATE TABLE `i18n_msg` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `locale` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '语言类型',
  `code` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `msg` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL COMMENT '消息,可以使用{}占位符',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci

先添加4条数据,方便后面使用:

JPA配置

配置文件application.properties 配置:

properties 复制代码
spring.datasource.url=jdbc:mysql://localhost:3306/springboot_practice?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
## 每次启动项目,数据库中没有表会新建一张表,如果有表表内有数据不会清空,只会更新
spring.jpa.hibernate.ddl-auto=update
# 打印SQL语句
spring.jpa.show-sql=true

JPA实体类:

java 复制代码
@Entity
@Table(name = "i18n_msg")
@Data
public class I18nMsgEntity {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "locale",length = 50)
    private String locale;

    @Column(name = "code",length = 50)
    private String code;

    @Column(name = "msg",length = 50)
    private String msg;
}

JpaRepository实现类,进行CRUD操作:

java 复制代码
/**
 * @author 公众号-索码理(suncodernote)
 * @date 2024/2/1 23:00
 */
public interface I18nMsgRepository extends JpaRepository<I18nMsgEntity,Long> {

    I18nMsgEntity findByCodeAndLocale(String code, String locale);
}
java 复制代码
/**
 * @author 公众号-索码理(suncodernote)
 */
public interface I18nMsgService {

    I18nMsgEntity findByCodeAndLocale(String code, String locale);
}
java 复制代码
@Service
public class I18nMsgServiceImpl implements I18nMsgService {

    @Resource
    private I18nMsgRepository i18nMsgRepository;

    @Override
    public I18nMsgEntity findByCodeAndLocale(String code, String locale) {
        return i18nMsgRepository.findByCodeAndLocale(code, locale);
    }
}

动态国际化信息提供类

创建一个接口I18nDynamicMsgProvider,该接口用于提供动态国际化信息。

java 复制代码
/**
 * 动态国际化接口
 * @author 公众号-索码理(suncodernote)
 */
public interface I18nDynamicMsgProvider {

    /**
     * 获取动态国际化信息
     * @param locale 指定区域
     * @param code 国际化code
     * @return
     */
    I18DynamicMessage getDynamicMsg(Locale locale, String code);
}

I18nDynamicMsgProvider接口实现类,在该类中通过区域和国际化信息code获取对应的国际化信息。

java 复制代码
@Component
public class MyI18nDynamicMsgProvider implements I18nDynamicMsgProvider{

    @Autowired
    private I18nMsgService i18nMsgService;

    @Override
    public I18DynamicMessage getDynamicMsg(Locale locale, String code) {
        I18DynamicMessage i18DynamicMessage = new I18DynamicMessage();
        I18nMsgEntity i18nMsgEntity = i18nMsgService.findByCodeAndLocale(code, locale.toString());
        if (i18nMsgEntity != null) {
            String msg = i18nMsgEntity.getMsg();
            i18DynamicMessage.setMsg(msg);
            i18DynamicMessage.setCode(code);
            i18DynamicMessage.setLocale(locale.toString());
        }
        return i18DynamicMessage;
    }
}

自定义动态国际化信息源

要自定义动态国际化信息源需要实现AbstractMessageSource抽象类,重写resolveCode(String , Locale)方法即可。

java 复制代码
@Component
public class DynamicMessageSource extends AbstractMessageSource {

    private final I18nDynamicMsgProvider i18nMessageProvider;

    public DynamicMessageSource(I18nDynamicMsgProvider i18nMessageProvider) {
        this.i18nMessageProvider = i18nMessageProvider;
    }

    @Override
    protected MessageFormat resolveCode(String code, Locale locale) {
        I18DynamicMessage i18nMessage = i18nMessageProvider.getDynamicMsg(locale, code);
        if (i18nMessage != null) {
            return createMessageFormat(i18nMessage.getMsg() , locale);
        }
        return null;
    }
}

测试

java 复制代码
@SpringBootTest
class SpringbootPracticeApplicationTests {
    @Autowired
    private DynamicMessageSource dynamicMessageSource;

    @Test
    public void testDynamicMessageSource(){
        String code = "dynamic.hello";
        Locale locale = Locale.CHINA;

        String message = dynamicMessageSource.getMessage(code, null, locale);
        System.out.println(message);
        System.out.println();

		//占位符替换
        code = "dynamic.welcome";
        message = dynamicMessageSource.getMessage(code, new String[]{"索码理"}, locale);
        System.out.println(message);
        System.out.println();

        locale = Locale.ENGLISH;
        message = dynamicMessageSource.getMessage(code, new String[]{"suncodernote"}, locale);
        System.out.println(message);
    }
}

测试结果:

sql 复制代码
Hibernate: select i1_0.id,i1_0.code,i1_0.locale,i1_0.msg from i18n_msg i1_0 where i1_0.code=? and i1_0.locale=?
动态国际化配置,你好

Hibernate: select i1_0.id,i1_0.code,i1_0.locale,i1_0.msg from i18n_msg i1_0 where i1_0.code=? and i1_0.locale=?
欢迎关注公众号,索码理

Hibernate: select i1_0.id,i1_0.code,i1_0.locale,i1_0.msg from i18n_msg i1_0 where i1_0.code=? and i1_0.locale=?
Welcome to follow WeChat Public Number, suncodernote!

从测试结果看到不管是有没有占位符,中文还是英文,国际化信息都能很好的展示,也说明了动态国际化配置的成功。

总结

本文只是简单介绍动态国际化信息的配置,有待优化,比如将国际化信息放入到缓存中,感兴趣的可以参考上面的示例,写一个自己的动态国际化配置。

相关推荐
一只叫煤球的猫8 小时前
写代码很6,面试秒变菜鸟?不卖课,面试官视角走心探讨
前端·后端·面试
bobz9658 小时前
tcp/ip 中的多路复用
后端
bobz9659 小时前
tls ingress 简单记录
后端
皮皮林55110 小时前
IDEA 源码阅读利器,你居然还不会?
java·intellij idea
你的人类朋友10 小时前
什么是OpenSSL
后端·安全·程序员
bobz96510 小时前
mcp 直接操作浏览器
后端
前端小张同学12 小时前
服务器部署 gitlab 占用空间太大怎么办,优化思路。
后端
databook13 小时前
Manim实现闪光轨迹特效
后端·python·动效
武子康13 小时前
大数据-98 Spark 从 DStream 到 Structured Streaming:Spark 实时计算的演进
大数据·后端·spark
该用户已不存在14 小时前
6个值得收藏的.NET ORM 框架
前端·后端·.net