suite('L10n', function() { 'use strict'; var _; var _translateFragment; var l10props = [ 'cropimage = Crop', 'delete-n-items = {[ plural(n) ]}', 'delete-n-items[zero] = Nothing selected', 'delete-n-items[one] = Delete selected item?', 'delete-n-items[other] = Delete {{ n }} items?', 'textcontent-test = this is text content', 'dom-overlay-test = this is text content ', 'attr-test.title = this is an attribute', 'euroSign = price: 10\\u20ac to 20\\u20ac', 'leadingSpaces = \\u0020\\u020\\u20%2F', 'trailingBackslash = backslash\\\\', 'multiLine = foo \\', ' bar \\', ' baz', 'update = {[ plural(n) ]}', 'update[zero] = No updates.', 'update[one] = {{n}} update available. \\', ' Tap for more info.', 'update[other] = {{n}} updates available. \\', ' Tap for more info.', 'overlay = {[ plural(n) ]}', 'overlay[zero] = No updates.', 'overlay[one] = {{n}} update available. \\', ' Tap for more info.', 'overlay[other] = {{n}} updates available. \\', ' Tap for more info.', 'inline-translation-test = static content provided by inlined JSON', 'a11y-label.ariaLabel = label via ARIA', 'a11y-label.ariaValueText = valuetext via ARIA', 'a11y-label.ariaMozHint = moz-hint via ARIA' ].join('\n'); var inlineL10Props = { 'inline-translation-test': 'static content provided by inlined JSON' }; var key_cropImage = 'cropimage'; var key_delete = 'delete-n-items'; var key_euroSign = 'euroSign'; var key_leadingSpaces = 'leadingSpaces'; var key_multiLine = 'multiLine'; var key_backslash = 'trailingBackslash'; var xhr; // Do not begin tests until the test locale has been loaded. suiteSetup(function(done) { xhr = sinon.useFakeXMLHttpRequest(); xhr.onCreate = function(request) { setTimeout(function() { request.respond(200, {}, l10props); }, 0); }; _ = navigator.mozL10n.get; _translateFragment = navigator.mozL10n.translateFragment; // en-US has already been loaded in setup.js and l10n.js is smart enough // not to re-fetch resources; hence, set the lang to something new var lang = 'fr'; var inline = document.createElement('script'); inline.setAttribute('type', 'application/l10n'); inline.setAttribute('lang', lang); inline.textContent = JSON.stringify(inlineL10Props); document.head.appendChild(inline); navigator.mozL10n.ctx.registerLocales('en-US', ['fr', 'zh-TW', 'ar']); navigator.mozL10n.language.code = lang; navigator.mozL10n.once(done); }); suiteTeardown(function() { xhr.restore(); }); suite('get', function() { test('existing key', function() { assert.strictEqual(_(key_cropImage), 'Crop'); }); test('inexisting key', function() { assert.strictEqual(_('bla'), ''); }); suite('plural', function() { test('n=0', function() { assert.strictEqual(_(key_delete, { n: 0 }), 'Nothing selected'); }); test('n=1', function() { assert.strictEqual(_(key_delete, { n: 1 }), 'Delete selected item?'); }); test('n=2', function() { assert.strictEqual(_(key_delete, { n: 2 }), 'Delete 2 items?'); }); }); test('unescape utf8 char codes', function() { assert.strictEqual(_(key_euroSign), 'price: 10€ to 20€'); assert.strictEqual(_(key_leadingSpaces), ' %2F'); }); test('multiline string', function() { assert.strictEqual(_(key_multiLine), 'foo bar baz'); }); test('escaped trailing backslash', function() { assert.strictEqual(_(key_backslash), 'backslash\\'); }); }); suite('translateFragment', function() { var elem; setup(function() { elem = document.createElement('div'); }); test('text content', function() { elem.dataset.l10nId = 'textcontent-test'; _translateFragment(elem); assert.equal(elem.textContent, 'this is text content'); }); test('properties', function() { elem.dataset.l10nId = 'attr-test'; _translateFragment(elem); assert.equal(elem.getAttribute('title'), 'this is an attribute'); }); test('ARIA labels', function() { elem.dataset.l10nId = 'a11y-label'; _translateFragment(elem); assert.equal(elem.getAttribute('aria-label'), 'label via ARIA'); }); test('ARIA valuetext', function() { elem.dataset.l10nId = 'a11y-label'; _translateFragment(elem); assert.equal(elem.getAttribute('aria-valuetext'), 'valuetext via ARIA'); }); test('ARIA moz-hint', function() { elem.dataset.l10nId = 'a11y-label'; _translateFragment(elem); assert.equal(elem.getAttribute('aria-moz-hint'), 'moz-hint via ARIA'); }); test('for a document fragment', function() { var fragment = document.createDocumentFragment(); elem.dataset.l10nId = 'textcontent-test'; fragment.appendChild(elem); _translateFragment(fragment); assert.equal(elem.textContent, 'this is text content'); }); test('for a shadowRoot', function() { var container = document.createElement('div'); container.createShadowRoot(); elem.dataset.l10nId = 'textcontent-test'; container.shadowRoot.appendChild(elem); _translateFragment(container.shadowRoot); assert.equal(elem.textContent, 'this is text content'); }); }); suite('localize + translate', function() { var elem; setup(function() { elem = document.createElement('div'); }); test('text content', function() { navigator.mozL10n.setAttributes(elem, 'textcontent-test'); _translateFragment(elem); assert.equal(elem.textContent, 'this is text content'); }); test('properties', function() { navigator.mozL10n.setAttributes(elem, 'attr-test'); _translateFragment(elem); assert.equal(elem.getAttribute('title'), 'this is an attribute'); }); // XXX Remove in https://bugzil.la/1027117 suite('properties + pluralization', function() { test('n=0', function() { navigator.mozL10n.setAttributes(elem, 'update', { n: 0 }); _translateFragment(elem); var info = elem.querySelector('strong'); var span = elem.querySelector('span'); assert.ok(info); assert.isNull(span); assert.equal(info.textContent, 'No updates.'); }); test('n=1', function() { navigator.mozL10n.setAttributes(elem, 'update', { n: 1 }); _translateFragment(elem); var info = elem.querySelector('strong'); var span = elem.querySelector('span'); assert.ok(info); assert.ok(span); assert.equal(info.textContent, '1 update available.'); }); test('n=2', function() { navigator.mozL10n.setAttributes(elem, 'update', { n: 2 }); _translateFragment(elem); var info = elem.querySelector('strong'); var span = elem.querySelector('span'); assert.ok(info); assert.ok(span); assert.equal(info.textContent, '2 updates available.'); }); }); suite('DOM overlays + pluralization', function() { test('n=0', function() { navigator.mozL10n.setAttributes(elem, 'overlay', { n: 0 }); _translateFragment(elem); var info = elem.querySelector('strong'); var span = elem.querySelector('span'); assert.ok(info); assert.isNull(span); assert.equal(info.textContent, 'No updates.'); }); test('n=1', function() { navigator.mozL10n.setAttributes(elem, 'overlay', { n: 1 }); _translateFragment(elem); var info = elem.querySelector('strong'); var span = elem.querySelector('span'); assert.ok(info); assert.ok(span); assert.equal(info.textContent, '1 update available.'); }); test('n=2', function() { navigator.mozL10n.setAttributes(elem, 'overlay', { n: 2 }); _translateFragment(elem); var info = elem.querySelector('strong'); var span = elem.querySelector('span'); assert.ok(info); assert.ok(span); assert.equal(info.textContent, '2 updates available.'); }); }); test('element with child, translation without a child', function() { elem.innerHTML = 'here is a button '; navigator.mozL10n.setAttributes(elem, 'textcontent-test'); _translateFragment(elem); assert.equal(elem.innerHTML, 'this is text content'); }); test('element and translation with child', function() { elem.innerHTML = 'here is a button '; navigator.mozL10n.setAttributes(elem, 'dom-overlay-test'); _translateFragment(elem); assert.equal(elem.innerHTML, 'this is text content '); }); }); suite('translate existing', function() { function setLang(lang, callback) { // we want onLocalized to be invoked only *after* the language change window.addEventListener('localized', function onLocalized() { window.removeEventListener('localized', onLocalized); callback(); }); navigator.mozL10n.language.code = lang; } var elem; setup(function() { elem = document.createElement('div'); document.body.appendChild(elem); }); teardown(function() { document.body.removeChild(elem); }); test('inline translation', function(done) { elem.dataset.l10nId = 'inline-translation-test'; assert.equal(elem.textContent, ''); setLang('zh-TW', function() { assert.equal(elem.textContent, 'static content provided by inlined JSON'); done(); }); }); test('downloaded translation', function(done) { elem.dataset.l10nId = 'cropimage'; assert.equal(elem.textContent, ''); setLang('ar', function() { assert.equal(elem.textContent, 'Crop'); done(); }); }); }); });