# easy-trans **Repository Path**: orangewest/easy-trans ## Basic Information - **Project Name**: easy-trans - **Description**: 一款通用高性能翻译框架 - **Primary Language**: Java - **License**: Apache-2.0 - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 9 - **Forks**: 3 - **Created**: 2024-04-26 - **Last Updated**: 2025-11-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # easy-trans 一款通用的数据翻译框架 [![Contributors][contributors-shield]][contributors-url] [![Forks][forks-shield]][forks-url] [![Stargazers][stars-shield]][stars-url] [![Issues][issues-shield]][issues-url] [![MIT License][license-shield]][license-url]

Logo

easy-trans

一款通用的数据翻译框架
探索本项目的文档 »

查看Demo · 报告Bug · 提出新特性

## 一、架构设计 架构如下:
Logo
翻译核心注解 ```java @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD, ElementType.ANNOTATION_TYPE}) public @interface Trans { /** * @return 需要翻译的字段 */ String trans() default ""; /** * @return 提取的字段 */ String key() default ""; /** * @return 翻译数据获取仓库 */ Class using(); } ``` ## 二、优点 1、核心源码简单,仅几百行,无任何依赖项;
2、高度可拓展,拓展逻辑仅仅只需要实现TransRepository接口;
3、支持数据库翻译、字典翻译、集合翻译、嵌套翻译等;
4、并行翻译,翻译不同字段是并行翻译的,性能高
## 三、基本使用 maven引入 ```java io.github.orangewest easy-trans-core 0.0.3 ``` 比如现在有一个老师的实体对象 ```java @Data @AllArgsConstructor @NoArgsConstructor public class TeacherDto { private Long id; private String name; // 关联教哪个学科 private Long subjectId; } ``` 课程科目实体对象 ```java @Data @AllArgsConstructor @NoArgsConstructor public class SubjectDto { private Long id; private String name; } ``` 学生实体对象 ```java @Data public class UserDto { private Long id; private String name; private String sex; @DictTrans(trans = "sex", group = "sexDict") private String sexName; private String job; @DictTrans(trans = "job", group = "jobDict") private String jobName; // 关联老师id private Long teacherId; @Trans(trans = "teacherId", key = "name", using = TeacherTransRepository.class) private String teacherName; @Trans(trans = "teacherId", key = "subjectId", using = TeacherTransRepository.class) private Long subjectId; @Trans(trans = "subjectId", using = SubjectTransRepository.class, key = "name") private String subjectName; public UserDto(Long id, String name, Long teacherId, String sex, String job) { this.id = id; this.name = name; this.teacherId = teacherId; this.sex = sex; this.job = job; } } ``` 我们在teacherName 上增加@Trans的注解,
其中trans 指名需要翻译哪个字段,
key说明需要使用的是哪个字段,
using 说明的是使用哪个数据仓库获取数据
TeacherTransRepository 代码如下: ```java public class TeacherTransRepository implements TransRepository { @Override public Map getTransValueMap(List transValues, Annotation transAnno) { return getTeachers().stream().filter(x -> transValues.contains(x.getId())).collect(Collectors.toMap(TeacherDto::getId, x -> x)); } public List getTeachers() { List teachers = new ArrayList<>(); teachers.add(new TeacherDto(1L, "老师1", 1L)); teachers.add(new TeacherDto(2L, "老师2", 2L)); teachers.add(new TeacherDto(3L, "老师3", 3L)); teachers.add(new TeacherDto(4L, "老师4", 4L)); return teachers; } } ``` 模拟根据id查询,获取指定id的数据。 SubjectTransRepository ```java public class SubjectTransRepository implements TransRepository { @Override public Map getTransValueMap(List transValues, Annotation transAnno) { return getSubjects().stream().filter(x -> transValues.contains(x.getId())).collect(Collectors.toMap(SubjectDto::getId, x -> x)); } public List getSubjects() { List subjects = new ArrayList<>(); subjects.add(new SubjectDto(1L, "语文")); subjects.add(new SubjectDto(2L, "数学")); subjects.add(new SubjectDto(3L, "英语")); subjects.add(new SubjectDto(4L, "物理")); return subjects; } } ``` 注册翻译仓库: ```java TransRepositoryFactory.register(new TeacherTransRepository()); TransRepositoryFactory.register(new SubjectTransRepository()); TransRepositoryFactory.register(new DictTransRepository(new DictLoader(){ @Override public Map loadDict(String dictGroup){ return dictMap().getOrDefault(dictGroup,new HashMap<>()); } private Map>dictMap(){ Map>map=new HashMap<>(); map.put("sexDict",new HashMap<>()); map.put("jobDict",new HashMap<>()); map.get("sexDict").put("1","男"); map.get("sexDict").put("2","女"); map.get("jobDict").put("1","学习委员"); map.get("jobDict").put("2","生活委员"); map.get("jobDict").put("3","宣传委员"); map.get("jobDict").put("4","班长"); map.get("jobDict").put("5","团支书"); map.get("jobDict").put("6","团长"); return map; } })); ``` 代码测试: ```java @Test void trans(){ UserDto userDto=new UserDto(1L,"张三",2L,"1","2"); System.out.println("翻译前:"+userDto); transService.trans(userDto); System.out.println("翻译后:"+userDto); List userDtoList=new ArrayList<>(); UserDto userDto2=new UserDto(2L,"李四",1L,"2","1"); UserDto userDto3=new UserDto(3L,"王五",2L,"1","3"); UserDto userDto4=new UserDto(4L,"赵六",3L,"2","4"); userDtoList.add(userDto4); userDtoList.add(userDto3); userDtoList.add(userDto2); System.out.println("翻译前:"+userDtoList); transService.trans(userDtoList); System.out.println("翻译后:"+userDtoList); } ``` 结果输出 ```java 翻译前:UserDto(id=1,name=张三,sex=1,sexName=null,job=2,jobName=null,teacherId=2,teacherName=null,subjectId=null,subjectName=null) 翻译后:UserDto(id=1,name=张三,sex=1,sexName=男,job=2,jobName=生活委员,teacherId=2,teacherName=老师2,subjectId=2,subjectName=数学) 翻译前:[UserDto(id=4,name=赵六,sex=2,sexName=null,job=4,jobName=null,teacherId=3,teacherName=null,subjectId=null,subjectName=null),UserDto(id=3,name=王五,sex=1,sexName=null,job=3,jobName=null,teacherId=2,teacherName=null,subjectId=null,subjectName=null),UserDto(id=2,name=李四,sex=2,sexName=null,job=1,jobName=null,teacherId=1,teacherName=null,subjectId=null,subjectName=null)] 翻译后:[UserDto(id=4,name=赵六,sex=2,sexName=女,job=4,jobName=班长,teacherId=3,teacherName=老师3,subjectId=3,subjectName=英语),UserDto(id=3,name=王五,sex=1,sexName=男,job=3,jobName=宣传委员,teacherId=2,teacherName=老师2,subjectId=2,subjectName=数学),UserDto(id=2,name=李四,sex=2,sexName=女,job=1,jobName=学习委员,teacherId=1,teacherName=老师1,subjectId=1,subjectName=语文)] ``` ## 四、高级功能 ### 1、自定义注解 使用@Trans注解标注在自定义注解上即可,自定义注解中需要有trans方法。 示例: ```java @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) // 使用@Trans标注 @Trans(using = TeacherTransRepository.class) public @interface TeacherTrans { /** * 需要翻译的字段 */ String trans() default ""; /** * key 提取的字段 */ String key() default ""; } ``` ```java @Data public class UserDto2 { private Long id; private String name; private List teacherIds; private List jobIds; @DictTrans(trans = "jobIds", group = "jobDict") private List jobNames; @TeacherTrans(trans = "teacherIds", key = "name") private List teacherName; @TeacherTrans(trans = "teacherIds", key = "subjectId") private List subjectIds; @Trans(using = SubjectTransRepository.class, trans = "subjectIds", key = "name") private List subjectNames; public UserDto2(Long id, String name, List teacherIds, List jobIds) { this.id = id; this.name = name; this.teacherIds = teacherIds; this.jobIds = jobIds; } } ``` 测试 ```java @Test void trans2(){ List teacherIds=new ArrayList<>(); teacherIds.add(1L); teacherIds.add(2L); List jobIds=new ArrayList<>(); jobIds.add("1"); jobIds.add("2"); UserDto2 userDto=new UserDto2(1L,"张三",teacherIds,jobIds); System.out.println("翻译前:"+userDto); transService.trans(userDto); System.out.println("翻译后:"+userDto); List userDtoList=new ArrayList<>(); UserDto2 userDto2=new UserDto2(2L,"李四",teacherIds,jobIds); List teacherIds2=new ArrayList<>(); teacherIds2.add(3L); teacherIds2.add(4L); List jobIds2=new ArrayList<>(); jobIds2.add("3"); jobIds2.add("4"); UserDto2 userDto3=new UserDto2(3L,"王五",teacherIds2,jobIds2); UserDto2 userDto4=new UserDto2(4L,"赵六",teacherIds2,jobIds2); userDtoList.add(userDto4); userDtoList.add(userDto3); userDtoList.add(userDto2); System.out.println("翻译前:"+userDtoList); transService.trans(userDtoList); System.out.println("翻译后:"+userDtoList); } ``` 结果输出 ```java 翻译前:UserDto2(id=1,name=张三,teacherIds=[1,2],jobIds=[1,2],jobNames=null,teacherName=null,subjectIds=null,subjectNames=null) 翻译后:UserDto2(id=1,name=张三,teacherIds=[1,2],jobIds=[1,2],jobNames=[学习委员,生活委员],teacherName=[老师1,老师2],subjectIds=[1,2],subjectNames=[语文,数学]) 翻译前:[UserDto2(id=4,name=赵六,teacherIds=[3,4],jobIds=[3,4],jobNames=null,teacherName=null,subjectIds=null,subjectNames=null),UserDto2(id=3,name=王五,teacherIds=[3,4],jobIds=[3,4],jobNames=null,teacherName=null,subjectIds=null,subjectNames=null),UserDto2(id=2,name=李四,teacherIds=[1,2],jobIds=[1,2],jobNames=null,teacherName=null,subjectIds=null,subjectNames=null)] 翻译后:[UserDto2(id=4,name=赵六,teacherIds=[3,4],jobIds=[3,4],jobNames=[宣传委员,班长],teacherName=[老师3,老师4],subjectIds=[3,4],subjectNames=[英语,物理]),UserDto2(id=3,name=王五,teacherIds=[3,4],jobIds=[3,4],jobNames=[宣传委员,班长],teacherName=[老师3,老师4],subjectIds=[3,4],subjectNames=[英语,物理]),UserDto2(id=2,name=李四,teacherIds=[1,2],jobIds=[1,2],jobNames=[学习委员,生活委员],teacherName=[老师1,老师2],subjectIds=[1,2],subjectNames=[语文,数学])] ``` ### 2、值提取 有些翻译类型匹配结果的时候是通过值去匹配的,比如说字典翻译,我们需要根据字典值去字典组里面去匹配数据,框架里面只需要标记key的值用#val即可实现,比如框架自带的@DictTrans注解。 ```java @Trans(using = DictTransRepository.class, key = "#val") @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) public @interface DictTrans { /** * @return 需要翻译的字段 */ String trans(); /** * 字典组 * * @return 字典分组 */ String group(); } ``` ### 3、包装类翻译 有些类是包装类,比如返回的结果,返回的分页对象等,需要翻译的数据一般都是里面的实际业务对象,这时候,需要我们去配置解析包装类的解析器。 示例: ```java @Data @AllArgsConstructor public class Result { private T data; private String message; } ``` 配置解析器,实现TransObjResolver接口即可 ```java public class ResultResolver implements TransObjResolver { @Override public boolean support(Object obj) { return obj instanceof Result; } @Override public Object resolveTransObj(Object obj) { return ((Result) obj).getData(); } } ``` ```java TransObjResolverFactory.register(new ResultResolver()); ``` 测试: ```java @Test void trans3(){ List teacherIds=new ArrayList<>(); teacherIds.add(1L); teacherIds.add(2L); List jobIds=new ArrayList<>(); jobIds.add("1"); jobIds.add("2"); jobIds.add("3"); UserDto2 userDto=new UserDto2(1L,"张三",teacherIds,jobIds); Result result=new Result<>(userDto,"success"); System.out.println("翻译前:"+result); transService.trans(result); System.out.println("翻译后:"+result); UserDto2 userDto2=new UserDto2(2L,"李四",teacherIds,jobIds); Result result2=new Result<>(userDto2,"success"); Result>result3=new Result<>(result2,"success"); System.out.println("翻译前:"+result3); transService.trans(result3); System.out.println("翻译后:"+result3); } ``` 结果输出: ```java 翻译前:Result(data=UserDto2(id=1,name=张三,teacherIds=[1,2],jobIds=[1,2,3],jobNames=null,teacherName=null,subjectIds=null,subjectNames=null),message=success) 翻译后:Result(data=UserDto2(id=1,name=张三,teacherIds=[1,2],jobIds=[1,2,3],jobNames=[学习委员,生活委员,宣传委员],teacherName=[老师1,老师2],subjectIds=[1,2],subjectNames=[语文,数学]),message=success) 翻译前:Result(data=Result(data=UserDto2(id=2,name=李四,teacherIds=[1,2],jobIds=[1,2,3],jobNames=null,teacherName=null,subjectIds=null,subjectNames=null),message=success),message=success) 翻译后:Result(data=Result(data=UserDto2(id=2,name=李四,teacherIds=[1,2],jobIds=[1,2,3],jobNames=[学习委员,生活委员,宣传委员],teacherName=[老师1,老师2],subjectIds=[1,2],subjectNames=[语文,数学]),message=success),message=success) ``` ### 4、与springboot集成 maven 引入 ```java io.github.orangewest easy-trans-spring-start 0.0.3 ``` 翻译仓库实现TransRepository,翻译解析器实现TransObjResolver,并在实现类上标注@Component; 在需要翻译的对象属性上面标注好相关注解; 需要翻译方法上使用@AutoTrans注解,框架会自动拦截需要翻译的对象,实现翻译。 ```java @GetMapping("/query") @AutoTrans public Result>page(Query query){ PageData page=bizService.page(query); return new Result>().ok(page); } ``` ### 5、与orm框架集成 通常很多翻译需求,都是根据id去查询实体对象,我们可以通过orm框架对此进行统一翻译 示例: 与mybatis-plus集成 定义翻译注解 ```java @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.FIELD}) @Trans(using = DbTransRepository.class) public @interface DbTrans { String trans(); String key() default ""; /** * 数据库目标class */ Class entity() default BaseEntity.class; } ``` 翻译仓库实现 ```java @Component public class DbTransRepository implements TransRepository { @Resource private TransDriver transDriver; @Override public Map getTransValueMap(List transValues, Annotation transAnno) { if (transAnno instanceof DbTrans) { DbTrans dbTrans = (DbTrans) transAnno; List entities = transDriver.findByIds(getIds(transValues), dbTrans.entity()); return entities.stream().collect(Collectors.toMap(BaseEntity::getId, x -> x)); } return Collections.emptyMap(); } /** * 获取查询id */ private List getIds(List transValues) { return transValues.stream() .map(x -> (Long) x) .collect(Collectors.toList()); } } ``` ```java public interface TransDriver { /** * 根据ids获取集合 * * @param ids ids * @param targetClass 目标类类名 */ List findByIds(List ids, Class targetClass); } ``` ```java @Component public class MybatisTransDriver implements TransDriver { @Override public List findByIds(List ids, Class targetClass) { try (SqlSession sqlSession = SqlHelper.sqlSession(targetClass)) { BaseMapper mapper = SqlHelper.getMapper(targetClass, sqlSession); return mapper.selectBatchIds(ids); } } } ``` [your-project-path]:orangewest/easy-trans [contributors-shield]: https://img.shields.io/github/contributors/orangewest/easy-trans.svg?style=flat-square [contributors-url]: https://github.com/orangewest/easy-trans/graphs/contributors [forks-shield]: https://img.shields.io/github/forks/orangewest/easy-trans.svg?style=flat-square [forks-url]: https://github.com/orangewest/easy-trans/network/members [stars-shield]: https://img.shields.io/github/stars/orangewest/easy-trans.svg?style=flat-square [stars-url]: https://github.com/orangewest/easy-trans/stargazers [issues-shield]: https://img.shields.io/github/issues/orangewest/easy-trans.svg?style=flat-square [issues-url]: https://img.shields.io/github/issues/orangewest/easy-trans.svg [license-shield]: https://img.shields.io/github/license/orangewest/easy-trans.svg?style=flat-square [license-url]: https://github.com/orangewest/easy-trans/blob/master/LICENSE.txt