关于javascript:从零到一开发vscode插件变量翻译

7次阅读

共计 7638 个字符,预计需要花费 20 分钟才能阅读完成。

需要的起因是英语渣在开发的过程中常常遇到一个变量晓得中文叫啥,然而英文单词可能就忘了,或者不晓得,这个时候,我之前做法是关上浏览器,关上谷歌翻译,输出中文,复制英文,而后切回 vscode,粘贴后果。切实是太麻烦了,年老的时候还好,忘性好,英文单词大部分都能记住,但随着年纪越来越大,头发越来越少,忘性也是越来越差,下面的步骤反复的次数也越来越多,所以痛定思痛,就开发了这款插件。因为本人也是这几天从零开始学习的插件开发,所以本文齐全的记录了一个小白开发的插件开发之路,内容次要是实战类的介绍,次要从四个方面介绍来残缺的展现整个插件从功能设计到公布的残缺历程。

  1. 功能设计
  2. 环境搭建
  3. 插件性能开发
  4. 插件打包公布

功能设计

性能次要是两个性能,中译英,其余语言翻译成中文

  1. 将中文变量替换为翻译后的英文变量,多个单词须要主动驼峰,解决的场景就是日常开发常常碰到的“英语卡壳”
  2. 划词翻译,主动将各种语言翻译成中文,这解决的场景是有时候看国外我的项目源代码的正文碰到不会的单词不晓得啥意思影响效率

环境搭建

上手的第一步,环境搭建

  1. 装置脚手架, yogenerator-code, 这两个工具能够帮忙咱们疾速构建我的项目,详情可见 Github

    // 装置
    yarn global add yo generator-code
  2. 装置 vsce,vsce 可用来将开发的代码打包成.vsix 后缀的文件,不便上传至微软插件商店或者本地装置

    yarn global add vsce
  3. 生成并初始化我的项目, 初始化信息依据本人状况填写

    // 初始化生成我的项目
    yo code

    到这一步后,抉择间接关上,Open with code

    关上后会主动建设一个工作区,并生成这些文件,可依据本人须要对文件进行删减,实现这步后,咱们能够间接进行开发与调试了

    如何进行调试?

    运行与调试 面板点击Run Extention, 或者快捷键F5,mac 能够间接点击触控栏的调试按钮

    关上后会弹出一个新的 vscode 窗口,这个新的窗口就是你的测试环境 ( 扩大开发宿主 ),你做的插件性能就是在这个新的窗口测试,打印的音讯在前一个窗口的 调试控制台 中, 比方自带的例子,在咱们新窗口 cmd/ctrl+shift+p后输出 Hello world 后会在前一个窗口的控制台打印一些信息

    到这里,开发筹备环境就筹备好了,下一步就是开始正式的插件性能开发

插件性能开发

插件开发中,有两个重要的文件,一个是 package.json, 一个是 extention.js

重要文件阐明

package.json

  • activationEvents用来注册激活事件,用来表明什么状况下会激活 extention.js 中的 active 函数。常见的有onLanguage,onCommand… 更多信息可查看 vscode 文档 activationEvents
  • main示意插件的主入口
  • contributes用来 注册命令 (commands), 绑定快捷键 (keybindings), 配置设置项 (configuration) 等等, 更多可配置项可看文档
extention.js

extention.js 次要作用是作为插件性能的实现点,通过 active,deactive 函数,以及 vscode 提供的 api 以及一些事件钩子来实现插件性能的开发

实现翻译性能

