本文目录
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 code
CLI 会询问一些问题。第一个问题是这样的:
这里要根据插件的具体功能来选择。对于我们的 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:
这个对象的属性和方法应该可以完成我们的工作。
这样思路就比较清楚了,注册快捷键和命令,选择当前光标所在的段落,发送文本到 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.commandscontributes.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 文件里调用。
YAML 代码如下:
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 万多个图标的扩展包,足够你挑一个了:
将做好的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