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

news2024/12/19 10:50:24

文章目录

      • 1. MessageSource源码
      • 2. 项目环境搭建
        • 1. 创建项目服务auth
        • 2. 工具类 I18nUtils
        • 3. 自定义异常 CommonException
        • 4. 统一异常处理 GlobalExceptionHandler
      • 3. 业务实现
        • 1. 实体类 UserEntity
        • 2. 请求实体 UserQo
        • 3. 控制层 UserController
        • 4. 业务逻辑层 UserService
        • 5. 将异常信息写入i18n资源文件
        • 6. 项目测试
      • 4. 多服务下使用 i18n
        • 1. 调整项目结构
        • 2. 父模块 auth
        • 3. 子模块 auth-core
        • 4. 子模块 auth-model

在我们开发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>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1821.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

144. 授人以渔 - 如何查找 SAP UI5 官网上没有提到的控件属性的使用明细

本教程第 113 步骤, SAP UI5 应用开发教程之一百一十三 - 授人以渔 - 如何自行查询任意 SAP UI5 控件属性的文档和技术实现细节我用一整篇文章的篇幅,解答了一位学习者这个疑问: 想请教一下 sap.m.Input 控件中,value里设置的内容,比如path,type,constraints,在哪里可以查…

C++ 多态

目录 一、多态的定义和实现 1.1 多态的构成条件&#xff1a; 1.2 虚函数的重写&#xff08;覆盖&#xff09;&#xff1a; 1.3 多态的两个特殊点&#xff1a; 1.4 析构函数的重写&#xff1a; 1.5 override和final 1.6 重载&#xff0c;重定义&#xff08;隐藏&#xff…

Linux【进程地址空间】

进程地址空间&#x1f4d6;1. 地址空间概念&#x1f4d6;2. 写时拷贝&#x1f4d6;3. 虚拟地址空间的优点&#x1f4d6;1. 地址空间概念 在学习C/C内存管理时&#xff0c;我们可能见过这样一幅图&#xff1a; 但是我们可能不是很理解它&#xff0c;首先有一个问题&#xff1a;…

OpenTCS客户端开发之Web客户端(一)

越来越多人私信我关于OpenTCS的问题。可以感觉到很多人对OpenTCS的研究的人多了很多&#xff0c;很好。这些问题很多是关于算法方面的&#xff0c;也有一部分是关于UI方面的&#xff0c;毕竟OpenTCS本质上是一个算法项目&#xff0c;但是如果希望把它进行商业化&#xff0c;那免…

【微服务】服务拆分和远程调用

2.1 服务拆分原则 这里总结了微服务拆分时的几个原则&#xff1a; 不同微服务&#xff0c;不要重复开发相同业务微服务数据独立&#xff0c;不要访问其它微服务的数据库微服务可以将自己的业务暴露为接口&#xff0c;供其它微服务调用 2.2 服务拆分示例 以微服务cloud-demo为…

第三节:运算符【java】

目录 &#x1f392;运算符 &#x1f4c3;1. 什么是运算符 &#x1f4d7;2. 算术运算符 2.1 基本四则运算符&#xff1a;加减乘除模( - * / %) 2.2 增量运算符 - * % 2.3 自增/自减运算符 -- &#x1f4d9;3. 关系运算符 &#x1f4d5;4.逻辑运算符(重点) 4.1 逻辑与…

隔离出来的“陋室铭”

被隔离了 日常锻炼身体就是去公司旁边的酒店游泳&#xff0c;结果酒店里除了小阳人&#xff0c;我就喜提次密称号&#xff0c;7天隔离走起&#xff1b;又因为不想耽误家里孩子上学&#xff0c;老人外出&#xff0c;就选择了单独隔离&#xff0c;结果就拉到了单独的隔离点&…

精通Git(三)——Git分支机制

文章目录前言分支机制简述创建分支切换分支基本的分支与合并操作基本的分支操作基本的合并操作基本的合并冲突处理分支管理与分支有关的工作流长期分支主题分支远程分支推送跟踪分支拉取删除远程分支变基基本的变基操作变基操作的潜在危害只在需要的时候执行变基操作变基操作与…

C++——vector容器的基本使用和模拟实现

1、vector的介绍 vector是表示可变大小数组的序列容器。 就像数组一样&#xff0c;vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问&#xff0c;和数组一样高效。但是又不像数组&#xff0c;它的大小是可以动态改变的&#xff0c;而且…

【动手学深度学习PyTorch版】18 使用块的网络 VGG

上一篇请移步【动手学深度学习PyTorch版】17 深度卷积神经网络 AlexNet_水w的博客-CSDN博客 目录 一、使用块的网络 VGG 1.1 AlexNet--->VGG ◼ VGG网络简介 1.2 VGG架构 1.3 总结 二、VGG网络的代码实现 2.1 VGG网络&#xff08;使用自定义&#xff09; 一、使用块的…

软件测试基本概念

目录本章要点什么是软件测试?软件测试的特定?软件测试和开发的区别?软件测试和软件开发中的调试有什么区别?软件测试在不同公司的定位?一个优秀的测试人员应该具备的素质(你为啥要选择测试开发)需求是衡量软件测试的依据从软件测试人员角度看需求为啥需求对软件测试人员如…

SpringBoot 面试题总结 (JavaGuide)

SpringBoot 面试题总结 &#xff08;JavaGuide&#xff09; 用 JavaGuide 复习 SpringBoot 时&#xff0c;找到一些面试题&#xff0c;没有答案&#xff0c;自己花了一天时间在网上找资料总结了一些&#xff0c;有些答案的来源比较杂忘了没有标注&#xff0c;望见谅。 1. 简单…

Visual Studio 2022开发Arduino详述

目录&#xff1a; 一、概述 二、软件的下载与安装 1、前言 2、Visual Studio 2022的下载与安装 3、Visual Micro扩展插件的导入 4、Visual Micro的使用 1&#xff09;安装修改插件 2&#xff09;搜索 : Visual.Micro.Processing.Sketch.dll 3&#xff09;打开Visual.…

【Linux学习】基础IO

目录前言一、C语言文件IO1. C语言文件接口以及打开方式2. 对当前路径的理解3. 默认打开的三个流二、 系统文件IO1. 系统接口openwritereadclose系统接口和库函数2. 文件描述符及其分配规则文件描述符文件描述符分配原则3. 重定向及dup2系统调用重定向标准输出和标准错误的区别d…

Linux XWindow的安装和配置

1.开始安装XWindow必须需要的组件 输入指令&#xff1a;yum groupinstall "X Window System" yum groupinstall "X Window System" 选择y继续安装。 当看到complete表示已经安装成功了。 输入startx测试一下 看到如上界面就证明你的XWindow安装成功了。 2…

Python数据分析(3):pandas

文章目录二. pandas入门2.1 数据结构2.1.1 Series对象2.1.2 DataFrame对象2.2 读取数据2.2.1 读取Excel&#xff1a;read_excel()1. 读取特定工作簿&#xff1a;sheet_name2. 指定列标签&#xff1a;header3. 指定行标签&#xff1a;index_col4. 读取指定列&#xff1a;usecols…

TypeScript接口——interface

目录 一、接口概述&#xff1a; 二、接口类型介绍&#xff1a; 1、属性接口&#xff1a; 2、 函数接口&#xff1a; 3、可索引接口&#xff1a; &#xff08;1&#xff09;可索引接口约束数组示例&#xff1a; &#xff08;2&#xff09; 可索引接口约束对象示例&#xf…

【Python】numpy矩阵运算大全

文章目录前言0 遇事不决&#xff0c;先查官网&#xff0c;查着查着就查熟了1 矩阵运算及其必要性2 矩阵的创建2.1 普通矩阵2.2 特殊矩阵3 矩阵的索引3.1 str, list, tupple的索引3.2 numpy索引4 矩阵的运算4.1 通用函数与广播机制4.3 矩阵乘法4.4 矩阵求逆4.5 矩阵转置4.6 向量…

SpringBoot整合mybatis-plus 实现增删改查和分页查询

SpringBoot整合mybatis-plus 实现增删改查和分页查询整体的运行图片&#xff1a;一、环境搭建&#xff1a;1、依赖2、application.yml文件3、数据库二、实体类&#xff1a;三、数据层开发——基础CRUD四、业务层开发——分页功能制作4.1分页配置类 configuration4.2service接口…

【Node.js】模块化学习

Node.js教学 专栏 从头开始学习 目录 模块化的基本概念 什么是模块化 现实中的模块化 编程领域中的模块化 模块化规范 Node.js中的模块化 Node.js中模块的分类 加载模块 Node.js中的模块作用域 什么是模块作用域 模块作用域好处 向外共享模块作用域中的成员 module对象 modu…