轻松实现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!

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

总结

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

相关推荐
独泪了无痕5 小时前
Spring Data Redis 快速上手
spring boot·redis·后端
wangchunyu1147 小时前
JAVA CompletableFuture异步编程实战
java·开发语言·python
paopaokaka_luck8 小时前
流浪动物救助与收养系统(DeepSeek AI对话、 ECharts多维分析、救助流程衔接、领养与回访衔接、 捐赠确认与公示、志愿者协作)
java·前端·javascript·人工智能·mysql·echarts·推荐算法
Thomas.Sir8 小时前
第9课:SpringBoot4.x Web开发基础:WebMvc自动配置、静态资源、请求参数接收
java·spring boot
程序 代码狂人8 小时前
切片表与分区表的区别(数据开发面试版)
java·大数据·开发语言·数据仓库·算法
m0_639906949 小时前
Linux 操作系统入门:目录结构、服务排查常用命令、Shell 脚本基础
linux·运维·后端
2501_944521599 小时前
Deepmerge 跨端三方库仓颉适配全流程
java·python·eclipse·github
holly就是我10 小时前
AI 编程助手为什么跑偏?用开源 ccglass 看清 Prompt、工具调用和 Token 消耗
人工智能·后端
鸿蒙开发10 小时前
共享 Schema 与 SchemaFactory
前端·后端·harmonyos
用户7884773163410 小时前
小程序后端三选一:云开发、.NET WebAPI、Node,我按客户现状分了三种答案
后端