build: package browser extension locale catalogs

This commit is contained in:
mirivlad 2026-07-11 22:22:20 +08:00
parent cae140a348
commit 604bb459c8
5 changed files with 46 additions and 5 deletions

View File

@ -67,6 +67,15 @@ Headers:
Rotating the token in Desktop invalidates the value stored by the extension. Rotating the token in Desktop invalidates the value stored by the extension.
## Language
The popup settings provide a persisted `System / English / Russian` language
selector. `System` follows the browser UI language: Russian browser locales use
Russian, and all other locales use English.
The extension and desktop application store their language choices
independently. Changing one does not change the other.
Payload: Payload:
```json ```json

View File

@ -38,6 +38,13 @@ function copyIcons(destRoot) {
} }
} }
function copyLocalization(destRoot) {
copy(path.join(shared, 'i18n.js'), path.join(destRoot, 'i18n.js'));
for (const locale of ['en', 'ru']) {
copy(path.join(shared, 'locales', `${locale}.json`), path.join(destRoot, 'locales', `${locale}.json`));
}
}
rm(dist); rm(dist);
const chromiumDist = path.join(dist, 'chromium'); const chromiumDist = path.join(dist, 'chromium');
@ -47,18 +54,21 @@ concat([
path.join(shared, 'protocol.js'), path.join(shared, 'protocol.js'),
path.join(shared, 'api.js'), path.join(shared, 'api.js'),
path.join(shared, 'queue.js'), path.join(shared, 'queue.js'),
path.join(shared, 'i18n.js'),
path.join(shared, 'background.js'), path.join(shared, 'background.js'),
], path.join(chromiumDist, 'background.js')); ], path.join(chromiumDist, 'background.js'));
copyPopup(chromiumDist); copyPopup(chromiumDist);
copyIcons(chromiumDist); copyIcons(chromiumDist);
copyLocalization(chromiumDist);
const firefoxDist = path.join(dist, 'firefox'); const firefoxDist = path.join(dist, 'firefox');
mkdir(firefoxDist); mkdir(firefoxDist);
copy(path.join(root, 'firefox', 'manifest.json'), path.join(firefoxDist, 'manifest.json')); copy(path.join(root, 'firefox', 'manifest.json'), path.join(firefoxDist, 'manifest.json'));
for (const name of ['protocol.js', 'api.js', 'queue.js', 'background.js']) { for (const name of ['protocol.js', 'api.js', 'queue.js', 'i18n.js', 'background.js']) {
copy(path.join(shared, name), path.join(firefoxDist, name)); copy(path.join(shared, name), path.join(firefoxDist, name));
} }
copyPopup(firefoxDist); copyPopup(firefoxDist);
copyIcons(firefoxDist); copyIcons(firefoxDist);
copyLocalization(firefoxDist);
console.log('built dist/chromium and dist/firefox'); console.log('built dist/chromium and dist/firefox');

View File

@ -34,9 +34,9 @@ const browser = {
}, },
}, },
contextMenus: { contextMenus: {
removeAll(callback) { removeAll() {
menuTitles = []; menuTitles = [];
if (callback) callback(); return Promise.resolve();
}, },
create(item) { create(item) {
menuTitles.push(item.title); menuTitles.push(item.title);

View File

@ -33,6 +33,18 @@ assert.strictEqual(tEn('error.value', { error: 'offline' }), 'Error: offline');
assert.strictEqual(tRu('missing', null, 'Fallback'), 'Fallback'); assert.strictEqual(tRu('missing', null, 'Fallback'), 'Fallback');
assert.strictEqual(tRu('missing.key'), 'missing.key'); assert.strictEqual(tRu('missing.key'), 'missing.key');
const dist = path.join(root, 'dist');
if (fs.existsSync(dist)) {
for (const target of ['chromium', 'firefox']) {
for (const relativePath of ['i18n.js', 'locales/en.json', 'locales/ru.json']) {
assert.ok(
fs.existsSync(path.join(dist, target, relativePath)),
`${target} build is missing ${relativePath}`,
);
}
}
}
i18n.loadCatalogs((locale) => Promise.resolve(locale === 'ru' ? ru : en)) i18n.loadCatalogs((locale) => Promise.resolve(locale === 'ru' ? ru : en))
.then((catalogs) => { .then((catalogs) => {
assert.strictEqual(catalogs.en, en); assert.strictEqual(catalogs.en, en);

View File

@ -139,12 +139,22 @@
var locale = i18n.resolveLocale(results[0].language, browserLocale()); var locale = i18n.resolveLocale(results[0].language, browserLocale());
var t = i18n.createTranslator(results[1], locale); var t = i18n.createTranslator(results[1], locale);
return new Promise(function (resolve) { return new Promise(function (resolve) {
ext.contextMenus.removeAll(function () { var created = false;
function createMenus() {
if (created) return;
created = true;
ext.contextMenus.create({ id: 'verstak-capture-page', title: t('context.sendPage', null, 'Send page to Verstak'), contexts: ['page'] }); ext.contextMenus.create({ id: 'verstak-capture-page', title: t('context.sendPage', null, 'Send page to Verstak'), contexts: ['page'] });
ext.contextMenus.create({ id: 'verstak-capture-selection', title: t('context.sendSelection', null, 'Send selection to Verstak'), contexts: ['selection'] }); ext.contextMenus.create({ id: 'verstak-capture-selection', title: t('context.sendSelection', null, 'Send selection to Verstak'), contexts: ['selection'] });
ext.contextMenus.create({ id: 'verstak-capture-link', title: t('context.sendLink', null, 'Send link to Verstak'), contexts: ['link'] }); ext.contextMenus.create({ id: 'verstak-capture-link', title: t('context.sendLink', null, 'Send link to Verstak'), contexts: ['link'] });
resolve(); resolve();
}); }
var removal;
try {
removal = ext.contextMenus.removeAll(createMenus);
} catch (_) {
removal = ext.contextMenus.removeAll();
}
if (removal && typeof removal.then === 'function') removal.then(createMenus, createMenus);
}); });
}); });
} }