Bug 1428769 - Add intl/l10n to be covered by eslint. r=Pike

MozReview-Commit-ID: 6Mu1A8xkxn4

--HG--
extra : rebase_source : e62867d32b0062a6aa076572b85fb9cec7afb81a
This commit is contained in:
Zibi Braniecki 2018-02-20 14:02:54 -10:00
parent aa92df7e1d
commit c797a57c90
20 changed files with 432 additions and 424 deletions

View file

@ -24,7 +24,6 @@ gfx/tests/chrome/**
gfx/tests/mochitest/**
gfx/tests/unit/**
image/**
intl/**
layout/**
memory/replace/dmd/test/**
modules/**
@ -303,6 +302,12 @@ dom/media/webvtt/**
gfx/ots/**
gfx/skia/**
# intl/ exclusions
intl/icu/**
intl/locale/**
intl/strres/**
intl/uconv/**
# Exclude everything but self-hosted JS
js/ductwork/**
js/examples/**

View file

@ -16,7 +16,7 @@
*/
/* fluent@0.6.0 */
/* fluent@0.6.3 */
const { Localization } =
ChromeUtils.import("resource://gre/modules/Localization.jsm", {});
@ -31,36 +31,36 @@ const reOverlay = /<|&#?\w+;/;
* Source: https://www.w3.org/TR/html5/text-level-semantics.html
*/
const LOCALIZABLE_ELEMENTS = {
'http://www.w3.org/1999/xhtml': [
'a', 'em', 'strong', 'small', 's', 'cite', 'q', 'dfn', 'abbr', 'data',
'time', 'code', 'var', 'samp', 'kbd', 'sub', 'sup', 'i', 'b', 'u',
'mark', 'ruby', 'rt', 'rp', 'bdi', 'bdo', 'span', 'br', 'wbr'
"http://www.w3.org/1999/xhtml": [
"a", "em", "strong", "small", "s", "cite", "q", "dfn", "abbr", "data",
"time", "code", "var", "samp", "kbd", "sub", "sup", "i", "b", "u",
"mark", "ruby", "rt", "rp", "bdi", "bdo", "span", "br", "wbr"
],
};
const LOCALIZABLE_ATTRIBUTES = {
'http://www.w3.org/1999/xhtml': {
global: ['title', 'aria-label', 'aria-valuetext', 'aria-moz-hint'],
a: ['download'],
area: ['download', 'alt'],
"http://www.w3.org/1999/xhtml": {
global: ["title", "aria-label", "aria-valuetext", "aria-moz-hint"],
a: ["download"],
area: ["download", "alt"],
// value is special-cased in isAttrNameLocalizable
input: ['alt', 'placeholder'],
menuitem: ['label'],
menu: ['label'],
optgroup: ['label'],
option: ['label'],
track: ['label'],
img: ['alt'],
textarea: ['placeholder'],
th: ['abbr']
input: ["alt", "placeholder"],
menuitem: ["label"],
menu: ["label"],
optgroup: ["label"],
option: ["label"],
track: ["label"],
img: ["alt"],
textarea: ["placeholder"],
th: ["abbr"]
},
'http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul': {
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul": {
global: [
'accesskey', 'aria-label', 'aria-valuetext', 'aria-moz-hint', 'label'
"accesskey", "aria-label", "aria-valuetext", "aria-moz-hint", "label"
],
key: ['key', 'keycode'],
textbox: ['placeholder'],
toolbarbutton: ['tooltiptext'],
key: ["key", "keycode"],
textbox: ["placeholder"],
toolbarbutton: ["tooltiptext"],
}
};
@ -75,7 +75,7 @@ const LOCALIZABLE_ATTRIBUTES = {
function overlayElement(targetElement, translation) {
const value = translation.value;
if (typeof value === 'string') {
if (typeof value === "string") {
if (!reOverlay.test(value)) {
// If the translation doesn't contain any markup skip the overlay logic.
targetElement.textContent = value;
@ -83,7 +83,8 @@ function overlayElement(targetElement, translation) {
// Else parse the translation's HTML using an inert template element,
// sanitize it and replace the targetElement's content.
const templateElement = targetElement.ownerDocument.createElementNS(
'http://www.w3.org/1999/xhtml', 'template');
"http://www.w3.org/1999/xhtml", "template");
// eslint-disable-next-line no-unsanitized/property
templateElement.innerHTML = value;
targetElement.appendChild(
// The targetElement will be cleared at the end of sanitization.
@ -92,9 +93,9 @@ function overlayElement(targetElement, translation) {
}
}
const explicitlyAllowed = targetElement.hasAttribute('data-l10n-attrs')
? targetElement.getAttribute('data-l10n-attrs')
.split(',').map(i => i.trim())
const explicitlyAllowed = targetElement.hasAttribute("data-l10n-attrs")
? targetElement.getAttribute("data-l10n-attrs")
.split(",").map(i => i.trim())
: null;
// Remove localizable attributes which may have been set by a previous
@ -182,7 +183,7 @@ function sanitizeUsing(translationFragment, sourceElement) {
// SourceElement might have been already modified by shiftNamedElement.
// Let's clear it to make sure other code doesn't rely on random leftovers.
sourceElement.textContent = '';
sourceElement.textContent = "";
return translationFragment;
}
@ -274,10 +275,10 @@ function isAttrNameLocalizable(name, element, explicitlyAllowed = null) {
}
// Special case for value on HTML inputs with type button, reset, submit
if (element.namespaceURI === 'http://www.w3.org/1999/xhtml' &&
elemName === 'input' && attrName === 'value') {
if (element.namespaceURI === "http://www.w3.org/1999/xhtml" &&
elemName === "input" && attrName === "value") {
const type = element.type.toLowerCase();
if (type === 'submit' || type === 'button' || type === 'reset') {
if (type === "submit" || type === "button" || type === "reset") {
return true;
}
}
@ -303,8 +304,8 @@ function shiftNamedElement(element, localName) {
return null;
}
const L10NID_ATTR_NAME = 'data-l10n-id';
const L10NARGS_ATTR_NAME = 'data-l10n-args';
const L10NID_ATTR_NAME = "data-l10n-id";
const L10NARGS_ATTR_NAME = "data-l10n-args";
const L10N_ELEMENT_QUERY = `[${L10NID_ATTR_NAME}]`;
@ -430,7 +431,7 @@ class DOMLocalization extends Localization {
if (root === newRoot ||
root.contains(newRoot) ||
newRoot.contains(root)) {
throw new Error('Cannot add a root that overlaps with existing root.');
throw new Error("Cannot add a root that overlaps with existing root.");
}
}
@ -500,10 +501,10 @@ class DOMLocalization extends Localization {
translateMutations(mutations) {
for (const mutation of mutations) {
switch (mutation.type) {
case 'attributes':
case "attributes":
this.pendingElements.add(mutation.target);
break;
case 'childList':
case "childList":
for (const addedNode of mutation.addedNodes) {
if (addedNode.nodeType === addedNode.ELEMENT_NODE) {
if (addedNode.childElementCount) {
@ -600,7 +601,7 @@ class DOMLocalization extends Localization {
getTranslatables(element) {
const nodes = Array.from(element.querySelectorAll(L10N_ELEMENT_QUERY));
if (typeof element.hasAttribute === 'function' &&
if (typeof element.hasAttribute === "function" &&
element.hasAttribute(L10NID_ATTR_NAME)) {
nodes.push(element);
}
@ -625,4 +626,4 @@ class DOMLocalization extends Localization {
}
this.DOMLocalization = DOMLocalization;
this.EXPORTED_SYMBOLS = ['DOMLocalization'];
this.EXPORTED_SYMBOLS = ["DOMLocalization"];

View file

@ -1,7 +1,7 @@
const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm", {});
const { Services } = ChromeUtils.import('resource://gre/modules/Services.jsm', {});
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
const { MessageContext } = ChromeUtils.import("resource://gre/modules/MessageContext.jsm", {});
Components.utils.importGlobalProperties(["fetch"]); /* globals fetch */
Cu.importGlobalProperties(["fetch"]);
/**
* L10nRegistry is a localization resource management system for Gecko.
@ -89,7 +89,7 @@ const L10nRegistry = {
* @param {Array} resourceIds
* @returns {AsyncIterator<MessageContext>}
*/
async * generateContexts(requestedLangs, resourceIds) {
async* generateContexts(requestedLangs, resourceIds) {
if (this.bootstrap !== null) {
await this.bootstrap;
}
@ -167,8 +167,8 @@ const L10nRegistry = {
* @returns {String}
*/
function generateContextID(locale, sourcesOrder, resourceIds) {
const sources = sourcesOrder.join(',');
const ids = resourceIds.join(',');
const sources = sourcesOrder.join(",");
const ids = resourceIds.join(",");
return `${locale}|${sources}|${ids}`;
}
@ -242,7 +242,7 @@ const MSG_CONTEXT_OPTIONS = {
}
}
}
}
};
/**
* Generates a single MessageContext by loading all resources
@ -360,11 +360,9 @@ class FileSource {
if (this.cache[fullPath].then) {
return this.cache[fullPath];
}
} else {
if (this.indexed) {
} else if (this.indexed) {
return Promise.reject(`The source has no resources for path "${fullPath}"`);
}
}
return this.cache[fullPath] = L10nRegistry.load(fullPath).then(
data => {
return this.cache[fullPath] = data;
@ -417,7 +415,7 @@ L10nRegistry.load = function(url) {
if (!response.ok) {
return Promise.reject(response.statusText);
}
return response.text()
return response.text();
});
};
@ -425,4 +423,4 @@ this.L10nRegistry = L10nRegistry;
this.FileSource = FileSource;
this.IndexedFileSource = IndexedFileSource;
this.EXPORTED_SYMBOLS = ['L10nRegistry', 'FileSource', 'IndexedFileSource'];
this.EXPORTED_SYMBOLS = ["L10nRegistry", "FileSource", "IndexedFileSource"];

View file

@ -16,15 +16,14 @@
*/
/* fluent@0.6.0 */
/* fluent@0.6.3 */
/* eslint no-console: ["error", { allow: ["warn", "error"] }] */
/* global console */
const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
const { L10nRegistry } = ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm", {});
const LocaleService = Cc["@mozilla.org/intl/localeservice;1"].getService(Ci.mozILocaleService);
const ObserverService = Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
/*
* CachedIterable caches the elements yielded by an iterable.
@ -45,7 +44,7 @@ class CachedIterable {
} else if (Symbol.iterator in Object(iterable)) {
this.iterator = iterable[Symbol.iterator]();
} else {
throw new TypeError('Argument must implement the iteration protocol.');
throw new TypeError("Argument must implement the iteration protocol.");
}
this.seen = [];
@ -104,7 +103,7 @@ class CachedIterable {
class L10nError extends Error {
constructor(message) {
super();
this.name = 'L10nError';
this.name = "L10nError";
this.message = message;
}
}
@ -121,7 +120,7 @@ class L10nError extends Error {
function defaultGenerateMessages(resourceIds) {
const availableLocales = L10nRegistry.getAvailableLocales();
const appLocales = LocaleService.getAppLocalesAsLangTags();
const appLocales = Services.locale.getAppLocalesAsLangTags();
return L10nRegistry.generateContexts(appLocales, resourceIds);
}
@ -162,7 +161,7 @@ class Localization {
for await (let ctx of this.ctxs) {
// This can operate on synchronous and asynchronous
// contexts coming from the iterator.
if (typeof ctx.then === 'function') {
if (typeof ctx.then === "function") {
ctx = await ctx;
}
const errors = keysFromContext(method, ctx, keys, translations);
@ -254,14 +253,14 @@ class Localization {
* Register weak observers on events that will trigger cache invalidation
*/
registerObservers() {
ObserverService.addObserver(this, 'intl:app-locales-changed', true);
Services.obs.addObserver(this, 'intl:app-locales-changed', true);
}
/**
* Unregister observers on events that will trigger cache invalidation
*/
unregisterObservers() {
ObserverService.removeObserver(this, 'intl:app-locales-changed');
Services.obs.removeObserver(this, 'intl:app-locales-changed');
}
/**
@ -273,7 +272,7 @@ class Localization {
*/
observe(subject, topic, data) {
switch (topic) {
case 'intl:app-locales-changed':
case "intl:app-locales-changed":
this.onLanguageChange();
break;
default:
@ -435,4 +434,4 @@ function keysFromContext(method, ctx, keys, translations) {
}
this.Localization = Localization;
this.EXPORTED_SYMBOLS = ['Localization'];
this.EXPORTED_SYMBOLS = ["Localization"];

View file

@ -16,7 +16,7 @@
*/
/* fluent@0.6.0 */
/* fluent@0.6.3 */
/* eslint no-magic-numbers: [0] */
@ -86,7 +86,7 @@ class RuntimeParser {
// The index here should either be at the beginning of the file
// or right after new line.
if (this._index !== 0 &&
this._source[this._index - 1] !== '\n') {
this._source[this._index - 1] !== "\n") {
throw this.error(`Expected an entry to start
at the beginning of the file or on a new line.`);
}
@ -94,14 +94,14 @@ class RuntimeParser {
const ch = this._source[this._index];
// We don't care about comments or sections at runtime
if (ch === '/' ||
(ch === '#' &&
[' ', '#', '\n'].includes(this._source[this._index + 1]))) {
if (ch === "/" ||
(ch === "#" &&
[" ", "#", "\n"].includes(this._source[this._index + 1]))) {
this.skipComment();
return;
}
if (ch === '[') {
if (ch === "[") {
this.skipSection();
return;
}
@ -116,7 +116,7 @@ class RuntimeParser {
*/
skipSection() {
this._index += 1;
if (this._source[this._index] !== '[') {
if (this._source[this._index] !== "[") {
throw this.error('Expected "[[" to open a section');
}
@ -126,8 +126,8 @@ class RuntimeParser {
this.getVariantName();
this.skipInlineWS();
if (this._source[this._index] !== ']' ||
this._source[this._index + 1] !== ']') {
if (this._source[this._index] !== "]" ||
this._source[this._index + 1] !== "]") {
throw this.error('Expected "]]" to close a section');
}
@ -145,7 +145,7 @@ class RuntimeParser {
this.skipInlineWS();
if (this._source[this._index] === '=') {
if (this._source[this._index] === "=") {
this._index++;
}
@ -153,27 +153,27 @@ class RuntimeParser {
const val = this.getPattern();
if (id.startsWith('-') && val === null) {
throw this.error('Expected term to have a value');
if (id.startsWith("-") && val === null) {
throw this.error("Expected term to have a value");
}
let attrs = null;
if (this._source[this._index] === ' ') {
if (this._source[this._index] === " ") {
const lineStart = this._index;
this.skipInlineWS();
if (this._source[this._index] === '.') {
if (this._source[this._index] === ".") {
this._index = lineStart;
attrs = this.getAttributes();
}
}
if (attrs === null && typeof val === 'string') {
if (attrs === null && typeof val === "string") {
this.entries[id] = val;
} else {
if (val === null && attrs === null) {
throw this.error('Expected message to have a value or attributes');
throw this.error("Expected message to have a value or attributes");
}
this.entries[id] = {};
@ -195,7 +195,7 @@ class RuntimeParser {
*/
skipWS() {
let ch = this._source[this._index];
while (ch === ' ' || ch === '\n' || ch === '\t' || ch === '\r') {
while (ch === " " || ch === "\n" || ch === "\t" || ch === "\r") {
ch = this._source[++this._index];
}
}
@ -207,7 +207,7 @@ class RuntimeParser {
*/
skipInlineWS() {
let ch = this._source[this._index];
while (ch === ' ' || ch === '\t') {
while (ch === " " || ch === "\t") {
ch = this._source[++this._index];
}
}
@ -223,7 +223,7 @@ class RuntimeParser {
this.skipInlineWS();
if (this._source[this._index] === '\n') {
if (this._source[this._index] === "\n") {
this._index += 1;
} else {
this._index = ptr;
@ -271,7 +271,7 @@ class RuntimeParser {
* @private
*/
getVariantName() {
let name = '';
let name = "";
const start = this._index;
let cc = this._source.charCodeAt(this._index);
@ -281,7 +281,7 @@ class RuntimeParser {
cc === 95 || cc === 32) { // _ <space>
cc = this._source.charCodeAt(++this._index);
} else {
throw this.error('Expected a keyword (starting with [a-zA-Z_])');
throw this.error("Expected a keyword (starting with [a-zA-Z_])");
}
while ((cc >= 97 && cc <= 122) || // a-z
@ -301,7 +301,7 @@ class RuntimeParser {
name += this._source.slice(start, this._index);
return { type: 'varname', name };
return { type: "varname", name };
}
/**
@ -320,8 +320,8 @@ class RuntimeParser {
break;
}
if (ch === '\n') {
throw this.error('Unterminated string expression');
if (ch === "\n") {
throw this.error("Unterminated string expression");
}
}
@ -343,7 +343,7 @@ class RuntimeParser {
// Then, if either the line contains a placeable opening `{` or the
// next line starts an indentation, we switch to complex pattern.
const start = this._index;
let eol = this._source.indexOf('\n', this._index);
let eol = this._source.indexOf("\n", this._index);
if (eol === -1) {
eol = this._length;
@ -352,7 +352,7 @@ class RuntimeParser {
const firstLineContent = start !== eol ?
this._source.slice(start, eol) : null;
if (firstLineContent && firstLineContent.includes('{')) {
if (firstLineContent && firstLineContent.includes("{")) {
return this.getComplexPattern();
}
@ -360,7 +360,7 @@ class RuntimeParser {
this.skipBlankLines();
if (this._source[this._index] !== ' ') {
if (this._source[this._index] !== " ") {
// No indentation means we're done with this message. Callers should check
// if the return value here is null. It may be OK for messages, but not OK
// for terms, attributes and variants.
@ -371,7 +371,7 @@ class RuntimeParser {
this.skipInlineWS();
if (this._source[this._index] === '.') {
if (this._source[this._index] === ".") {
// The pattern is followed by an attribute. Rewind _index to the first
// column of the current line as expected by getAttributes.
this._index = lineStart;
@ -398,7 +398,7 @@ class RuntimeParser {
*/
/* eslint-disable complexity */
getComplexPattern() {
let buffer = '';
let buffer = "";
const content = [];
let placeables = 0;
@ -407,7 +407,7 @@ class RuntimeParser {
while (this._index < this._length) {
// This block handles multi-line strings combining strings separated
// by new line.
if (ch === '\n') {
if (ch === "\n") {
this._index++;
// We want to capture the start and end pointers
@ -419,15 +419,15 @@ class RuntimeParser {
const blankLinesEnd = this._index;
if (this._source[this._index] !== ' ') {
if (this._source[this._index] !== " ") {
break;
}
this.skipInlineWS();
if (this._source[this._index] === '}' ||
this._source[this._index] === '[' ||
this._source[this._index] === '*' ||
this._source[this._index] === '.') {
if (this._source[this._index] === "}" ||
this._source[this._index] === "[" ||
this._source[this._index] === "*" ||
this._source[this._index] === ".") {
this._index = blankLinesEnd;
break;
}
@ -435,17 +435,17 @@ class RuntimeParser {
buffer += this._source.substring(blankLinesStart, blankLinesEnd);
if (buffer.length || content.length) {
buffer += '\n';
buffer += "\n";
}
ch = this._source[this._index];
continue;
} else if (ch === '\\') {
} else if (ch === "\\") {
const ch2 = this._source[this._index + 1];
if (ch2 === '"' || ch2 === '{' || ch2 === '\\') {
if (ch2 === '"' || ch2 === "{" || ch2 === "\\") {
ch = ch2;
this._index++;
}
} else if (ch === '{') {
} else if (ch === "{") {
// Push the buffer to content array right before placeable
if (buffer.length) {
content.push(buffer);
@ -454,7 +454,7 @@ class RuntimeParser {
throw this.error(
`Too many placeables, maximum allowed is ${MAX_PLACEABLES}`);
}
buffer = '';
buffer = "";
content.push(this.getPlaceable());
this._index++;
@ -495,13 +495,13 @@ class RuntimeParser {
this.skipWS();
if (this._source[this._index] === '*' ||
(this._source[this._index] === '[' &&
this._source[this._index + 1] !== ']')) {
if (this._source[this._index] === "*" ||
(this._source[this._index] === "[" &&
this._source[this._index + 1] !== "]")) {
const variants = this.getVariants();
return {
type: 'sel',
type: "sel",
exp: null,
vars: variants[0],
def: variants[1]
@ -518,31 +518,31 @@ class RuntimeParser {
const ch = this._source[this._index];
if (ch === '}') {
if (selector.type === 'attr' && selector.id.name.startsWith('-')) {
if (ch === "}") {
if (selector.type === "attr" && selector.id.name.startsWith("-")) {
throw this.error(
'Attributes of private messages cannot be interpolated.'
"Attributes of private messages cannot be interpolated."
);
}
return selector;
}
if (ch !== '-' || this._source[this._index + 1] !== '>') {
if (ch !== "-" || this._source[this._index + 1] !== ">") {
throw this.error('Expected "}" or "->"');
}
if (selector.type === 'ref') {
throw this.error('Message references cannot be used as selectors.');
if (selector.type === "ref") {
throw this.error("Message references cannot be used as selectors.");
}
if (selector.type === 'var') {
throw this.error('Variants cannot be used as selectors.');
if (selector.type === "var") {
throw this.error("Variants cannot be used as selectors.");
}
if (selector.type === 'attr' && !selector.id.name.startsWith('-')) {
if (selector.type === "attr" && !selector.id.name.startsWith("-")) {
throw this.error(
'Attributes of public messages cannot be used as selectors.'
"Attributes of public messages cannot be used as selectors."
);
}
@ -551,8 +551,8 @@ class RuntimeParser {
this.skipInlineWS();
if (this._source[this._index] !== '\n') {
throw this.error('Variants should be listed in a new line');
if (this._source[this._index] !== "\n") {
throw this.error("Variants should be listed in a new line");
}
this.skipWS();
@ -560,11 +560,11 @@ class RuntimeParser {
const variants = this.getVariants();
if (variants[0].length === 0) {
throw this.error('Expected members for the select expression');
throw this.error("Expected members for the select expression");
}
return {
type: 'sel',
type: "sel",
exp: selector,
vars: variants[0],
def: variants[1]
@ -580,48 +580,48 @@ class RuntimeParser {
getSelectorExpression() {
const literal = this.getLiteral();
if (literal.type !== 'ref') {
if (literal.type !== "ref") {
return literal;
}
if (this._source[this._index] === '.') {
if (this._source[this._index] === ".") {
this._index++;
const name = this.getIdentifier();
this._index++;
return {
type: 'attr',
type: "attr",
id: literal,
name
};
}
if (this._source[this._index] === '[') {
if (this._source[this._index] === "[") {
this._index++;
const key = this.getVariantKey();
this._index++;
return {
type: 'var',
type: "var",
id: literal,
key
};
}
if (this._source[this._index] === '(') {
if (this._source[this._index] === "(") {
this._index++;
const args = this.getCallArgs();
if (!functionIdentifierRe.test(literal.name)) {
throw this.error('Function names must be all upper-case');
throw this.error("Function names must be all upper-case");
}
this._index++;
literal.type = 'fun';
literal.type = "fun";
return {
type: 'call',
type: "call",
fun: literal,
args
};
@ -642,7 +642,7 @@ class RuntimeParser {
while (this._index < this._length) {
this.skipInlineWS();
if (this._source[this._index] === ')') {
if (this._source[this._index] === ")") {
return args;
}
@ -650,12 +650,12 @@ class RuntimeParser {
// MessageReference in this place may be an entity reference, like:
// `call(foo)`, or, if it's followed by `:` it will be a key-value pair.
if (exp.type !== 'ref') {
if (exp.type !== "ref") {
args.push(exp);
} else {
this.skipInlineWS();
if (this._source[this._index] === ':') {
if (this._source[this._index] === ":") {
this._index++;
this.skipInlineWS();
@ -666,18 +666,18 @@ class RuntimeParser {
//
// We don't have to check here if the pattern is quote delimited
// because that's the only type of string allowed in expressions.
if (typeof val === 'string' ||
if (typeof val === "string" ||
Array.isArray(val) ||
val.type === 'num') {
val.type === "num") {
args.push({
type: 'narg',
type: "narg",
name: exp.name,
val
});
} else {
this._index = this._source.lastIndexOf(':', this._index) + 1;
this._index = this._source.lastIndexOf(":", this._index) + 1;
throw this.error(
'Expected string in quotes, number.');
"Expected string in quotes, number.");
}
} else {
@ -687,9 +687,9 @@ class RuntimeParser {
this.skipInlineWS();
if (this._source[this._index] === ')') {
if (this._source[this._index] === ")") {
break;
} else if (this._source[this._index] === ',') {
} else if (this._source[this._index] === ",") {
this._index++;
} else {
throw this.error('Expected "," or ")"');
@ -706,12 +706,12 @@ class RuntimeParser {
* @private
*/
getNumber() {
let num = '';
let num = "";
let cc = this._source.charCodeAt(this._index);
// The number literal may start with negative sign `-`.
if (cc === 45) {
num += '-';
num += "-";
cc = this._source.charCodeAt(++this._index);
}
@ -744,7 +744,7 @@ class RuntimeParser {
}
return {
type: 'num',
type: "num",
val: num
};
}
@ -759,12 +759,12 @@ class RuntimeParser {
const attrs = {};
while (this._index < this._length) {
if (this._source[this._index] !== ' ') {
if (this._source[this._index] !== " ") {
break;
}
this.skipInlineWS();
if (this._source[this._index] !== '.') {
if (this._source[this._index] !== ".") {
break;
}
this._index++;
@ -773,7 +773,7 @@ class RuntimeParser {
this.skipInlineWS();
if (this._source[this._index] !== '=') {
if (this._source[this._index] !== "=") {
throw this.error('Expected "="');
}
this._index++;
@ -783,10 +783,10 @@ class RuntimeParser {
const val = this.getPattern();
if (val === null) {
throw this.error('Expected attribute to have a value');
throw this.error("Expected attribute to have a value");
}
if (typeof val === 'string') {
if (typeof val === "string") {
attrs[key] = val;
} else {
attrs[key] = {
@ -814,16 +814,16 @@ class RuntimeParser {
while (this._index < this._length) {
const ch = this._source[this._index];
if ((ch !== '[' || this._source[this._index + 1] === '[') &&
ch !== '*') {
if ((ch !== "[" || this._source[this._index + 1] === "[") &&
ch !== "*") {
break;
}
if (ch === '*') {
if (ch === "*") {
this._index++;
defaultIndex = index;
}
if (this._source[this._index] !== '[') {
if (this._source[this._index] !== "[") {
throw this.error('Expected "["');
}
@ -836,7 +836,7 @@ class RuntimeParser {
const val = this.getPattern();
if (val === null) {
throw this.error('Expected variant to have a value');
throw this.error("Expected variant to have a value");
}
variants[index++] = {key, val};
@ -865,7 +865,7 @@ class RuntimeParser {
literal = this.getVariantName();
}
if (this._source[this._index] !== ']') {
if (this._source[this._index] !== "]") {
throw this.error('Expected "]"');
}
@ -885,7 +885,7 @@ class RuntimeParser {
if (cc0 === 36) { // $
this._index++;
return {
type: 'ext',
type: "ext",
name: this.getIdentifier()
};
}
@ -899,7 +899,7 @@ class RuntimeParser {
if ((cc1 >= 97 && cc1 <= 122) || // a-z
(cc1 >= 65 && cc1 <= 90)) { // A-Z
return {
type: 'ref',
type: "ref",
name: this.getEntryIdentifier()
};
}
@ -912,7 +912,7 @@ class RuntimeParser {
return this.getString();
}
throw this.error('Expected literal');
throw this.error("Expected literal");
}
/**
@ -923,15 +923,15 @@ class RuntimeParser {
skipComment() {
// At runtime, we don't care about comments so we just have
// to parse them properly and skip their content.
let eol = this._source.indexOf('\n', this._index);
let eol = this._source.indexOf("\n", this._index);
while (eol !== -1 &&
((this._source[eol + 1] === '/' && this._source[eol + 2] === '/') ||
(this._source[eol + 1] === '#' &&
[' ', '#'].includes(this._source[eol + 2])))) {
((this._source[eol + 1] === "/" && this._source[eol + 2] === "/") ||
(this._source[eol + 1] === "#" &&
[" ", "#"].includes(this._source[eol + 2])))) {
this._index = eol + 3;
eol = this._source.indexOf('\n', this._index);
eol = this._source.indexOf("\n", this._index);
if (eol === -1) {
break;
@ -967,7 +967,7 @@ class RuntimeParser {
let start = this._index;
while (true) {
if (start === 0 || this._source[start - 1] === '\n') {
if (start === 0 || this._source[start - 1] === "\n") {
const cc = this._source.charCodeAt(start);
if ((cc >= 97 && cc <= 122) || // a-z
@ -978,7 +978,7 @@ class RuntimeParser {
}
}
start = this._source.indexOf('\n', start);
start = this._source.indexOf("\n", start);
if (start === -1) {
this._index = this._length;
@ -1044,13 +1044,13 @@ class FluentType {
* @returns {string}
*/
toString() {
throw new Error('Subclasses of FluentType must implement toString.');
throw new Error("Subclasses of FluentType must implement toString.");
}
}
class FluentNone extends FluentType {
toString() {
return this.value || '???';
return this.value || "???";
}
}
@ -1119,7 +1119,7 @@ class FluentSymbol extends FluentType {
match(ctx, other) {
if (other instanceof FluentSymbol) {
return this.value === other.value;
} else if (typeof other === 'string') {
} else if (typeof other === "string") {
return this.value === other;
} else if (other instanceof FluentNumber) {
const pr = ctx._memoizeIntlObject(
@ -1145,9 +1145,9 @@ class FluentSymbol extends FluentType {
*/
const builtins = {
'NUMBER': ([arg], opts) =>
"NUMBER": ([arg], opts) =>
new FluentNumber(arg.valueOf(), merge(arg.opts, opts)),
'DATETIME': ([arg], opts) =>
"DATETIME": ([arg], opts) =>
new FluentDateTime(arg.valueOf(), merge(arg.opts, opts)),
};
@ -1211,13 +1211,12 @@ function values(opts) {
* This is used to prevent cyclic resolutions.
*/
// Prevent expansion of too long placeables.
const MAX_PLACEABLE_LENGTH = 2500;
// Unicode bidi isolation characters.
const FSI = '\u2068';
const PDI = '\u2069';
const FSI = "\u2068";
const PDI = "\u2069";
/**
@ -1240,7 +1239,7 @@ function DefaultMember(env, members, def) {
}
const { errors } = env;
errors.push(new RangeError('No default'));
errors.push(new RangeError("No default"));
return new FluentNone();
}
@ -1259,12 +1258,12 @@ function DefaultMember(env, members, def) {
*/
function MessageReference(env, {name}) {
const { ctx, errors } = env;
const message = name.startsWith('-')
const message = name.startsWith("-")
? ctx._terms.get(name)
: ctx._messages.get(name);
if (!message) {
const err = name.startsWith('-')
const err = name.startsWith("-")
? new ReferenceError(`Unknown term: ${name}`)
: new ReferenceError(`Unknown message: ${name}`);
errors.push(err);
@ -1301,7 +1300,7 @@ function VariantExpression(env, {id, key}) {
function isVariantList(node) {
return Array.isArray(node) &&
node[0].type === 'sel' &&
node[0].type === "sel" &&
node[0].exp === null;
}
@ -1418,7 +1417,7 @@ function SelectExpression(env, {exp, vars, def}) {
function Type(env, expr) {
// A fast-path for strings which are the most common case, and for
// `FluentNone` which doesn't require any additional logic.
if (typeof expr === 'string' || expr instanceof FluentNone) {
if (typeof expr === "string" || expr instanceof FluentNone) {
return expr;
}
@ -1430,29 +1429,29 @@ function Type(env, expr) {
switch (expr.type) {
case 'varname':
case "varname":
return new FluentSymbol(expr.name);
case 'num':
case "num":
return new FluentNumber(expr.val);
case 'ext':
case "ext":
return ExternalArgument(env, expr);
case 'fun':
case "fun":
return FunctionReference(env, expr);
case 'call':
case "call":
return CallExpression(env, expr);
case 'ref': {
case "ref": {
const message = MessageReference(env, expr);
return Type(env, message);
}
case 'attr': {
case "attr": {
const attr = AttributeExpression(env, expr);
return Type(env, attr);
}
case 'var': {
case "var": {
const variant = VariantExpression(env, expr);
return Type(env, variant);
}
case 'sel': {
case "sel": {
const member = SelectExpression(env, expr);
return Type(env, member);
}
@ -1463,7 +1462,7 @@ function Type(env, expr) {
}
const { errors } = env;
errors.push(new RangeError('No value'));
errors.push(new RangeError("No value"));
return new FluentNone();
}
default:
@ -1500,11 +1499,11 @@ function ExternalArgument(env, {name}) {
// Convert the argument to a Fluent type.
switch (typeof arg) {
case 'string':
case "string":
return arg;
case 'number':
case "number":
return new FluentNumber(arg);
case 'object':
case "object":
if (arg instanceof Date) {
return new FluentDateTime(arg);
}
@ -1539,7 +1538,7 @@ function FunctionReference(env, {name}) {
return new FluentNone(`${name}()`);
}
if (typeof func !== 'function') {
if (typeof func !== "function") {
errors.push(new TypeError(`Function ${name}() is not callable`));
return new FluentNone(`${name}()`);
}
@ -1572,7 +1571,7 @@ function CallExpression(env, {fun, args}) {
const keyargs = {};
for (const arg of args) {
if (arg.type === 'narg') {
if (arg.type === "narg") {
keyargs[arg.name] = Type(env, arg.val);
} else {
posargs.push(Type(env, arg));
@ -1601,7 +1600,7 @@ function Pattern(env, ptn) {
const { ctx, dirty, errors } = env;
if (dirty.has(ptn)) {
errors.push(new RangeError('Cyclic reference'));
errors.push(new RangeError("Cyclic reference"));
return new FluentNone();
}
@ -1610,7 +1609,7 @@ function Pattern(env, ptn) {
const result = [];
for (const elem of ptn) {
if (typeof elem === 'string') {
if (typeof elem === "string") {
result.push(elem);
continue;
}
@ -1624,7 +1623,7 @@ function Pattern(env, ptn) {
if (part.length > MAX_PLACEABLE_LENGTH) {
errors.push(
new RangeError(
'Too many characters in placeable ' +
"Too many characters in placeable " +
`(${part.length}, max allowed is ${MAX_PLACEABLE_LENGTH})`
)
);
@ -1639,7 +1638,7 @@ function Pattern(env, ptn) {
}
dirty.delete(ptn);
return result.join('');
return result.join("");
}
/**
@ -1773,7 +1772,7 @@ class MessageContext {
addMessages(source) {
const [entries, errors] = parse(source);
for (const id in entries) {
if (id.startsWith('-')) {
if (id.startsWith("-")) {
// Identifiers starting with a dash (-) define terms. Terms are private
// and cannot be retrieved from MessageContext.
this._terms.set(id, entries[id]);
@ -1817,12 +1816,12 @@ class MessageContext {
*/
format(message, args, errors) {
// optimize entities which are simple strings with no attributes
if (typeof message === 'string') {
if (typeof message === "string") {
return message;
}
// optimize simple-string entities with attributes
if (typeof message.val === 'string') {
if (typeof message.val === "string") {
return message.val;
}
@ -1848,4 +1847,4 @@ class MessageContext {
}
this.MessageContext = MessageContext;
this.EXPORTED_SYMBOLS = ['MessageContext'];
this.EXPORTED_SYMBOLS = ["MessageContext"];

View file

@ -1,6 +1,6 @@
{
const { DOMLocalization } =
ChromeUtils.import("resource://gre/modules/DOMLocalization.jsm");
ChromeUtils.import("resource://gre/modules/DOMLocalization.jsm", {});
/**
* Polyfill for document.ready polyfill.
@ -14,11 +14,11 @@
* @returns {Promise}
*/
function documentReady(callback) {
if (document.contentType === 'application/vnd.mozilla.xul+xml') {
if (document.contentType === "application/vnd.mozilla.xul+xml") {
// XUL
return new Promise(
resolve => document.addEventListener(
'MozBeforeInitialXULLayout', () => {
"MozBeforeInitialXULLayout", () => {
resolve(callback());
}, { once: true }
)
@ -27,12 +27,12 @@
// HTML
const rs = document.readyState;
if (rs === 'interactive' || rs === 'completed') {
return Promise.resolve(callback());
if (rs === "interactive" || rs === "completed") {
return Promise.resolve(callback);
}
return new Promise(
resolve => document.addEventListener(
'readystatechange', () => {
"readystatechange", () => {
resolve(callback());
}, { once: true }
)
@ -47,7 +47,7 @@
*/
function getResourceLinks(elem) {
return Array.from(elem.querySelectorAll('link[rel="localization"]')).map(
el => el.getAttribute('href')
el => el.getAttribute("href")
);
}
@ -60,7 +60,7 @@
document.l10n.ready = documentReady(() => {
document.l10n.registerObservers();
window.addEventListener('unload', () => {
window.addEventListener("unload", () => {
document.l10n.unregisterObservers();
});
document.l10n.connectRoot(document.documentElement);

View file

@ -0,0 +1,8 @@
"use strict";
module.exports = {
"extends": [
"plugin:mozilla/xpcshell-test",
"plugin:mozilla/browser-test"
]
};

View file

@ -10,10 +10,10 @@
const { DOMLocalization } =
ChromeUtils.import("resource://gre/modules/DOMLocalization.jsm", {});
async function * mockGenerateMessages(locales, resourceIds) {
async function* mockGenerateMessages(locales, resourceIds) {
}
window.onload = async function () {
window.onload = async function() {
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
@ -23,7 +23,7 @@
);
const frag = document.querySelectorAll('div')[0];
const frag = document.querySelectorAll("div")[0];
domLoc.connectRoot(frag);
is(domLoc.roots.has(frag), true);

View file

@ -10,10 +10,10 @@
const { DOMLocalization } =
ChromeUtils.import("resource://gre/modules/DOMLocalization.jsm", {});
async function * mockGenerateMessages(locales, resourceIds) {
async function* mockGenerateMessages(locales, resourceIds) {
}
window.onload = async function () {
window.onload = async function() {
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
@ -22,7 +22,7 @@
mockGenerateMessages
);
const frag = document.querySelectorAll('div')[0];
const frag = document.querySelectorAll("div")[0];
domLoc.connectRoot(frag);
is(domLoc.roots.has(frag), true);

View file

@ -10,9 +10,9 @@
const { DOMLocalization } =
ChromeUtils.import("resource://gre/modules/DOMLocalization.jsm", {});
async function * mockGenerateMessages(locales, resourceIds) {}
async function* mockGenerateMessages(locales, resourceIds) {}
window.onload = function () {
window.onload = function() {
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
@ -21,9 +21,9 @@
mockGenerateMessages
);
const p1 = document.querySelectorAll('p')[0];
const p2 = document.querySelectorAll('p')[1];
const p3 = document.querySelectorAll('p')[2];
const p1 = document.querySelectorAll("p")[0];
const p2 = document.querySelectorAll("p")[1];
const p3 = document.querySelectorAll("p")[2];
const attrs1 = domLoc.getAttributes(p1);
const attrs2 = domLoc.getAttributes(p2);
const attrs3 = domLoc.getAttributes(p3);

View file

@ -12,14 +12,14 @@
const { MessageContext } =
ChromeUtils.import("resource://gre/modules/MessageContext.jsm", {});
async function * mockGenerateMessages(locales, resourceIds) {
async function* mockGenerateMessages(locales, resourceIds) {
const mc = new MessageContext(locales);
mc.addMessages('title = Hello World');
mc.addMessages('title2 = Hello Another World');
mc.addMessages("title = Hello World");
mc.addMessages("title2 = Hello Another World");
yield mc;
}
window.onload = async function () {
window.onload = async function() {
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
@ -28,7 +28,7 @@
mockGenerateMessages
);
const h1 = document.querySelectorAll('h1')[0];
const h1 = document.querySelectorAll("h1")[0];
domLoc.connectRoot(document.body);
@ -45,7 +45,7 @@
mo.observe(h1, { childList: true, characterData: true });
domLoc.setAttributes(h1, 'title2');
domLoc.setAttributes(h1, "title2");
};
</script>
</head>

View file

@ -12,14 +12,14 @@
const { MessageContext } =
ChromeUtils.import("resource://gre/modules/MessageContext.jsm", {});
async function * mockGenerateMessages(locales, resourceIds) {
async function* mockGenerateMessages(locales, resourceIds) {
const mc = new MessageContext(locales);
mc.addMessages('title = <strong>Hello</strong> World');
mc.addMessages('title2 = This is <a>a link</a>!');
mc.addMessages("title = <strong>Hello</strong> World");
mc.addMessages("title2 = This is <a>a link</a>!");
yield mc;
}
window.onload = async function () {
window.onload = async function() {
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
@ -28,20 +28,20 @@
mockGenerateMessages
);
const p1 = document.querySelectorAll('p')[0];
const p2 = document.querySelectorAll('p')[1];
const a = p2.querySelector('a');
a.addEventListener('click', function() {
const p1 = document.querySelectorAll("p")[0];
const p2 = document.querySelectorAll("p")[1];
const a = p2.querySelector("a");
a.addEventListener("click", function() {
SimpleTest.finish();
});
await domLoc.translateFragment(document.body);
is(p1.querySelector('strong').textContent, "Hello");
is(p1.querySelector("strong").textContent, "Hello");
is(p2.querySelector('a').getAttribute('href'), "http://www.mozilla.org");
is(p2.querySelector('a').textContent, "a link");
is(p2.querySelector("a").getAttribute("href"), "http://www.mozilla.org");
is(p2.querySelector("a").textContent, "a link");
a.click();
};

View file

@ -12,13 +12,13 @@
const { MessageContext } =
ChromeUtils.import("resource://gre/modules/MessageContext.jsm", {});
async function * mockGenerateMessages(locales, resourceIds) {
async function* mockGenerateMessages(locales, resourceIds) {
const mc = new MessageContext(locales);
mc.addMessages('title = Visit <a>Mozilla</a> or <a>Firefox</a> website!');
mc.addMessages("title = Visit <a>Mozilla</a> or <a>Firefox</a> website!");
yield mc;
}
window.onload = async function () {
window.onload = async function() {
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
@ -29,14 +29,14 @@
await domLoc.translateFragment(document.body);
const p1 = document.querySelectorAll('p')[0];
const linkList = p1.querySelectorAll('a');
const p1 = document.querySelectorAll("p")[0];
const linkList = p1.querySelectorAll("a");
is(linkList[0].getAttribute('href'), 'http://www.mozilla.org');
is(linkList[0].textContent, 'Mozilla');
is(linkList[1].getAttribute('href'), 'http://www.firefox.com');
is(linkList[1].textContent, 'Firefox');
is(linkList[0].getAttribute("href"), "http://www.mozilla.org");
is(linkList[0].textContent, "Mozilla");
is(linkList[1].getAttribute("href"), "http://www.firefox.com");
is(linkList[1].textContent, "Firefox");
is(linkList.length, 2, "There should be exactly two links in the result.");

View file

@ -12,13 +12,13 @@
const { MessageContext } =
ChromeUtils.import("resource://gre/modules/MessageContext.jsm", {});
async function * mockGenerateMessages(locales, resourceIds) {
async function* mockGenerateMessages(locales, resourceIds) {
const mc = new MessageContext(locales);
mc.addMessages('title = Visit <a>Mozilla</a> or <a>Firefox</a> website!');
mc.addMessages("title = Visit <a>Mozilla</a> or <a>Firefox</a> website!");
yield mc;
}
window.onload = async function () {
window.onload = async function() {
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
@ -29,14 +29,14 @@
await domLoc.translateFragment(document.body);
const p1 = document.querySelectorAll('p')[0];
const linkList = p1.querySelectorAll('a');
const p1 = document.querySelectorAll("p")[0];
const linkList = p1.querySelectorAll("a");
is(linkList[0].getAttribute('href'), 'http://www.mozilla.org');
is(linkList[0].textContent, 'Mozilla');
is(linkList[1].getAttribute('href'), 'http://www.firefox.com');
is(linkList[1].textContent, 'Firefox');
is(linkList[0].getAttribute("href"), "http://www.mozilla.org");
is(linkList[0].textContent, "Mozilla");
is(linkList[1].getAttribute("href"), "http://www.firefox.com");
is(linkList[1].textContent, "Firefox");
SimpleTest.finish();
};

View file

@ -10,9 +10,9 @@
const { DOMLocalization } =
ChromeUtils.import("resource://gre/modules/DOMLocalization.jsm", {});
async function * mockGenerateMessages(locales, resourceIds) {}
async function* mockGenerateMessages(locales, resourceIds) {}
window.onload = function () {
window.onload = function() {
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
@ -21,14 +21,14 @@
mockGenerateMessages
);
const p1 = document.querySelectorAll('p')[0];
const p1 = document.querySelectorAll("p")[0];
domLoc.setAttributes(p1, 'title');
is(p1.getAttribute('data-l10n-id'), 'title');
domLoc.setAttributes(p1, "title");
is(p1.getAttribute("data-l10n-id"), "title");
domLoc.setAttributes(p1, 'title2', {userName: "John"});
is(p1.getAttribute('data-l10n-id'), 'title2');
is(p1.getAttribute('data-l10n-args'), JSON.stringify({userName: "John"}));
domLoc.setAttributes(p1, "title2", {userName: "John"});
is(p1.getAttribute("data-l10n-id"), "title2");
is(p1.getAttribute("data-l10n-args"), JSON.stringify({userName: "John"}));
SimpleTest.finish();
};

View file

@ -12,14 +12,14 @@
const { MessageContext } =
ChromeUtils.import("resource://gre/modules/MessageContext.jsm", {});
async function * mockGenerateMessages(locales, resourceIds) {
async function* mockGenerateMessages(locales, resourceIds) {
const mc = new MessageContext(locales);
mc.addMessages('title = Hello World');
mc.addMessages('link\n .title = Click me');
mc.addMessages("title = Hello World");
mc.addMessages("link\n .title = Click me");
yield mc;
}
window.onload = async function () {
window.onload = async function() {
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
@ -28,13 +28,13 @@
mockGenerateMessages
);
const p1 = document.querySelectorAll('p')[0];
const link1 = document.querySelectorAll('a')[0];
const p1 = document.querySelectorAll("p")[0];
const link1 = document.querySelectorAll("a")[0];
await domLoc.translateElements([p1, link1]);
is(p1.textContent, "Hello World");
is(link1.getAttribute('title'), "Click me");
is(link1.getAttribute("title"), "Click me");
SimpleTest.finish();
};

View file

@ -12,14 +12,14 @@
const { MessageContext } =
ChromeUtils.import("resource://gre/modules/MessageContext.jsm", {});
async function * mockGenerateMessages(locales, resourceIds) {
async function* mockGenerateMessages(locales, resourceIds) {
const mc = new MessageContext(locales);
mc.addMessages('title = Hello World');
mc.addMessages('subtitle = Welcome to Fluent');
mc.addMessages("title = Hello World");
mc.addMessages("subtitle = Welcome to Fluent");
yield mc;
}
window.onload = async function () {
window.onload = async function() {
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
@ -28,9 +28,9 @@
mockGenerateMessages
);
const frag = document.querySelectorAll('div')[0];
const h1 = document.querySelectorAll('h1')[0];
const p1 = document.querySelectorAll('p')[0];
const frag = document.querySelectorAll("div")[0];
const h1 = document.querySelectorAll("h1")[0];
const p1 = document.querySelectorAll("p")[0];
await domLoc.translateFragment(frag);
is(h1.textContent, "Hello World");

View file

@ -12,14 +12,14 @@
const { MessageContext } =
ChromeUtils.import("resource://gre/modules/MessageContext.jsm", {});
async function * mockGenerateMessages(locales, resourceIds) {
async function* mockGenerateMessages(locales, resourceIds) {
const mc = new MessageContext(locales);
mc.addMessages('title = Hello World');
mc.addMessages('title2 = Hello Another World');
mc.addMessages("title = Hello World");
mc.addMessages("title2 = Hello Another World");
yield mc;
}
window.onload = async function () {
window.onload = async function() {
SimpleTest.waitForExplicitFinish();
const domLoc = new DOMLocalization(
@ -28,10 +28,10 @@
mockGenerateMessages
);
const frag1 = document.querySelectorAll('div')[0];
const frag2 = document.querySelectorAll('div')[1];
const h1 = document.querySelectorAll('h1')[0];
const h2 = document.querySelectorAll('h2')[0];
const frag1 = document.querySelectorAll("div")[0];
const frag2 = document.querySelectorAll("div")[1];
const h1 = document.querySelectorAll("h1")[0];
const h2 = document.querySelectorAll("h2")[0];
domLoc.connectRoot(frag1);
domLoc.connectRoot(frag2);

View file

@ -11,10 +11,10 @@ ChromeUtils.import("resource://gre/modules/Timer.jsm");
let fs;
L10nRegistry.load = async function(url) {
if (!fs.hasOwnProperty(url)) {
return Promise.reject('Resource unavailable');
return Promise.reject("Resource unavailable");
}
return fs[url];
}
};
add_task(function test_methods_presence() {
equal(typeof L10nRegistry.generateContexts, "function");
@ -29,17 +29,17 @@ add_task(function test_methods_presence() {
*/
add_task(async function test_methods_calling() {
fs = {
'/localization/en-US/browser/menu.ftl': 'key = Value',
"/localization/en-US/browser/menu.ftl": "key = Value",
};
const source = new FileSource('test', ['en-US'], '/localization/{locale}');
const source = new FileSource("test", ["en-US"], "/localization/{locale}");
L10nRegistry.registerSource(source);
const ctxs = L10nRegistry.generateContexts(['en-US'], ['/browser/menu.ftl']);
const ctxs = L10nRegistry.generateContexts(["en-US"], ["/browser/menu.ftl"]);
const ctx = (await ctxs.next()).value;
equal(ctx.hasMessage('key'), true);
equal(ctx.hasMessage("key"), true);
// cleanup
L10nRegistry.sources.clear();
@ -51,9 +51,9 @@ add_task(async function test_methods_calling() {
* for the single source scenario
*/
add_task(async function test_has_one_source() {
let oneSource = new FileSource('app', ['en-US'], './app/data/locales/{locale}/');
let oneSource = new FileSource("app", ["en-US"], "./app/data/locales/{locale}/");
fs = {
'./app/data/locales/en-US/test.ftl': 'key = value en-US'
"./app/data/locales/en-US/test.ftl": "key = value en-US"
};
L10nRegistry.registerSource(oneSource);
@ -61,21 +61,21 @@ add_task(async function test_has_one_source() {
// has one source
equal(L10nRegistry.sources.size, 1);
equal(L10nRegistry.sources.has('app'), true);
equal(L10nRegistry.sources.has("app"), true);
// returns a single context
let ctxs = L10nRegistry.generateContexts(['en-US'], ['test.ftl']);
let ctxs = L10nRegistry.generateContexts(["en-US"], ["test.ftl"]);
let ctx0 = (await ctxs.next()).value;
equal(ctx0.hasMessage('key'), true);
equal(ctx0.hasMessage("key"), true);
equal((await ctxs.next()).done, true);
// returns no contexts for missing locale
ctxs = L10nRegistry.generateContexts(['pl'], ['test.ftl']);
ctxs = L10nRegistry.generateContexts(["pl"], ["test.ftl"]);
equal((await ctxs.next()).done, true);
@ -89,50 +89,50 @@ add_task(async function test_has_one_source() {
* for the dual source scenario.
*/
add_task(async function test_has_two_sources() {
let oneSource = new FileSource('platform', ['en-US'], './platform/data/locales/{locale}/');
let oneSource = new FileSource("platform", ["en-US"], "./platform/data/locales/{locale}/");
L10nRegistry.registerSource(oneSource);
let secondSource = new FileSource('app', ['pl'], './app/data/locales/{locale}/');
let secondSource = new FileSource("app", ["pl"], "./app/data/locales/{locale}/");
L10nRegistry.registerSource(secondSource);
fs = {
'./platform/data/locales/en-US/test.ftl': 'key = platform value',
'./app/data/locales/pl/test.ftl': 'key = app value'
"./platform/data/locales/en-US/test.ftl": "key = platform value",
"./app/data/locales/pl/test.ftl": "key = app value"
};
// has two sources
equal(L10nRegistry.sources.size, 2);
equal(L10nRegistry.sources.has('app'), true);
equal(L10nRegistry.sources.has('platform'), true);
equal(L10nRegistry.sources.has("app"), true);
equal(L10nRegistry.sources.has("platform"), true);
// returns correct contexts for en-US
let ctxs = L10nRegistry.generateContexts(['en-US'], ['test.ftl']);
let ctxs = L10nRegistry.generateContexts(["en-US"], ["test.ftl"]);
let ctx0 = (await ctxs.next()).value;
equal(ctx0.hasMessage('key'), true);
let msg = ctx0.getMessage('key');
equal(ctx0.format(msg), 'platform value');
equal(ctx0.hasMessage("key"), true);
let msg = ctx0.getMessage("key");
equal(ctx0.format(msg), "platform value");
equal((await ctxs.next()).done, true);
// returns correct contexts for [pl, en-US]
ctxs = L10nRegistry.generateContexts(['pl', 'en-US'], ['test.ftl']);
ctxs = L10nRegistry.generateContexts(["pl", "en-US"], ["test.ftl"]);
ctx0 = (await ctxs.next()).value;
equal(ctx0.locales[0], 'pl');
equal(ctx0.hasMessage('key'), true);
let msg0 = ctx0.getMessage('key');
equal(ctx0.format(msg0), 'app value');
equal(ctx0.locales[0], "pl");
equal(ctx0.hasMessage("key"), true);
let msg0 = ctx0.getMessage("key");
equal(ctx0.format(msg0), "app value");
let ctx1 = (await ctxs.next()).value;
equal(ctx1.locales[0], 'en-US');
equal(ctx1.hasMessage('key'), true);
let msg1 = ctx1.getMessage('key');
equal(ctx1.format(msg1), 'platform value');
equal(ctx1.locales[0], "en-US");
equal(ctx1.hasMessage("key"), true);
let msg1 = ctx1.getMessage("key");
equal(ctx1.format(msg1), "platform value");
equal((await ctxs.next()).done, true);
@ -149,20 +149,20 @@ add_task(async function test_has_two_sources() {
* missing files as `false` instead of `undefined`.
*/
add_task(async function test_indexed() {
let oneSource = new IndexedFileSource('langpack-pl', ['pl'], '/data/locales/{locale}/', [
'/data/locales/pl/test.ftl',
let oneSource = new IndexedFileSource("langpack-pl", ["pl"], "/data/locales/{locale}/", [
"/data/locales/pl/test.ftl",
]);
L10nRegistry.registerSource(oneSource);
fs = {
'/data/locales/pl/test.ftl': 'key = value'
"/data/locales/pl/test.ftl": "key = value"
};
equal(L10nRegistry.sources.size, 1);
equal(L10nRegistry.sources.has('langpack-pl'), true);
equal(L10nRegistry.sources.has("langpack-pl"), true);
equal(oneSource.getPath('pl', 'test.ftl'), '/data/locales/pl/test.ftl');
equal(oneSource.hasFile('pl', 'test.ftl'), true);
equal(oneSource.hasFile('pl', 'missing.ftl'), false);
equal(oneSource.getPath("pl", "test.ftl"), "/data/locales/pl/test.ftl");
equal(oneSource.hasFile("pl", "test.ftl"), true);
equal(oneSource.hasFile("pl", "missing.ftl"), false);
// cleanup
L10nRegistry.sources.clear();
@ -174,34 +174,34 @@ add_task(async function test_indexed() {
* scenarios where a new file source is added on top of the default one.
*/
add_task(async function test_override() {
let fileSource = new FileSource('app', ['pl'], '/app/data/locales/{locale}/');
let fileSource = new FileSource("app", ["pl"], "/app/data/locales/{locale}/");
L10nRegistry.registerSource(fileSource);
let oneSource = new IndexedFileSource('langpack-pl', ['pl'], '/data/locales/{locale}/', [
'/data/locales/pl/test.ftl'
let oneSource = new IndexedFileSource("langpack-pl", ["pl"], "/data/locales/{locale}/", [
"/data/locales/pl/test.ftl"
]);
L10nRegistry.registerSource(oneSource);
fs = {
'/app/data/locales/pl/test.ftl': 'key = value',
'/data/locales/pl/test.ftl': 'key = addon value'
"/app/data/locales/pl/test.ftl": "key = value",
"/data/locales/pl/test.ftl": "key = addon value"
};
equal(L10nRegistry.sources.size, 2);
equal(L10nRegistry.sources.has('langpack-pl'), true);
equal(L10nRegistry.sources.has("langpack-pl"), true);
let ctxs = L10nRegistry.generateContexts(['pl'], ['test.ftl']);
let ctxs = L10nRegistry.generateContexts(["pl"], ["test.ftl"]);
let ctx0 = (await ctxs.next()).value;
equal(ctx0.locales[0], 'pl');
equal(ctx0.hasMessage('key'), true);
let msg0 = ctx0.getMessage('key');
equal(ctx0.format(msg0), 'addon value');
equal(ctx0.locales[0], "pl");
equal(ctx0.hasMessage("key"), true);
let msg0 = ctx0.getMessage("key");
equal(ctx0.format(msg0), "addon value");
let ctx1 = (await ctxs.next()).value;
equal(ctx1.locales[0], 'pl');
equal(ctx1.hasMessage('key'), true);
let msg1 = ctx1.getMessage('key');
equal(ctx1.format(msg1), 'value');
equal(ctx1.locales[0], "pl");
equal(ctx1.hasMessage("key"), true);
let msg1 = ctx1.getMessage("key");
equal(ctx1.format(msg1), "value");
equal((await ctxs.next()).done, true);
@ -215,33 +215,33 @@ add_task(async function test_override() {
* after source update.
*/
add_task(async function test_updating() {
let oneSource = new IndexedFileSource('langpack-pl', ['pl'], '/data/locales/{locale}/', [
'/data/locales/pl/test.ftl',
let oneSource = new IndexedFileSource("langpack-pl", ["pl"], "/data/locales/{locale}/", [
"/data/locales/pl/test.ftl",
]);
L10nRegistry.registerSource(oneSource);
fs = {
'/data/locales/pl/test.ftl': 'key = value'
"/data/locales/pl/test.ftl": "key = value"
};
let ctxs = L10nRegistry.generateContexts(['pl'], ['test.ftl']);
let ctxs = L10nRegistry.generateContexts(["pl"], ["test.ftl"]);
let ctx0 = (await ctxs.next()).value;
equal(ctx0.locales[0], 'pl');
equal(ctx0.hasMessage('key'), true);
let msg0 = ctx0.getMessage('key');
equal(ctx0.format(msg0), 'value');
equal(ctx0.locales[0], "pl");
equal(ctx0.hasMessage("key"), true);
let msg0 = ctx0.getMessage("key");
equal(ctx0.format(msg0), "value");
const newSource = new IndexedFileSource('langpack-pl', ['pl'], '/data/locales/{locale}/', [
'/data/locales/pl/test.ftl'
const newSource = new IndexedFileSource("langpack-pl", ["pl"], "/data/locales/{locale}/", [
"/data/locales/pl/test.ftl"
]);
fs['/data/locales/pl/test.ftl'] = 'key = new value';
fs["/data/locales/pl/test.ftl"] = "key = new value";
L10nRegistry.updateSource(newSource);
equal(L10nRegistry.sources.size, 1);
ctxs = L10nRegistry.generateContexts(['pl'], ['test.ftl']);
ctxs = L10nRegistry.generateContexts(["pl"], ["test.ftl"]);
ctx0 = (await ctxs.next()).value;
msg0 = ctx0.getMessage('key');
equal(ctx0.format(msg0), 'new value');
msg0 = ctx0.getMessage("key");
equal(ctx0.format(msg0), "new value");
// cleanup
L10nRegistry.sources.clear();
@ -253,60 +253,60 @@ add_task(async function test_updating() {
* after sources are being removed.
*/
add_task(async function test_removing() {
let fileSource = new FileSource('app', ['pl'], '/app/data/locales/{locale}/');
let fileSource = new FileSource("app", ["pl"], "/app/data/locales/{locale}/");
L10nRegistry.registerSource(fileSource);
let oneSource = new IndexedFileSource('langpack-pl', ['pl'], '/data/locales/{locale}/', [
'/data/locales/pl/test.ftl'
let oneSource = new IndexedFileSource("langpack-pl", ["pl"], "/data/locales/{locale}/", [
"/data/locales/pl/test.ftl"
]);
L10nRegistry.registerSource(oneSource);
fs = {
'/app/data/locales/pl/test.ftl': 'key = value',
'/data/locales/pl/test.ftl': 'key = addon value'
"/app/data/locales/pl/test.ftl": "key = value",
"/data/locales/pl/test.ftl": "key = addon value"
};
equal(L10nRegistry.sources.size, 2);
equal(L10nRegistry.sources.has('langpack-pl'), true);
equal(L10nRegistry.sources.has("langpack-pl"), true);
let ctxs = L10nRegistry.generateContexts(['pl'], ['test.ftl']);
let ctxs = L10nRegistry.generateContexts(["pl"], ["test.ftl"]);
let ctx0 = (await ctxs.next()).value;
equal(ctx0.locales[0], 'pl');
equal(ctx0.hasMessage('key'), true);
let msg0 = ctx0.getMessage('key');
equal(ctx0.format(msg0), 'addon value');
equal(ctx0.locales[0], "pl");
equal(ctx0.hasMessage("key"), true);
let msg0 = ctx0.getMessage("key");
equal(ctx0.format(msg0), "addon value");
let ctx1 = (await ctxs.next()).value;
equal(ctx1.locales[0], 'pl');
equal(ctx1.hasMessage('key'), true);
let msg1 = ctx1.getMessage('key');
equal(ctx1.format(msg1), 'value');
equal(ctx1.locales[0], "pl");
equal(ctx1.hasMessage("key"), true);
let msg1 = ctx1.getMessage("key");
equal(ctx1.format(msg1), "value");
equal((await ctxs.next()).done, true);
// Remove langpack
L10nRegistry.removeSource('langpack-pl');
L10nRegistry.removeSource("langpack-pl");
equal(L10nRegistry.sources.size, 1);
equal(L10nRegistry.sources.has('langpack-pl'), false);
equal(L10nRegistry.sources.has("langpack-pl"), false);
ctxs = L10nRegistry.generateContexts(['pl'], ['test.ftl']);
ctxs = L10nRegistry.generateContexts(["pl"], ["test.ftl"]);
ctx0 = (await ctxs.next()).value;
equal(ctx0.locales[0], 'pl');
equal(ctx0.hasMessage('key'), true);
msg0 = ctx0.getMessage('key');
equal(ctx0.format(msg0), 'value');
equal(ctx0.locales[0], "pl");
equal(ctx0.hasMessage("key"), true);
msg0 = ctx0.getMessage("key");
equal(ctx0.format(msg0), "value");
equal((await ctxs.next()).done, true);
// Remove app source
L10nRegistry.removeSource('app');
L10nRegistry.removeSource("app");
equal(L10nRegistry.sources.size, 0);
ctxs = L10nRegistry.generateContexts(['pl'], ['test.ftl']);
ctxs = L10nRegistry.generateContexts(["pl"], ["test.ftl"]);
equal((await ctxs.next()).done, true);
// cleanup
@ -319,30 +319,30 @@ add_task(async function test_removing() {
* file in the FileSource scenario.
*/
add_task(async function test_missing_file() {
let oneSource = new FileSource('app', ['en-US'], './app/data/locales/{locale}/');
let oneSource = new FileSource("app", ["en-US"], "./app/data/locales/{locale}/");
L10nRegistry.registerSource(oneSource);
let twoSource = new FileSource('platform', ['en-US'], './platform/data/locales/{locale}/');
let twoSource = new FileSource("platform", ["en-US"], "./platform/data/locales/{locale}/");
L10nRegistry.registerSource(twoSource);
fs = {
'./app/data/locales/en-US/test.ftl': 'key = value en-US',
'./platform/data/locales/en-US/test.ftl': 'key = value en-US',
'./platform/data/locales/en-US/test2.ftl': 'key2 = value2 en-US'
"./app/data/locales/en-US/test.ftl": "key = value en-US",
"./platform/data/locales/en-US/test.ftl": "key = value en-US",
"./platform/data/locales/en-US/test2.ftl": "key2 = value2 en-US"
};
// has two sources
equal(L10nRegistry.sources.size, 2);
equal(L10nRegistry.sources.has('app'), true);
equal(L10nRegistry.sources.has('platform'), true);
equal(L10nRegistry.sources.has("app"), true);
equal(L10nRegistry.sources.has("platform"), true);
// returns a single context
let ctxs = L10nRegistry.generateContexts(['en-US'], ['test.ftl', 'test2.ftl']);
let ctx0 = (await ctxs.next()).value;
let ctx1 = (await ctxs.next()).value;
let ctxs = L10nRegistry.generateContexts(["en-US"], ["test.ftl", "test2.ftl"]);
(await ctxs.next());
(await ctxs.next());
equal((await ctxs.next()).done, true);
@ -368,33 +368,33 @@ add_task(async function test_parallel_io() {
}
fetchIndex.set(url, fetchIndex.get(url) + 1);
if (url === '/en-US/slow-file.ftl') {
if (url === "/en-US/slow-file.ftl") {
return new Promise((resolve, reject) => {
setTimeout(() => {
// Despite slow-file being the first on the list,
// by the time the it finishes loading, the other
// two files are already fetched.
equal(fetchIndex.get('/en-US/test.ftl'), 1);
equal(fetchIndex.get('/en-US/test2.ftl'), 1);
equal(fetchIndex.get("/en-US/test.ftl"), 1);
equal(fetchIndex.get("/en-US/test2.ftl"), 1);
resolve('');
resolve("");
}, 10);
});
};
return Promise.resolve('');
}
let oneSource = new FileSource('app', ['en-US'], '/{locale}/');
return Promise.resolve("");
};
let oneSource = new FileSource("app", ["en-US"], "/{locale}/");
L10nRegistry.registerSource(oneSource);
fs = {
'/en-US/test.ftl': 'key = value en-US',
'/en-US/test2.ftl': 'key2 = value2 en-US',
'/en-US/slow-file.ftl': 'key-slow = value slow en-US',
"/en-US/test.ftl": "key = value en-US",
"/en-US/test2.ftl": "key2 = value2 en-US",
"/en-US/slow-file.ftl": "key-slow = value slow en-US",
};
// returns a single context
let ctxs = L10nRegistry.generateContexts(['en-US'], ['slow-file.ftl', 'test.ftl', 'test2.ftl']);
let ctxs = L10nRegistry.generateContexts(["en-US"], ["slow-file.ftl", "test.ftl", "test2.ftl"]);
equal(fetchIndex.size, 0);
@ -406,7 +406,7 @@ add_task(async function test_parallel_io() {
// When requested again, the cache should make the load operation not
// increase the fetchedIndex count
let ctxs2= L10nRegistry.generateContexts(['en-US'], ['test.ftl', 'test2.ftl', 'slow-file.ftl']);
L10nRegistry.generateContexts(["en-US"], ["test.ftl", "test2.ftl", "slow-file.ftl"]);
// cleanup
L10nRegistry.sources.clear();

View file

@ -3,6 +3,7 @@
const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm", {});
const { Localization } = ChromeUtils.import("resource://gre/modules/Localization.jsm", {});
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
add_task(function test_methods_presence() {
equal(typeof Localization.prototype.formatValues, "function");
@ -13,58 +14,55 @@ add_task(function test_methods_presence() {
add_task(async function test_methods_calling() {
const { L10nRegistry, FileSource } =
ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm", {});
const LocaleService =
Components.classes["@mozilla.org/intl/localeservice;1"].getService(
Components.interfaces.mozILocaleService);
const fs = {
'/localization/de/browser/menu.ftl': 'key = [de] Value2',
'/localization/en-US/browser/menu.ftl': 'key = [en] Value2\nkey2 = [en] Value3',
"/localization/de/browser/menu.ftl": "key = [de] Value2",
"/localization/en-US/browser/menu.ftl": "key = [en] Value2\nkey2 = [en] Value3",
};
const originalLoad = L10nRegistry.load;
const originalRequested = LocaleService.getRequestedLocales();
const originalRequested = Services.locale.getRequestedLocales();
L10nRegistry.load = async function(url) {
return fs[url];
}
};
const source = new FileSource('test', ['de', 'en-US'], '/localization/{locale}');
const source = new FileSource("test", ["de", "en-US"], "/localization/{locale}");
L10nRegistry.registerSource(source);
async function* generateMessages(resIds) {
yield * await L10nRegistry.generateContexts(['de', 'en-US'], resIds);
yield * await L10nRegistry.generateContexts(["de", "en-US"], resIds);
}
const l10n = new Localization([
'/browser/menu.ftl'
"/browser/menu.ftl"
], generateMessages);
let values = await l10n.formatValues([['key'], ['key2']]);
let values = await l10n.formatValues([["key"], ["key2"]]);
equal(values[0], '[de] Value2');
equal(values[1], '[en] Value3');
equal(values[0], "[de] Value2");
equal(values[1], "[en] Value3");
L10nRegistry.sources.clear();
L10nRegistry.load = originalLoad;
LocaleService.setRequestedLocales(originalRequested);
Services.locale.setRequestedLocales(originalRequested);
});
add_task(async function test_builtins() {
const { L10nRegistry, FileSource } =
Components.utils.import("resource://gre/modules/L10nRegistry.jsm", {});
ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm", {});
const known_platforms = {
'linux': 'linux',
'win': 'windows',
'macosx': 'macos',
'android': 'android',
"linux": "linux",
"win": "windows",
"macosx": "macos",
"android": "android",
};
const fs = {
'/localization/en-US/test.ftl': `
"/localization/en-US/test.ftl": `
key = { PLATFORM() ->
${ Object.values(known_platforms).map(
name => ` [${ name }] ${ name.toUpperCase() } Value\n`).join('') }
name => ` [${ name }] ${ name.toUpperCase() } Value\n`).join("") }
*[other] OTHER Value
}`,
};
@ -72,20 +70,20 @@ key = { PLATFORM() ->
L10nRegistry.load = async function(url) {
return fs[url];
}
};
const source = new FileSource('test', ['en-US'], '/localization/{locale}');
const source = new FileSource("test", ["en-US"], "/localization/{locale}");
L10nRegistry.registerSource(source);
async function* generateMessages(resIds) {
yield * await L10nRegistry.generateContexts(['en-US'], resIds);
yield * await L10nRegistry.generateContexts(["en-US"], resIds);
}
const l10n = new Localization([
'/test.ftl'
"/test.ftl"
], generateMessages);
let values = await l10n.formatValues([['key']]);
let values = await l10n.formatValues([["key"]]);
ok(values[0].includes(
`${ known_platforms[AppConstants.platform].toUpperCase() } Value`));