VS Code 整活:100行代码写一个悬浮翻译插件

前端劝退师

共 4832字,需浏览 10分钟

 ·

2022-02-22 10:37

前言

要说哪个插件对效率提升最大,可能各有推荐,各有千秋。但我要说对初学者,以及英文有亿点点差的同学来讲:翻译,是日常开发中必不可少的一环。在下找过N个VSCode 翻译插件 发现一个神器:

VSCode 插件:Google Translate Extension

这款插件不像其它要么需科学上网,要么强绑定快捷键的憨憨插件,有个最直观的功能:选中悬浮翻译

啊这...也太作弊了吧,编程能力+1s。

因为这两年都在从事VSCode二次/插件 开发,这个功能深得我心,于是想捣腾一下,看看能不能写轮眼学习一番

撒话不说,开干!

1. 代码目录分析

其中代码逻辑都在src目录里。

command.js //全部命令
extension.js //插件入口文件
tranlate.js //翻译工具函数

先从入口文件extension.js出发:

// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
const command = require('./command')

function activate(context) {
    console.log('Congratulations, your extension "vsc-google-translate" is now active!');

    command.initSetting(context);

    context.subscriptions.push(command.hoverDisposable);
    context.subscriptions.push(command.tranDisposable);
    context.subscriptions.push(command.switchDisposable);
    context.subscriptions.push(command.copyDisposable);
    context.subscriptions.push(command.replaceDisposable);
    context.subscriptions.push(command.canDisposable);
    context.subscriptions.push(command.switchLangDisposable);
    context.subscriptions.push(command.fromLangDisposable);
    context.subscriptions.push(command.settingsDisposable);
}

exports.activate = activate;


function deactivate() {
}

exports.deactivate = deactivate;

可以看到相当整洁,其中我们关注的功能只有hoverDisposabletranDisposable,为了方便阅读,我精简了一遍。从500行减到100行。

2. 源码分析

精简完的入口文件:

// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
const command = require('./command')

function activate(context) {
    console.log('Congratulations, your extension "vsc-google-translate" is now active!');
    
    command.initSetting(context);

    context.subscriptions.push(command.hoverDisposable);
}

exports.activate = activate;


function deactivate() {
}

exports.deactivate = deactivate;

2.1 初始化配置

// 获取用户状态,防止重复使用
function initSetting(cxt) {
    hoverOpen = cxt.globalState.get('hover') || false;
    langFrom = cxt.globalState.get('fromLang') || 'auto'
    if (!translate.languages.isSupported(langFrom)) langFrom = 'auto';
    cxt.globalState.update('hover', hoverOpen);
}

2.2 编写触发函数

// 向文件类型提供悬停内容的简单方法
let hoverDisposable = vscode.languages.registerHoverProvider({scheme: 'file'}, {
    provideHover: async (document, position, token) => {
        // 获取正在激活的编辑器窗口区域
        let editor = vscode.window.activeTextEditor;
        if (!editor || !hoverOpen) {
            return; // No open text editor
        }

        let length = editor.selections.length;
        for (let i = 0; i < length; i++) {
            let selection = editor.selections[i];
            let line = { 
                begin: Math.min(selection.anchor.line, selection.active.line),
                end: Math.max(selection.anchor.line, selection.active.line)
            }, character = {
                begin: Math.min(selection.anchor.character, selection.active.character),
                end: Math.max(selection.anchor.character, selection.active.character)
            };
            // 这里是检验 从后往前 或 从前往后 选择的内容。
            if (line.begin > position.line || character.begin > position.character) continue;
            if (line.end < position.line || character.end < position.character) continue;
            try {
                // 开始翻译
                let trans = await translate(editor.document.getText(selection), langTo);
                if (!trans) return;
                let word = trans.word  
                // 悬停显示的内容
                let pre = `**[Google Translate](https://translate.google.cn/?sl=auto&tl=${trans.lang.to}&text=${encodeURI(trans.text)})**\n\n`;
                console.log("word", word);
                return new vscode.Hover(pre + word.replace(/\r\n/g, '  \r\n'));
            } catch (error) {
                return new vscode.Hover('**[Error](https://github.com/imlinhanchao/vsc-google-translate/issues)**\n\n' + error.message);
            }
        }

    }
})

