likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 9

动态配置国际化信息

引入依赖

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

<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表结构如下:

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条数据,方便后面使用: 轻松实现Springboot国际化动态配置,有点干

JPA配置

配置文件application.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实体类:

@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操作:

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

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

    I18nMsgEntity findByCodeAndLocale(String code, String locale);
}
@Service
public class I18nMsgServiceImpl implements I18nMsgService {

    @Resource
    private I18nMsgRepository i18nMsgRepository;

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

动态国际化信息提供类

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

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

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

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

@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)方法即可。

@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;
    }
}

测试

@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);
    }
}

测试结果:

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!

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

总结

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