从零开始开发 VS Code 插件之 Translator Helper
本文目錄
Translator Helper 介紹
開發概述
創建第一個VS Code Extension
需求分析
操作文本
調用Google Translation API
實現核心功能
配置命令
插件配置
測試插件
打包插件
發布插件
CI/CD
Icon及README
小結
Translator Helper 介紹
微軟 Docs 網站上線之后,我發現很多中文內容是由機器翻譯的,可讀性比較差。2017 年開始我參與了中文文檔的本地化工作,對機器翻譯的文本進行校對。Docs 的內容全部托管在 GitHub 上,參與者可以 fork 倉庫后進行修改,然后提交 PR。在此過程中,我寫了一個一鍵翻譯的 VS Code Extension,極大的提高了翻譯效率。圣誕節假期正好有空,完善了一下代碼,并添加了圖標、README 等內容,正式發布到 VS Code 的 Marketplace 里了。在此介紹一下該插件的開發過程,希望更多人能參與到本地化工作中,為其他開發者提供高質量的中文內容。
之前我寫過一篇如何參與 Docs 本地化的文章,詳見:如何向微軟 Docs 和本地化社區提交翻譯貢獻
還有一篇文章介紹了這個插件的使用方式:提高文檔翻譯效率神器:VS Code 插件之 Translator Helper
在開發此插件之前,為了方便翻譯,我一般是把段落復制到 Google 翻譯里,翻譯成中文復制粘貼過來,再手動修改。但選擇>復制>粘貼的過程非常無趣,于是想找一個簡單的方法自動完成這個動作。但是找遍了 VS Code Markedplace 里的翻譯插件,大都是在狀態欄提示翻譯,或懸浮框顯示翻譯,沒有一個能完成這個動作。于是只好自己動手寫一個了。好在 VS Code 提供了非常完善的開發文檔,我花了兩三個小時就完成了主要功能的開發。其實對一個完善的插件來說,找 icon、寫文檔、做示例也相當費時間,于是拖到最近才正式發布。
我現在的做法是,同時開兩個 VS Code,一個是英文原版的倉庫,一個是中文倉庫,兩邊對照看。使用 Translator Helper 可以一鍵翻譯指定段落并插入到英文文本后面,人工校對修改一下即可,翻譯效率大大提高。再也不用在 VS Code 和瀏覽器之間來回復制粘貼了。
將光標定位在一個段落的任意位置,按Alt+T,即可將當前段落自動翻譯成中文并插入到該段落后面。
然后可以人工對部分語句進行調整即可。翻譯后的文本也會自動選中,方便進行復制粘貼等操作。使用了這個小工具后,翻譯文檔的速度大大提高了。
求五星好評啊?
開發概述
開發一個 VS Code Extension 并不難,但開發一個好的Extension不容易。讓我們還是從Hello World開始吧。VS Code 官網提供了詳細的開發文檔:https://code.visualstudio.com/api。
首先我們要確定我們要實現的功能屬于哪個分類:
Theming[1] :使用顏色或圖標主題改變 VS Code 的外觀
Extending the Workbench[2] :在 UI 中添加自定義組件、視圖
Webview Guide[3]:創建 WebView 來顯示 HTML/CSS/JS 構造的自定義頁面
Language Extensions Overview[4]:支持新的編程語言
Debugger Extension Guide[5]:支持調試某種運行時
不同的分類在開發中有不同的側重點,使用到的 API 也不一樣。比如我們要實現的翻譯功能,屬于 Extending the Workbench 這一類。接下來給大家介紹一下Translator Helper是怎么開發出來的。
創建第一個Extension
可以在這里找到詳細的入門教程:https://code.visualstudio.com/api/get-started/your-first-extension
首先,使用npm安裝Yeoman和VS Code Extension Generator:
npm install -g yo generator-code導航到要創建 Extension 的目錄,使用如下命令創建項目:
yo codeCLI 會詢問一些問題。第一個問題是這樣的:
創建項目這里要根據插件的具體功能來選擇。對于我們的 hello world 項目,可以選擇使用TypeScript來開發。完整的輸入輸出如下所示:
# ? 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這樣就創建了一個名為helloworld的 VS Code 插件,并使用 VS Code 打開了項目。可以直接按F5來運行,這樣會打開一個新的加載了該插件的 VS Code 窗口。
按F1或Ctrl+Shift+P,輸入Hello World命令,可以看到右下角彈出了一個消息框并顯示Hello World。這說明插件已經正常工作了。
關鍵的代碼都在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()方法來顯示右下角的消息框。
需求分析
我們的需求是,按某個快捷鍵,將當前段落翻譯成中文,然后插入到該段落之后。讓我們來查一下 VS Code支持的API。
在https://code.visualstudio.com/api/references/vscode-api這個頁面可以查看所有VS Code 支持的 API。我們要操作當前的編輯器界面,實現自動選擇段落、插入文本等操作,所以我找到了TextEditor這個對象:https://code.visualstudio.com/api/references/vscode-api#TextEditor:
TextEditor API這個對象的屬性和方法應該可以完成我們的工作。
這樣思路就比較清楚了,注冊快捷鍵和命令,選擇當前光標所在的段落,發送文本到 Google 翻譯 API 進行翻譯,將返回的文本插入到段落之后。
操作文本
我封裝了一個DocService來完成選擇段落、插入文本等與編輯器操作相關的功能:
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);}});}}調用 Google Translation API
這個就是一般的發送 Http 請求了。可以使用一個 npm 包@vitalets/google-translate-api來實現。代碼如下:
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;} }實現核心功能
現在我們可以把這些功能組合起來了。照著 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}`);}});代碼很容易理解,首先調用DocService獲取當前段落,自動選擇段落文本,然后發送到 Google 翻譯 API,把返回的翻譯文本插入到該段落之后。
配置命令
只在代碼里注冊命令是無法起作用的,我們還需要修改package.json文件來配置新添加的命令。VS Code 插件開發中有一個很重要的概念叫Contribution Points[6],詳細的 API 可參考此文檔:https://code.visualstudio.com/api/references/contribution-points。Contribution Points 是在package.json文件中的一系列配置,用來聲明插件的一些屬性,快捷鍵、配置項等也需要在這里進行配置。部分代碼如下:
"activationEvents": ["onCommand:translatorHelper.translateInsert"]以及:
"contributes": {"commands": [{"command": "translatorHelper.translateInsert","title": "Translate & Insert"}],"keybindings": [{"command": "translatorHelper.translateInsert","key": "alt+t","when": "editorTextFocus"}],...可以看到,我添加了名為translatorHelper.translateInsert的命令,與注冊命令代碼中的命令名稱是一致的。同時還添加了一個keybindings,當按下Alt+T的時候會觸發這個命令。
contributes.commands: https://code.visualstudio.com/api/references/contribution-points#contributes.commands
contributes.keybindings: https://code.visualstudio.com/api/references/contribution-points#contributes.keybindings
插件配置
翻譯的語言應該是可配置的,因此我們需要添加幾個配置項。需要用到的是contributes.configuration。VS Code 把所有插件的配置項統一進行管理,提供了統一的 API 來讀取。在package.json的contributes節里添加如下代碼:
"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."}}}配置項可以是文本、枚舉或其他類型,還可以設置默認值、給枚舉添加描述等,這樣在下拉列表框里就可以看到詳細的枚舉項描述。詳細的配置方式可參考: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;這樣一個基本的插件就開發好了。實際效果如本文開頭圖片所示。
測試插件
在一個程序的完整開發流程中,測試也是重要的一個環節。在 DevOps 的整個流程中,良好的測試能夠保證程序的功能質量。所以我們還需要添加測試。具體步驟可參考此文檔:https://code.visualstudio.com/api/working-with-extensions/testing-extension。
默認的項目模板已經自帶了完整的測試用例,存放在src/test目錄下。所以我們只需要修改測試用例的部分代碼即可。我在src/test/suite目錄中,添加了一個默認的 markdown 文件test.md,里面寫了個"Hello World"。在運行測試的時候,自動運行 VS Code 載入這個文件,并調用命令翻譯。部分代碼如下:
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);});});這段代碼會自動載入文本,并調用該命令進行翻譯,然后獲取新插入的文本,對比翻譯后的內容,符合要求即說明翻譯功能正常。
打包插件
在我們開發前端應用時,都會使用某些打包工具壓縮代碼,減小體積。對于 VS Code 插件來說,這也是一個很重要的要求。載入大量零散的 js 文件速度會比較慢。VS Code 建議使用 webpack 來打包。詳細過程可參考: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 后會在項目中添加一個webpack.config.js文件。此外還要根據文檔來修改package.json的內容,使用 webpack 來進行打包:
"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"},有前端開發經驗的同學應該比較熟悉了,此處不再贅述。
發布插件
在發布之前,要安裝vsce工具。這是一個用來打包、發布、管理 VS Code 插件的一個命令行工具。使用以下命令安裝:
npm install -g vsce在正式發布之前,還需要在 Marketplace 中注冊一個賬戶,獲取 Access Token,才能用vsce來發布。注冊方式可參考:https://code.visualstudio.com/api/working-with-extensions/publishing-extension
后面的步驟還有創建 publisher 等。因為文檔已經詳細描述了過程,此處就不復制了。需要注意,如果沒有創建 Publisher 的話,直接使用命令發布是無法成功的。
CI/CD
每次手工發布還是比較繁瑣的,最好集成 Azure DevOps 來做 CI/CD。可以參考此文檔來建立 Pipeline:https://code.visualstudio.com/api/working-with-extensions/continuous-integration。
這里需要注意的是,我們需要用到 Access Token 來進行發布,但這是一個敏感信息,所以需要在 Pipeline 里創建一個 Variable 來保存這個 Token,再在 YAML 文件里調用。
在Pipeline中創建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這樣我們就實現了使用 Azure DevOps 來做自動發布,每次提交更改后,Pipelines 會自動編譯,并發布到 Marketplace。
Icon 及 README
哦還有很重要的一個,插件的圖標及 README 也是必須的。推薦一個免費工具Lunacy,在 Windows 10 商店里就可以下載,這個工具自帶了一些圖標,還可以下載包含 10 萬多個圖標的擴展包,足夠你挑一個了:
Lunacy支持下載圖標擴展包將做好的icon文件放到項目目錄里,在package.json文件中進行配置:
"icon": "images/icon.png",此外,還需要在package.json中定義插件的id、顯示名稱、描述、版本、發布者、倉庫地址、支持的VS Code版本、插件類別等等。還可以給插件添加幾個badges,用來顯示插件的各種狀態,如Pipelines的編譯狀態、當前版本、下載量、評分等等。因為都比較直觀,就不貼文件內容了。可參考此文檔:https://code.visualstudio.com/api/references/extension-manifest
README 文件會顯示在 Marketplace 的插件頁面上,要修改自帶的README文件模板詳細說明插件的作用、使用方法、配置項等,最好制作幾個 Gif 動畫來展示插件可以做什么。制作 Gif 動畫的工具很多,可自行搜索。我使用的是 SNAGIT。
小結
這只是一個非常非常簡單的 VS Code 插件,但確實提高了我的工作效率。我還開發了一個支持 Word 的翻譯插件,思路也是一樣的。當手邊沒有趁手的工具時,何不自己打造一個呢,開發的過程還是非常有樂趣的。希望大家下載插件試用一下,給個五星好評就更好了哈哈 O(∩_∩)O 另外該項目也開源在 GitHub 了:https://github.com/yanxiaodi/vscode-translator-helper。歡迎加星啊
下一步計劃是支持微軟的Cognitive Services API,如果有興趣的話歡迎參與,一起改進這個項目。
最后希望大家可以關注我的公眾號:程序員在新西蘭,了解新西蘭碼農的真實生活。感謝大家閱讀。
了解新西蘭IT行業真實碼農生活
請長按上方二維碼關注“程序員在新西蘭”
推薦閱讀
如何向微軟 Docs 和本地化社區提交翻譯貢獻
提高文檔翻譯效率神器:VS Code 插件之 Translator Helper
為WPF, UWP 及 Xamarin實現一個簡單的消息組件
【手把手教程】如何讓你的求職簡歷敲開新西蘭雇主的大門(文末送福利)
再不拼老命我們就真老了——大齡碼農DIY新西蘭技術移民全記錄
移民路上為什么別人總能得到更多的信息,今天知道真相還不算太晚
雅思之路——只有絕境沒有捷徑
身在中國,如何應對海外公司的電話面試?
參考資料
[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
創作挑戰賽新人創作獎勵來咯,堅持創作打卡瓜分現金大獎總結
以上是生活随笔為你收集整理的从零开始开发 VS Code 插件之 Translator Helper的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EntityFramework Core
- 下一篇: 为什么说云原生会成为未来企业技术变迁的趋