从零开始开发 VS Code 插件之 Translator Helper
本文目錄
Translator Helper 介紹
開發(fā)概述
創(chuàng)建第一個VS Code Extension
需求分析
操作文本
調(diào)用Google Translation API
實(shí)現(xiàn)核心功能
配置命令
插件配置
測試插件
打包插件
發(fā)布插件
CI/CD
Icon及README
小結(jié)
Translator Helper 介紹
微軟 Docs 網(wǎng)站上線之后,我發(fā)現(xiàn)很多中文內(nèi)容是由機(jī)器翻譯的,可讀性比較差。2017 年開始我參與了中文文檔的本地化工作,對機(jī)器翻譯的文本進(jìn)行校對。Docs 的內(nèi)容全部托管在 GitHub 上,參與者可以 fork 倉庫后進(jìn)行修改,然后提交 PR。在此過程中,我寫了一個一鍵翻譯的 VS Code Extension,極大的提高了翻譯效率。圣誕節(jié)假期正好有空,完善了一下代碼,并添加了圖標(biāo)、README 等內(nèi)容,正式發(fā)布到 VS Code 的 Marketplace 里了。在此介紹一下該插件的開發(fā)過程,希望更多人能參與到本地化工作中,為其他開發(fā)者提供高質(zhì)量的中文內(nèi)容。
之前我寫過一篇如何參與 Docs 本地化的文章,詳見:如何向微軟 Docs 和本地化社區(qū)提交翻譯貢獻(xiàn)
還有一篇文章介紹了這個插件的使用方式:提高文檔翻譯效率神器:VS Code 插件之 Translator Helper
在開發(fā)此插件之前,為了方便翻譯,我一般是把段落復(fù)制到 Google 翻譯里,翻譯成中文復(fù)制粘貼過來,再手動修改。但選擇>復(fù)制>粘貼的過程非常無趣,于是想找一個簡單的方法自動完成這個動作。但是找遍了 VS Code Markedplace 里的翻譯插件,大都是在狀態(tài)欄提示翻譯,或懸浮框顯示翻譯,沒有一個能完成這個動作。于是只好自己動手寫一個了。好在 VS Code 提供了非常完善的開發(fā)文檔,我花了兩三個小時就完成了主要功能的開發(fā)。其實(shí)對一個完善的插件來說,找 icon、寫文檔、做示例也相當(dāng)費(fèi)時間,于是拖到最近才正式發(fā)布。
我現(xiàn)在的做法是,同時開兩個 VS Code,一個是英文原版的倉庫,一個是中文倉庫,兩邊對照看。使用 Translator Helper 可以一鍵翻譯指定段落并插入到英文文本后面,人工校對修改一下即可,翻譯效率大大提高。再也不用在 VS Code 和瀏覽器之間來回復(fù)制粘貼了。
將光標(biāo)定位在一個段落的任意位置,按Alt+T,即可將當(dāng)前段落自動翻譯成中文并插入到該段落后面。
然后可以人工對部分語句進(jìn)行調(diào)整即可。翻譯后的文本也會自動選中,方便進(jìn)行復(fù)制粘貼等操作。使用了這個小工具后,翻譯文檔的速度大大提高了。
求五星好評啊?
開發(fā)概述
開發(fā)一個 VS Code Extension 并不難,但開發(fā)一個好的Extension不容易。讓我們還是從Hello World開始吧。VS Code 官網(wǎng)提供了詳細(xì)的開發(fā)文檔:https://code.visualstudio.com/api。
首先我們要確定我們要實(shí)現(xiàn)的功能屬于哪個分類:
Theming[1] :使用顏色或圖標(biāo)主題改變 VS Code 的外觀
Extending the Workbench[2] :在 UI 中添加自定義組件、視圖
Webview Guide[3]:創(chuàng)建 WebView 來顯示 HTML/CSS/JS 構(gòu)造的自定義頁面
Language Extensions Overview[4]:支持新的編程語言
Debugger Extension Guide[5]:支持調(diào)試某種運(yùn)行時
不同的分類在開發(fā)中有不同的側(cè)重點(diǎn),使用到的 API 也不一樣。比如我們要實(shí)現(xiàn)的翻譯功能,屬于 Extending the Workbench 這一類。接下來給大家介紹一下Translator Helper是怎么開發(fā)出來的。
創(chuàng)建第一個Extension
可以在這里找到詳細(xì)的入門教程:https://code.visualstudio.com/api/get-started/your-first-extension
首先,使用npm安裝Yeoman和VS Code Extension Generator:
npm install -g yo generator-code導(dǎo)航到要創(chuàng)建 Extension 的目錄,使用如下命令創(chuàng)建項(xiàng)目:
yo codeCLI 會詢問一些問題。第一個問題是這樣的:
創(chuàng)建項(xiàng)目這里要根據(jù)插件的具體功能來選擇。對于我們的 hello world 項(xiàng)目,可以選擇使用TypeScript來開發(fā)。完整的輸入輸出如下所示:
# ? What type of extension do you want to create? New Extension (TypeScript) # ? What's the name of your extension? HelloWorld ### Press <Enter> to choose default for all options below #### ? What's the identifier of your extension? helloworld # ? What's the description of your extension? LEAVE BLANK # ? Initialize a git repository? Yes # ? Which package manager to use? npmcode ./helloworld這樣就創(chuàng)建了一個名為helloworld的 VS Code 插件,并使用 VS Code 打開了項(xiàng)目。可以直接按F5來運(yùn)行,這樣會打開一個新的加載了該插件的 VS Code 窗口。
按F1或Ctrl+Shift+P,輸入Hello World命令,可以看到右下角彈出了一個消息框并顯示Hello World。這說明插件已經(jīng)正常工作了。
關(guān)鍵的代碼都在extension.ts文件里。打開該文件看一下:
// The module 'vscode' contains the VS Code extensibility API // Import the module and reference it with the alias vscode in your code below import * as vscode from 'vscode';// this method is called when your extension is activated // your extension is activated the very first time the command is executed export function activate(context: vscode.ExtensionContext) {// Use the console to output diagnostic information (console.log) and errors (console.error)// This line of code will only be executed once when your extension is activatedconsole.log('Congratulations, your extension "helloworld" is now active!');// The command has been defined in the package.json file// Now provide the implementation of the command with registerCommand// The commandId parameter must match the command field in package.jsonlet disposable = vscode.commands.registerCommand('extension.helloWorld', () => {// The code you place here will be executed every time your command is executed// Display a message box to the uservscode.window.showInformationMessage('Hello World!');});context.subscriptions.push(disposable); }// this method is called when your extension is deactivated export function deactivate() {}代碼很簡單,就是使用vscode.commands.registerCommand()方法注冊了一個命令,然后使用vscode.window.showInformationMessage()方法來顯示右下角的消息框。
需求分析
我們的需求是,按某個快捷鍵,將當(dāng)前段落翻譯成中文,然后插入到該段落之后。讓我們來查一下 VS Code支持的API。
在https://code.visualstudio.com/api/references/vscode-api這個頁面可以查看所有VS Code 支持的 API。我們要操作當(dāng)前的編輯器界面,實(shí)現(xiàn)自動選擇段落、插入文本等操作,所以我找到了TextEditor這個對象:https://code.visualstudio.com/api/references/vscode-api#TextEditor:
TextEditor API這個對象的屬性和方法應(yīng)該可以完成我們的工作。
這樣思路就比較清楚了,注冊快捷鍵和命令,選擇當(dāng)前光標(biāo)所在的段落,發(fā)送文本到 Google 翻譯 API 進(jìn)行翻譯,將返回的文本插入到段落之后。
操作文本
我封裝了一個DocService來完成選擇段落、插入文本等與編輯器操作相關(guān)的功能:
class DocService {editor: vscode.TextEditor | undefined;setCurrentEditor(): void {this.editor = vscode.window.activeTextEditor;}getParagraph(): string {if (this.editor !== undefined) {let startLine = this.editor.selection.start.line;let endLine = this.editor.selection.end.line;const endCharacter = this.editor.document.lineAt(endLine).text.length;this.editor.selection = new vscode.Selection(startLine, 0, startLine, endCharacter);var paragraph = this.editor.selection;let result = this.editor.document.getText(paragraph);if (result !== undefined) {return result;}else {return '';}} else {return '';}}getSelectionText(): string {if (this.editor !== undefined) {return this.editor.document.getText(this.editor.selection);} else {return '';}}insertText(text: string): void {if (this.editor !== undefined) {let end = this.editor.selection.end;this.editor.edit(editBuilder => {editBuilder.insert(end, '\n');editBuilder.insert(end, text);}).then(success => {if (success && this.editor !== undefined) {let end = this.editor.selection.end;this.editor.selection = new vscode.Selection(end, end);let startLine = this.editor.selection.start.line;let endLine = this.editor.selection.end.line;const endCharacter = this.editor.document.lineAt(endLine).text.length;this.editor.selection = new vscode.Selection(startLine, 0, startLine, endCharacter);}});}}調(diào)用 Google Translation API
這個就是一般的發(fā)送 Http 請求了。可以使用一個 npm 包@vitalets/google-translate-api來實(shí)現(xiàn)。代碼如下:
class GoogleTranslationService implements ITranslatorService {async translate(text: string, source: string, target: string): Promise<string> {const service = googleTranslate;let result = await service(text, { from: source, to: target });return result.text;} }實(shí)現(xiàn)核心功能
現(xiàn)在我們可以把這些功能組合起來了。照著 Hello World 例子里的代碼抄一個:
let translateInsert = vscode.commands.registerCommand('translatorHelper.translateInsert', async () => {// The code you place here will be executed every time your command is executeddocService.setCurrentEditor();const text = docService.getParagraph();try {if (text.trim() !== '') {let result = await servie.translate(text, source, target);docService.insertText(result);}} catch (error) {vscode.window.showErrorMessage(`Error occurs. ${error.message}`);}});代碼很容易理解,首先調(diào)用DocService獲取當(dāng)前段落,自動選擇段落文本,然后發(fā)送到 Google 翻譯 API,把返回的翻譯文本插入到該段落之后。
配置命令
只在代碼里注冊命令是無法起作用的,我們還需要修改package.json文件來配置新添加的命令。VS Code 插件開發(fā)中有一個很重要的概念叫Contribution Points[6],詳細(xì)的 API 可參考此文檔:https://code.visualstudio.com/api/references/contribution-points。Contribution Points 是在package.json文件中的一系列配置,用來聲明插件的一些屬性,快捷鍵、配置項(xiàng)等也需要在這里進(jìn)行配置。部分代碼如下:
"activationEvents": ["onCommand:translatorHelper.translateInsert"]以及:
"contributes": {"commands": [{"command": "translatorHelper.translateInsert","title": "Translate & Insert"}],"keybindings": [{"command": "translatorHelper.translateInsert","key": "alt+t","when": "editorTextFocus"}],...可以看到,我添加了名為translatorHelper.translateInsert的命令,與注冊命令代碼中的命令名稱是一致的。同時還添加了一個keybindings,當(dāng)按下Alt+T的時候會觸發(fā)這個命令。
contributes.commands: https://code.visualstudio.com/api/references/contribution-points#contributes.commands
contributes.keybindings: https://code.visualstudio.com/api/references/contribution-points#contributes.keybindings
插件配置
翻譯的語言應(yīng)該是可配置的,因此我們需要添加幾個配置項(xiàng)。需要用到的是contributes.configuration。VS Code 把所有插件的配置項(xiàng)統(tǒng)一進(jìn)行管理,提供了統(tǒng)一的 API 來讀取。在package.json的contributes節(jié)里添加如下代碼:
"configuration": {"title": "Translator Helper","properties": {"translatorHelper.api": {"type": "string","default": "google","enum": ["google","google-cn"],"enumDescriptions": ["Google Translation API.","Google Translation API for Chinese users."],"description": "Specify the api to translate the text."},"translatorHelper.sourceLanguage": {"type": "string","default": "en","description": "The source language to be translated."},"translatorHelper.targetLanguage": {"type": "string","default": "zh-CN","description": "The target language."}}}配置項(xiàng)可以是文本、枚舉或其他類型,還可以設(shè)置默認(rèn)值、給枚舉添加描述等,這樣在下拉列表框里就可以看到詳細(xì)的枚舉項(xiàng)描述。詳細(xì)的配置方式可參考:https://code.visualstudio.com/api/references/contribution-points#contributes.configuration。
在代碼中,使用如下方式讀取配置:
let config = vscode.workspace.getConfiguration("translatorHelper");const api = config.api;const source = config.sourceLanguage;const target = config.targetLanguage;這樣一個基本的插件就開發(fā)好了。實(shí)際效果如本文開頭圖片所示。
測試插件
在一個程序的完整開發(fā)流程中,測試也是重要的一個環(huán)節(jié)。在 DevOps 的整個流程中,良好的測試能夠保證程序的功能質(zhì)量。所以我們還需要添加測試。具體步驟可參考此文檔:https://code.visualstudio.com/api/working-with-extensions/testing-extension。
默認(rèn)的項(xiàng)目模板已經(jīng)自帶了完整的測試用例,存放在src/test目錄下。所以我們只需要修改測試用例的部分代碼即可。我在src/test/suite目錄中,添加了一個默認(rèn)的 markdown 文件test.md,里面寫了個"Hello World"。在運(yùn)行測試的時候,自動運(yùn)行 VS Code 載入這個文件,并調(diào)用命令翻譯。部分代碼如下:
test("Should get the correct translation then insert it.", async () => {const uri = vscode.Uri.file(path.join(__dirname + testFileLocation));const document = await vscode.workspace.openTextDocument(uri);const editor = await vscode.window.showTextDocument(document);// Make sure the file is fully loaded before interacting with it.await sleep(200);vscode.commands.executeCommand('extension.translateInsert').then(result => {assert.equal(editor.document.getText(editor.selection).indexOf('你好') > -1, true);});});這段代碼會自動載入文本,并調(diào)用該命令進(jìn)行翻譯,然后獲取新插入的文本,對比翻譯后的內(nèi)容,符合要求即說明翻譯功能正常。
打包插件
在我們開發(fā)前端應(yīng)用時,都會使用某些打包工具壓縮代碼,減小體積。對于 VS Code 插件來說,這也是一個很重要的要求。載入大量零散的 js 文件速度會比較慢。VS Code 建議使用 webpack 來打包。詳細(xì)過程可參考:https://code.visualstudio.com/api/working-with-extensions/bundling-extension
首先要安裝 webpack:
npm i --save-dev webpack webpack-cli還要安裝ts-loader來讓 webpack 支持 TypeScript:
npm i --save-dev ts-loader安裝 webpack 后會在項(xiàng)目中添加一個webpack.config.js文件。此外還要根據(jù)文檔來修改package.json的內(nèi)容,使用 webpack 來進(jìn)行打包:
"scripts": {"vscode:prepublish": "webpack --mode production","webpack": "webpack --mode development","webpack-dev": "webpack --mode development --watch","test-compile": "tsc -p ./","compile": "tsc -p ./","watch": "tsc -watch -p ./","pretest": "npm run compile","test": "node ./out/test/runTest.js","deploy": "vsce publish --yarn"},有前端開發(fā)經(jīng)驗(yàn)的同學(xué)應(yīng)該比較熟悉了,此處不再贅述。
發(fā)布插件
在發(fā)布之前,要安裝vsce工具。這是一個用來打包、發(fā)布、管理 VS Code 插件的一個命令行工具。使用以下命令安裝:
npm install -g vsce在正式發(fā)布之前,還需要在 Marketplace 中注冊一個賬戶,獲取 Access Token,才能用vsce來發(fā)布。注冊方式可參考:https://code.visualstudio.com/api/working-with-extensions/publishing-extension
后面的步驟還有創(chuàng)建 publisher 等。因?yàn)槲臋n已經(jīng)詳細(xì)描述了過程,此處就不復(fù)制了。需要注意,如果沒有創(chuàng)建 Publisher 的話,直接使用命令發(fā)布是無法成功的。
CI/CD
每次手工發(fā)布還是比較繁瑣的,最好集成 Azure DevOps 來做 CI/CD。可以參考此文檔來建立 Pipeline:https://code.visualstudio.com/api/working-with-extensions/continuous-integration。
這里需要注意的是,我們需要用到 Access Token 來進(jìn)行發(fā)布,但這是一個敏感信息,所以需要在 Pipeline 里創(chuàng)建一個 Variable 來保存這個 Token,再在 YAML 文件里調(diào)用。
在Pipeline中創(chuàng)建VariableYAML 代碼如下:
trigger:branches:include: ['master']tags:include: ['*']strategy:matrix:linux:imageName: 'ubuntu-16.04'mac:imageName: 'macos-10.13'windows:imageName: 'vs2017-win2016'pool:vmImage: $(imageName)steps:- task: NodeTool@0inputs:versionSpec: '8.x'displayName: 'Install Node.js'- bash: |/usr/bin/Xvfb :99 -screen 0 1024x768x24 > /dev/null 2>&1 &echo ">>> Started xvfb"displayName: Start xvfbcondition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))- bash: |echo ">>> Compile vscode-test"yarn && yarn compileecho ">>> Compiled vscode-test"cd sampleecho ">>> Run sample integration test"yarn && yarn compile && yarn testdisplayName: Run Testsenv:DISPLAY: ':99.0'- bash: |echo ">>> Publish"yarn deploy -p $(VSCODE_MARKETPLACE_TOKEN)displayName: Publishcondition: and(succeeded(), eq(variables['Agent.OS'], 'Linux'))這里使用了$(VSCODE_MARKETPLACE_TOKEN)來獲取 Access Token的值。在trigger這樣我們就實(shí)現(xiàn)了使用 Azure DevOps 來做自動發(fā)布,每次提交更改后,Pipelines 會自動編譯,并發(fā)布到 Marketplace。
Icon 及 README
哦還有很重要的一個,插件的圖標(biāo)及 README 也是必須的。推薦一個免費(fèi)工具Lunacy,在 Windows 10 商店里就可以下載,這個工具自帶了一些圖標(biāo),還可以下載包含 10 萬多個圖標(biāo)的擴(kuò)展包,足夠你挑一個了:
Lunacy支持下載圖標(biāo)擴(kuò)展包將做好的icon文件放到項(xiàng)目目錄里,在package.json文件中進(jìn)行配置:
"icon": "images/icon.png",此外,還需要在package.json中定義插件的id、顯示名稱、描述、版本、發(fā)布者、倉庫地址、支持的VS Code版本、插件類別等等。還可以給插件添加幾個badges,用來顯示插件的各種狀態(tài),如Pipelines的編譯狀態(tài)、當(dāng)前版本、下載量、評分等等。因?yàn)槎急容^直觀,就不貼文件內(nèi)容了。可參考此文檔:https://code.visualstudio.com/api/references/extension-manifest
README 文件會顯示在 Marketplace 的插件頁面上,要修改自帶的README文件模板詳細(xì)說明插件的作用、使用方法、配置項(xiàng)等,最好制作幾個 Gif 動畫來展示插件可以做什么。制作 Gif 動畫的工具很多,可自行搜索。我使用的是 SNAGIT。
小結(jié)
這只是一個非常非常簡單的 VS Code 插件,但確實(shí)提高了我的工作效率。我還開發(fā)了一個支持 Word 的翻譯插件,思路也是一樣的。當(dāng)手邊沒有趁手的工具時,何不自己打造一個呢,開發(fā)的過程還是非常有樂趣的。希望大家下載插件試用一下,給個五星好評就更好了哈哈 O(∩_∩)O 另外該項(xiàng)目也開源在 GitHub 了:https://github.com/yanxiaodi/vscode-translator-helper。歡迎加星啊
下一步計劃是支持微軟的Cognitive Services API,如果有興趣的話歡迎參與,一起改進(jìn)這個項(xiàng)目。
最后希望大家可以關(guān)注我的公眾號:程序員在新西蘭,了解新西蘭碼農(nóng)的真實(shí)生活。感謝大家閱讀。
了解新西蘭IT行業(yè)真實(shí)碼農(nóng)生活
請長按上方二維碼關(guān)注“程序員在新西蘭”
推薦閱讀
如何向微軟 Docs 和本地化社區(qū)提交翻譯貢獻(xiàn)
提高文檔翻譯效率神器:VS Code 插件之 Translator Helper
為WPF, UWP 及 Xamarin實(shí)現(xiàn)一個簡單的消息組件
【手把手教程】如何讓你的求職簡歷敲開新西蘭雇主的大門(文末送福利)
再不拼老命我們就真老了——大齡碼農(nóng)DIY新西蘭技術(shù)移民全記錄
移民路上為什么別人總能得到更多的信息,今天知道真相還不算太晚
雅思之路——只有絕境沒有捷徑
身在中國,如何應(yīng)對海外公司的電話面試?
參考資料
[1]
Theming: https://code.visualstudio.com/api/extension-capabilities/theming
[2]Extending the Workbench: https://code.visualstudio.com/api/extension-capabilities/extending-workbench
[3]Webview Guide: https://code.visualstudio.com/api/extension-guides/webview
[4]Language Extensions Overview: https://code.visualstudio.com/api/language-extensions/overview
[5]Debugger Extension Guide: https://code.visualstudio.com/api/extension-guides/debugger-extension
[6]Contribution Points: https://code.visualstudio.com/api/references/contribution-points
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
以上是生活随笔為你收集整理的从零开始开发 VS Code 插件之 Translator Helper的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EntityFramework Core
- 下一篇: 为什么说云原生会成为未来企业技术变迁的趋