手写《自动化提词和回填工具》实现前端国际化(语言切换)

前端国际化的功能要求 :下拉选择语言,然后对应切换语言,先支持中英文。(目前有10+项目,要逐步处理完)

技术工具 :(项目是vue工程) vue-i18n,需要把所有的中文提出来,并且还要回填

难点 : 项目老旧、庞大,手动提词太笨了,效率低(而且有10+项目),此时需要一个工具,能够自动完成提词和回填

实现大纲:

  1. vue-i18n 的使用介绍
  2. 开发自动化提词和回填工具分析
  3. 提词碰到的难点
  4. 回填碰到的难点
  5. 加入工具平台,让小组成员能够很容易使用

1. vue-i18n 的使用介绍

  1. // 入口 main.js, 引入
  2. import i18n from './i18n'
  3. ...
  4. new Vue({
  5. i18n,
  6. ...
  7. })
  1. // i18n.js
  2. import Vue from 'vue'
  3. import iView from 'view-design'
  4. import VueI18n from 'vue-i18n'
  5. import zhView from 'view-design/dist/locale/zh-CN'
  6. import enView from 'view-design/dist/locale/en-US'
  7. import zh from './langs/zh.json'
  8. import en from './langs/en.json'
  9. Vue.use(VueI18n)
  10. Vue.locale = () => {}
  11. const messages = {
  12. en: Object.assign(enView, en), // 将自己的英文包和iview提供的结合
  13. zh: Object.assign(zhView, zh) // 将自己的中文包和iview提供的结合
  14. }
  15. let locale = localStorage.getItem('lan')
  16. if (!locale) {
  17. if (navigator.language === 'zh-CN') { // 获取浏览器的语言
  18. locale = 'zh'
  19. } else {
  20. locale = 'en'
  21. }
  22. }
  23. const i18n = new VueI18n({
  24. locale: locale, // 设置语言,如果本地存储了则用本地的,没有则默认 'en'
  25. messages
  26. })
  27. Vue.use(iView, {
  28. i18n: (key, value) => i18n.t(key, value)
  29. })
  30. export default i18n
  1. // 对应的切换语言按钮 的逻辑
  2. <Select @on-change="languageChange" placeholder="Language" size="small">
  3. <Option value="zh">简体中文</Option>
  4. <Option value="en">English</Option>
  5. </Select>
  6. languageChange(lang) {
  7. localStorage.setItem('lan', lang)
  8. location.reload()
  9. }

vue-i18n 现在已经配置好了,还需要资源zh.json, en.json, 还有回填

  • vue-i18n注册完后,会有一个全局的 $t,处理语言的切换
  1. // zh.json
  2. {
  3. "确定", "确定",
  4. "取消", "取消",
  5. ...
  6. }
  7. // en.json
  8. {
  9. "确定", "confirm",
  10. "取消", "cancel",
  11. ...
  12. }
  13. // xx.vue 回填, vue-i18n注册完后,会有一个全局的 $t,处理语言的切换
  14. <template>
  15. 原来的写法:<button>确定</button>
  16. 回填后: <button>{{$t('确定')}}</button>
  17. </template>
  18. <script>
  19. export default {
  20. data() {
  21. return {
  22. msg: '取消', // 原来的写法
  23. msg: this.$t('取消') // 回填后
  24. }
  25. }
  26. }
  27. </script>

2. 开发自动化提词和回填工具分析

根据上面的vue-i18n使用介绍,我们还需要资源zh.json, en.json, 还有回填

目标是:写一个脚本,执行一条命令,比如lan /src/views, 就可以得到/src/views目录内的 zh.json, en.json,并且回填好

解耦开发分3步:

  1. 有一个前端工具平台(内部前端工具平台搭建),可以执行命令,类似比如 终端输入:eslint 或者 webpack,可以执行对应的工具库
    • (好处)目的是,组员们可以很轻松的使用,比如 npm i -g feTools,然后fe lan /src/views 就可以执行这个脚本。而不是用copy源文件去使用(copy大法也不好管理版本)
  1. 提词
    用正则匹配,拿到所有的中文,然后生成zh.json和en.json,如下

    1. // zh.json
    2. {
    3. "确定", "确定",
    4. "取消", "取消",
    5. ...
    6. }
    7. // en.json 后续把这个文件交给产品去翻译,或者自己翻译,翻译的结果写到对应的value
    8. {
    9. "确定", "",
    10. "取消", "",
    11. ...
    12. }
  2. 回填
    用replace配合正则,把中文,替换成 $(‘’) 的形式

    • 如:<span>查询</span> 替换成=> <span>{{$t('查询')}}</span>
  1. /* 处理 vue的template 部分 */
  2. let vueStr = (源代码).replace(re, word => {
  3. if (zhJson[word.trim()]) {
  4. return "$t('" + word.trim() + "')"
  5. }
  6. return word
  7. })

