GitZip Pro 源码解析:一个 GitHub 文件/文件夹下载扩展是如何工作的(七)国际化、Popup 与构建流程
项目地址:fthux/GitZipPro。这是 GitZip Pro 源码解析系列最后一篇,收束到国际化、Popup 和多浏览器构建。
前六篇已经把 GitZip Pro 的主流程串起来了:页面注入、URL 解析、GitHub API、zip 打包、background 下载、设置页和历史统计。最后一篇收束三个辅助但很关键的部分:popup.js、i18n.js 和 build.js。
它们不直接构成下载核心,但决定了扩展的完整体验:用户点击扩展图标看到什么,不同语言如何切换,以及源码如何构建成 Chrome、Firefox、Edge 可发布的扩展包。
Popup:扩展图标弹窗
popup 由 manifest.json 的 action 指定:
"action": {
"default_popup": "popup.html"
}
对应源码是:
source/popup.html
source/popup.js
popup.js 文件不长,主要做五件事:
它首先读取主题:
const result = await chrome.storage.local.get([STORAGE.THEME]);
const theme = result[STORAGE.THEME] || DEFAULTS.THEME;
applyTheme(theme);
然后初始化国际化:
await GZP_I18N.init();
GZP_I18N.applyTranslations();
再通过 chrome.runtime.getManifest().version 显示当前版本号。
Popup 如何判断当前页面状态
popup 会查询当前活动标签页:

