Posted on: 2021 May 02
词到用时方恨少,这句话说的就是第一次写论文的时候的我。口语表达需要用到的词,真的比书面表达的词少很多。但是,在平时的论文阅读的过程中,总能发现一些「陌生词」,而这些陌生词往往用得非常的巧妙,在英语语境中,是更能precisely 地表达语义。我就是一直在思考这个问题,如何才能收藏起来以便以后为我所用呢?
本文就是提出这么一个免费的解决方案,来帮助我们摘抄陌生词,形成自己的词典,并能方便地查看和温故。当然,我们的目标还是希望能够多平台覆盖和同步的,不然实用意义就变少了。
首先要解决的第一个问题,就是如何把想要的单词摘抄并存储起来。这里,很多的单词翻译软件都有自己的单词本功能,但是同步功能基本上都是付费的功能。对我这种低频使用者而言,不太实惠。
我考虑了使用在线表格作为后端的存储,Airtable 就是这个领域的佼佼者了,并且它提供的基于 HTTP 的 API 接口方便和其他的软件交互完成存储和访问,就非常地符合我们的需求。
你需要做的准备有以下的几点:
# Name - Field Type
Query - Long text
Translation - Single line text
isWord - Check box
Explains - Long text
US-Phonetic - Single line text
UK-Phonetic - Single line text
Count - Number - Integer
访问 https://airtable.com/api,点击对应的 Table,选择左侧的 AUTHENTICATION,点击右上角的 show API key,你就能找到对应的 <your table id>
和 <your app token>
。
当选择了「陌生词」后,我们需要做的是存储它原词的同时,存储其对应的中文翻译。当然,这个版本也可以离线完成,通过 Airtable Automation 或者 Serverless Function 可以做到。但是前者需要付费订阅 Pro,后者使得平台更复杂化了。后来实际考虑,其实这个也放在本地完成就是可以的,从某种程度上来说也更简单一些。
首先,要做的获取有道智云 AI 开放平台的接口密钥。你需要访问:https://ai.youdao.com/doc.s#guide ,根据上述的指南注册开发者账号并获取密钥,上述官方有详细的图解步骤,我在这里就不重复了。关于价格,有道云文本翻译服务是48元/百万字符,每月调用量清零。换句话说,每月100万字符以下翻译免费,对于我们翻译几个单词来说,实在是绰绰有余。更详细的价格可以参考官方定价文档。
最终你获得的有两个关键的字符串:<your app key>
and your app scret
, 这里的 app 指的是有道智云 AI 开放平台上面的一个实例。
好了,万事俱备,只欠东风。接下来,就是通过 HTTP 接口,把「陌生词」存储到 Airtable 中。
这里,由于选择了本地调用有道云的接口,因此需要比较复杂的 JS 代码才能构建出 API 访问的参数,否则其实可以使用更轻量级的 捷径app 来完成这样的动作的。所以,我终于不得不选择 JSBox 作为 Client 调用接口。对于读者来说的好消息是,JSBox 免费下载且 1.x 版本的功能免费且我们只需要用到 1.x 版本的功能。对于我来说,我开发这个 workflow 专门买了JSBox,—_—
关于调用的代码,我已经全部开源并放到这个 Gist 里面了,文末我也放置了一份相同的作为附录,以便不方便访问 Gist 的读者们访问。
在 JSBox 里面,选择 New Project,名称可以自定义,Type 选择 JSBox script 即可,把开源的代码粘贴到里面,并替换其中的对应的密钥即可。
<your app key> -> 你的有道云应用ID
<your app secret> -> 你的有道云应用密钥
<your table id> -> 你的Airtable的表格ID
<your app token> -> 你的Airtable的API Token
为了在调用菜单里简化一级跳转逻辑,我们还需要借助 捷径APP 调用 JSBox
由于我阅读论文的设备是 iPad,因此这里用 iPad 做个演示。在你喜欢的论文阅读软件中选中相应的单词/短语,通过系统分享菜单选择对应的捷径。
对应的 Gallery View 的单词卡效果是这样子的,而且对响应式网站有适配,写论文的时候它给了我很大的帮助。
理论上,这个 worflow 也支持句子摘抄和翻译,新增一个 Gallery View 和 Filtering 就可以,只不过我没有这个需求而已。
好了,以上就是全部的内容。感谢阅读。
const CryptoJS = require("crypto-js")
function uuidv4() {
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8);
return v.toString(16);
});
}
var entrance = null
var query = null
if ($context.text) {
query = $context.text
entrance = 'jsbox'
} else if (Object.keys($context.query).length) {
query = $context.query.word
entrance = 'shortcut'
} else {
console.log('Please execute via share sheet')
$context.close()
$app.close()
}
console.log('query', query)
const appkey = '<your app key>'
const salt = uuidv4()
console.log('salt:', salt)
const curtime = (Math.round(new Date().getTime() / 1000)).toString()
console.log('curtime:', curtime)
const appsecret = '<your app secret>'
var input = query
if (query && query.length > 20) {
input = query.slice(0, 10) + query.length + query.slice(query.length - 10, query.length)
}
console.log('input:', input)
var concat = appkey + input + salt + curtime + appsecret
console.log('concat:', concat)
const sign = CryptoJS.SHA256(concat).toString()
console.log('sign:', sign)
var formData = {
q: query,
from: 'en',
to: 'zh-CHS',
appKey: appkey,
salt: salt,
sign: sign,
signType: 'v3',
curtime: curtime,
strict: 'true'
}
if (query) {
$http.post({
url: "https://openapi.youdao.com/api",
form: formData,
handler: function (resp) {
var ydreq = resp.data
console.log(ydreq)
var field = {
Query: ydreq['query'],
Translation: ydreq['translation'].join('\n'),
isWord: ydreq['isWord'],
Count: 1
}
if (ydreq['isWord']) {
field['Explains'] = ydreq['basic']['explains'].join('\n')
field['US-Phonetic'] = ydreq['basic']['us-phonetic']
field['UK-Phonetic'] = ydreq['basic']['uk-phonetic']
}
$http.post({
url: "https://api.airtable.com/v0/<your table id>/Main",
header: {
'Authorization': 'Bearer <your app token>',
'Content-Type': 'application/json'
},
body: {
records: [{
fields: field
}]
},
handler: function(resp) {
var atreq = resp.data
console.log(atreq)
var display = 'Created: ' + atreq.records[0].id
if (entrance === 'shortcut') {
$intents.finish(display)
} else if (entrance === 'jsbox') {
$ui.preview({
title: "AirTable API Response",
text: display
});
}
}
});
}
})
}
Contact me if you have any comments or questions about this aritcle.
Appriciate if you would like to support me if the article really helps you.