Skip to content

A personal (and brutal) assistant program for English webpage reading

Notifications You must be signed in to change notification settings

HaloWang/english_flow

Repository files navigation

EnglishFlow

解决什么问题

  1. 我需要经常 StackOverflow, 英文文章, 英文文档, 但其中有不少英语单词我不知道什么意思
  2. 英文社区也有很多有趣的东西(比如 reddit.ProgrammerHumor)
  3. 我希望能逐渐提升自己的英语水平, 理解英文使用者的"脑回路"

但是

  1. 我经常忘记英语单词的意思, 或不敢确认一个单词的意思
  2. 当前各种查单词的应用/网页对于我来说都有些繁琐, 心智负担较大
  3. 使用全文翻译软件, 会导致很多关键信息的错误和丢失, 心智负担较大
  4. 这些应用不能完全满足需求
    • Google Translate
    • DeepL
    • Youdao
    • Google Search: 'define FooBar'
    • Google Search: 'pronounce FooBar'

所以我希望有一种功能:

  1. 不破坏当前英文的上下文
    • 不离开当前网页, 有时候使用某些工具全文翻译还不如不翻译, 很多上下文都丢失了
  2. 快速获取中文释义
    • 最快的方式当然是眼球追踪, 但当前最优的方式显然是跟随鼠标
  3. 顺便优化一下我自己查单词的体验
    • 使用快捷键打(shift+D)开以选中单词(句子)为 query keyword 的多个网页(deepl, google translate, youdao, etc), 同时发音, 或进行更多的操作
  4. 重要的, 下次就当我在任何网页再看到这个单词, 我就不用再进行第 3 步操作了, 直至不需要像地 2 步那样需要使用中文做转译, 心智负担减轻了 🎉

所以我写了这个叫做 EnglishFlow (以下简称 EF)的脚本

功能展示

StackOverflow Github Wikipedia React RFC&IETF

使用流程

前置条件

  • 需要在电脑上安装 node+npm+yarn
    • 当前 EF 不支持 node 12 之前的 node 环境
  • 需要编辑器, 推荐 vscode
  • 一丁点儿 yaml 书写能力
  • 在浏览器中安装 TamperMonkey (以下简称 TM) 插件(extension)
    • 一些基础的 TM 知识
    • 请确保 TM 有访问本地文件的权限

开启项目

  1. clone/download
  2. 将本项目移动至桌面(Desktop), 因为当前 EF 默认生成的 tampermonkey 配置文件 dist/TM_entry.js 是从桌面读取脚本的
  3. 运行 yarn
    • 添加依赖
  4. 运行 yarn t, 注意, 该进程为常驻进程, 你需要在新的 terminal 中唤起后续的 yarn 指令
    • 编译 index.ts / server.ts 至 dist 中
  5. 运行 yarn w, 注意, 该进程为常驻进程, 你需要在新的 terminal 中唤起后续的 yarn 指令
    • _w_dict: 监听 src/data/dict.yaml, 编译为 json
    • _w_profile: 监听 src/data/profile.yaml, 编译为 json
    • _w_style: 监听 src/frontend/style.less, 编译为 css
    • _w_entry: 监听 entry/ef.js, 改写其 @require/@resource
  6. 运行 yarn s, 注意, 该进程为常驻进程
    • 开启 node.js 服务器, 作为运行在 TM 中 EF 的数据源
      • EF 当前使用轮询的方式和服务器交互
  7. 可选: 当你成功运行上述三个指令一次之后, 以后再打开本地环境可以直接使用 yarn tws 同时唤起三个进程

在 TM 中添加脚本

  1. 打开 chrome 或者 edge(with chromium)
  2. 打开 TM, 并确保 google chrome 赋予了 TM 访问本地文件的权限
  3. 在 TM 中选择添加新脚本
  4. 在本项目中找到 dist/TM_entry.js 将其中的内容全选+复制, 并全部替换上一步中 chrome.TM 已经显示的编辑器页面中的代码, 保存
  5. 确保本项目文件夹在电脑的桌面
  6. 刷新页面, 打开某个英文 wikipedia, 你应该能看到 EF 已经在运行了

在 profile.yaml 添加英文网页标识

这一步不是必须的, EF 会检测当前 html 的 lang 属性, 若 lang 的值以 "en" 开始, 则 EF 会自动执行

EF 执行规则
  • 当前 html 的 lang 属性的值以 "en" 开始
  • profile.yaml 中的 sites 字段包含目标网页
  • profile.yaml 中的 siteSpecificConfig 字段包含目标网页
部分字段解释
  • applyTo: 当前网页的 url 如果包含 applyTo 中的字符串, 则使用该 profile
  • rootSelector: 从那些节点开始遍历
  • style: 独特的样式
  • strategies: 脚本执行策略
  • notMatchClassName: 不匹配哪些 className

开始阅读英文网页

这就是我的需求来源

