이 글은 vscode 변수 번역 플러그인을 처음부터 개발하는 데 도움이 될 것입니다. 이 글은 기능 설계부터 출시까지 전체 플러그인 과정을 4가지 측면에서 완벽하게 보여드릴 것입니다. !
요청의 이유는 개발 과정에서 영어 쓰레기들이 종종 한자 이름은 알지만 영어 단어를 잊어버렸을 수도 있고, 이때 제가 한 일이 무엇인지도 모르기 때문입니다. 이전에는 브라우저를 열고 Google 번역을 열고 중국어를 입력하고 영어를 복사한 다음 vscode로 다시 전환하여 결과를 붙여넣었습니다.
정말 귀찮습니다. 어렸을 때 기억력이 좋았고 영어 단어도 대부분 기억할 수 있었는데, 나이가 들수록 머리숱도 점점 줄어들고 기억력도 점점 나빠지네요. 반복되는 일이 점점 많아져서 그 경험을 바탕으로 이 플러그인을 개발하게 되었습니다.
저도 지난 며칠 동안 플러그인 개발을 처음부터 배우고 있기 때문에 이 글은 초보자가 개발한 플러그인 개발 경로를 완벽하게 기록했습니다. 내용은 주로 실용적인 소개로, 주로 4가지 측면에서 소개됩니다. 기능 설계부터 출시까지 전체 플러그인의 전체 과정을 보여줍니다.
기능적 디자인
환경 구축
플러그인 기능 개발
플러그인 패키징 및 출시
[추천 학습: "vscode 입문 튜토리얼 》】
이 기능은 주로 중국어에서 영어로 번역하고, 다른 언어를 중국어로 번역하는 두 가지 기능을 가지고 있습니다.
중국어 변수를 번역된 영어 변수로 대체하려면 시나리오가 필요합니다. 해결은 일상적인 개발에서 자주 접하는 시나리오입니다. "영어 막힘"
단어 번역, 자동으로 다양한 언어를 중국어로 번역합니다. 익숙하지 않은 단어를 접하고 그 의미를 알지 못해 효율성에 영향을 미칩니다
을 시작하는 첫 번째 단계는 스캐폴딩
yo을 설치하는 것입니다. 자세한 내용은 https://github.com/Microsoft/vscode-generator-code에서 확인하세요//安装
yarn global add yo generator-code
vsce를 설치하면 vsce를 사용하여 개발된 코드를 .vsix 파일로 패키징할 수 있습니다. Microsoft 플러그인 스토어에 쉽게 업로드하거나 로컬로 설치할 수 있는 접미사
yarn global add vsce
프로젝트 생성 및 초기화, 상황에 맞게 초기화 정보를 입력하세요
//初始化生成项目 yo code
Run and Debugging
패널로 이동하여Run Extention을 클릭하거나 단축키
F5을 누르면 Mac에서 터치 바의 디버그 버튼을 직접 클릭할 수 있습니다
연 후 새 vscode 창이 열립니다. 이 새로운 창은 테스트 환경입니다(확장 개발 호스트). 인쇄된 메시지는 이전 창의 디버그 콘솔에 있습니다. 예를 들어, 내장된 예제는 다음과 같습니다. 새 창cmd/ctrl+shift+p
에Hello world를 입력하면 이전 창의 콘솔에 일부 정보가 출력됩니다
이 시점에서, 개발 준비 환경이 준비되었으며 다음 단계는 정식 플러그인 기능 개발을 시작하는 것입니다플러그인 기능 개발
플러그인 개발에는 두 가지 중요한 파일이 있는데 하나는package.json입니다. 다른 하나는
extension.jspackage.json
ActivationEvents 입니다. Extension.js가 활성화됩니다. 일반적인 것은 onLanguage
,onCommand...입니다. 자세한 내용은 vscode 문서 activateEvents(https://code.visualstudio.com/api/references/activation-events)
main을 참조하세요. 플러그인 정문을 나타냅니다
contributes用来注册命令(commands),绑定快捷键(keybindings),**配置设置项(configuration)**等等,更多可配置项可看文档(https://code.visualstudio.com/api/references/contribution-points)
extention.js
extention.js主要作用是作为插件功能的实现点,通过active,deactive函数,以及vscode提供的api以及一些事件钩子来完成插件功能的开发
实现翻译功能
翻译这里主要是使用了两个服务,谷歌和百度翻译。
谷歌翻译参考了别人的做法,使用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;
百度翻译,百度翻译的比较简单,申请服务,获得appid和key,然后构造请求url直接请求就行
不知道如何申请的,可查看我之前的一篇文章 Electron+Vue从零开始打造一个本地文件翻译器 进行申请
https://juejin.cn/post/6899581622471884813
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插件商店,当然也能本地安装使用。
传入商店
发布到线上需要到 微软插件商店管理页面(https://marketplace.visualstudio.com/manage/createpublisher),创建发布者信息,如果没有微软账号,需要去申请。
创建完成后,选择发布到vscode商店
本地安装
本地是可以直接安装.vsix后缀插件的,找到插件菜单
选择从VSIX安装,安装上面打包的插件就好了
이 개발 시간의 대부분을 영어 문서를 읽는 데 보냈고 영어는 나중에 더 많이 배울 수 있기를 바랍니다. 당신이 만든 이 플러그인을 사용하는 횟수는 점점 줄어들 것입니다. 프로젝트는 오픈 소스였으며 사용 지침 및 소스 코드 포털(https://github.com/Kerinlin/translate-variable)
VSCode에 대한 자세한 내용을 보려면 vscode 튜토리얼을 방문하세요! !
위 내용은 vscode 변수 번역 플러그인 개발을 처음부터 차근차근 가르쳐드립니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!