翻译这里次要是应用了两个服务,谷歌和百度翻译。

  1. 谷歌翻译参考了他人的做法,应用 google-translate-token 获取到 token,而后结构申请 url, 再解决返回的 body, 拿到返回后果。这里还有一个没搞懂的中央就是申请 url 的生成很迷,不晓得这一块是啥意思。

    const qs = require('querystring');
    const got = require('got');
    const safeEval = require('safe-eval');
    const googleToken = require('google-translate-token');
    const languages = require('../utils/languages.js');
    const config = require('../config/index.js');
    
    // 获取申请 url
    async function getRequestUrl(text, opts) {let token = await googleToken.get(text);
        const data = {
            client: 'gtx',
            sl: opts.from,
            tl: opts.to,
            hl: opts.to,
            dt: ['at', 'bd', 'ex', 'ld', 'md', 'qca', 'rw', 'rm', 'ss', 't'],
            ie: 'UTF-8',
            oe: 'UTF-8',
            otf: 1,
            ssel: 0,
            tsel: 0,
            kc: 7,
            q: text
        };
        data[token.name] = token.value;
        const requestUrl = `${config.googleBaseUrl}${qs.stringify(data)}`;
        return requestUrl;
    }
    
    // 解决返回的 body
    async function handleBody(url, opts) {const result = await got(url);
        let resultObj = {
            text: '',
            from: {
                language: {
                    didYouMean: false,
                    iso: ''
                },
                text: {
                    autoCorrected: false,
                    value: '',
                    didYouMean: false
                }
            },
            raw: ''
        };
    
        if (opts.raw) {resultObj.raw = result.body;}
        const body = safeEval(result.body);
    
        // console.log('body', body);
        body[0].forEach(function(obj) {if (obj[0]) {resultObj.text += obj[0];
            }
        });
    
        if (body[2] === body[8][0][0]) {resultObj.from.language.iso = body[2];
        } else {
            resultObj.from.language.didYouMean = true;
            resultObj.from.language.iso = body[8][0][0];
        }
    
        if (body[7] && body[7][0]) {let str = body[7][0];
    
            str = str.replace(/<b><i>/g, '[');
            str = str.replace(/<\/i><\/b>/g, ']');
    
            resultObj.from.text.value = str;
    
            if (body[7][5] === true) {resultObj.from.text.autoCorrected = true;} else {resultObj.from.text.didYouMean = true;}
        }
        return resultObj;
    }
    
    // 翻译
    async function translate(text, opts) {opts = opts || {};
        opts.from = opts.from || 'auto';
        opts.to = opts.to || 'en';
    
        opts.from = languages.getCode(opts.from);
        opts.to = languages.getCode(opts.to);
    
        try {const requestUrl = await getRequestUrl(text, opts);
            const result = await handleBody(requestUrl, opts);
            return result;
        } catch (error) {console.log(error);
        }
    }
    
    // 获取翻译后果
    const getGoogleTransResult = async(originText, ops = {}) => {const { from, to} = ops;
        try {const result = await translate(originText, { from: from || config.defaultFrom, to: to || defaultTo});
            console.log('谷歌翻译后果', result.text);
            return result;
        } catch (error) {console.log(error);
            console.log('翻译失败');
        }
    }
    
    module.exports = getGoogleTransResult;
  1. 百度翻译,百度翻译的比较简单,申请服务,取得 appid 和 key,而后结构申请 url 间接申请就行,不晓得如何申请的,可查看我之前的一篇文章 Electron+Vue 从零开始打造一个本地文件翻译器进行申请

    const md5 = require("md5");
    const axios = require("axios");
    const config = require('../config/index.js');
    axios.defaults.withCredentials = true;
    axios.defaults.crossDomain = true;
    axios.defaults.headers.post["Content-Type"] =
        "application/x-www-form-urlencoded";
    
    // 百度翻译
    async function getBaiduTransResult(text = "", opt = {}) {const { from, to, appid, key} = opt;
        try {
            const q = text;
            const salt = parseInt(Math.random() * 1000000000);
            let str = `${appid}${q}${salt}${key}`;
            const sign = md5(str);
            const query = encodeURI(q);
            const params = `q=${query}&from=${from}&to=${to}&appid=${appid}&salt=${salt}&sign=${sign}`;
            const url = `${config.baiduBaseUrl}${params}`;
            console.log(url);
            const res = await axios.get(url);
            console.log('百度翻译后果', res.data.trans_result[0]);
            return res.data.trans_result[0];
        } catch (error) {console.log({ error});
        }
    }
    
    module.exports = getBaiduTransResult;

获取选中的文本

应用事件钩子onDidChangeTextEditorSelection,获取选中的文本

    onDidChangeTextEditorSelection(({textEditor, selections}) => {text = textEditor.document.getText(selections[0]);
    })

配置项的获取更新

通过 vscode.workspace.getConfiguration 获取到工作区的配置项,而后通过事件钩子 onDidChangeConfiguration 监听配置项的变动。

获取更新配置项

const {getConfiguration} = vscode.workspace;
const config = getConfiguration();

// 留神 get 外面的参数其实就是 package.json 配置项外面的 contributes.configuration.properties.xxx
const isCopy = config.get(IS_COPY);
const isReplace = config.get(IS_REPLACE);
const isHump = config.get(IS_HUMP);
const service = config.get(SERVICE);
const baiduAppid = config.get(BAIDU_APPID);
const baiduKey = config.get(BAIDU_KEY);