其中几个关键点:

  1. vscode.languages.registerHoverProvider({scheme: 'file'},{...}: 向文件类型提供悬停内容的简单方法。
  2. 中间的两端if: 检验 从后往前 或 从前往后 选择的内容是否满足需求。

然后到了悬停调用

await translate(editor.document.getText(selection), langTo);

2.3 触发翻译

再来看tranlate.js,我稍微精简了下:

const vscode = require('vscode');
const translator = require('@imlinhanchao/google-translate-api');

let config = {};


async function translate(text, lang) {
    try{
        let result = await translator(text, {
            from: lang.from == 'auto' ? undefined : lang.from,
            to: lang.to,
        })

        return {
            lang,
            text,
            word: result.text || '',
            candidate: result.candidates
        };
    } catch (err) {
        throw new Error(`Translate failed, Error message: '${err.message}'. Please post an issues for me.`);
    }      
}

function getConfig() {
    let keys = [
        'google-translate.firstLanguage',
        'google-translate.secondLanguage',
    ];
    let values = {};
    keys.forEach(k => values[k] = vscode.workspace.getConfiguration().get(k))
    return values;
}

module.exports = async (word, l, from='auto') => {
    if (word == ''return null;
    config = getConfig();
    let lang = {
        from,
        to: l || config['google-translate.firstLanguage']
    };

    // 解析驼峰函数。
    word = word.replace(/([a-z])([A-Z])/g, "$1 $2")
            .replace(/([_])/g, " ").replace(/=/g, ' = ')
            .replace(/(\b)\.(\b)/g, '$1 \n{>}\n $2 ');

    let tran = await translate(word, lang);
    
    // 若翻译无结果,则调用翻译成第二语言
    if (tran.word.replace(/\s/g, '') == word.replace(/\s/g, '') || !tran.word.trim()) {
        lang.to = config['google-translate.secondLanguage'];
        let tranSecond = await translate(word, lang);
        if (tranSecond.word) tran = tranSecond;
    }
    // 去除多余字符
    tran.word = tran.word.replace(/\n{>}\n/g, '.');
    tran.candidate = tran.candidate.map(c => c.replace(/{([^>]*?)>}/g, '$1\n{>}').replace(/\n{>}\n/g, '.'));
    return tran;
};

module.exports.getConfig = getConfig;
module.exports.languages = translator.languages;
  1. @imlinhanchao/google-translate-api:是作者集成的翻译API。
  2. getConfig:获取当前vscode的首选和次选语言,若无则自动翻译(默认英语-> 汉语)。

整个文件导出一个翻译方法。

中间那段正则,可以解析出驼峰函数

word = word.replace(/([a-z])([A-Z])/g, "$1 $2")
        .replace(/([_])/g, " ").replace(/=/g, ' = ')
        .replace(/(\b)\.(\b)/g, '$1 \n{>}\n $2 ');

2.4 显示悬停

回到hoverDisposable:

let trans = await translate(editor.document.getText(selection), langTo);
if (!trans) return;
let word = trans.word    
let pre = `**[Google Translate](https://translate.google.cn/?sl=auto&tl=${trans.lang.to}&text=${encodeURI(trans.text)})**\n\n`;
return new vscode.Hover(pre + word.replace(/\r\n/g, '  \r\n'));

拿到翻译结果后,触发显示为:

这里一个很有意思的点,给你拼装了一段可打开的Google Translate链接,非常体贴了可以说。

这个插件作为一个vscode插件开发初体验,是不错的。

2.5 额外小发现:google-translate-api

一个用于 Google 翻译的免费且无限制的 API💵

总结

约有一年没写文章了,这段时间都在摸鱼以及健身。都在写vscode二次/插件开发。比较少关注新技术。准备重新出发,整整账号了。

源码:

VSCode 插件:Google Translate Extension

精简后的:https://github.com/roger-hiro/vscode-google-translate

不想拉代码的同学可以试试CS的自定义模板

来自:https://cloudstudio.net/templates/5oulv1ZVoy





如果你觉得这篇内容对你挺有启发,我想邀请你帮我三个小忙:

  1. 点个「在看」,让更多的人也能看到这篇内容(喜欢不点在看,都是耍流氓 -_-)

  2. 欢迎加我微信「huab119」拉你进技术群,长期交流学习...

    关注公众号「前端劝退师」,持续为你推送精选好文,也可以加我为好友,随时聊骚。



点个在看支持我吧,转发就更好了

如果觉得这篇文章还不错,来个【转发、收藏、在看】三连吧,让更多的人也看到~



浏览 65
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报