SpringBoot - SpringBoot整合i18n实现消息国际化

在开发可能面向国外用户的WEB项目时,需实现项目国际化。本文介绍了基于Spring Boot和Java的项目国际化开发,包括MessageSource源码分析、项目环境搭建、业务实现,还阐述了多服务下使用i18n的方法,如调整项目结构、处理异常信息国际化等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在我们开发WEB项目的时候,项目可能涉及到在国外部署或者应用,也有可能会有国外的用户对项目进行访问,那么在这种项目中, 为客户展现的页面或者操作的信息就需要根据客户系统的环境来使用不同的语言,这就是我们所说的项目国际化。

1. MessageSource源码

Spring中定义了一个MessageSource接口,以用于支持信息的国际化和包含参数的信息的替换,MessageSource接口源码如下:

public interface MessageSource {

   /**
    * 解析code对应的信息进行返回,如果对应的code不能被解析则返回默认信息defaultMessage。
    *
    * @param code 需要进行解析的code,对应资源文件中的一个属性名
    * @param args 当对应code对应的信息不存在时需要返回的默认值
    * @param defaultMessage 需要用来替换code对应的信息中包含参数的内容,如:{0},{1,date},{2,time}
    * @param locale 对应的Locale
    * @return 国际化翻译值
    */
   @Nullable
   String getMessage(String code, @Nullable Object[] args, @Nullable String defaultMessage, Locale locale);

   /**
    * 解析code对应的信息进行返回,如果对应的code不能被解析则抛出异常NoSuchMessageException
    *
    * @param code 需要进行解析的code,对应资源文件中的一个属性名
    * @param args 当对应code对应的信息不存在时需要返回的默认值
    * @param locale 需要用来替换code对应的信息中包含参数的内容,如:{0},{1,date},{2,time}
    * @return 国际化翻译值
    * @throws NoSuchMessageException 如果对应的code不能被解析则抛出该异常
    */
   String getMessage(String code, @Nullable Object[] args, Locale locale) throws NoSuchMessageException;

   /**
    * 通过传递的MessageSourceResolvable对应来解析对应的信息
    *
    * @param resolvable  MessageSourceResolvable
    * @param locale 对应的Locale
    * @return 国际化翻译值
    * @throws NoSuchMessageException 如不能解析则抛出该异常
    */
   String getMessage(MessageSourceResolvable resolvable, Locale locale) throws NoSuchMessageException;

}

2. 项目环境搭建

1. 创建项目服务auth

① 创建一个服务auth,导入常用依赖,项目整体结构为:

在这里插入图片描述

② SpringBoot配置文件

server:
  port: 8080

spring:
  datasource:
    druid:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/storage?useUnicode=true&characterEncoding=utf-8&useSSL=false
      username: root
      password: root
  # 资源信息
  messages:
    encoding: utf-8
    # 国际化资源文件路径(配置文件路径)
    basename: i18n/messages

mybatis:
  mapper-locations: classpath:mapper/*.xml
2. 工具类 I18nUtils
// @Autowired 自动装配仅在托管类中有效(例如,注释为@ Component,@ Service或在应用程序上下文xml中定义)。
@Component
@Slf4j
public class I18nUtils {
    
    // 如果当前bean不加@Component注解,则messageSource无法注入,始终为null
    private static MessageSource messageSource;

    @Autowired
    public void setMessageSource(MessageSource messageSource) {
        I18nUtils.messageSource = messageSource;
    }

    /**
     * 解析code对应的信息进行返回,如果对应的code不能被解析则抛出异常NoSuchMessageException
     *
     * @param code 需要进行解析的code,对应资源文件中的一个属性名
     * @param args 当对应code对应的信息不存在时需要返回的默认值
     * @return 国际化翻译值
     */
    public static String i18n(String code, Object... args) {
        return messageSource.getMessage(code, args, LocaleContextHolder.getLocale());
    }

    /**
     * 解析code对应的信息进行返回,如果对应的code不能被解析则返回默认信息defaultMessage。
     *
     * @param code 需要进行解析的code,对应资源文件中的一个属性名
     * @param defaultMessage 当对应code对应的信息不存在时需要返回的默认值
     * @param args 需要用来替换code对应的信息中包含参数的内容,如:{0},{1,date},{2,time}
     * @return 对应的Locale
     */
    public static String i18nOrDefault(String code, String defaultMessage, Object... args) {
        return messageSource.getMessage(code, args, defaultMessage, LocaleContextHolder.getLocale());
    }

    /**
     * 因为i18n方法如果获取不到对应的键值,会抛异常NoSuchMessageException
     * 本方法是对i18n方法的封装。当报错时并不抛出异常,而是返回source
     *
     * @param source 模板
     * @param args   参数
     * @return 返回I18n(正常结束)或者source(抛出异常)
     * @see #i18n(String, Object...)
     */
    @NonNull
    public static String tryI18n(@NonNull String source, @NonNull Object... args) {
        String res;
        try {
            res = i18n(source, args);
        } catch (Exception ignored) {
            res = source;
        }
        return res;
    }
}
3. 自定义异常 CommonException
public class CommonException extends RuntimeException {

    public CommonException(String i18eCode) {
        // 根据资源文件的属性名以及当前语言环境,获取国际化信息
        super(I18nUtils.tryI18n(i18eCode));
    }