const [tab] = await chrome.tabs.query({
active: true,
currentWindow: true
});
如果当前 tab 不是 GitHub,显示“不在 GitHub 页面”。如果是 GitHub,但不是仓库文件页,则显示“不在仓库页面”。如果路径符合仓库页结构,就把状态点设置为 active,并显示当前 owner/repo。
判断逻辑和 content script 的目标一致,但 popup 并不注入页面,也不下载文件。它只是给用户一个快速状态反馈,并提供进入 options 页、项目仓库、issue 页面和商店评分页的入口。
国际化系统:i18n.js
GitZip Pro 有两套国际化资源:
source/_locales/
source/locales/
_locales 是浏览器扩展标准目录,用于扩展名称、描述等 manifest 相关文案。source/locales 则是应用内部 UI 使用的 JSON 语言包,例如 popup、options、content、background 中的文案。
真正的应用内国际化逻辑在 i18n.js。
它通过立即执行函数暴露全局 API:
global.GZP_I18N = {
init,
t,
setLocale,
getCurrentLocale,
applyTranslations,
getTranslatedMessage,
loadLocale,
reloadLocale,
SUPPORTED_LOCALES,
DETECTED
};
支持语言定义为:
const SUPPORTED_LOCALES = ['en', 'zh-CN'];
const DEFAULT_LOCALE = 'en';
语言检测与加载
首次初始化时,initI18n() 会从 storage 中读取 gzpLocale。如果没有保存过语言,就调用 detectBrowserLocale()。
detectBrowserLocale() 会把中文浏览器识别为 zh-CN,其他情况回退到 en。
语言文件通过扩展资源 URL 加载:
fetch(chrome.runtime.getURL(`locales/${normalized}.json`))
如果目标语言加载失败,再回退到英文。这样即使某个语言包缺失,界面也不会完全失去文案。
t() 与 applyTranslations()
t(key, vars) 支持点路径读取:
GZP_I18N.t('popup.active_on', { owner, repo })
内部会把 key 按 . 拆开,在语言 JSON 中逐层查找。如果文案里有变量,例如 {owner},则用传入的 vars 替换。
applyTranslations() 则负责扫描 DOM:
[data-i18n]
[data-i18n-html]
[data-i18n-placeholder]
[data-i18n-title]
[data-i18n-value]
这解释了为什么 popup 和 options 的 HTML 中可以只写 data-i18n,然后在 JS 初始化后统一替换文案。
语言切换时,setLocale(locale) 会保存 storage、重新加载语言包、调用 applyTranslations(),并派发:
document.dispatchEvent(new CustomEvent('gzp-locale-changed', {
detail: { locale }
}));
这样其他脚本也可以响应语言变化。
background 中的国际化
background 没有普通页面 DOM,所以它不会使用 applyTranslations()。它会调用 GZP_I18N.loadLocale(locale) 取得语言对象,然后自己实现一个 t(key, vars) 从 backgroundTranslations 中取值。
这个设计让右键菜单和系统通知也能使用同一套语言 JSON。
整体国际化关系如下:
构建入口:package.json
GitZip Pro 的构建命令定义在 package.json:
"scripts": {
"build": "node build.js",
"build:chrome": "node build.js --channel=chrome",
"build:firefox": "node build.js --channel=firefox",
"build:edge": "node build.js --channel=edge"
}
这说明项目支持按渠道构建。默认渠道是 Chrome,也可以显式指定 Firefox 或 Edge。
构建依赖包括:
terser
clean-css
html-minifier
分别用于 JS、CSS、HTML 压缩。
build.js 的处理流程
build.js 开头定义了构建目录和渠道:
const SOURCE_DIR = 'source';
const BUILD_ROOT_DIR = 'build';
const VALID_STORE_CHANNELS = new Set(['chrome', 'firefox', 'edge']);
const BUILD_DIR = path.join(BUILD_ROOT_DIR, STORE_CHANNEL);
resolveStoreChannel() 会从命令行参数 --channel= 或环境变量 GZP_STORE_CHANNEL 中读取渠道。如果没有传入,则默认 chrome。
构建开始时,脚本会清空当前渠道的 build 目录:
fs.rmSync(BUILD_DIR, { recursive: true, force: true });
然后复制 icons,再遍历 source:
walkDir(SOURCE_DIR, (filePath, relativePath) => {
tasks.push(processFile(filePath, relativePath));
});
不同文件类型进入不同处理函数:
.js -> processJavaScript()
.css -> processCSS()
.html -> processHTML()
.json -> processJSON()
其他 -> 直接复制
JS、CSS、HTML 与 JSON 处理
JavaScript 通过 Terser 压缩。jszip.min.js 已经是压缩文件,所以直接复制。
constants.js 有一个特殊处理:
code = code.replaceAll('__GZP_STORE_CHANNEL__', STORE_CHANNEL);
这会把源码中的商店渠道占位符替换成当前构建渠道,从而让扩展在运行时知道自己是 Chrome、Firefox 还是 Edge 版本。
CSS 使用 CleanCSS 压缩,HTML 使用 html-minifier 压缩。
JSON 处理主要针对 manifest.json。对于 Chrome 和 Edge 这种 Chrome-like 渠道,如果 manifest 是 MV3 且存在 background 配置,构建脚本会删除:
delete json.background.scripts;
这是为了适配不同浏览器对 background 字段的要求。
多渠道构建图
构建完成后,build/{channel} 目录就是对应商店可以加载或打包的扩展源码。
系列总结
到这里,GitZip Pro 的源码主线已经完整闭环:
manifest.json
-> 注入 constants / i18n / jszip / downloader / content
-> content.js 改造 GitHub 文件列表
-> downloader.js 解析 URL、请求 API、生成 zip
-> background.js 调用 downloads API 并保存历史
-> options.js 管理配置、token、历史和统计
-> popup.js 提供轻量状态入口
-> build.js 构建多浏览器扩展包
如果把 GitZip Pro 抽象成一句话,它的源码结构就是:
浏览器扩展装配层 + GitHub 页面适配层 + GitHub API 下载层 + ZIP 打包层 + 后台下载层 + 设置与历史数据层
这一系列从入口到运行链路再到构建流程,已经覆盖了 GitZip Pro 的主要源码结构。后续如果继续深挖,可以单独挑 content.js 的 GitHub DOM 适配、downloader.js 的 symlink 和忽略规则、options.js 的 OAuth 流程做更细粒度的函数级解析。
系列文章对应的完整源码在 fthux/GitZipPro。如果 GitZip Pro 对你有帮助,欢迎 Star、试用或继续基于源码学习浏览器扩展开发。
如果你也好奇这个 Pro 版为什么会做起来,可以接着看这篇项目缘起:用了 GitZip 这么多年,我动手做了一个「Pro」版。
更多推荐



所有评论(0)