国际化组件 (i18n)
API文档
https://tech.rydeen.com.cn/apidocs/i18n/4.6.0/index.html
组件作用
本组件提供了国际化的支持,可以用于国际化的文本资源的加载和使用。本组件同时支持静态文本资源和数据库中动态文本(即主档数据)的国际化,以应对不同用户对国际化的需求。
本插件的静态文本国际化基于Spring Resource的国际化支持,而动态文本基于rydeen-component-cache组件,通过切面的方式在控制器返回时从缓存中获取国际化文本数据源,由于动态文本的保存位置并非properties文件,可能来源于关系型数据库或非关系型数据库,所以对文本的最终目标存储源不做限制,但需要在项目启动时,将国际化文本通过组件提供的API,注册到组件中,以便实现国际化文本的加载和使用。
组件用法
引入依赖
国际化组件配置方式如下:
首先在项目所属的Maven模块中引入组件坐标:
<dependency>
<groupId>cn.com.rydeen.component</groupId>
<artifactId>rydeen-component-i18n</artifactId>
</dependency>
配置文件
编写配置文件,如果我们需要对组件提供的默认配置进行修改请执行本步骤,否则请跳过本节。配置文件对应的配置类路径为cn.com.rydeen.component.i18n.config.I18nProperties,配置项如下:
platform:
component:
i18n:
# 国际化资源存放路径
path: i18n/messages
# 国际化资源文件编码
encoding: UTF-8
# 国际化资源文件默认区域(默认:中国)
default-locale: CHINA
# 是否启用动态数据的国际化切面(默认:否)
enable-aspect-i18n: false
# 是否对控制器进行国际化处理(默认:false,推荐:true)
# ☆☆☆ 重要:可提升国际化切面执行的性能,但是需要在所有需要国际化的 Controller 类上增加注解,否则切面国际化功能将不会生效。
enable-i18n-control: false
静态文本国际化
静态文本的国际化,进需要在项目的resources目录下创建好platform.component.i18n.path中配置的路径和对应properties文件,然后在文件中编写国际化文本即可,如下图所示:

