From d71fa224b3bdc33299361093003f0ae6eed4b3fc Mon Sep 17 00:00:00 2001 From: Christoph Reiter <reiter.christoph@gmail.com> Date: Thu, 10 Jun 2021 16:00:44 +0200 Subject: [PATCH] common: Add an i18next API for overriding translations Create a new instance with createInstance() and call setOverrides() with override data to override translations. See the tests for some examples. --- packages/common/i18next.js | 49 +++++++++++++++++++++++++++++---- packages/common/test/i18next.js | 21 ++++++++++++++ 2 files changed, 65 insertions(+), 5 deletions(-) diff --git a/packages/common/i18next.js b/packages/common/i18next.js index 26d78a3f..6130fa25 100644 --- a/packages/common/i18next.js +++ b/packages/common/i18next.js @@ -42,6 +42,17 @@ export function humanFileSize(bytes, si = false) { return bytes.toFixed(1)+' '+units[u]; } +/** + * @param {string} namespace The namespace to override + * @returns {string} The new namespace name + */ +function getOverrideNamespace(namespace) { + // This just needs to be different to the namespace, make it special + // so it's clear what it is used for in case it ends up in some error + // message or something + return '--' + namespace + '-override'; +} + /** * Creates a new i18next instance that is fully initialized. * @@ -54,16 +65,18 @@ export function humanFileSize(bytes, si = false) { * @returns {i18next.i18n} A new independent i18next instance */ export function createInstance(languages, lng, fallback, namespace) { - if (namespace === undefined) + if (namespace === undefined) { namespace = 'translation'; + } + let overrideNamespace = getOverrideNamespace(namespace); var options = { lng: lng, fallbackLng: fallback, debug: false, - ns: [namespace + '-override'], - defaultNS: namespace + '-override', - fallbackNS: [namespace], + ns: [overrideNamespace, namespace], + defaultNS: namespace, + fallbackNS: namespace, initImmediate: false, // Don't init async resources: {}, }; @@ -77,4 +90,30 @@ export function createInstance(languages, lng, fallback, namespace) { console.assert(i18n.isInitialized); return i18n; -} \ No newline at end of file +} + +/** + * Sets translation overrides for the given i18next instance. Any previously + * applied overrides will be removed first. So calling this with an empty overrides + * object is equal to removing all overrides. + * + * @param {i18next.i18n} i18n - The i18next instance + * @param {object} overrides - The override data in the following format: "{language: {namespace: {key: value}}}" + */ +export function setOverrides(i18n, overrides) { + // We add a special namespace which gets used with priority and falls back + // to the original one. This way we an change the overrides at runtime + // and can even remove them. + let namespace = i18n.options.fallbackNS; + let overrideNamespace = getOverrideNamespace(namespace); + let hasOverrides = false; + for(let lng of i18n.languages) { + i18n.removeResourceBundle(lng, overrideNamespace); + if (overrides[lng] === undefined || overrides[lng][namespace] === undefined) + continue; + let resources = overrides[lng][namespace]; + hasOverrides = true; + i18n.addResourceBundle(lng, overrideNamespace, resources); + } + i18n.setDefaultNamespace(hasOverrides ? overrideNamespace : namespace); +} diff --git a/packages/common/test/i18next.js b/packages/common/test/i18next.js index 3b75700e..ef116042 100644 --- a/packages/common/test/i18next.js +++ b/packages/common/test/i18next.js @@ -47,4 +47,25 @@ suite('i18next', () => { assert.equal(i18next.numberFormat(inst, 1.25), '1.25'); assert.equal(i18next.numberFormat(inst, 1234), '1,234'); }); + + test('overrides', () => { + let namespace = 'ns'; + var inst = i18next.createInstance({en: {foo: 'bar'}}, 'en', 'en', namespace); + assert.equal(inst.t('foo'), 'bar'); + assert.equal(inst.t('quux'), 'quux'); + i18next.setOverrides(inst, {en: {[namespace]: {quux: 'bla'}}}); + assert.equal(inst.t('quux'), 'bla'); + assert.equal(inst.t('foo'), 'bar'); + i18next.setOverrides(inst, {en: {[namespace]: {}}}); + assert.equal(inst.t('quux'), 'quux'); + assert.equal(inst.t('foo'), 'bar'); + i18next.setOverrides(inst, {en: {[namespace]: {foo: 'hmm'}}}); + assert.equal(inst.t('foo'), 'hmm'); + i18next.setOverrides(inst, {en: {[namespace]: {quux: 'bla'}}}); + assert.equal(inst.t('foo'), 'bar'); + assert.equal(inst.t('quux'), 'bla'); + i18next.setOverrides(inst, {}); + assert.equal(inst.t('foo'), 'bar'); + assert.equal(inst.t('quux'), 'quux'); + }); }); -- GitLab