diff --git a/README.md b/README.md index 25a354b..acd5735 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,15 @@ Headers: 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: ```json diff --git a/scripts/build-extension.js b/scripts/build-extension.js index 69c48cc..a1b8616 100755 --- a/scripts/build-extension.js +++ b/scripts/build-extension.js @@ -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); const chromiumDist = path.join(dist, 'chromium'); @@ -47,18 +54,21 @@ concat([ path.join(shared, 'protocol.js'), path.join(shared, 'api.js'), path.join(shared, 'queue.js'), + path.join(shared, 'i18n.js'), path.join(shared, 'background.js'), ], path.join(chromiumDist, 'background.js')); copyPopup(chromiumDist); copyIcons(chromiumDist); +copyLocalization(chromiumDist); const firefoxDist = path.join(dist, 'firefox'); mkdir(firefoxDist); 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)); } copyPopup(firefoxDist); copyIcons(firefoxDist); +copyLocalization(firefoxDist); console.log('built dist/chromium and dist/firefox'); diff --git a/scripts/test-background-i18n.js b/scripts/test-background-i18n.js index e2ea9e5..e37f277 100644 --- a/scripts/test-background-i18n.js +++ b/scripts/test-background-i18n.js @@ -34,9 +34,9 @@ const browser = { }, }, contextMenus: { - removeAll(callback) { + removeAll() { menuTitles = []; - if (callback) callback(); + return Promise.resolve(); }, create(item) { menuTitles.push(item.title); diff --git a/scripts/test-i18n.js b/scripts/test-i18n.js index 6a7ade6..5ddcc47 100644 --- a/scripts/test-i18n.js +++ b/scripts/test-i18n.js @@ -33,6 +33,18 @@ assert.strictEqual(tEn('error.value', { error: 'offline' }), 'Error: offline'); assert.strictEqual(tRu('missing', null, 'Fallback'), 'Fallback'); 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)) .then((catalogs) => { assert.strictEqual(catalogs.en, en); diff --git a/shared/background.js b/shared/background.js index 5e84e99..dfdbf41 100644 --- a/shared/background.js +++ b/shared/background.js @@ -139,12 +139,22 @@ var locale = i18n.resolveLocale(results[0].language, browserLocale()); var t = i18n.createTranslator(results[1], locale); 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-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'] }); resolve(); - }); + } + var removal; + try { + removal = ext.contextMenus.removeAll(createMenus); + } catch (_) { + removal = ext.contextMenus.removeAll(); + } + if (removal && typeof removal.then === 'function') removal.then(createMenus, createMenus); }); }); }