3. 提词碰到的难点

匹配中文的正则表达式:/[\u4e00-\u9fa5]{1,}/g,但实际上很多都不是简单的中文

情况有:

  1. 1. 中文混杂英文
  2. <span>http content-type采用json格式,建议使用的格式</span>
  3. 2. 中文混杂各种标点符号
  4. <span>应用描述不允许为空 (长度150以内)</span>
  5. 3. 中文混杂vue的模板语法:`<span>企业{{obj.name}}图谱</span>`
  6. <span>企业{{obj.name}}图谱</span>
  7. 4. 过滤注释(html注释,js注释)
  8. // 注释
  9. <!-- 注释 -->

分析问题:

  1. 1. 中文混杂英文
  2. <span>http content-type采用json格式,建议使用的格式</span>
  3. 2. 中文混杂各种标点符号
  4. <span>应用描述不允许为空 (长度150以内)</span>
  5. 3. 中文混杂vue的模板语法:
  6. <span>企业{{obj.name}}图谱</span>
  7. (考虑的后续的回填)这两种情况最好把标点和英文都一同匹配完整,不然要分太多的段,可能会影响意思的表达
  8. 错误示例:(回填后): <span>http{{$t('的')}}content-type{{$t('采用')}}json{{$t('格式')}},{{$t('建议使用的格式')}}</span>
  9. 分多段实在太丑了
  10. 正确示例:
  11. <span>{{$t('http 的content-type采用json格式,建议使用的格式')}}</span>
  12. 4. 过滤注释(html注释,js注释)
  13. // 注释
  14. /* 注释
  15. * 注释注释注释注释
  16. */
  17. <!-- 注释 -->
  18. <!--
  19. 注释
  20. -->

最终的正则表达式结果:[\u4e00-\u9fa5|\w|\s|,,.。\/\*::=()()!!?\?\-]{1,}

正则表达式分析:中文 + 英文数字 + 空格或换行符等 + 部分标点符号(因为要过滤掉<>和{},所以就不能直接写\S)

  • 关键点是:不要以为一条正则就能搞定所有问题,因为上面匹配了英文,所以所有的英文也都会匹配出来,需要在写一次正则为结果做过滤
  1. 正则字符串:
  2. <span>企业{{obj.name}}图谱</span>
  3. <span>http content-type采用json格式,建议使用的格式</span>
  4. // 注释
  5. <!-- 注释 -->
  6. 正则表达式:
  7. [\u4e00-\u9fa5|\w|\s|,,.。\/\*::=()()!!?\?\-]{1,}
  8. 匹配结果:
  9. 共找到 10 处匹配:
  10. span
  11. 企业
  12. obj.name
  13. 图谱
  14. /span
  15. span
  16. http content-type采用json格式,建议使用的格式
  17. /span
  18. // 注释
  19. !-- 注释 --
  20. 源代码:
  21. function getChineseList (str) { // 获取所有 中文 混合英文 混合空格 及 标点符号
  22. const re = /[\u4e00-\u9fa5|\w|\s|,,.。\/\*::=()()!!?\?-]{1,}/g
  23. return str.match(re).map(e => {
  24. e = e.trim()
  25. // 把 英文 和 注释 过滤掉
  26. if (/[\u4E00-\u9FA5]/.test(e) && !/\/\/|\/\*--/.test(e)) return e
  27. }).filter(e => e)
  28. }

最终总结:

  1. 思考问题的过程中,不要以为一条正则就能搞定所有问题,这样容易走入死胡同。可以多几次正则 或者 为正则结果做过滤,思路要打开
  2. 场景会很复杂,还是会有些多提取的情况(比如注释内有<>尖括号这种),但不会少提取。很难做到100%的准确性,起码保证不会少就行

4. 回填碰到的难点

情况很多,一次正则是不可能处理完的

情况有: