根据 Google Translate 开发的 Popclip 插件,响应速度是我用过的翻译插件里最快的,可以选择翻译源(.com .cn),有多种国外语言可以翻译,点击按钮后在屏幕右上方显示译文。 ——MAC玩儿法报道
继上次自己开发 Alfred Workflow 后,这次我又盯上了 popclip。
事因是自己一直渴望有一种完美的谷歌翻译使用方式,目的是用鼠标选中待选定句子,然后弹出谷歌翻译内容。无奈一直没人做,只好自己动手实现。
展示一下最终的自制 popclip 谷歌翻译插件使用效果:
首先设置里面有几个选项可以设置:
Google Translate Site
: 可选 translate.google.cn
和 translate.google.com
作为翻译服务器,前者给墙内的朋友使用,后者则是给墙外的使用。Destination Language
和 Source Language
: 目标外语 (destination language) 和母语 (source language)。程序将对划中的语言进行检测,若检测的语言非母语,则翻译为母语;若检测的语言为母语,则翻译为目标外语。使用效果GIF:
英译中:
中译英:
确实很方便~
其实 Mac 上的翻译方案很多,在本插件出来之前,个人觉得最好用的当属 Translate Tab了,鼠标选中待翻译字段,点击其 popclip 图标,然后通知栏弹出翻译:
然而 Translate Tab 也是有缺点的:
translate.google.com
,不翻墙无法使用。我给开发者发邮件,开发者回复道 Theoretically google must redirect app to the domain according to geolocation. I will check this moment
,然后我再回复就没有后文了。。。如果用 Surge 的 URL Rewrite 功能设置translate.google.com
跳转到 translate.google.cn
,需要开启 MitM,比较浪费性能。因为我非常喜欢 popclip 这种形式的小软件,因此决定转入 popclip 插件开发。popclip 官方 API 提供了一种 show-result
形式的 GUI 显示文本,然而效果实在太挫了:只能在屏幕中像下面一样显示一长条,不支持换行,太长了就自动截断了,也是让人很醉:
一开始我使用的方案是采用第三方 cocoaDialog 做结果显示 GUI,做了右上角弹出 Bubble 或者屏幕中央弹出 Msgbox 的方式。使用几个版本后还是抛弃了 cocoaDialog,因为弹出的 MsgBox 30 秒后自动消失,往往翻译结果还没看完就消失了。研究 AppleScript 时发现自带的 osascript 提供了 dialog 的组件显示文本,非常好用。就是 dialog 要显示自定义图标文件全网缺少相关文档,找的方案基本不可行,硬是被自己试出来。。。
如何调用谷歌翻译,我第一反应当然是爬虫,然而用 Chrome 到 translate.google.cn 抓包一看:
上图中的 Request URL 中有一个 tk 值,这个值是根据输入的文字调用 JavaScript 计算的,代码经过了非常复杂的混淆包装,几万行代码就为了混淆一个 tk 值,太可怕了。。。
那就试试谷歌官方的收费 API 吧。于是我去开通了 Google Cloud,绑定了信用卡,然后发现新用户居然赠送 300 美金优惠券(限一年用完)!激动!另外,谷歌翻译官方 API 使用方式也很简单,用自己的 API KEY 组合成这样一个 URL: https://translation.googleapis.com/language/translate/v2?key=' + API_KEY
,然后把 {'target':target_lang, 'q': query}
post 上去,就可以得到结果。不需要科学上网,速度也是超级快。然而谷歌计费方式是每 1 M 个 characters 收费 20 美金,我在写代码过程中就随便小测了一下,居然用了 3 K 个 characters,所以这个 characters 咋算的,字母数么。。。要是分享出去用的人多了,用不了几天 300 美金就花光了。
然后我又去搜索,有没有免费的谷歌翻译 API,果然在知乎找到了: 请问如何调用谷歌翻译API?。用 http://translate.google.cn/translate_a/single?client=gtx&sl=en&tl=zh-CN&dt=t&q=query
的形式去查询就可以免费调用谷歌翻译。难道是漏网之鱼?不一会我就发现,woc,同一 IP 下调用此 API 多次就直接被 Google 给 ban 了!心机你 Google!
好在 GitHub 大神多,一通搜索,有人逆向了谷歌的 tk,还封装好了😅: py-googletrans。好了,既然有现成的轮子了,我就直接用了~
虽说第一次接触 popclip 插件的编写,小试一下,发现还是蛮简单的。写起来比 Alfred Workflow 简单不少,然而没法调试,略蛋疼。全程主要参考了少数派 让剪切板在 OS X 上飞起来:PopClip 插件编写教程 和 popclip 官方 github documentation。
一个 popclip 插件,主要包括三部分:
Icon.png: 图标文件。这么好的功能当然要有个卡哇伊的图标啦。
Script: 脚本文件。执行目标功能。popclip 默认只支持 Shell Script 和 Apple Script。要用其他语言,也只能通过这两种 Script 来调用。
其实,最终实现谷歌翻译的 Python 脚本很简单,一共才 60 余行代码,和上次那个优越加速 Alfred Workflow 几百行相比简单太多,逻辑上也更简单。
首先我的 translate.py
通过 argparse
接受以下几个参数:
--site site_arg --srclang srclang_arg --destlang destlang_arg
然后通过 go.sh
来执行此 py 文件:
/usr/bin/python translate.py --site $POPCLIP_OPTION_SITE --destlang $POPCLIP_OPTION_DESTLANG --srclang $POPCLIP_OPTION_SRCLANG $POPCLIP_URLENCODED_TEXT &
上面的 $POPCLIP_XXX
是 popclip 传递来的变量。以 --site
为例,在上面提到的 Config.plist文件的 Options 中,我设置了一个标题为 Google Translate Site
的多选栏,设定一个变量,其 key
设定为 site
,value
设定为 translate.google.cn
或 translate.google.com
(都是字符串),靠用户来选择使用哪个 value。然后选定的 value 以名为 $POPCLIP_OPTION_SITE
的变量存在于整个插件运行的过程中,可随时调用。同样的,$POPCLIP_OPTION_LANG
代表 Target Foreign Language
那一栏的变量的 value,这里的详细设置参见我的 Config.plist。
然后有必要交代一下最后那个 $POPCLIP_URLENCODED_TEXT
。如何去把鼠标选中的文字转换成一个变量,传递给脚本呢。
起初,我是在 Config.plist 中的 Actions 中是这么设定的:
<key>Before</key>
<string>copy</string>
然后用的变量名为 $POPCLIP_TEXT
。Before 指的是在 popclip 执行主要 action 之前要干的事,这里我设定为 copy
。这样,只要鼠标选中一段文字,将自动执行 Command + C
,同时以 string 的形式保存为一个名叫 $POPCLIP_TEXT
的变量。这样的弊端也是显而易见的,每次鼠标划词都复制文字到剪切板,会扰乱剪切板。因此我改用:
<key>Requirements</key>
<string>copy</string>
根据官方说明,这种情况下的 copy
就不会复制文字到剪切板了。另外之所以用 $POPCLIP_URLENCODED_TEXT
,是因为坑爹的 $POPCLIP_TEXT
,在遇到空格时就会停止复制,比如鼠标选中了 a b c
,其实变量只存了 a
。所以只好用 url-encoded 的形式,最后再用 urllib 库的 unquote 转换为正常的 String。
最后,整体流程框图如下:
整体下来思路还是挺清晰的。然后只需要把以上所有文件放在一个文件夹中,强行加上一个 .popclipext
就大功告成啦~
虽然代码很简单,开源到 GitHub 了,方便一下其他人参考编写 popclip 吧~