查询单词

  • shift+E, 根据选中的关键字(短语/句子)发音
  • shift+D, 根据选中的关键字(短语/句子)发音, 并设置粘贴板为当前单词, 快捷打开如下网页
    • 有道词典
    • Google Translate
      • 词频信息对我来说比较重要
    • 可自行添加: Google search define XXX
    • 可自行添加: Google search pronounce XXX
    • 可自行添加: Deepl XXX
  • EF 这时会同时查询 google translate 的发音
  • 我经常参考有道词典的联想功能来修饰(删除末尾)我在 dict.yaml 写入的关键字
  • shift+R, 当该单词已经被添加至 dict.yaml 忠厚, 如果鼠标悬停在该单词上, 则发音
  • shift+F, 当该单词已经被添加至 dict.yaml 忠厚, 如果鼠标悬停在该单词上, 则快捷打开如下网页, 同时发音

发音

  • 优先查询 Google 的发音
  • youdao 的发音也可以添加, 但是通常情况下我还是想选 Google
  • 如果没有结果, 或者选中发音的是一个句子, 则使用 SpeechSynthesis API
  • 查询单词的功能不需要在 profile.yaml 中添加对应网站即可运作

也可以查询语句

使用 shift+D 也可以快捷查询语句

添加单词/助记标识/伪词根

  • 🚧 我已经在其中添加了很多单词了

  • 打开 dict.yaml, 添加单词, 我选择使用 | 来分隔同一个单词(伪单词/伪词根)的释义

  • 这种伪词根的概念有点像 Greek/Latin

个人习惯

  • 我个人会尽量合并相同词根的单词
    • NotFull 表示不匹配和该 key 完全相同的单词
      • 比如 foo 匹配 foobar, 而匹配 foo
    • FullMatch 表示仅仅匹配和该 key 完全相同的单词
      • 比如 foo 匹配 foo, 而不匹配 foobar

回到网页

  • 你会发现你刚才添加的标识(关键字)已经同步到网页上了

词典当然应该根据首字母

我会同时编译一份根据 keyword 重新排序的 DONT_USE_dict.yaml 至 dist/data 中, 手动 copy 至 src/data/dict.yaml 即可

自行开发

核心逻辑

开启网页后, EF 会以 profile.yaml 中配置的 rootSelector 为根节点遍历 DOM 中的 Text. 如果发现 Text 中的文本能匹配到 dict.yaml 中添加的 keyword, 则将其替换为名为 eft 的标签

在网页上创建一个 id 为 ef-word-detail-panel 的标签, 用于展示单词的翻译

通过监听 eft 标签的 mouseenter 事件, EF 会使用 #ef-word-detail-panel 在网页上展示对应单词的翻译, 以此来达到助记/提醒的效果. 让我可以不脱离上下文获取英文单词的意思. 并且仅仅在有需要时"背"这个单词(伪词根)

EF 会以轮询的方式请求本地在 localhost:8000 开启的 node.js server, 当 dict.yaml 变更时, EF 会刷新整个页面

网页端主要功能

src/index.ts

样式

style.less

