31

写个vscode插件, 帮助阅读i18n项目的代码

image.png

介绍

     作者当前参与的项目是面向全球用户的, 项目免不了要做国际化处理, 哪怕只是一个简单的ok按钮ok文案都要翻译成不同国家的语言展示, 所以在代码里面往往我们不可以直接命名为ok, 而是可能命名为user_base_plan_edit_ok_button这类的名字, 并且为了区分i18n与其它命名, 我们团队会采用全大写的方式类似USER_BASE_PLAN_EDIT_OK_BUTTON, 而这样的代码多了之后阅读起来会让眼睛干涩, 类似下面图里的伪代码:

image.png

     为了解决这个不好阅读的问题我比较预期的效果是, 当鼠标悬浮在目标文字身上时会浮现出一个弹框, 而这个弹框至少要告诉我这个词语的中文或英文意思, 效果如下:

image.png

     可以看出这个插件如果做好了不一定只能用在i18n这个只是点上, 可以用在很多方面, 比如某个code103993283这个code具体代表什么含义可以做个插件展示出来。

一: 初始化vscode项目

     没做过vscode插件的同学推荐先读读我写的入门教程:

     这次做的插件是针对我当前工程的, 没有广泛应用能力所以就没发布到vscode官方, 开发好后打包发到公司内网与群里就ok了。

     创建项目: 这里项目名暂定叫i18n2c

   yo code

image.png

二: 初始化架构

     extension.js文件我们清理干净:

const vscode = require("vscode");

function activate(context) {
  vscode.window.showInformationMessage("i18n翻译插件加载完成");
}

module.exports = {
  activate,
};

     修改package.json文件: 设置当资源加载完就开始加载我们的插件

{
// ...

  "activationEvents": [
    "onStartupFinished"
  ],

// ...
}

     点击f5开启调试模式, 出现下图就证明插件可以正常启动了:
image.png

三: 初始化hover文件

     我们把hover后的逻辑都放在src里面:

image.png

简单改造一下extension.js, 把hover方法加进去:

const vscode = require("vscode");
const hover = require("./src/hover");

function activate(context) {
  vscode.window.showInformationMessage("i18n翻译插件加载完成");

  context.subscriptions.push(hover);
}

module.exports = {
  activate,
};

     hover.js 导出悬停时的处理方法

const vscode = require("vscode");

module.exports = vscode.languages.registerHoverProvider("*", {
  provideHover(document, position) {
    return new vscode.Hover(`i18n插件前来助你渡劫`);
  },
});

     当你悬停在任何位置的时候效果如下:

image.png

四: i18n目标文案的识别

     每个团队的技术方案都不一样, 这里我只针对我们团队的方案进行设计, 希望起到一个抛砖引玉的目的:

     我们团队的i18n文案都是大写, 并且使用下划线相互链接, 而我发现全局除了i18n文案外没有字符串里超过三个下划线, 并且i18n文案至少四个下划线, 所以当前我使用的是判断字符串里面是否含有超过三个 '_'

const vscode = require("vscode");

module.exports = vscode.languages.registerHoverProvider("*", {
  provideHover(document, position) {
    const word = document.getText(document.getWordRangeAtPosition(position));

    if (word.split("_").length > 3) {
      return new vscode.Hover(`i18n插件前来助你渡劫`);
    }

  },
});

上面的word就是我们获取到的被悬停的文本

五: 如何翻译成中文

     上面我们找到了要被翻译的文案, 这里我们就要研究如何翻译这个文案了, 一般针对i18n都会有个翻译的字典包, 我们团队的就是一个json文件, 不同的是每个key是小写的, 大致的样子如下:

image.png

     我要做的就是读取这个文件, 然后将要被翻译的文案转换为小写, 再去匹配一下就ok了。

const vscode = require("vscode");
const fs = require("fs");

module.exports = vscode.languages.registerHoverProvider("*", {
  provideHover(document, position) {
    const word = document.getText(document.getWordRangeAtPosition(position));

    let jsonCN = JSON.parse(
      fs.readFileSync("/xxxx/xxxxx/langs/zh-CN.json")
    );

    if (word.split("_").length > 3) {
      return new vscode.Hover(`i18n插件前来助你渡劫:
- 中文: ${jsonCN[word.toLocaleLowerCase()]}
      `);
    }

  },
});

image.png

     我们的i18n字典都是放在同一个文件夹里面的, 所以我只需要知道这个文件夹即可, 默认取出里面的中文与英文文案, 改造一下吧。

const vscode = require("vscode");
const fs = require("fs");

module.exports = vscode.languages.registerHoverProvider("*", {
  provideHover(document, position) {
    const word = document.getText(document.getWordRangeAtPosition(position));

    if (word.split("_").length > 3) {
      const i18nPath = "/xxxx/xxxxx/langs";
      const jsonUrlCN = i18nPath + "/zh-CN.json";
      const jsonUrlEN = i18nPath + "/en.json";
      let jsonCN = JSON.parse(fs.readFileSync(jsonUrlCN));
      let jsonEN = JSON.parse(fs.readFileSync(jsonUrlEN));
      return new vscode.Hover(`i18n插件前来助你渡劫:
- 中文: ${jsonCN[word.toLocaleLowerCase()]}
- 英文: ${jsonEN[word.toLocaleLowerCase()]}
      `);
    }
  },
});