然后,在项目中你有2种方式来获取国际化文本,一种是通过注入I18nResolver接口的实现,另一种是使用框架提供的I18n静态类的方式来获取,方式一的好处是当系统默认提供的国际化实现不满足需求时,您可以自行实现I18nResolver接口,然后在项目中注入即可使用自定义的国际化实现,而方式二的好处是使用简单,不需要注入,直接使用即可,当你没有自定义需求时,我们推荐使用方式二进行国际化。下面是两种用法的示例:
方式一:
package cn.com.rydeen.component.template.api.service.impl;
import cn.com.rydeen.base.common.api.vo.ResultVO;
import cn.com.rydeen.component.i18n.resolver.I18nResolver;
import cn.com.rydeen.component.template.api.domain.ApiInfo;
import cn.com.rydeen.component.template.api.mapper.ApiMapper;
import cn.com.rydeen.component.template.api.service.ApiService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;
/**
* API服务接口实现类
*
* @author richie696
* @version 1.0
* @since 2022-10-09 11:09:24
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class ApiServiceImpl extends ServiceImpl<ApiMapper, ApiInfo> implements ApiService {
@Qualifier("customI18nResolver")
private final I18nResolver i18nResolver;
@Override
public ResultVO<Page<ApiInfo>> getApiList(ApiInfo apiInfo) {
// 获取国际化文本
var i18nText = i18nResolver.get("MSG_API_INFO", apiInfo.getName(), apiInfo.getRole().getName());
Page<ApiInfo> page = new Page<>(apiInfo.getCurrentPage(), apiInfo.getPageSize());
page.setOrders(apiInfo.getOrders());
return ResultVO.getSuccess(i18nText, page(page, new QueryWrapper<>(apiInfo)));
}
}
方式二:
package cn.com.rydeen.component.template.api.service.impl;
import cn.com.rydeen.base.common.api.vo.ResultVO;
import cn.com.rydeen.component.i18n.resolver.I18n;
import cn.com.rydeen.component.template.api.domain.ApiInfo;
import cn.com.rydeen.component.template.api.mapper.ApiMapper;
import cn.com.rydeen.component.template.api.service.ApiService;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
/**
* API服务接口实现类
*
* @author richie696
* @version 1.0
* @since 2022-10-09 11:09:24
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class ApiServiceImpl extends ServiceImpl<ApiMapper, ApiInfo> implements ApiService {
@Override
public ResultVO<Page<ApiInfo>> getApiList(ApiInfo apiInfo) {
var i18nText = I18n.get("MSG_API_INFO", apiInfo.getName(), apiInfo.getRole().getName());
Page<ApiInfo> page = new Page<>(apiInfo.getCurrentPage(), apiInfo.getPageSize());
page.setOrders(apiInfo.getOrders());
return ResultVO.getSuccess(i18nText, page(page, new QueryWrapper<>(apiInfo)));
}
}
至此,静态文本国际化使用方法就介绍完毕了。
动态文本国际化
全局生效
下面我们开始主要介绍一下动态文本的国际化。其使用也很简单,此处不负责动态数据的国际化文本存放,文本的存放请根据业务需求自行设计安排,在明确好文本存放位置后,我们需要在项目启动时,将国际化文本通过组件提供的API注册到组件中,以便实现国际化文本的加载和使用。下面是动态文本国际化的使用示例:
在项目启动类或配置类中执行国际化信息的注册
package cn.com.rydeen.component.template.config;
import cn.com.rydeen.component.i18n.handle.I18nHandle;
import cn.com.rydeen.component.template.api.domain.I18nData;
import cn.com.rydeen.component.template.api.service.ApiService;
import jakarta.annotation.PostConstruct;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.Map;
import java.util.stream.Collectors;
@Component
@RequiredArgsConstructor
public class I18nInit {
private final ApiService apiService;
@PostConstruct
public void init() {
Map<String, Map<String, String>> i18nDictMap = apiService.getI18nDataList().stream()
.collect(Collectors.toMap(I18nData::key, data -> Map.of(
"zh_CN", data.zhCN(),
"zh_TW", data.zhTW(),
"en_US", data.enUS()
)));
I18nHandle.addI18nDictionaries(i18nDictMap);
}
}
在需要启用国际化的VO、DTO或Domain类的字段头上增加@I18nDict注解
package cn.com.rydeen.component.template.api.domain;
import cn.com.rydeen.component.i18n.annotation.I18nDict;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.util.List;
/**
* API信息表域对象
*
* @author richie696
* @version 1.0
* @since 2022-10-09 12:56:41
*/
@Data
@EqualsAndHashCode(callSuper = true)
@Accessors(chain = true)
@Schema(title = "api_info对象", description = "API信息表")
@TableName(value = "api_info", autoResultMap = true)
public class ApiInfo extends BaseInfo {
/**
* API信息ID
*/
@TableId(value = "id", type = IdType.AUTO)
@Schema(title = "API信息ID")
private Long id;
/**
* API名称
*/
@I18nDict
@TableField("name")
@Schema(title = "API名称")
private String name;
/**
* 角色信息
*/
@TableField(value = "role", typeHandler = JacksonTypeHandler.class)
@Schema(title = "角色信息")
private RoleInfo role;
/**
* 权限信息列表
*/
@TableField(value = "permissions", typeHandler = JacksonTypeHandler.class)
@Schema(title = "权限信息列表")
private List<PermissionInfo> permissions;
/**
* 角色信息
*
* @author richie696
* @version 1.0
* @since 2022-10-09 13:01:47
*/
@Data
@Schema(title = "角色信息", description = "角色信息")
public static class RoleInfo {
/**
* 角色ID
*/
@Schema(title = "角色ID", type = "java.lang.Long", requiredMode = Schema.RequiredMode.REQUIRED, accessMode = Schema.AccessMode.READ_ONLY)
private Long id;
/**
* 角色名称
*/
@I18nDict
@Schema(title = "角色名称", type = "java.lang.String", requiredMode = Schema.RequiredMode.REQUIRED, accessMode = Schema.AccessMode.READ_ONLY)
private String name;
}
/**
* 权限信息
*
* @author richie696
* @version 1.0
* @since 2022-10-09 13:02:01
*/
@Data
@Schema(title = "权限信息", description = "权限信息描述")
public static class PermissionInfo {
/**
* 权限ID
*/
@Schema(title = "权限ID", type = "java.lang.Long", requiredMode = Schema.RequiredMode.REQUIRED, accessMode = Schema.AccessMode.READ_ONLY)
private Long id;
/**
* 权限名称
*/
@Schema(title = "权限名称", type = "java.lang.String", requiredMode = Schema.RequiredMode.REQUIRED, accessMode = Schema.AccessMode.READ_ONLY)
private String name;
}
}
至此,动态文本国际化的全局使用方法就介绍完毕了。
限定范围
需要注意的是,动态数据国际化默认是通过在VO、DTO或Domain类的字段头上增加的@I18nDict注解,然后拦截controller包下的所有请求来实现的,所以多少会对访问性能产生影响,所以我们推荐在需要国际化的类头上增加@I18nControl注解显式的声明该类需要国际化处理,以提升国际化切面执行的性能,并且将enable-i18n-control设置为true,就会启用对需要的控制器进行国际化处理,而非全局拦截,以达到提升访问性能的目的。
platform:
component:
i18n:
# 是否对控制器进行国际化处理(默认:false,推荐:true)
# ☆☆☆ 重要:可提升国际化切面执行的性能,但是需要在所有需要国际化的 Controller 类上增加注解,否则切面国际化功能将不会生效。
enable-i18n-control: true
package cn.com.rydeen.component.template.api.controller;
import cn.com.rydeen.base.common.api.vo.ResultVO;
import cn.com.rydeen.component.i18n.annotation.I18nControl;
import cn.com.rydeen.component.template.api.domain.ApiInfo;
import cn.com.rydeen.component.template.api.service.ApiService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* API DOC 测试控制器
*
* @author richie696
* @version 1.0
* @since 2022-10-01 20:27:39
*/
@Slf4j
@I18nControl
@RequestMapping("/api-doc")
@RestController
@RequiredArgsConstructor
public class ApiDocDemoController {
private final ApiService apiService;
@GetMapping("/api/list")
public ResultVO<ApiInfo> getApiInfoList() {
List<ApiInfo> apiList = apiService.getApiList();
return ResultVO.getSuccess(apiList);
}
}
至此,动态文本国际化的限定范围使用方法就介绍完毕了。