(function(global) { 'use strict'; /* global mozIntl */ /************************* IntlHelper.define('shortDate', 'datetime', { year: 'numeric', month: 'long', }); function setValue() { const formatter = IntlHelper.get('shortDate'); console.log(formatter.format(new Date())); } setValue(); IntlHelper.observe('shortDate', setValue); ****************************/ const helperCache = new Map(); const knownObjects = { datetime: { create: function(options) { const customOptions = Object.assign({}, options); if (options.hour) { customOptions.hour12 = navigator.mozHour12; } return Intl.DateTimeFormat( navigator.languages, customOptions ); }, isAffected: function(obj, reason) { if (['languagechange', 'moztimechange'].includes(reason)) { return true; } if (reason === 'timeformatchange') { return 'hour' in obj.options; } return false; } }, mozdatetime: { create: function(options) { const customOptions = Object.assign({}, options); if (options.hour) { customOptions.hour12 = navigator.mozHour12; } return mozIntl.DateTimeFormat( navigator.languages, customOptions ); }, isAffected: function(obj, reason) { if (['languagechange', 'moztimechange'].includes(reason)) { return true; } if (reason === 'timeformatchange') { return 'hour' in obj.options; } return false; } }, mozduration: { create: function(options) { return mozIntl.DurationFormat( navigator.languages, options ); }, isAffected: function(obj, reason) { return reason === 'languagechange'; } }, number: { create: function(options) { return Intl.NumberFormat( navigator.languages, options ); }, isAffected: function(obj, reason) { return reason === 'languagechange'; } }, collator: { create: function(options) { return Intl.Collator( navigator.languages, options ); }, isAffected: function(obj, reason) { return reason === 'languagechange'; } }, }; global.IntlHelper = { define: function(name, type, options) { if (!knownObjects.hasOwnProperty(type)) { throw new Error('Unknown type: ' + type); } if (helperCache.has(name)) { throw new Error( 'Intl Object with name "' + name + '" already exists'); } helperCache.set(name, { options, type, intlObject: undefined, listeners: new Set() }); }, get: function(name) { if (!helperCache.has(name)) { throw new Error('Intl Object with name "' + name + '" is not defined'); } const obj = helperCache.get(name); if (!knownObjects.hasOwnProperty(obj.type)) { throw new Error('Unknown type: ' + obj.type); } if (obj.intlObject === undefined) { obj.intlObject = knownObjects[obj.type].create(obj.options); } return obj.intlObject; }, observe: function(names, cb) { if (typeof names === 'string') { names = [names]; } for (var name of names) { if (!helperCache.has(name)) { throw new Error( 'Intl Object with name "' + name + '" is not defined'); } const obj = helperCache.get(name); obj.listeners.add(cb); } }, unobserve: function(name, cb) { const obj = helperCache.get(name); obj.listeners.delete(cb); }, _fireObservers: function(affectedObjects, ...args) { const affectedCallbacks = new Set(); affectedObjects.forEach(obj => obj.listeners.forEach(cb => affectedCallbacks.add(cb)) ); affectedCallbacks.forEach(cb => { try { cb(...args); } catch(e) { console.error('Error in callback: ' + e.toString()); console.error(e.stack); } }); }, handleEvent: function(evt) { // This is a temporary hack to help handling l10n.js' event const affectedObjects = resetObjects(evt.type); if (evt.type === 'languagechange' && navigator.mozL10n) { // We will fire observers only on DOMLocalized // We're using DOMLocalized here instead of languagechange because // mozIntl formatters would restart before new language is loaded // which means they'd be using old strings // // Once we switch completely to l20n.js, we will remove this // limitation which will make IntlHelper useful without mozL10n API // We will only have to make sure that mozL10n reacted to // languagechange before we fired our observers here. waitOnDOMLocalized(() => global.IntlHelper._fireObservers(affectedObjects) ); } else { global.IntlHelper._fireObservers(affectedObjects); } }, _resetObjectCache: function() { helperCache.clear(); } }; function waitOnDOMLocalized(cb) { const resolver = () => { document.removeEventListener('DOMRetranslated', resolver); window.removeEventListener('localized', resolver); cb(); }; document.addEventListener('DOMRetranslated', resolver, false); // Support for l10n.js until we can remove it window.addEventListener('localized', resolver, false); } function resetObjects(reason) { const affectedObjects = new Set(); helperCache.forEach((obj, name) => { if (knownObjects[obj.type].isAffected(obj, reason)) { obj.intlObject = undefined; affectedObjects.add(obj); } }); return affectedObjects; } window.addEventListener('timeformatchange', global.IntlHelper, false); window.addEventListener('languagechange', global.IntlHelper, false); window.addEventListener('moztimechange', global.IntlHelper, false); })(this);