mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 1480881 - Upgrade Gecko to Fluent 0.6. r=stas
Upgrade Gecko to Fluent 0.6. Differential Revision: https://phabricator.services.mozilla.com/D2740 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
7b8f8f1076
commit
6ddc6d1828
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
|
||||
/* fluent-dom@aa95b1f (July 10, 2018) */
|
||||
/* fluent-dom@cab517f (July 31, 2018) */
|
||||
|
||||
const { Localization } =
|
||||
ChromeUtils.import("resource://gre/modules/Localization.jsm", {});
|
||||
@ -60,10 +60,10 @@ const LOCALIZABLE_ATTRIBUTES = {
|
||||
th: ["abbr"]
|
||||
},
|
||||
"http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul": {
|
||||
description: ["value"],
|
||||
global: [
|
||||
"accesskey", "aria-label", "aria-valuetext", "aria-moz-hint", "label"
|
||||
],
|
||||
description: ["value"],
|
||||
key: ["key", "keycode"],
|
||||
label: ["value"],
|
||||
textbox: ["placeholder"],
|
||||
@ -523,7 +523,7 @@ class DOMLocalization extends Localization {
|
||||
if (this.windowElement) {
|
||||
if (this.windowElement !== newRoot.ownerGlobal) {
|
||||
throw new Error(`Cannot connect a root:
|
||||
DOMLocalization already has a root from a different window`);
|
||||
DOMLocalization already has a root from a different window.`);
|
||||
}
|
||||
} else {
|
||||
this.windowElement = newRoot.ownerGlobal;
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
|
||||
/* fluent-dom@aa95b1f (July 10, 2018) */
|
||||
/* fluent-dom@cab517f (July 31, 2018) */
|
||||
|
||||
/* eslint no-console: ["error", { allow: ["warn", "error"] }] */
|
||||
/* global console */
|
||||
@ -25,13 +25,25 @@ const { L10nRegistry } = ChromeUtils.import("resource://gre/modules/L10nRegistry
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
|
||||
const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm", {});
|
||||
|
||||
/*
|
||||
* CachedAsyncIterable caches the elements yielded by an iterable.
|
||||
*
|
||||
* It can be used to iterate over an iterable many times without depleting the
|
||||
* iterable.
|
||||
*/
|
||||
class CachedAsyncIterable {
|
||||
class CachedIterable extends Array {
|
||||
/**
|
||||
* Create a `CachedIterable` instance from an iterable or, if another
|
||||
* instance of `CachedIterable` is passed, return it without any
|
||||
* modifications.
|
||||
*
|
||||
* @param {Iterable} iterable
|
||||
* @returns {CachedIterable}
|
||||
*/
|
||||
static from(iterable) {
|
||||
if (iterable instanceof this) {
|
||||
return iterable;
|
||||
}
|
||||
|
||||
return new this(iterable);
|
||||
}
|
||||
}
|
||||
|
||||
class CachedAsyncIterable extends CachedIterable {
|
||||
/**
|
||||
* Create an `CachedAsyncIterable` instance.
|
||||
*
|
||||
@ -39,6 +51,8 @@ class CachedAsyncIterable {
|
||||
* @returns {CachedAsyncIterable}
|
||||
*/
|
||||
constructor(iterable) {
|
||||
super();
|
||||
|
||||
if (Symbol.asyncIterator in Object(iterable)) {
|
||||
this.iterator = iterable[Symbol.asyncIterator]();
|
||||
} else if (Symbol.iterator in Object(iterable)) {
|
||||
@ -46,20 +60,46 @@ class CachedAsyncIterable {
|
||||
} else {
|
||||
throw new TypeError("Argument must implement the iteration protocol.");
|
||||
}
|
||||
|
||||
this.seen = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronous iterator over the cached elements.
|
||||
*
|
||||
* Return a generator object implementing the iterator protocol over the
|
||||
* cached elements of the original (async or sync) iterable.
|
||||
*/
|
||||
[Symbol.iterator]() {
|
||||
const cached = this;
|
||||
let cur = 0;
|
||||
|
||||
return {
|
||||
next() {
|
||||
if (cached.length === cur) {
|
||||
return {value: undefined, done: true};
|
||||
}
|
||||
return cached[cur++];
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Asynchronous iterator caching the yielded elements.
|
||||
*
|
||||
* Elements yielded by the original iterable will be cached and available
|
||||
* synchronously. Returns an async generator object implementing the
|
||||
* iterator protocol over the elements of the original (async or sync)
|
||||
* iterable.
|
||||
*/
|
||||
[Symbol.asyncIterator]() {
|
||||
const { seen, iterator } = this;
|
||||
const cached = this;
|
||||
let cur = 0;
|
||||
|
||||
return {
|
||||
async next() {
|
||||
if (seen.length <= cur) {
|
||||
seen.push(await iterator.next());
|
||||
if (cached.length <= cur) {
|
||||
cached.push(await cached.iterator.next());
|
||||
}
|
||||
return seen[cur++];
|
||||
return cached[cur++];
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -71,13 +111,17 @@ class CachedAsyncIterable {
|
||||
* @param {number} count - number of elements to consume
|
||||
*/
|
||||
async touchNext(count = 1) {
|
||||
const { seen, iterator } = this;
|
||||
let idx = 0;
|
||||
while (idx++ < count) {
|
||||
if (seen.length === 0 || seen[seen.length - 1].done === false) {
|
||||
seen.push(await iterator.next());
|
||||
const last = this[this.length - 1];
|
||||
if (last && last.done) {
|
||||
break;
|
||||
}
|
||||
this.push(await this.iterator.next());
|
||||
}
|
||||
// Return the last cached {value, done} object to allow the calling
|
||||
// code to decide if it needs to call touchNext again.
|
||||
return this[this.length - 1];
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,8 +156,8 @@ class Localization {
|
||||
constructor(resourceIds = [], generateMessages = defaultGenerateMessages) {
|
||||
this.resourceIds = resourceIds;
|
||||
this.generateMessages = generateMessages;
|
||||
this.ctxs =
|
||||
new CachedAsyncIterable(this.generateMessages(this.resourceIds));
|
||||
this.ctxs = CachedAsyncIterable.from(
|
||||
this.generateMessages(this.resourceIds));
|
||||
}
|
||||
|
||||
addResourceIds(resourceIds) {
|
||||
@ -276,8 +320,8 @@ class Localization {
|
||||
* that language negotiation or available resources changed.
|
||||
*/
|
||||
onChange() {
|
||||
this.ctxs =
|
||||
new CachedAsyncIterable(this.generateMessages(this.resourceIds));
|
||||
this.ctxs = CachedAsyncIterable.from(
|
||||
this.generateMessages(this.resourceIds));
|
||||
this.ctxs.touchNext(2);
|
||||
}
|
||||
}
|
||||
|
@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
|
||||
/* fluent@aa95b1f (July 10, 2018) */
|
||||
/* fluent@0.7.0 */
|
||||
|
||||
/* eslint no-magic-numbers: [0] */
|
||||
|
||||
@ -25,6 +25,9 @@ const MAX_PLACEABLES = 100;
|
||||
const entryIdentifierRe = /-?[a-zA-Z][a-zA-Z0-9_-]*/y;
|
||||
const identifierRe = /[a-zA-Z][a-zA-Z0-9_-]*/y;
|
||||
const functionIdentifierRe = /^[A-Z][A-Z_?-]*$/;
|
||||
const unicodeEscapeRe = /^[a-fA-F0-9]{4}$/;
|
||||
const trailingWSRe = /[ \t\n\r]+$/;
|
||||
|
||||
|
||||
/**
|
||||
* The `Parser` class is responsible for parsing FTL resources.
|
||||
@ -94,46 +97,15 @@ 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 === "#" &&
|
||||
[" ", "#", "\n"].includes(this._source[this._index + 1])) {
|
||||
this.skipComment();
|
||||
return;
|
||||
}
|
||||
|
||||
if (ch === "[") {
|
||||
this.skipSection();
|
||||
return;
|
||||
}
|
||||
|
||||
this.getMessage();
|
||||
}
|
||||
|
||||
/**
|
||||
* Skip the section entry from the current index.
|
||||
*
|
||||
* @private
|
||||
*/
|
||||
skipSection() {
|
||||
this._index += 1;
|
||||
if (this._source[this._index] !== "[") {
|
||||
throw this.error('Expected "[[" to open a section');
|
||||
}
|
||||
|
||||
this._index += 1;
|
||||
|
||||
this.skipInlineWS();
|
||||
this.getVariantName();
|
||||
this.skipInlineWS();
|
||||
|
||||
if (this._source[this._index] !== "]" ||
|
||||
this._source[this._index + 1] !== "]") {
|
||||
throw this.error('Expected "]]" to close a section');
|
||||
}
|
||||
|
||||
this._index += 2;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the source string from the current index as an FTL message
|
||||
* and add it to the entries property on the Parser.
|
||||
@ -147,6 +119,8 @@ class RuntimeParser {
|
||||
|
||||
if (this._source[this._index] === "=") {
|
||||
this._index++;
|
||||
} else {
|
||||
throw this.error("Expected \"=\" after the identifier");
|
||||
}
|
||||
|
||||
this.skipInlineWS();
|
||||
@ -236,7 +210,7 @@ class RuntimeParser {
|
||||
* Get identifier using the provided regex.
|
||||
*
|
||||
* By default this will get identifiers of public messages, attributes and
|
||||
* external arguments (without the $).
|
||||
* variables (without the $).
|
||||
*
|
||||
* @returns {String}
|
||||
* @private
|
||||
@ -311,21 +285,30 @@ class RuntimeParser {
|
||||
* @private
|
||||
*/
|
||||
getString() {
|
||||
const start = this._index + 1;
|
||||
let value = "";
|
||||
this._index++;
|
||||
|
||||
while (++this._index < this._length) {
|
||||
while (this._index < this._length) {
|
||||
const ch = this._source[this._index];
|
||||
|
||||
if (ch === '"') {
|
||||
this._index++;
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch === "\n") {
|
||||
throw this.error("Unterminated string expression");
|
||||
}
|
||||
|
||||
if (ch === "\\") {
|
||||
value += this.getEscapedCharacter(["{", "\\", "\""]);
|
||||
} else {
|
||||
this._index++;
|
||||
value += ch;
|
||||
}
|
||||
}
|
||||
|
||||
return this._source.substring(start, this._index++);
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -349,10 +332,17 @@ class RuntimeParser {
|
||||
eol = this._length;
|
||||
}
|
||||
|
||||
const firstLineContent = start !== eol ?
|
||||
this._source.slice(start, eol) : null;
|
||||
// If there's any text between the = and the EOL, store it for now. The next
|
||||
// non-empty line will decide what to do with it.
|
||||
const firstLineContent = start !== eol
|
||||
// Trim the trailing whitespace in case this is a single-line pattern.
|
||||
// Multiline patterns are parsed anew by getComplexPattern.
|
||||
? this._source.slice(start, eol).replace(trailingWSRe, "")
|
||||
: null;
|
||||
|
||||
if (firstLineContent && firstLineContent.includes("{")) {
|
||||
if (firstLineContent
|
||||
&& (firstLineContent.includes("{")
|
||||
|| firstLineContent.includes("\\"))) {
|
||||
return this.getComplexPattern();
|
||||
}
|
||||
|
||||
@ -439,13 +429,19 @@ class RuntimeParser {
|
||||
}
|
||||
ch = this._source[this._index];
|
||||
continue;
|
||||
} else if (ch === "\\") {
|
||||
const ch2 = this._source[this._index + 1];
|
||||
if (ch2 === '"' || ch2 === "{" || ch2 === "\\") {
|
||||
ch = ch2;
|
||||
this._index++;
|
||||
}
|
||||
} else if (ch === "{") {
|
||||
}
|
||||
|
||||
if (ch === undefined) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (ch === "\\") {
|
||||
buffer += this.getEscapedCharacter();
|
||||
ch = this._source[this._index];
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch === "{") {
|
||||
// Push the buffer to content array right before placeable
|
||||
if (buffer.length) {
|
||||
content.push(buffer);
|
||||
@ -457,18 +453,13 @@ class RuntimeParser {
|
||||
buffer = "";
|
||||
content.push(this.getPlaceable());
|
||||
|
||||
this._index++;
|
||||
|
||||
ch = this._source[this._index];
|
||||
ch = this._source[++this._index];
|
||||
placeables++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch) {
|
||||
buffer += ch;
|
||||
}
|
||||
this._index++;
|
||||
ch = this._source[this._index];
|
||||
buffer += ch;
|
||||
ch = this._source[++this._index];
|
||||
}
|
||||
|
||||
if (content.length === 0) {
|
||||
@ -476,13 +467,42 @@ class RuntimeParser {
|
||||
}
|
||||
|
||||
if (buffer.length) {
|
||||
content.push(buffer);
|
||||
// Trim trailing whitespace, too.
|
||||
content.push(buffer.replace(trailingWSRe, ""));
|
||||
}
|
||||
|
||||
return content;
|
||||
}
|
||||
/* eslint-enable complexity */
|
||||
|
||||
/**
|
||||
* Parse an escape sequence and return the unescaped character.
|
||||
*
|
||||
* @returns {string}
|
||||
* @private
|
||||
*/
|
||||
getEscapedCharacter(specials = ["{", "\\"]) {
|
||||
this._index++;
|
||||
const next = this._source[this._index];
|
||||
|
||||
if (specials.includes(next)) {
|
||||
this._index++;
|
||||
return next;
|
||||
}
|
||||
|
||||
if (next === "u") {
|
||||
const sequence = this._source.slice(this._index + 1, this._index + 5);
|
||||
if (unicodeEscapeRe.test(sequence)) {
|
||||
this._index += 5;
|
||||
return String.fromCodePoint(parseInt(sequence, 16));
|
||||
}
|
||||
|
||||
throw this.error(`Invalid Unicode escape sequence: \\u${sequence}`);
|
||||
}
|
||||
|
||||
throw this.error(`Unknown escape sequence: \\${next}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a single placeable in a Message pattern and returns its
|
||||
* expression.
|
||||
@ -519,7 +539,7 @@ class RuntimeParser {
|
||||
const ch = this._source[this._index];
|
||||
|
||||
if (ch === "}") {
|
||||
if (selector.type === "attr" && selector.id.name.startsWith("-")) {
|
||||
if (selector.type === "getattr" && selector.id.name.startsWith("-")) {
|
||||
throw this.error(
|
||||
"Attributes of private messages cannot be interpolated."
|
||||
);
|
||||
@ -536,11 +556,11 @@ class RuntimeParser {
|
||||
throw this.error("Message references cannot be used as selectors.");
|
||||
}
|
||||
|
||||
if (selector.type === "var") {
|
||||
if (selector.type === "getvar") {
|
||||
throw this.error("Variants cannot be used as selectors.");
|
||||
}
|
||||
|
||||
if (selector.type === "attr" && !selector.id.name.startsWith("-")) {
|
||||
if (selector.type === "getattr" && !selector.id.name.startsWith("-")) {
|
||||
throw this.error(
|
||||
"Attributes of public messages cannot be used as selectors."
|
||||
);
|
||||
@ -578,6 +598,10 @@ class RuntimeParser {
|
||||
* @private
|
||||
*/
|
||||
getSelectorExpression() {
|
||||
if (this._source[this._index] === "{") {
|
||||
return this.getPlaceable();
|
||||
}
|
||||
|
||||
const literal = this.getLiteral();
|
||||
|
||||
if (literal.type !== "ref") {
|
||||
@ -590,7 +614,7 @@ class RuntimeParser {
|
||||
const name = this.getIdentifier();
|
||||
this._index++;
|
||||
return {
|
||||
type: "attr",
|
||||
type: "getattr",
|
||||
id: literal,
|
||||
name
|
||||
};
|
||||
@ -602,7 +626,7 @@ class RuntimeParser {
|
||||
const key = this.getVariantKey();
|
||||
this._index++;
|
||||
return {
|
||||
type: "var",
|
||||
type: "getvar",
|
||||
id: literal,
|
||||
key
|
||||
};
|
||||
@ -640,7 +664,7 @@ class RuntimeParser {
|
||||
const args = [];
|
||||
|
||||
while (this._index < this._length) {
|
||||
this.skipInlineWS();
|
||||
this.skipWS();
|
||||
|
||||
if (this._source[this._index] === ")") {
|
||||
return args;
|
||||
@ -657,7 +681,7 @@ class RuntimeParser {
|
||||
|
||||
if (this._source[this._index] === ":") {
|
||||
this._index++;
|
||||
this.skipInlineWS();
|
||||
this.skipWS();
|
||||
|
||||
const val = this.getSelectorExpression();
|
||||
|
||||
@ -685,7 +709,7 @@ class RuntimeParser {
|
||||
}
|
||||
}
|
||||
|
||||
this.skipInlineWS();
|
||||
this.skipWS();
|
||||
|
||||
if (this._source[this._index] === ")") {
|
||||
break;
|
||||
@ -885,7 +909,7 @@ class RuntimeParser {
|
||||
if (cc0 === 36) { // $
|
||||
this._index++;
|
||||
return {
|
||||
type: "ext",
|
||||
type: "var",
|
||||
name: this.getIdentifier()
|
||||
};
|
||||
}
|
||||
@ -925,12 +949,11 @@ class RuntimeParser {
|
||||
// to parse them properly and skip their content.
|
||||
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._index = eol + 3;
|
||||
while (eol !== -1
|
||||
&& this._source[eol + 1] === "#"
|
||||
&& [" ", "#"].includes(this._source[eol + 2])) {
|
||||
|
||||
this._index = eol + 3;
|
||||
eol = this._source.indexOf("\n", this._index);
|
||||
|
||||
if (eol === -1) {
|
||||
@ -972,7 +995,7 @@ class RuntimeParser {
|
||||
|
||||
if ((cc >= 97 && cc <= 122) || // a-z
|
||||
(cc >= 65 && cc <= 90) || // A-Z
|
||||
cc === 47 || cc === 91) { // /[
|
||||
cc === 45) { // -
|
||||
this._index = start;
|
||||
return;
|
||||
}
|
||||
@ -1169,11 +1192,11 @@ function values(opts) {
|
||||
* The role of the Fluent resolver is to format a translation object to an
|
||||
* instance of `FluentType` or an array of instances.
|
||||
*
|
||||
* Translations can contain references to other messages or external arguments,
|
||||
* Translations can contain references to other messages or variables,
|
||||
* conditional logic in form of select expressions, traits which describe their
|
||||
* grammatical features, and can use Fluent builtins which make use of the
|
||||
* `Intl` formatters to format numbers, dates, lists and more into the
|
||||
* context's language. See the documentation of the Fluent syntax for more
|
||||
* context's language. See the documentation of the Fluent syntax for more
|
||||
* information.
|
||||
*
|
||||
* In case of errors the resolver will try to salvage as much of the
|
||||
@ -1436,8 +1459,8 @@ function Type(env, expr) {
|
||||
return new FluentSymbol(expr.name);
|
||||
case "num":
|
||||
return new FluentNumber(expr.val);
|
||||
case "ext":
|
||||
return ExternalArgument(env, expr);
|
||||
case "var":
|
||||
return VariableReference(env, expr);
|
||||
case "fun":
|
||||
return FunctionReference(env, expr);
|
||||
case "call":
|
||||
@ -1446,11 +1469,11 @@ function Type(env, expr) {
|
||||
const message = MessageReference(env, expr);
|
||||
return Type(env, message);
|
||||
}
|
||||
case "attr": {
|
||||
case "getattr": {
|
||||
const attr = AttributeExpression(env, expr);
|
||||
return Type(env, attr);
|
||||
}
|
||||
case "var": {
|
||||
case "getvar": {
|
||||
const variant = VariantExpression(env, expr);
|
||||
return Type(env, variant);
|
||||
}
|
||||
@ -1474,7 +1497,7 @@ function Type(env, expr) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve a reference to an external argument.
|
||||
* Resolve a reference to a variable.
|
||||
*
|
||||
* @param {Object} env
|
||||
* Resolver environment object.
|
||||
@ -1485,11 +1508,11 @@ function Type(env, expr) {
|
||||
* @returns {FluentType}
|
||||
* @private
|
||||
*/
|
||||
function ExternalArgument(env, {name}) {
|
||||
function VariableReference(env, {name}) {
|
||||
const { args, errors } = env;
|
||||
|
||||
if (!args || !args.hasOwnProperty(name)) {
|
||||
errors.push(new ReferenceError(`Unknown external: ${name}`));
|
||||
errors.push(new ReferenceError(`Unknown variable: ${name}`));
|
||||
return new FluentNone(name);
|
||||
}
|
||||
|
||||
@ -1512,7 +1535,7 @@ function ExternalArgument(env, {name}) {
|
||||
}
|
||||
default:
|
||||
errors.push(
|
||||
new TypeError(`Unsupported external type: ${name}, ${typeof arg}`)
|
||||
new TypeError(`Unsupported variable type: ${name}, ${typeof arg}`)
|
||||
);
|
||||
return new FluentNone(name);
|
||||
}
|
||||
@ -1691,13 +1714,13 @@ class FluentResource extends Map {
|
||||
* responsible for parsing translation resources in the Fluent syntax and can
|
||||
* format translation units (entities) to strings.
|
||||
*
|
||||
* Always use `MessageContext.format` to retrieve translation units from
|
||||
* a context. Translations can contain references to other entities or
|
||||
* external arguments, conditional logic in form of select expressions, traits
|
||||
* which describe their grammatical features, and can use Fluent builtins which
|
||||
* make use of the `Intl` formatters to format numbers, dates, lists and more
|
||||
* into the context's language. See the documentation of the Fluent syntax for
|
||||
* more information.
|
||||
* Always use `MessageContext.format` to retrieve translation units from a
|
||||
* context. Translations can contain references to other entities or variables,
|
||||
* conditional logic in form of select expressions, traits which describe their
|
||||
* grammatical features, and can use Fluent builtins which make use of the
|
||||
* `Intl` formatters to format numbers, dates, lists and more into the
|
||||
* context's language. See the documentation of the Fluent syntax for more
|
||||
* information.
|
||||
*/
|
||||
class MessageContext {
|
||||
|
||||
@ -1849,8 +1872,8 @@ class MessageContext {
|
||||
* Format a message to a string or null.
|
||||
*
|
||||
* Format a raw `message` from the context into a string (or a null if it has
|
||||
* a null value). `args` will be used to resolve references to external
|
||||
* arguments inside of the translation.
|
||||
* a null value). `args` will be used to resolve references to variables
|
||||
* passed as arguments to the translation.
|
||||
*
|
||||
* In case of errors `format` will try to salvage as much of the translation
|
||||
* as possible and will still return a string. For performance reasons, the
|
||||
@ -1868,7 +1891,7 @@ class MessageContext {
|
||||
*
|
||||
* // Returns 'Hello, name!' and `errors` is now:
|
||||
*
|
||||
* [<ReferenceError: Unknown external: name>]
|
||||
* [<ReferenceError: Unknown variable: name>]
|
||||
*
|
||||
* @param {Object | string} message
|
||||
* @param {Object | undefined} args
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -19,10 +19,10 @@
|
||||
async function * generateMessages(locales, resourceIds) {
|
||||
const mc = new MessageContext(locales);
|
||||
mc.addMessages(`
|
||||
file-menu
|
||||
file-menu =
|
||||
.label = File
|
||||
.accesskey = F
|
||||
new-tab
|
||||
new-tab =
|
||||
.label = New Tab
|
||||
.accesskey = N
|
||||
`);
|
||||
|
@ -15,7 +15,7 @@
|
||||
async function* mockGenerateMessages(locales, resourceIds) {
|
||||
const mc = new MessageContext(locales);
|
||||
mc.addMessages("title = Hello World");
|
||||
mc.addMessages("link\n .title = Click me");
|
||||
mc.addMessages("link =\n .title = Click me");
|
||||
yield mc;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user