likes
comments
collection
share

十几行递归算法,助你轻松比对国外系统多语言配置的差异

作者站长头像
站长
· 阅读数 63

  往期精彩回顾(以下四篇都是自己精心总结的文章,有兴趣的朋友可以传送过去看看):

  无论如何,你都必须得掌握的JS知识

  传送门——金石计划二期之深度剖析render函数、函数式组件与JSX之间的爱恨情仇

  传送门——金石计划一期之面试不面试,你都必须得掌握的vue知识

  传送门——金石计划一期之我的css世界

前言

  大家好,我是前端贰货道士。最近在开发国外系统的过程中,配置了很多英文翻译。但由于某些英文翻译却未能及时和中文翻译同步,导致中英文翻译之前存在差异。由于系统比较大,切换中英文版本,一个一个页面去查找缺失的字体翻译,必然会耗费大量的开发时间。因此,我们决定写一个算法盘它,去递归比较多语言字体翻译之间存在的差异,并记录出现差异的位置。另外,如果本文对您有那么一丝一毫的启发,烦请大家一键三连哦, 蟹蟹大家~

需求分析

  • 字体翻译的定义规则是,公共组件使用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
评论
请登录