# database-i18n **Repository Path**: pifutan/database-i18n ## Basic Information - **Project Name**: database-i18n - **Description**: 数据库多语言框架:mybatisPlus+mysql - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 1 - **Created**: 2022-07-16 - **Last Updated**: 2024-06-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 数据库多语言 场景:有时用户填写的内容需要支持国际化,比如管理员需要维护数据字典,不同语言下看到的字典label需要不同,此时需要用到数据库多语言。 介绍:仅需要加几个配置和一些注解,就能够实现数据库多语言的功能,大大简化实现,也不需要设计多语言表,且对接pifutan翻译和谷歌翻译提供一定能力的自动翻译功能 已使用项目:流程中心vform # 使用步骤: 1、引入jar包 2、Application上添加注解@EnableI18n 3、配置i18n.default-language=zh_cn以及i18n.primary-key-column=id,配置需要支持的语言i18n.languages(应用启动时会自动检测并建表),其他配置见I18nProperties 4、需要支持数据库多语言的实体类添加@I18nTable注解,需要支持多语言的字段添加@I18nColumn注解 5、sql执行前调用I18nHelper.setLanguage(),建议在controller拦截器中做结合uuc的vlang,由于用到了ThreadLocal所有请求结束后需要I18nHelper.removeLanguage(); 6、注意事项: (1)service方法最好添加@Transactional(保证事务一致),update和delete目前仅支持根据主键更新和删除, 即不支持deleteByMap和update(仅支持updateById) (2)需要开启驼峰转下划线(默认规范),xml的sql中不能出现与i18n.suffix=lan冲突的表别名,也别使用alias作为别名 (3)如果使用缓存则需要加入语言参数,可以在@Cacheable(keyGenerator = I18nKeyGenerator.BEAN)和@CacheEvict(keyGenerator = I18nKeyGenerator.BEAN) (4)mybatisPlus需要使用3.4.2以上的版本 (5)如果项目中已经存在MybatisPlusInterceptor,需要使用 ``` @Bean public MybatisPlusInterceptor mybatisPlusInterceptor(I18nProperties i18nProperties) { PifutanMybatisPlusInterceptor interceptor = new PifutanMybatisPlusInterceptor(); PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL); paginationInnerInterceptor.setMaxLimit(i18nProperties.getMaxLimit()); interceptor.addInnerInterceptor(paginationInnerInterceptor); interceptor.addPifutanInnerInterceptor(new I18nInterceptor(i18nProperties)); return interceptor; } ``` 7、dubbo调用可使用RpcContext进行设置和获取,如 RpcContext.getContext().setAttachment(UucConstant.HEADER_LANG, vlang); RpcContext.getContext().getAttachment(UucConstant.HEADER_LANG); 建议使用Filter方式,详见https://dubbo.apache.org/zh/docs/v2.7/dev/impls/filter/ ``` import org.apache.dubbo.common.constants.CommonConstants; import org.apache.dubbo.common.extension.Activate; import org.apache.dubbo.rpc.*; import java.util.Locale; @Activate(group = CommonConstants.CONSUMER) public class DubboConsumerContextFilter implements Filter { @Override public Result invoke(Invoker invoker, Invocation invocation) throws RpcException { // 获取当前请求的语言 Locale locale = RequestHolder.getLocale(); if (locale != null) { // 设置dubbo调用的语言 RpcContext.getContext().setAttachment("language", locale.getLanguage()); } return invoker.invoke(invocation); } } ``` # 设计说明 ## 一、调研方案总结 目前没有通用框架能够支持,以下是网上收集到的各种数据库多语言方案 1.1 字段列方法,如label_zh,label_en,语言扩展不方便,且语言多了之后会产生很多字段列,适用于语言少不需扩展的场景 1.2 记录行方法,每行数据是一种语言,即一个数据会有多行对应多种语言,会有冗余数据 1.3 附加表方法,每个表附件一个语言表,即一对多,一行记录多个语言记录,取不到则使用默认,查询时需要去join对应的语言表(尝试用MybatisPlus实现或者Mybatis简化统一化开发),表记录数=n条主表记录*m种语言,插入更新会变成多表操作 1.4 统一语言表,根据表、id、语言和值,每次都去这个表查询,数据量很大可能会有问题 1.5 分库,通用数据不可复用,没有必要 1.6 分表,即记录行方法变成记录表方法,多行变成多表,会生成许多表,而且通用数据不可复用,也可以类似附加表的方式用join,虽然会产生很多表,但是会比较清晰,主表和语言表记录数相同,join时会更快,同样插入和更新会变成多表操作 1.7 临时变量替换法,操作数据库时通过AOP将需要国际化的字段替换为如${licenseinfo}格式字符串,语言单独存储并且缓存起来,在服务器json序列化时再转成对应的语言,详见 https://xueshu.baidu.com/usercenter/paper/show?paperid=46259ec78400608c5f88ddb36099aa22&site=xueshu_se 缺点:1、性能未知,大数据量可能会有问题(可以进一步分表)。2、至少会多出1次查询,使用缓存的话,缓存也会有很多压力 ## 二、方案决定:采用附件表+分表+框架的方式实现 命名规范:多语言表和字段统一在源表和源字段的基础上添加_lan后缀,如user表的name字段,多语言表则为user_lan的name_lan字段,如果采用分表的方式则为user_en_us的name_lan,需要用id(或者其他指定主键)关联 其他方案:新增一张附加表使用id关联且新增一个语言字段也是可以的,但是数据量大的话性能稍微低一些,但是好处是新增语言不需要新增表。考虑作为扩展可选方案。 ## 其他多语言 ### 前端多语言:使用vue-i18n即可 ### 后端多语言:使用spring的MessageSource即可 ### dubbo调用多语言: 使用dubbo的filter结合RpcContext加入语言变量放入ThreadLocal中即可,dubbo-filter使用可参照 https://dubbo.apache.org/zh/docs/v2.7/dev/impls/filter/ 注意在resource下面添加META-INF.dubbo下添加org.apache.dubbo.rpc.Filter文件,文件中添加 ``` DubboProviderContextFilter=com.pifutan.i18n.test.interceptor.DubboProviderContextFilter DubboConsumerContextFilter=com.pifutan.i18n.test.interceptor.DubboConsumerContextFilter ``` ## 三、实现思路 基于MybatisPlus和Mybatis 1、 有四种操作:insert、update、select、delete,先手工建表和字段实现拦截处理 2、 通过注解来识别哪些需要翻译哪些不需要 3、 应用启动时检验去生成表或者检测多语言表是否存在 4、 优化性能 5、 自动翻译已有的需要多语言的表 # 其他说明 1、目前仅支持mysql和h2,其他数据库未测试,但是基础sql语法应该是没问题的 2、insert、update、select、delete以外的语法可能存在问题,未经过严格测试