diff --git a/packages/localize/src/LocalizeManager.js b/packages/localize/src/LocalizeManager.js index 6f8038935..4dc730f52 100644 --- a/packages/localize/src/LocalizeManager.js +++ b/packages/localize/src/LocalizeManager.js @@ -16,6 +16,7 @@ export class LocalizeManager extends LionSingleton { } this._autoLoadOnLocaleChange = !!params.autoLoadOnLocaleChange; + this._fallbackLocale = params.fallbackLocale; this.__storage = {}; this.__namespacePatternsMap = new Map(); this.__namespaceLoadersCache = {}; @@ -163,13 +164,24 @@ export class LocalizeManager extends LionSingleton { return loader; } - _getNamespaceLoaderPromise(loader, locale, namespace) { + _getNamespaceLoaderPromise(loader, locale, namespace, fallbackLocale = this._fallbackLocale) { return loader(locale, namespace).catch(() => { const lang = this._getLangFromLocale(locale); return loader(lang, namespace).catch(() => { + if (fallbackLocale) { + return this._getNamespaceLoaderPromise(loader, fallbackLocale, namespace, false).catch( + () => { + const fallbackLang = this._getLangFromLocale(fallbackLocale); + throw new Error( + `Data for namespace "${namespace}" and current locale "${locale}" or fallback locale "${fallbackLocale}" could not be loaded. ` + + `Make sure you have data either for locale "${locale}" (and/or generic language "${lang}") or for fallback "${fallbackLocale}" (and/or "${fallbackLang}").`, + ); + }, + ); + } throw new Error( `Data for namespace "${namespace}" and locale "${locale}" could not be loaded. ` + - `Make sure you have data for locale "${locale}" and/or generic language "${lang}".`, + `Make sure you have data for locale "${locale}" (and/or generic language "${lang}").`, ); }); }); diff --git a/packages/localize/src/localize.js b/packages/localize/src/localize.js index 6ca4d3f06..0df5027b3 100644 --- a/packages/localize/src/localize.js +++ b/packages/localize/src/localize.js @@ -3,6 +3,7 @@ import { LocalizeManager } from './LocalizeManager.js'; // eslint-disable-next-line import/no-mutable-exports export let localize = LocalizeManager.getInstance({ autoLoadOnLocaleChange: true, + fallbackLocale: 'en-GB', }); export function setLocalize(newLocalize) { diff --git a/packages/localize/test/LocalizeManager.test.js b/packages/localize/test/LocalizeManager.test.js index 1d0b1b653..fecc2f8d7 100644 --- a/packages/localize/test/LocalizeManager.test.js +++ b/packages/localize/test/LocalizeManager.test.js @@ -213,7 +213,7 @@ describe('LocalizeManager', () => { }); }); - it('fallbacks to language file if locale file is not found', async () => { + it('loads generic language file if locale file is not found', async () => { setupFakeImport('./my-component/en.js', { default: { greeting: 'Hello!' } }); manager = new LocalizeManager(); @@ -240,13 +240,69 @@ describe('LocalizeManager', () => { expect(e).to.be.instanceof(Error); expect(e.message).to.equal( 'Data for namespace "my-component" and locale "en-GB" could not be loaded. ' + - 'Make sure you have data for locale "en-GB" and/or generic language "en".', + 'Make sure you have data for locale "en-GB" (and/or generic language "en").', ); return; } throw new Error('did not throw'); }); + + describe('fallback locale', () => { + it('can load a fallback locale if current one can not be loaded', async () => { + manager = new LocalizeManager({ fallbackLocale: 'en-GB' }); + manager.locale = 'nl-NL'; + + setupFakeImport('./my-component/en-GB.js', { default: { greeting: 'Hello!' } }); + + await manager.loadNamespace({ + 'my-component': locale => fakeImport(`./my-component/${locale}.js`), + }); + + expect(manager.__storage).to.deep.equal({ + 'nl-NL': { + 'my-component': { greeting: 'Hello!' }, + }, + }); + }); + + it('can load fallback generic language file if fallback locale file is not found', async () => { + manager = new LocalizeManager({ fallbackLocale: 'en-GB' }); + manager.locale = 'nl-NL'; + + setupFakeImport('./my-component/en.js', { default: { greeting: 'Hello!' } }); + + await manager.loadNamespace({ + 'my-component': locale => fakeImport(`./my-component/${locale}.js`), + }); + + expect(manager.__storage).to.deep.equal({ + 'nl-NL': { + 'my-component': { greeting: 'Hello!' }, + }, + }); + }); + + it('throws if neither current locale nor fallback locale are found', async () => { + manager = new LocalizeManager({ fallbackLocale: 'en-GB' }); + manager.locale = 'nl-NL'; + + try { + await manager.loadNamespace({ + 'my-component': locale => fakeImport(`./my-component/${locale}.js`), + }); + } catch (e) { + expect(e).to.be.instanceof(Error); + expect(e.message).to.equal( + 'Data for namespace "my-component" and current locale "nl-NL" or fallback locale "en-GB" could not be loaded. ' + + 'Make sure you have data either for locale "nl-NL" (and/or generic language "nl") or for fallback "en-GB" (and/or "en").', + ); + return; + } + + throw new Error('did not throw'); + }); + }); }); describe('loading using routes predefined via setupNamespaceLoader()', () => { diff --git a/packages/localize/test/localize.test.js b/packages/localize/test/localize.test.js index 6ad0bb783..b87b77dd8 100644 --- a/packages/localize/test/localize.test.js +++ b/packages/localize/test/localize.test.js @@ -38,4 +38,8 @@ describe('localize', () => { it('is configured to automatically load namespaces if locale is changed', () => { expect(localize._autoLoadOnLocaleChange).to.equal(true); }); + + it('is configured to fallback to the locale "en-GB"', () => { + expect(localize._fallbackLocale).to.equal('en-GB'); + }); });