image.png

这里的i18nPath当然可以由用户来配置啦, 接下来我们就把它研究。

六: setting配置

     我们翻译字典文件所在的位置肯定不能写死在插件里面, 很多时候需要用户自己来设置, 这时我们就需要vscode的setting的概念了, 具体如下图所示:

image.png

第一步: 初始化配置项

我们来到package.json文件添加setting配置项:

{
  "contributes": {
    "configuration": {
      "type": "object",
      "title": "i18n翻译配置",
      "properties": {
        "vscodePluginI18n.i18nPath": {
          "type": "string",
          "default": "",
          "description": "翻译文件的位置"
        },
        "vscodePluginI18n.open": {
          "type": "boolean",
          "default": true,
          "description": "开启18n翻译"
        }
      }
    }
  },
}

type设置为布尔会自动生成CheckBox还挺方便的。

第二步: 初始插件读取配置

extension.js文件:

const vscode = require("vscode");

function activate(context) {
  const i18nPath = vscode.workspace
    .getConfiguration()
    .get("vscodePluginI18n.i18nPath");
  const open = vscode.workspace.getConfiguration().get("vscodePluginI18n.open");

  if (open && i18nPath) {
    vscode.window.showInformationMessage("i18n翻译插件已就位");
    const hover = require("./src/hover");
    context.subscriptions.push(hover);
  }
}

module.exports = {
  activate,
};

这里要注意, 如果const hover = require("./src/hover"); 写在最上方, 可能会导致无法关闭悬停翻译效果, 这个里面有坑我们一会详细说说。

第三步: 悬停时读取路径

hover.js文件:

const vscode = require("vscode");
const fs = require("fs");

module.exports = vscode.languages.registerHoverProvider("*", {
  provideHover(document, position) {
    const i18nPath = vscode.workspace
      .getConfiguration()
      .get("vscodePluginI18n.i18nPath");
    const open = vscode.workspace
      .getConfiguration()
      .get("vscodePluginI18n.open");

    if (i18nPath && open) {
      const word = document.getText(document.getWordRangeAtPosition(position));

      if (word.split("_").length > 3) {
        const i18nPath = "/xxxxx/xxxx/langs";
        const jsonUrlCN = i18nPath + "/zh-CN.json";
        const jsonUrlEN = i18nPath + "/en.json";
        let jsonCN = JSON.parse(fs.readFileSync(jsonUrlCN));
        let jsonEN = JSON.parse(fs.readFileSync(jsonUrlEN));
        return new vscode.Hover(`i18n插件前来助你渡劫:
- 中文: ${jsonCN[word.toLocaleLowerCase()]}
- 英文: ${jsonEN[word.toLocaleLowerCase()]}
      `);
      }
    }
  },
});

七: 何时判断是否开启插件

     如果在extension.js文件里面引入了hover模块, 则就算我们判断了用户未开启i18n翻译流程也会走到hover模块, 但是如果我们在判断用户是否开启i18n翻译后进行引入hover模块就会造成, 如果用户改变配置开始翻译无法及时响应, 需要用户重启一下, 所以具体这里如何设计需要大家酌情啦。

八: 提示弹框

     如果用户开启了i18n翻译但是没配置路径, 那么我们其实可以出现一个弹框提示用户是否要来填写这个翻译字典的路径, 类似下图的效果:

image.png

extension.js文件里面使用showInformationMessage这个方法, 但是要增加两个参数:

const vscode = require("vscode");

function activate(context) {
  const i18nPath = vscode.workspace
    .getConfiguration()
    .get("vscodePluginI18n.i18nPath");
  const open = vscode.workspace.getConfiguration().get("vscodePluginI18n.open");

  if (open && i18nPath) {
    vscode.window.showInformationMessage("i18n翻译插件已就位");
    const hover = require("./src/hover");
    context.subscriptions.push(hover);
  } else if (open && !i18nPath) {
    vscode.window
      .showInformationMessage("是否设置翻译文件路径", "是", "否")
      .then((result) => {
           if (result === "是") {

           }
      });
  }
}

module.exports = {
  activate,
};

九: 选择文件

     当用户点击'是'的时候我们需要借助vscode提供的api进行文件的选择, 并且拿到文件的返回值进行主动的设置操作:

vscode.window.showOpenDialog 方法, 他有四个主要的参数:

  1. canSelectFiles 是否可选文件
  2. canSelectFolders 是否可选文件夹
  3. canSelectMany 是否可以多选
  4. openLabel 提示文案
  5. 返回值是数组, 第一个元素的path属性, 就是文件的全局路径

image.png

最后再用update方法主动更新一下全局的配置就可以啦。

vscode.window
      .showInformationMessage("是否设置翻译文件路径", "是", "否")
      .then((result) => {
        if (result === "是") {
          vscode.window
            .showOpenDialog({
              canSelectFiles: false, // 是否可选文件
              canSelectFolders: true, // 是否可选文件夹
              canSelectMany: false, // 是否可以选择多个
              openLabel: "请选择翻译文件夹",
            })
            .then(function (res) {
              vscode.workspace
                .getConfiguration()
                .update("vscodePluginI18n.i18nPath", res[0].path, true);
            });
        }
      });
  }

end

     这次就是这样, 希望与你一起进步。


lulu_up
5.7k 声望6.9k 粉丝

自信自律, 终身学习, 创业者