Bug 1455649 - DocumentL10n, part 1 - Add FTLResource object to Fluent. draft
authorZibi Braniecki <zbraniecki@mozilla.com>
Fri, 22 Jun 2018 11:00:55 -0700
changeset 815115 8ca755489d961db52e103fc3bc5119e1eae981b1
parent 814704 afdeb0288690f0acde2ba6bd008f860e1acdd026
child 815116 93448ee31634580275cfcd744219f0805fcebc44
push id115447
push userbmo:gandalf@aviary.pl
push dateFri, 06 Jul 2018 20:45:09 +0000
bugs1455649
milestone63.0a1
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
intl/l10n/MessageContext.jsm
--- 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