Chrome / Edge 扩展(Manifest V3):按你的词汇量给英文新闻和视频网站上的生词加中文注释、美式音标和发音,点词可调用大模型结合上下文解释。原生 JS,无构建步骤。
开源协议:MIT · 隐私政策:PRIVACY.md
| 浏览器 | 地址 |
|---|---|
| Microsoft Edge | Edge 加载项商店 |
| Google Chrome | Chrome 应用商店 |
手动安装(Chrome / Edge 通用,用于试用未上架的新版本):
inkling-<版本>.zip 并解压;或者直接用本仓库的 extension/ 目录。chrome://extensions(Edge 为 edge://extensions),开启「开发者模式」。extension/ 目录)。https://alex-chan.github.io/inkling/glosspacks/),按 index.json 里的 sha256 校验后缓存在 IndexedDB,之后离线可用。浏览器语言为日语 / 韩语时,首次安装会自动下载对应的包。| 释义包 | 大小 | 词数 | 覆盖率(词频前 2 万) |
|---|---|---|---|
日语 glosspacks/ja/ |
0.68 MB | 16,591 | 71% |
韩语 glosspacks/ko/ |
0.46 MB | 12,626 | 56% |
释义包由英文维基词典里日语、韩语词条的英文释义反查生成(例如 関税 的释义是 “tariff; customs duty”,于是 tariff → 関税),许可为 CC BY-SA 4.0,见 glosspacks/LICENSE.md。重新生成:
mkdir -p /tmp/wikt && cd /tmp/wikt
curl -LO https://kaikki.org/dictionary/Japanese/kaikki.org-dictionary-Japanese.jsonl # 385 MB,只在构建时下载
curl -LO https://kaikki.org/dictionary/Korean/kaikki.org-dictionary-Korean.jsonl # 205 MB
cd - && python3 -I tools/build_gloss_pack.py ja /tmp/wikt/kaikki.org-dictionary-Japanese.jsonl
python3 -I tools/build_gloss_pack.py ko /tmp/wikt/kaikki.org-dictionary-Korean.jsonl
脚本会更新 glosspacks/index.json(版本、大小、sha256、覆盖率),推送后 GitHub Pages 自动发布,用户在设置里点「检查更新」即可拿到新包。反查质量的已知问题:极常用词有时会挑到少见的义项(如 go → 碁),这些词一般远低于生词阈值,基本不会被注释;词卡里会列出最多 3 个候选。
JA / KO 里;生成时校验五种语言 key 和占位符一致。改完后运行 python3 -I tools/i18n_table.py,生成 extension/_locales/*/messages.json。chrome.i18n 只能跟随浏览器)。页面用 data-i18n / data-i18n-title / data-i18n-aria 标注;content script 从后台取当前语言的文案。__MSG_appName__ / __MSG_appDesc__,两个商店会按语言显示。各语言的商店详细说明见 store/listing-i18n.md。tests/i18n.spec.js)。默认只在指定的英文新闻和视频网站上运行,列表见 extension/lib/sites.js:CNN、NBC News、CNBC、Reuters、BBC、Google 新闻、WSJ、YouTube、NYT、Guardian、AP、Bloomberg 等,共 28 个站点。这个列表与 manifest.json 的 content_scripts.matches 和 host_permissions 一致,测试会校验三者没有分叉。安装时不申请 <all_urls>。另有两种情况通过 optional_host_permissions 按单个网站授权:
chrome.scripting.registerContentScripts 注册内容脚本,并立即注入当前标签页。已启用的网站可在设置页「支持的网站」里移除。YouTube:页面文字和视频字幕都会注释。字幕用浅色释义,不做 AI 优化,点击字幕里的词不会触发播放器。被 -webkit-line-clamp 截断行数的标题(YouTube 侧栏、Google 新闻卡片等)只加下划线、不加释义,以免撑高行距把末行挤掉。
extension/ ← 扩展本体(加载这个目录)
manifest.json
background.js service worker(module):消息路由、AI 流式解释、OAuth 回调兜底
lib/
settings.js 默认值、档位、滑杆刻度(options / popup / background 共用)
dict.js 离线词典懒加载为 Map、词形还原、示例词、40 词小测
ai.js 提示词、语境解释、段落自动注释(并发 2 + IndexedDB 缓存)
openai-compat.js A. API Key:OpenAI 兼容 Chat Completions(SSE)
chatgpt.js B. Sign in with ChatGPT:PKCE、JWKS 验签、串行刷新、撤销、Responses API
sse.js SSE 解析与错误对象
content.js/.css 扫描正文、注释、词卡(Shadow DOM)、划词、复制去注释
options.* popup.* callback.* ui.css
data/dict.tsv 56,743 词,4.4 MB(ECDICT 精简 + CMUdict 美式 IPA)
data/forms.tsv 37,891 个变形 → 原形(含变形本身的美式 IPA)
tools/
build_dict.py 从 ecdict.csv 重新生成 data/
make_icons.py 生成图标
smoke-real-sites.js 真实新闻站点冒烟检查(需联网)
tests/ Playwright 端到端测试 + 本地 mock 服务器
curl -L -o /tmp/ecdict.csv https://raw.githubusercontent.com/skywind3000/ECDICT/master/ecdict.csv
curl -L -o /tmp/cmudict.dict https://raw.githubusercontent.com/cmusphinx/cmudict/master/cmudict.dict
python3 -I tools/build_dict.py /tmp/ecdict.csv /tmp/cmudict.dict
规则:只收纯字母词,且有词频(frq,没有则用 bnc)、考试标签或柯林斯星级;都没有排名的按考试标签/星级估算。释义最多 4 行,有通用释义时丢掉 [计]、[网络] 等专业释义。
格式在需求文档的基础上各加了一列:
dict.tsv:词 \t 音标(ECDICT) \t 释义 \t 词频排名 \t 考试标签 \t 美式IPAforms.tsv:变形 \t 原形 \t 变形的美式IPA美式 IPA 由 CMUdict 的 ARPAbet 转换而来,词频前 2 万的词覆盖 95%。重音符号按最大首音原则放在音节开头;按学习词典的惯例,主重音之后的次重音不标(tomato → /təˈmeɪtoʊ/)。ECDICT 自带的音标是老式写法、多偏英式,只在没有美式 IPA 时作为后备显示在词卡里。
npm install
npm test # 20 个端到端测试,约 1 分钟
HEADED=1 npm test # 有界面运行
node tools/smoke-real-sites.js /tmp/out # 真实站点检查,输出截图和 report.json
测试用 Playwright 加载 extension/,通过 chrome.storage.local.devConfig 把 OAuth 和 API 端点指向 tests/mock-server.js(127.0.0.1:18080)。mock 服务器只用于测试,扩展运行时直接调用 LLM API,不需要任何服务器。
可覆盖的开发配置(写到 storage.local 的 devConfig):authBase、issuer、apiBase、resource、port(回调端口,默认 14551),以及单独的 authorizeUrl / tokenUrl / jwksUrl / discoveryUrl。
接入大模型后,可在设置页或工具栏弹窗打开(默认关闭,用量会明显增加)。流程:
{"items":[{p, text, gloss, level, kind}]},该段改按 AI 结果重新注释:
kind: sense,如 markets brace for → 准备应对)和短语动词、习语(kind: phrase,如 rule out、in the wake of),短语整体注释;规则:生词本里的词总会注释,即使模型没有选中;熟词本里的词和短语永不注释。模型返回的条目必须在原段落里原样出现,否则丢弃;同一表达重复返回时,保留更具体的类型。结果按「模型 + 水平档(每 500 一档)+ 段落 + 候选」缓存在 IndexedDB,水平变了会按新水平重新请求。关闭开关后回到纯词典注释。词卡里「AI 语境解释」的提示词也会带上读者水平。
设置页「显示方式」→「释义颜色」:默认黑色,提供 7 个预设色和自定义取色器,附实时预览。颜色作用于所有中文释义,包括「AI 优化注释」生成的释义;美式音标保留自己的颜色,便于区分。改动后已打开的页面立即生效。在深色背景的网页上,如果所选颜色与背景的对比度低于 3:1(比如默认的黑色),会自动改用浅灰 #e8e8e8;反过来,在浅色网页上选了很浅的颜色,会改用深灰。
设置页「音标与发音」:
/ˈtɛrəfs/ 关税,按页面上实际出现的词形注音,比如 tariffs 而不是 tariff。词卡里总会显示音标,标着「美」。「词上方小字」模式开启音标后注释会变宽。为了不把原文字距撑开,注释左对齐、可以向右溢出;同一行两个注释可能重叠时,才把后一个词往右推开,刚好够用为止。
谁来判断”生词”:默认由本地词频排名 + 阈值 N + 熟词本/生词本判断,不经过大模型。这样即时、免费、离线可用、结果稳定,滑杆拖动立即生效。开启「AI 优化注释」后,词典结果作为初筛和兜底,最终取舍按模型的语境判断(见上文)。
懒处理:保留。词典注释很快,但长页面、无限滚动页面一次性改整页 DOM 没有必要,所以用 IntersectionObserver,上下各预取约 1.5 屏。AI 优化注释必须懒处理,否则一开页面就把整篇文章发给模型;它提前约 1 屏触发,用来掩盖模型延迟。动态内容用 MutationObserver 补扫。
专有名词:以下情况不注释:
另外,Title Case 标题(NYT 风格)里的词按普通词判断。少量新闻高频、又恰好是普通英文词的人名(trump、pence、barr、xi)写死在 NAME_HINTS 里。
OAuth 回调拦截:用 declarativeNetRequest 动态规则把 http://127.0.0.1:14551/auth/callback?… 重定向到 callback.html。规则只在登录过程中存在,登录完成后移除。另外用 webNavigation 的 onBeforeNavigate 和 onErrorOccurred 兜底,按 state 去重。两条路径都有单独的测试。
agent_name_hint 用 Inkling,没有沿用文档里的 LexiNote(看起来是旧名字)。error=access_denied 时提示”未开启套餐用量共享”;client_id 与已保存的不一致,就拒绝;sub 一致;需要换账号时用「使用其他账号登录」重新注册;id_token_hint(只发 login_hint);令牌失效后重新登录时,会带上 id_token_hint;usage_unavailable / user_unavailable)有限次退避重试。subscription_sharing_invalid_user)才清除令牌、要求重新登录。官方文档要求”确认失效后才要求重新登录”,这样做兼顾了两边。ytp-caption-window-container。<ruby> 本身的特性;介意的话可以选「词后括注」或「只加下划线」。chrome.storage.local。Chrome 默认允许 content script 读取这块存储,不过 content script 只读取设置相关的 key。./tools/package.sh # → dist/inkling-<version>.zip,manifest.json 位于根目录
node store/make-assets.mjs # 重新生成商店截图与宣传图(store/assets/)
商店后台要填的各项内容(说明、权限理由、数据使用声明、隐私政策网址):
store/assets/edge-logo-300.png在其他 Chromium 内核浏览器上跑整套测试(例如 Edge):
BROWSER_PATH="/Applications/Microsoft Edge.app/Contents/MacOS/Microsoft Edge" npm test