十几行递归算法,助你轻松比对国外系统多语言配置的差异
往期精彩
回顾(以下四篇都是自己精心总结
的文章,有兴趣的朋友可以传送过去看看):
传送门——金石计划二期之深度剖析render函数、函数式组件与JSX之间的爱恨情仇
传送门——金石计划一期之面试不面试,你都必须得掌握的vue知识
前言
大家好,我是前端贰货道士。最近在开发国外系统的过程中,配置了很多英文翻译。但由于某些英文翻译却未能及时和中文翻译同步,导致中英文翻译之前存在差异。由于系统比较大,切换中英文版本,一个一个页面去查找缺失的字体翻译,必然会耗费大量的开发时间。因此,我们决定写一个算法盘它,去递归比较多语言字体翻译之间存在的差异,并记录出现差异的位置。另外,如果本文对您有那么一丝一毫的启发,烦请大家一键三连哦, 蟹蟹大家~
需求分析
- 字体翻译的定义规则是,公共组件使用
base
作为key
值, 公共组件下的不同组件使用不同名称作为key
值,不同组件下使用不同的key
值标识字体翻译;自定义组件使用comp
作为key
值,页面级vue
文件使用page
作为key
值,定义规则同公共组件。说这么多,其实是为了说明:所定义的字体翻译是一个对象,对象中可能会嵌套多级对象和数组,我们需要比较多语言字体翻译之间存在的差异,并记录出现差异的位置
字体翻译部分截图:
- 目前的国外系统项目中,只存在中英文两种字体翻译,但不保证后续不会引入新的字体翻译
最终效果浏览:

思路分析
- 考虑到字体翻译文件中的嵌套层级较多,我们可以使用递归的思想去解决这个问题。 编写一个递归算法去比较多语言字体翻译中多出的那部分,并在用户登陆开发环境后,使用
vue
中的递归组件
去展示多语言字体翻译之间的差异 - 不用对数组和对象进行分类讨论,直接使用
Object.entries
方法判断即可 - 我们只需要找出多语言字体翻译中多出的那部分即可。 没必要对多语言字体翻译中
增加
的和缺少
的部分心存执念。因为使用定义好的方法,调换实参顺序,就是多语言字体翻译中缺少的那部分,所以没必要将相同逻辑处理两次 - 善用
lodash
。 使用lodash
中的isPlainObject
方法判断传入数据是否为一个对象,使用isArray
方法判断传入数据是否为一个数组,使用isUndefined
方法判断传入数据是否为undefined
核心算法及分析(全剧终
)
`该方法用于判断obj1比obj2多的key值`
isEqual(obj1, obj2, propsList = []) {
`propsList的定义是为了记录当前产生差异的key的位置,是该地方的所有前置key和当前key组合而成的数组,
用来记录一次递归的结果`
`list是完成递归后,用来存放多语言字体翻译产生差异的所有路径数组`
const list = []
`判断当前传入值obj1是否为对象或者数组,即判断obj1是否可递归`
const isRecursive = this.isRecursive(obj1)
if (!isRecursive) {
`如果obj1不为数组或者对象,且obj2不存在,就把obj1全部放入list并返回`
if (isUndefined(obj2)) list.push(obj1)
return list
}
`Object.entries的对象如果是数组,key就是索引,val就是value值;如果是对象,key就是key,val就是val`
Object.entries(obj1).map(([key, val]) => {
if (this.isRecursive(val)) {
`如果obj1可以递归, 则往下递归调用方法`
`之所以要 || {}是因为后续递归方法的obj2[key]可能是undefined, 所以要给一个空的默认值{}`
`将前置路径展开,并和当前key进行拼接,就是记录当前产生差异的key数组`
const childList = this.isEqual(val, obj2[key] || {}, [...propsList, key])
`如果childList有值,说明这次递归找到了多语言字体翻译的差异,就将这次递归的结果push到list中`
`此时的childList是每次递归后的结果`
`而list是最终的结果`
if (childList.length) list.push(childList)
}
if (isUndefined(obj2[key])) {
`每次递归的出口:将数组展开为字符串并以.隔开,记录当前递归的地址,即每次递归后的childList的值`
list.push(propsList.join('.') + '.' + key)
}
})
`将最终的结果返回出去`
return list
}
代码分析截图:
最终路径截图:
不难发现,最终返回的数据是一个递归数据
。因此,我们需要使用递归组件
去展示多语言字体翻译的差异。
代码整合
`混入文件:`
import { isPlainObject, isArray, isUndefined } from 'lodash'
import en from '@/i18n/locales/en.js'
import zh from '@/i18n/locales/zh-CN.js'
export default {
data() {
return {
langMappingList: {
zh,
en
},
showVisible: false,
dataList: []
}
},
mounted() {
this.getDataList()
this.showVisible = this.dataList.some((item) => !!item.data.length)
},
methods: {
getDataList() {
this.pushToDataList('en', 'zh')
this.pushToDataList('zh', 'en')
},
clickHandler() {
this.showVisible = true
},
pushToDataList(lang1, lang2) {
const data = this.isEqual(this.langMappingList[lang1], this.langMappingList[lang2])
this.dataList.push({
description: `${lang1}相比${lang2}多了: `,
data
})
},
isRecursive(data) {
return isPlainObject(data) || isArray(data)
},
isEqual(obj1, obj2, propsList = []) {
const list = []
const isRecursive = this.isRecursive(obj1)
if (!isRecursive) {
if (isUndefined(obj2)) list.push(obj1)
return list
}
Object.entries(obj1).map(([key, val]) => {
if (this.isRecursive(val)) {
const childList = this.isEqual(val, obj2[key] || {}, [...propsList, key])
if (childList.length) list.push(childList)
}
if (isUndefined(obj2[key])) {
list.push(propsList.join('.') + '.' + key)
}
})
return list
}
}
}
dataList(多语言字体翻译比对)截图:
// langItem.vue 递归组件的封装
<template>
<div class="mt20" v-if="data.length">
<div class="emphasize">{{ description }}</div>
<div v-for="(item, index) in data">
<div v-if="isArray(item)">
<langItem :data="item"></langItem>
</div>
// 递归结束的出口:相当于将数组全都展开了
<div class="mt10" v-else>{{ item }}</div>
</div>
</div>
</template>
<script>
import { isArray } from 'lodash'
export default {
name: 'langItem',
props: {
data: Array,
description: String
},
methods: {
isArray
}
}
</script>
<style lang="scss" scoped>
.emphasize {
font-size: 18px;
color: red;
font-weight: bold;
}
</style>
// compareDialog.vue 模态框的封装
<template>
<appDialog width="1000px" title="Lang Compare" v-bind="$attrs" v-on="$listeners" :hasBtn="false" :hasFooter="false">
<div class="compare-warpper">
<div v-for="({ data, description }, index) in dataList" :key="index">
<langItem v-if="data.length" :data="data" :description="description"></langItem>
</div>
</div>
</appDialog>
</template>
<script>
import langItem from './langItem'
export default {
components: { langItem },
props: {
dataList: Array
}
}
</script>
<style lang="scss" scoped>
.compare-warpper {
overflow: auto;
height: 50vh;
}
</style>
结语
如果大家有更好的解决方案,欢迎大家在评论区中提出自己的高见,大概就这样吧~
转载自:https://juejin.cn/post/7175342679935418425