    public CommonException(String i18eCode, Object... args) {
        // 根据资源文件的属性名,属性值中的参数以及当前语言环境,获取国际化信息
        // args用来替换资源文件属性值中的占位符参数
        super(I18nUtils.tryI18n(i18eCode, args));
    }
}
4. 统一异常处理 GlobalExceptionHandler
@RestControllerAdvice
@Slf4j
public class GlobalExceptionHandler {

    @ExceptionHandler(CommonException.class)
    public ApiResponse<Object> handleCommonException(CommonException e) {
        log.error(e.getMessage(), e);
        return new ApiResponse<>(-1,"error",e.getMessage());
    }
}

3. 业务实现

1. 实体类 UserEntity
@Data
public class UserEntity {

    private Integer id;

    private String name;

    private String password;

    private Date createTime;
}
2. 请求实体 UserQo
@Data
public class UserQo {

    private Integer id;

    @ApiModelProperty("用户名")
    private String name;

    @ApiModelProperty("密码")
    private String password;
}
3. 控制层 UserController
@RestController
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/add")
    public ApiResponse<Object> add(@Validated @RequestBody UserQo userQo){
        userService.insertUser(userQo);
        return new ApiResponse<>(0,"success");
    }
}
4. 业务逻辑层 UserService
@Service
public class UserService {

    @Resource
    private UserDao userDao;

    public void insertUser(UserQo userQo){
        UserEntity userEntity = userDao.findByName(userQo.getName());
        if(Objects.nonNull(userEntity)){
            // i18n带有参数
            String name = userQo.getName();
            throw new CommonException("exception.name.can.not.repeat",name);
        }
        userEntity = new UserEntity();
        BeanUtils.copyProperties(userQo,userEntity);
        userEntity.setCreateTime(new Date());
        int count = userDao.insert(userEntity);
        if(count==1){
            // i18n不带有参数
            throw new CommonException("exception.insert.data.to.db");
        }
    }
}
5. 将异常信息写入i18n资源文件

messages.properties:

exception.name.can.not.repeat=exception name can not repeat,the name is {0}
exception.insert.data.to.db=exception insert data to db

messages_zh_CN.properties:

exception.name.can.not.repeat=用户名不能为重复,用户名为:{0}
exception.insert.data.to.db=新增数据到数据库异常
6. 项目测试

用户名重复时抛出异常CommonException,异常会被捕获并进行统一异常处理:

在这里插入图片描述

添加数据到数据库中时抛出异常CommonException,异常会被捕获并进行统一异常处理:

在这里插入图片描述

4. 多服务下使用 i18n

1. 调整项目结构

将原来的 auth 服务拆分为为 auth-model和 auth-core 服务,他们都在auth服务下:

在这里插入图片描述

其中auth-model模块主要请求Qo,响应Vo,实体类Entity,在响应的依赖中导入对应的服务即可。

2. 父模块 auth

pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.6</version>
        <relativePath/>
    </parent>

    <artifactId>auth</artifactId>
    <groupId>com.hh</groupId>
    <version>1.0-SNAPSHOT</version>

    <packaging>pom</packaging>

    <modules>
        <module>auth-core</module>
        <module>auth-model</module>
    </modules>

</project>
3. 子模块 auth-core

① pom文件

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>auth</artifactId>
        <groupId>com.hh</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>auth-core</artifactId>
    
    <dependencies>
        <!--依赖auth-module模块-->
        <dependency>
            <groupId>com.hh</groupId>
            <artifactId>auth-model</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        
         <!--省略.....-->
    </dependencies>
</project>

② SpringBoot 配置:

server:
  port: 8080

spring:
  datasource:
    druid:
      driver-class-name: com.mysql.jdbc.Driver
      url: jdbc:mysql://localhost:3306/storage?useUnicode=true&characterEncoding=utf-8&useSSL=false
      username: root
      password: root
  # 资源信息
  messages:
    encoding: utf-8
    # 国际化资源文件路径,需要配置当前模块和依赖模块的资源文件路径
    basename: i18n/core,i18n/module

mybatis:
  mapper-locations: classpath:mapper/*.xml

③ 抛出异常信息,并对消息进行国际化处理:

@Service
public class UserService {

    @Resource
    private UserDao userDao;

    public void insertUser(UserQo userQo){
        UserEntity userEntity = userDao.findByName(userQo.getName());
        if(Objects.nonNull(userEntity)){
            // i18n带有参数
            String name = userQo.getName();
            throw new CommonException("exception.name.can.not.repeat",name);
        }
        userEntity = new UserEntity();
        BeanUtils.copyProperties(userQo,userEntity);
        userEntity.setCreateTime(new Date());
        int count = userDao.insert(userEntity);
        if(count==1){
            // i18n不带有参数
            throw new CommonException("exception.insert.data.to.db");
        }
    }
}

④ 因为是auth-core模块抛出的异常消息,因此需要写在i18n/core下面的资源文件中,如果是auth-model模块抛出的异常,则写在auth-model服务i18n/model下面的资源文件中。

4. 子模块 auth-model
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>auth</artifactId>
        <groupId>com.hh</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>auth-model</artifactId>
    
    <dependencies>
        <!--省略.....-->
    </dependencies>
</project>
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

我一直在流浪

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值