TODO (or not)

  • 尝试使用 flutter + OCR 将 EF 蕴含的逻辑部署到多个客户端
  • 在 pub.dev 中直接 macOS 双指侧滑返回时, 脚本报错
  • 不是不可以在 vscode 中部署
  • BUG: 在 github 写 issue 时, 会导致存在于 issue 和 dict.yaml 中的单词消失掉
    • 编辑 commits 时也会出现, 应该是 github 会直接拿 DOM 中的内容作为(改写) input 的 initial value, 导致被 eft 括起来的内容消失了, 这非常不好, 很侵入性
  • 更高级的匹配, 能省去很多事情, 去冗余, 感觉有点像学习英语的"偏旁部首"
    • 所以是不是应该考虑下处理 string 的算法(题)
    • 不仅仅从头开始匹配单词, 还要从中间, 从末尾
    • "-te, -tion, -tive, -able, -ably, -ability...-e, -ed, -y, -ies, over-, un-, in-, ir-, dis-, con-, com-, em-, pre-, pro-, re-..., trans-, sub-..., a-"
    • 有些单词确实会造成困扰, 比如 hang 匹配到了 change, 我也许可以通过一些规则来处理这个问题, 匹配单词中间的某个词意味着, 前面的部分也是有意义的, 不能是 c.hang.e, 这样的无意义前缀/后缀, 注意, async/sync, asy/sy
    • 感觉真的有必要把计算工作放到服务端, 这样我看网页也舒服了, multi-thread/multi-core 也利用起来了
  • 你早晚要 getWordDetail 写成 async request
    • 前端方面要大改...不能用循序遍历的方法改 Text Node 中的值了, 使用某种 boundingrect 一样的方法?
    • 前端同时发起的网络请求数量似乎有点限制, 合并网络请求?
      • 或许可以在前端筛选单词, 一起发给服务器, 然后服务器集中式地返回对应网页的字典
    • 或许可以为所有的单词添加 <eft>, 因为我当前还没发现有什么性能问题
  • 自己的字典应该加入 .gitignore
    • my.yaml
      • 哪些词汇应该根据我自己的个人情况做特别的解析
    • not.yaml
      • 哪些词汇不应该被解析
    • all.yaml
  • 有一些 space 会被 inline-flex 忽略掉
  • 爬虫?
    • google translate
    • youdao
    • word start with
  • 是否可以自动为你在 dict.yaml 加入的生词添加上下文
    1. 手动选择网页中你想要记忆的句子发送至 localhost (可以使用某种快捷键触发)
    2. 如果句子中存在能匹配到(大概率为 true) dict.yaml 中的 keyword 的单词, 则保存该 keyword 和该 sentence 的关联
    3. 下次再 mouseover this keyword 时, 在网页上展示这个 keyword 关联的 sentences
  • 立即刷新 EF 的快捷键
  • dict.yaml 重命名
  • dict.yaml 自动格式化
  • 直接在 <p> 上覆盖你的 <eft> 要比现在这样修改 Text Node 好上不少
    • 页面重新布局不就有点麻烦了?
    • 要计算文本最终的展示位置
    • 可以更好地兼容 PDF.js? 当前还不是要紧的需求
  • 一部分页面不允许使用 audio 发声
  • 监听 profile.yaml
  • 当 profile.yaml 中添加或减少页面时, 刷新页面
  • profile.yaml 还需要调整
  • Add ef_dict.yaml for better tutorial
  • Add YAML schema to dict.yaml
  • 写 TM 脚本时应该将所有函数解耦合, 不要图省事儿在函数中定义函数
  • 合并 yarn 脚本, 并且去除不同脚本之间的依赖, 是不是要考虑 pipeline/webpack?
  • 在 yaml 中输入的格式有错时, 脚本不应该中断. w_tools 和 serve 不应该有时间上的依赖
  • node 也能播放声音, 某些网站不让播其他 domain 的 mp3, 我可以使用 node 播放声音. 顺带把音频 URL 移动到后端来
  • 播放声音的功能还不是太好
    • 初次播放声音时头部声音丢失
  • 代码还需要优化
  • 现在我暂时没有发现性能问题, 从 taskmgr 看性能占用约为 1%. Chrome devtool Performance 页面我暂时不太会用 😂
  • 是否要添加一些 Greek/Latin 的 reference?
  • EF 也可以针对不同的语言
    • EF 也可以反向"翻译"(当然和 Google Translate 做的事情不一样), 把汉语变成英语

Q&A

为啥不用各种字典 API? chrome extensions

我看了看没有啥我想用的

如果我不想一个一个添加单词怎么办? 我可以直接导入字典吗?

我个人的选择是一个一个的添加, 当然也可以全量添加. 有需要的话可以将 github.ecditc 中的 csv 数据编译为 json ... 当然如果你已经这样做了的话, 相信你不一定愿意使用 EF 中现有的, 以 json 格式存储的数据. 当前 EF 每次从本地服务器全量拉取字典数据(30kb)对于 ecdit 中动辄 100mb 的也是不可取的. 同时, 当前 TM 无法做到懒加载数据. 想导入并使用 ecdict 之类超大型的字典还是需要按照按照单词向 local server 请求数据

总的来说:

  1. 要将 csv 转化为 database, 或任意已有的数据库
  2. node.js server 要实现查询 DB 的功能
  3. index.ts 中的 getWordDetail 要改写为 Promise 形式
  4. 前端做一下 throttle
  5. 需要忍受大量的, 可能这辈子都用不到的单词

用了这个脚本就能读懂英文了吗?

不能完全读懂每一句话. 以我当前的水平长难句还是要慢慢理解

比如下面这句话:

Are you confident that, given that it appears to have done so every day throughout your entire life and presumably the entire existence of humanity beforehand, the sun will once again rise tomorrow?

EF 的效果大致效果:

Are you confident(自信) that, given that it appears to have done so every day throughout(始终) your entire life and presumably(想必) the entire existence of humanity(人类) beforehand(事前), the sun will once again rise tomorrow?

所以这位 reddit 哲学爱好者在说啥? 我完全不知道...这是在说英语吗?

还得借助 Google Translate / youdao 才能慢慢了解这句话的意思:

  1. 你是否有自信 that?
  2. 考虑(given it)到 it 每天都完成
  3. it 贯穿(你生命的始终/人类存在之前)
  4. it = (the sun will once again rise tomorrow)

最终:

你是否会怀疑, 太阳明早会再次升起这一件贯穿你生命始终乃至整个人类的存在之前的事?

Chrome Extension Store 上有很多同样功能的插件, 你要和他们竞争吗?

我想要的是及时满足自己的需求

有道字典用户单词本可以导入吗?

没开发, 但是技术上有道导出的词库文件转化成任意的数据格式都是可行的