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

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

总结

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

相关推荐
吴声子夜歌7 分钟前
Java面试——Spring Cloud原理及应用(二)
java·spring cloud·面试
SomeB1oody27 分钟前
【RustyML入门】6.3. 并行归约
开发语言·后端·机器学习·rust·教程
东小西1 小时前
【SAA实战】第 1 篇:ReactAgent 入门——先撸个"会调工具的助手"跑起来
java·人工智能·spring
Profile排查笔记1 小时前
指纹浏览器哪个好?从 Profile、代理、权限和自动化能力判断是否适合
前端·人工智能·后端·自动化
用户938515635072 小时前
用 AI 结对编程从 0 搭一个"单词后台管理系统":Next.js + Supabase + Drizzle + shadcn/ui 全记录
后端·postgresql·next.js
mqiqe2 小时前
AgentScope Java 2.0 协议集成全景解析:A2A、AG-UI、Agent Protocol 三大开放协议实战指南
java·开发语言·ui
脉动数据行情2 小时前
Java 实现台股 TWSE/TPEx 行情采集(个股 + K 线)
java·开发语言·twse·tpex·台股
爱学习的小邓同学2 小时前
Golang --- (1)第一个Golang程序
开发语言·后端·golang
小灰灰搞电子3 小时前
Rust+Slint 实现的“DNA双螺旋”加载动画源码分享
后端·rust·slint·加载动画
面向Google编程3 小时前
向量库不再囤数据:Milvus 3.0 零拷贝直读数据湖
后端