// 更新应用 update 办法,第三个参数为 true 代表利用到全局
config.update(SERVICE, selectedItem, true);

监听配置项的变动

const {getConfiguration, onDidChangeConfiguration} = vscode.workspace;
const config = getConfiguration();

// 监听变动
const disposeConfig = onDidChangeConfiguration(() => {config = getConfiguration();
})

监听个别配置项的变动

const disposeConfig = onDidChangeConfiguration((e) => {if (e && e.affectsConfiguration(BAIDU_KEY)) {// 干些什么}
})

获取以后关上的编辑器对象

vscode.window.activeTextEditor代表以后关上的编辑器,如果切换标签页,而没有设置监听,那么这个这个对象不会自动更新,所以须要应用 onDidChangeActiveTextEditor 来监听,并替换之前的编辑器对象

const {activeTextEditor, onDidChangeActiveTextEditor} = vscode.window;
let active = activeTextEditor;
const edit = onDidChangeActiveTextEditor((textEditor) => {console.log('activeEditor 扭转了');
  // 更换关上的编辑器对象
  if (textEditor) {active = textEditor;}
})

划词翻译悬浮提醒

通过 vscode.languages.registerHoverProvider 注册一个 Hover,而后通过 activeTextEditor 拿到选中的词语进行翻译,而后再通过 new vscode.Hover 将翻译后果悬浮提醒

// 划词翻译检测
const disposeHover = vscode.languages.registerHoverProvider("*", {async provideHover(document, position, token) {const service = config.get(SERVICE);
        const baiduAppid = config.get(BAIDU_APPID);
        const baiduKey = config.get(BAIDU_KEY);

        let response, responseText;
        const selected = document.getText(active.selection);
        // 谷歌翻译
        if (service === 'google') {response = await getGoogleTransResult(selected, { from: 'auto', to: 'zh-cn'});
            responseText = response.text;
        }

        // 百度翻译
        if (service === 'baidu') {response = await getBaiduTransResult(selected, { from: "auto", to: "zh", appid: baiduAppid, key: baiduKey});
            responseText = response.dst;
        }
        // 悬浮提醒
        return new vscode.Hover(`${responseText}`);
    }
})

替换选中的文本

获取到 activeTextEditor, 调用他的edit 办法, 而后应用回调中的replace

// 是否替换原文
if (isReplace) {
  let selectedItem = active.selection;
  active.edit(editBuilder => {editBuilder.replace(selectedItem, result)
  })
}

复制到剪贴板

应用vscode.env.clipboard.writeText;

// 是否复制翻译后果
if (isCopy) {vscode.env.clipboard.writeText(result);
}

驼峰解决

function toHump(str) {if (!str) {return}
    const strArray = str.split(' ');
    const firstLetter = [strArray.shift()];
    const newArray = strArray.map(item => {return `${item.substring(0,1).toUpperCase()}${item.substring(1)}`;
    })
    const result = firstLetter.concat(newArray).join('');
    return result;
}

module.exports = toHump;

快捷键绑定

通过 vscode.commands.registerCommand 注册绑定之前 package.json 中设置的 keybindings, 须要留神的是 registerCommand 的第一个参数须要与 keybindings 的 command 保持一致能力绑定

registerCommand('translateVariable.toEN', async() => {//do something})


//package.json
"keybindings": [{
  "key": "ctrl+t",
  "mac": "cmd+t",
  "when": "editorTextFocus",
  "command": "translateVariable.toEN"
}],

插件打包公布

打包

vsce package

打包后会在目录下生成.vsix 后缀的插件

公布

插件公布次要是把打包的 vsix 后缀插件,传入微软 vscode 插件商店,当然也能本地装置应用。

传入商店

公布到线上须要到微软插件商店治理页面, 创立发布者信息,如果没有微软账号,须要去申请。

创立实现后,抉择公布到 vscode 商店

本地装置

本地是能够间接装置.vsix 后缀插件的,找到插件菜单

抉择 从 VSIX 装置, 装置下面打包的插件就好了

最初

vscode 的中文材料有点少,这次开发大多数工夫都在看英文文档,以及上外网寻找材料,真的英语太重要了,前面得多学点英语了,心愿前面我应用本人做的这个插件的次数会越来越少,我的项目已开源,应用阐明与源码传送门

正文完
 0