@lion/localize
Advanced tools
Comparing version 0.4.1 to 0.4.2
@@ -6,2 +6,14 @@ # Change Log | ||
## [0.4.2](https://github.com/ing-bank/lion/compare/@lion/localize@0.4.1...@lion/localize@0.4.2) (2019-07-09) | ||
### Bug Fixes | ||
* **localize:** don't fire localeChanged event if set to the same locale ([3115c50](https://github.com/ing-bank/lion/commit/3115c50)) | ||
* **localize:** observe <html lang> attribute ([18589f4](https://github.com/ing-bank/lion/commit/18589f4)) | ||
## [0.4.1](https://github.com/ing-bank/lion/compare/@lion/localize@0.4.0...@lion/localize@0.4.1) (2019-06-27) | ||
@@ -8,0 +20,0 @@ |
{ | ||
"name": "@lion/localize", | ||
"version": "0.4.1", | ||
"version": "0.4.2", | ||
"description": "The localization system helps to manage localization data split into locales and automate its loading", | ||
@@ -44,3 +44,3 @@ "author": "ing-bank", | ||
}, | ||
"gitHead": "187d50b6bc14b5c9a2fec3e4908a242513257617" | ||
"gitHead": "a598a47dabccc92e68f12411b7b613a8e5132762" | ||
} |
@@ -9,3 +9,4 @@ import { LocalizeManager } from './LocalizeManager.js'; | ||
export function setLocalize(newLocalize) { | ||
localize.teardown(); | ||
localize = newLocalize; | ||
} |
@@ -14,5 +14,6 @@ import MessageFormat from '@bundled-es-modules/message-format/MessageFormat.js'; | ||
if (!this.locale) { | ||
this.locale = 'en-GB'; | ||
if (!document.documentElement.lang) { | ||
document.documentElement.lang = 'en-GB'; | ||
} | ||
this._autoLoadOnLocaleChange = !!params.autoLoadOnLocaleChange; | ||
@@ -24,4 +25,10 @@ this.__storage = {}; | ||
this.formatNumberOptions = { returnIfNaN: '' }; | ||
this._setupHtmlLangAttributeObserver(); | ||
} | ||
teardown() { | ||
this._teardownHtmlLangAttributeObserver(); | ||
} | ||
// eslint-disable-next-line class-methods-use-this | ||
@@ -34,3 +41,7 @@ get locale() { | ||
const oldLocale = document.documentElement.lang; | ||
this._teardownHtmlLangAttributeObserver(); | ||
document.documentElement.lang = value; | ||
this._setupHtmlLangAttributeObserver(); | ||
this._onLocaleChanged(value, oldLocale); | ||
@@ -96,2 +107,21 @@ } | ||
_setupHtmlLangAttributeObserver() { | ||
if (!this._htmlLangAttributeObserver) { | ||
this._htmlLangAttributeObserver = new MutationObserver(mutations => { | ||
mutations.forEach(mutation => { | ||
this._onLocaleChanged(document.documentElement.lang, mutation.oldValue); | ||
}); | ||
}); | ||
} | ||
this._htmlLangAttributeObserver.observe(document.documentElement, { | ||
attributes: true, | ||
attributeFilter: ['lang'], | ||
attributeOldValue: true, | ||
}); | ||
} | ||
_teardownHtmlLangAttributeObserver() { | ||
this._htmlLangAttributeObserver.disconnect(); | ||
} | ||
_isNamespaceInCache(locale, namespace) { | ||
@@ -188,2 +218,5 @@ return !!(this.__storage[locale] && this.__storage[locale][namespace]); | ||
_onLocaleChanged(newLocale, oldLocale) { | ||
if (newLocale === oldLocale) { | ||
return; | ||
} | ||
this.dispatchEvent(new CustomEvent('localeChanged', { detail: { newLocale, oldLocale } })); | ||
@@ -190,0 +223,0 @@ if (this._autoLoadOnLocaleChange) { |
import { expect } from '@open-wc/testing'; | ||
import sinon from 'sinon'; | ||
@@ -25,6 +26,12 @@ import { LionSingleton } from '@lion/core'; | ||
const oldLocalize = localize; | ||
const newLocalize = {}; | ||
const oldLocalizeTeardown = localize.teardown; | ||
localize.teardown = sinon.spy(); | ||
const newLocalize = { teardown: () => {} }; | ||
setLocalize(newLocalize); | ||
expect(localize).to.equal(newLocalize); | ||
expect(oldLocalize.teardown.callCount).to.equal(1); | ||
setLocalize(oldLocalize); | ||
localize.teardown = oldLocalizeTeardown; | ||
}); | ||
@@ -31,0 +38,0 @@ |
@@ -1,2 +0,3 @@ | ||
import { expect, oneEvent } from '@open-wc/testing'; | ||
import { expect, oneEvent, aTimeout } from '@open-wc/testing'; | ||
import sinon from 'sinon'; | ||
import { fetchMock } from '@bundled-es-modules/fetch-mock'; | ||
@@ -13,2 +14,4 @@ import { setupFakeImport, resetFakeImport, fakeImport } from './test-utils.js'; | ||
describe('LocalizeManager', () => { | ||
let manager; | ||
beforeEach(() => { | ||
@@ -20,2 +23,6 @@ // makes sure that between tests the localization is reset to default state | ||
afterEach(() => { | ||
manager.teardown(); | ||
}); | ||
afterEach(() => { | ||
fetchMock.restore(); | ||
@@ -26,3 +33,3 @@ resetFakeImport(); | ||
it('initializes locale from <html> by default', () => { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
expect(manager.locale).to.equal('en-GB'); | ||
@@ -32,3 +39,3 @@ }); | ||
it('syncs locale back to <html> if changed', () => { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
manager.locale = 'nl-NL'; | ||
@@ -40,3 +47,3 @@ expect(document.documentElement.lang).to.equal('nl-NL'); | ||
document.documentElement.lang = ''; | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
expect(manager.locale).to.equal('en-GB'); | ||
@@ -46,10 +53,38 @@ expect(document.documentElement.lang).to.equal('en-GB'); | ||
it('fires "localeChanged" event with detail.newLocale and detail.oldLocale if locale was changed', async () => { | ||
const manager = new LocalizeManager(); | ||
setTimeout(() => { | ||
it('has teardown() method removing all side effects', () => { | ||
manager = new LocalizeManager(); | ||
const disconnectObserverSpy = sinon.spy(manager._htmlLangAttributeObserver, 'disconnect'); | ||
manager.teardown(); | ||
expect(disconnectObserverSpy.callCount).to.equal(1); | ||
}); | ||
describe('"localeChanged" event with detail.newLocale and detail.oldLocale', () => { | ||
it('fires "localeChanged" event if locale was changed via manager', async () => { | ||
manager = new LocalizeManager(); | ||
setTimeout(() => { | ||
manager.locale = 'en-US'; | ||
}); | ||
const event = await oneEvent(manager, 'localeChanged'); | ||
expect(event.detail.newLocale).to.equal('en-US'); | ||
expect(event.detail.oldLocale).to.equal('en-GB'); | ||
}); | ||
it('fires "localeChanged" event if locale was changed via <html lang> attribute', async () => { | ||
manager = new LocalizeManager(); | ||
setTimeout(() => { | ||
document.documentElement.lang = 'en-US'; | ||
}); | ||
const event = await oneEvent(manager, 'localeChanged'); | ||
expect(event.detail.newLocale).to.equal('en-US'); | ||
expect(event.detail.oldLocale).to.equal('en-GB'); | ||
}); | ||
it('does not fire "localeChanged" event if it was set to the same locale', () => { | ||
manager = new LocalizeManager(); | ||
const eventSpy = sinon.spy(); | ||
manager.addEventListener('localeChanged', eventSpy); | ||
manager.locale = 'en-US'; | ||
manager.locale = 'en-US'; | ||
expect(eventSpy.callCount).to.equal(1); | ||
}); | ||
const event = await oneEvent(manager, 'localeChanged'); | ||
expect(event.detail.newLocale).to.equal('en-US'); | ||
expect(event.detail.oldLocale).to.equal('en-GB'); | ||
}); | ||
@@ -59,3 +94,3 @@ | ||
it('allows to provide inline data', () => { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
@@ -95,3 +130,3 @@ manager.addData('en-GB', 'lion-hello', { greeting: 'Hi!' }); | ||
it('prevents mutating existing data for the same locale & namespace', () => { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
@@ -114,3 +149,3 @@ manager.addData('en-GB', 'lion-hello', { greeting: 'Hi!' }); | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
@@ -131,3 +166,3 @@ await manager.loadNamespace({ | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
manager.locale = 'en-US'; | ||
@@ -153,3 +188,3 @@ | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
@@ -173,3 +208,3 @@ await manager.loadNamespaces([ | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
manager.locale = 'en-US'; | ||
@@ -196,3 +231,3 @@ | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
@@ -211,3 +246,3 @@ await manager.loadNamespace({ | ||
it('throws if both locale and language files could not be loaded', async () => { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
@@ -235,3 +270,3 @@ try { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
@@ -256,3 +291,3 @@ manager.setupNamespaceLoader('my-component', async locale => { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
@@ -281,3 +316,3 @@ manager.setupNamespaceLoader('my-defaults', async locale => { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
@@ -302,3 +337,3 @@ manager.setupNamespaceLoader(/my-.+/, async (locale, namespace) => { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
@@ -321,22 +356,31 @@ manager.setupNamespaceLoader(/my-.+/, async (locale, namespace) => { | ||
describe('loading extra features', () => { | ||
it('has a Promise "loadingComplete" that resolved once all pending loading is done', async () => { | ||
describe('{ autoLoadOnLocaleChange: true }', () => { | ||
it('loads namespaces automatically when locale is changed via manager', async () => { | ||
setupFakeImport('./my-component/en-GB.js', { default: { greeting: 'Hello!' } }); | ||
const manager = new LocalizeManager(); | ||
setupFakeImport('./my-component/nl-NL.js', { default: { greeting: 'Hallo!' } }); | ||
manager.loadNamespace({ | ||
manager = new LocalizeManager({ autoLoadOnLocaleChange: true }); | ||
await manager.loadNamespace({ | ||
'my-component': locale => fakeImport(`./my-component/${locale}.js`, 25), | ||
}); | ||
expect(manager.__storage).to.deep.equal({}); | ||
expect(manager.__storage).to.deep.equal({ | ||
'en-GB': { 'my-component': { greeting: 'Hello!' } }, | ||
}); | ||
manager.locale = 'nl-NL'; | ||
await manager.loadingComplete; | ||
expect(manager.__storage).to.deep.equal({ | ||
'en-GB': { 'my-component': { greeting: 'Hello!' } }, | ||
'nl-NL': { 'my-component': { greeting: 'Hallo!' } }, | ||
}); | ||
}); | ||
it('supports auto loading of namespaces when locale has been changed', async () => { | ||
it('loads namespaces automatically when locale is changed via <html lang> attribute', async () => { | ||
setupFakeImport('./my-component/en-GB.js', { default: { greeting: 'Hello!' } }); | ||
setupFakeImport('./my-component/nl-NL.js', { default: { greeting: 'Hallo!' } }); | ||
const manager = new LocalizeManager({ autoLoadOnLocaleChange: true }); | ||
manager = new LocalizeManager({ autoLoadOnLocaleChange: true }); | ||
@@ -351,3 +395,4 @@ await manager.loadNamespace({ | ||
manager.locale = 'nl-NL'; | ||
document.documentElement.lang = 'nl-NL'; | ||
await aTimeout(); // wait for mutation observer to be called | ||
await manager.loadingComplete; | ||
@@ -360,3 +405,19 @@ | ||
}); | ||
}); | ||
describe('loading extra features', () => { | ||
it('has a Promise "loadingComplete" that resolved once all pending loading is done', async () => { | ||
setupFakeImport('./my-component/en-GB.js', { default: { greeting: 'Hello!' } }); | ||
manager = new LocalizeManager(); | ||
manager.loadNamespace({ | ||
'my-component': locale => fakeImport(`./my-component/${locale}.js`, 25), | ||
}); | ||
expect(manager.__storage).to.deep.equal({}); | ||
await manager.loadingComplete; | ||
expect(manager.__storage).to.deep.equal({ | ||
'en-GB': { 'my-component': { greeting: 'Hello!' } }, | ||
}); | ||
}); | ||
it('loads namespace only once for the same locale', async () => { | ||
@@ -370,3 +431,3 @@ let called = 0; | ||
}; | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
@@ -385,3 +446,3 @@ await Promise.all([ | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
manager.addData('en-GB', 'my-component', { greeting: 'Hello!' }); | ||
@@ -412,3 +473,3 @@ | ||
it('gets the message for the key in the format of "namespace:name"', () => { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
manager.addData('en-GB', 'my-ns', { greeting: 'Hello!' }); | ||
@@ -419,3 +480,3 @@ expect(manager.msg('my-ns:greeting')).to.equal('Hello!'); | ||
it('supports nested names in the format of "namespace:path.to.deep.name"', () => { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
manager.addData('en-GB', 'my-ns', { 'login-section': { greeting: 'Hello!' } }); | ||
@@ -426,3 +487,3 @@ expect(manager.msg('my-ns:login-section.greeting')).to.equal('Hello!'); | ||
it('supports variables', () => { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
manager.addData('en-GB', 'my-ns', { greeting: 'Hello {name}!' }); | ||
@@ -433,3 +494,3 @@ expect(manager.msg('my-ns:greeting', { name: 'John' })).to.equal('Hello John!'); | ||
it('supports Intl MessageFormat proposal for messages', () => { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
manager.addData('en-GB', 'my-ns', { | ||
@@ -449,3 +510,3 @@ date: 'I was written on {today, date}.', | ||
it('takes into account globally changed locale', () => { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
manager.locale = 'nl-NL'; | ||
@@ -458,3 +519,3 @@ manager.addData('en-GB', 'my-ns', { greeting: 'Hi!' }); | ||
it('allows to provide a different locale for specific call', () => { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
manager.addData('en-GB', 'my-ns', { greeting: 'Hi!' }); | ||
@@ -472,3 +533,3 @@ manager.addData('nl-NL', 'my-ns', { greeting: 'Hey!' }); | ||
it('allows to provide an ordered list of keys where the first resolved is used', () => { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
const keys = ['overridden-ns:greeting', 'default-ns:greeting']; | ||
@@ -483,3 +544,3 @@ expect(manager.msg(keys)).to.equal(''); | ||
it('throws a custom error when namespace prefix is missing', () => { | ||
const manager = new LocalizeManager(); | ||
manager = new LocalizeManager(); | ||
const msgKey = 'greeting'; | ||
@@ -486,0 +547,0 @@ manager.addData('en-GB', 'my-ns', { [msgKey]: 'Hello!' }); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
133683
2724