Bug 1455649 - DocumentL10n, part 1 - Add FTLResource object to Fluent.
In order to be able to cache parsed resources and cheaply construct multiple
MessageContext's with them, we want to add a class that encapsulate parsed
list of entries and will become a good singleton candidate for IPC.
FTLResource allows us to achieve that without leaking internal data structure
and without requiring us the specify the AST for the public API.
MozReview-Commit-ID: 3XnZKgA06dl
--- a/intl/l10n/MessageContext.jsm
+++ b/intl/l10n/MessageContext.jsm
@@ -21,16 +21,23 @@
/* eslint no-magic-numbers: [0] */
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_?-]*$/;
+class FluentDocument extends Map {
+ constructor(entries, errors = []) {
+ super(entries);
+ this.errors = errors;
+ }
+}
+
/**
* The `Parser` class is responsible for parsing FTL resources.
*
* It's only public method is `getResource(source)` which takes an FTL string
* and returns a two element Array with an Object of entries generated from the
* source as the first element and an array of SyntaxError objects as the
* second.
*
@@ -1754,16 +1761,21 @@ class MessageContext {
*
* @param {string} id - The identifier of the message to check.
* @returns {Any}
*/
getMessage(id) {
return this._messages.get(id);
}
+ static parseDocument(source) {
+ let [entries, errors] = parse(source);
+ return new FluentDocument(Object.entries(entries), errors);
+ }
+
/**
* Add a translation resource to the context.
*
* The translation resource must use the Fluent syntax. It will be parsed by
* the context and each translation unit (message) will be available in the
* context by its identifier.
*
* ctx.addMessages('foo = Foo');
@@ -1773,35 +1785,40 @@ class MessageContext {
*
* Parsed entities should be formatted with the `format` method in case they
* contain logic (references, select expressions etc.).
*
* @param {string} source - Text resource with translations.
* @returns {Array<Error>}
*/
addMessages(source) {
- const [entries, errors] = parse(source);
- for (const id in entries) {
+ const doc = this.parseDocument(source);
+ return this.addDocument(doc);
+ }
+
+ addDocument(doc) {
+ const errors = doc.errors.slice();
+
+ for (const [id, value] of doc) {
if (id.startsWith("-")) {
// Identifiers starting with a dash (-) define terms. Terms are private
// and cannot be retrieved from MessageContext.
if (this._terms.has(id)) {
errors.push(`Attempt to override an existing term: "${id}"`);
continue;
}
- this._terms.set(id, entries[id]);
+ this._terms.set(id, value);
} else {
if (this._messages.has(id)) {
errors.push(`Attempt to override an existing message: "${id}"`);
continue;
}
- this._messages.set(id, entries[id]);
+ this._messages.set(id, value);
}
}
-
return errors;
}
/**
* 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