intl/l10n/fluent.js.patch
author Anne van Kesteren <annevk@annevk.nl>
Tue, 05 Mar 2019 12:18:39 +0000
changeset 464583 8566ae4d152605453ebe6715ec4f2f8af60a95d3
parent 451208 9e8d8d2f85803727a4d7630d59fbde7c64a9e994
child 477630 e752661a791578c31e1a998efa8e2db7bfa2d010
permissions -rw-r--r--
Bug 1529726 [wpt PR 15414] - HTML: window.length and named access, a=testonly Automatic update from web-platform-tests HTML: window.length and named access For https://github.com/whatwg/html/pull/4368. -- wpt-commits: 3a43f99a56a4c016e3f1cda41330f1a2c0e780ff wpt-pr: 15414

diff --git a/intl/l10n/DOMLocalization.jsm b/intl/l10n/DOMLocalization.jsm
--- a/intl/l10n/DOMLocalization.jsm
+++ b/intl/l10n/DOMLocalization.jsm
@@ -15,12 +15,13 @@
  * limitations under the License.
  */
 
-/* fluent-dom@fa25466f (October 12, 2018) */
+
+/* fluent-dom@0.4.0 */
 
-const { Localization } =
-  ChromeUtils.import("resource://gre/modules/Localization.jsm", {});
-const { Services } =
-  ChromeUtils.import("resource://gre/modules/Services.jsm", {});
+import Localization from '../../fluent-dom/src/localization.js';
+
+/* eslint no-console: ["error", {allow: ["warn"]}] */
+/* global console */
 
 // Match the opening angle bracket (<) in HTML tags, and HTML entities like
 // &amp;, &#0038;, &#x0026;.
@@ -38,7 +39,7 @@ const TEXT_LEVEL_ELEMENTS = {
   "http://www.w3.org/1999/xhtml": [
     "em", "strong", "small", "s", "cite", "q", "dfn", "abbr", "data",
     "time", "code", "var", "samp", "kbd", "sub", "sup", "i", "b", "u",
-    "mark", "bdi", "bdo", "span", "br", "wbr",
+    "mark", "bdi", "bdo", "span", "br", "wbr"
   ],
 };
 
@@ -56,17 +57,16 @@ const LOCALIZABLE_ATTRIBUTES = {
     track: ["label"],
     img: ["alt"],
     textarea: ["placeholder"],
-    th: ["abbr"],
+    th: ["abbr"]
   },
   "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul": {
     global: [
-      "accesskey", "aria-label", "aria-valuetext", "aria-moz-hint", "label",
-      "title", "tooltiptext"],
-    description: ["value"],
+      "accesskey", "aria-label", "aria-valuetext", "aria-moz-hint", "label"
+    ],
     key: ["key", "keycode"],
-    label: ["value"],
     textbox: ["placeholder"],
-  },
+    toolbarbutton: ["tooltiptext"],
+  }
 };
 
 
@@ -96,7 +96,6 @@ function translateElement(element, trans
       const templateElement = element.ownerDocument.createElementNS(
         "http://www.w3.org/1999/xhtml", "template"
       );
-      // eslint-disable-next-line no-unsanitized/property
       templateElement.innerHTML = value;
       overlayChildNodes(templateElement.content, element);
     }
@@ -350,46 +349,6 @@ function shallowPopulateUsing(fromElemen
   return toElement;
 }
 
-/**
- * Sanitizes a translation before passing them to Node.localize API.
- *
- * It returns `false` if the translation contains DOM Overlays and should
- * not go into Node.localize.
- *
- * Note: There's a third item of work that JS DOM Overlays do - removal
- * of attributes from the previous translation.
- * This is not trivial to implement for Node.localize scenario, so
- * at the moment it is not supported.
- *
- * @param {{
- *          localName: string,
- *          namespaceURI: string,
- *          type: string || null
- *          l10nId: string,
- *          l10nArgs: Array<Object> || null,
- *          l10nAttrs: string ||null,
- *        }}                                     l10nItems
- * @param {{value: string, attrs: Object}} translations
- * @returns boolean
- * @private
- */
-function sanitizeTranslationForNodeLocalize(l10nItem, translation) {
-  if (reOverlay.test(translation.value)) {
-    return false;
-  }
-
-  if (translation.attributes) {
-    const explicitlyAllowed = l10nItem.l10nAttrs === null ? null :
-      l10nItem.l10nAttrs.split(",").map(i => i.trim());
-    for (const [j, {name}] of translation.attributes.entries()) {
-      if (!isAttrNameLocalizable(name, l10nItem, explicitlyAllowed)) {
-        translation.attributes.splice(j, 1);
-      }
-    }
-  }
-  return true;
-}
-
 const L10NID_ATTR_NAME = "data-l10n-id";
 const L10NARGS_ATTR_NAME = "data-l10n-args";
 
@@ -427,12 +386,12 @@ class DOMLocalization extends Localizati
       characterData: false,
       childList: true,
       subtree: true,
-      attributeFilter: [L10NID_ATTR_NAME, L10NARGS_ATTR_NAME],
+      attributeFilter: [L10NID_ATTR_NAME, L10NARGS_ATTR_NAME]
     };
   }
 
-  onChange(eager = false) {
-    super.onChange(eager);
+  onChange() {
+    super.onChange();
     this.translateRoots();
   }
 
@@ -497,7 +456,7 @@ class DOMLocalization extends Localizati
   getAttributes(element) {
     return {
       id: element.getAttribute(L10NID_ATTR_NAME),
-      args: JSON.parse(element.getAttribute(L10NARGS_ATTR_NAME) || null),
+      args: JSON.parse(element.getAttribute(L10NARGS_ATTR_NAME) || null)
     };
   }
 
@@ -519,17 +478,18 @@ class DOMLocalization extends Localizati
     }
 
     if (this.windowElement) {
-      if (this.windowElement !== newRoot.ownerGlobal) {
+      if (this.windowElement !== newRoot.ownerDocument.defaultView) {
         throw new Error(`Cannot connect a root:
           DOMLocalization already has a root from a different window.`);
       }
     } else {
-      this.windowElement = newRoot.ownerGlobal;
+      this.windowElement = newRoot.ownerDocument.defaultView;
       this.mutationObserver = new this.windowElement.MutationObserver(
         mutations => this.translateMutations(mutations)
       );
     }
 
+
     this.roots.add(newRoot);
     this.mutationObserver.observe(newRoot, this.observerConfig);
   }
@@ -572,20 +532,7 @@ class DOMLocalization extends Localizati
   translateRoots() {
     const roots = Array.from(this.roots);
     return Promise.all(
-      roots.map(async root => {
-        // We want to first retranslate the UI, and
-        // then (potentially) flip the directionality.
-        //
-        // This means that the DOM alternations and directionality
-        // are set in the same microtask.
-        await this.translateFragment(root);
-        let primaryLocale = Services.locale.appLocaleAsBCP47;
-        let direction = Services.locale.isAppLocaleRTL ? "rtl" : "ltr";
-        root.setAttribute("lang", primaryLocale);
-        root.setAttribute(root.namespaceURI ===
-          "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-          ? "localedir" : "dir", direction);
-      })
+      roots.map(root => this.translateFragment(root))
     );
   }
 
@@ -652,10 +599,7 @@ class DOMLocalization extends Localizati
     if (this.pendingElements.size > 0) {
       if (this.pendingrAF === null) {
         this.pendingrAF = this.windowElement.requestAnimationFrame(() => {
-          // We need to filter for elements that lost their l10n-id while
-          // waiting for the animation frame.
-          this.translateElements(Array.from(this.pendingElements)
-            .filter(elem => elem.hasAttribute("data-l10n-id")));
+          this.translateElements(Array.from(this.pendingElements));
           this.pendingElements.clear();
           this.pendingrAF = null;
         });
@@ -677,63 +621,6 @@ class DOMLocalization extends Localizati
    * @returns {Promise}
    */
   translateFragment(frag) {
-    if (frag.localize) {
-      // This is a temporary fast-path offered by Gecko to workaround performance
-      // issues coming from Fluent and XBL+Stylo performing unnecesary
-      // operations during startup.
-      // For details see bug 1441037, bug 1442262, and bug 1363862.
-
-      // A sparse array which will store translations separated out from
-      // all translations that is needed for DOM Overlay.
-      const overlayTranslations = [];
-
-      const getTranslationsForItems = async l10nItems => {
-        const keys = l10nItems.map(
-          l10nItem => ({id: l10nItem.l10nId, args: l10nItem.l10nArgs}));
-        const translations = await this.formatMessages(keys);
-
-        // Here we want to separate out elements that require DOM Overlays.
-        // Those elements will have to be translated using our JS
-        // implementation, while everything else is going to use the fast-path.
-        for (const [i, translation] of translations.entries()) {
-          if (translation === undefined) {
-            continue;
-          }
-
-          const hasOnlyText =
-            sanitizeTranslationForNodeLocalize(l10nItems[i], translation);
-          if (!hasOnlyText) {
-            // Removing from translations to make Node.localize skip it.
-            // We will translate it below using JS DOM Overlays.
-            overlayTranslations[i] = translations[i];
-            translations[i] = undefined;
-          }
-        }
-
-        // We pause translation observing here because Node.localize
-        // will translate the whole DOM next, using the `translations`.
-        //
-        // The observer will be resumed after DOM Overlays are localized
-        // in the next microtask.
-        this.pauseObserving();
-        return translations;
-      };
-
-      return frag.localize(getTranslationsForItems.bind(this))
-        .then(untranslatedElements => {
-          for (let i = 0; i < overlayTranslations.length; i++) {
-            if (overlayTranslations[i] !== undefined &&
-                untranslatedElements[i] !== undefined) {
-              translateElement(untranslatedElements[i], overlayTranslations[i]);
-            }
-          }
-          this.resumeObserving();
-        })
-        .catch(e => {
-          this.resumeObserving();
-          throw e;
-        });
-    }
     return this.translateElements(this.getTranslatables(frag));
   }
 
@@ -808,10 +695,42 @@ class DOMLocalization extends Localizati
   getKeysForElement(element) {
     return {
       id: element.getAttribute(L10NID_ATTR_NAME),
-      args: JSON.parse(element.getAttribute(L10NARGS_ATTR_NAME) || null),
+      args: JSON.parse(element.getAttribute(L10NARGS_ATTR_NAME) || null)
     };
   }
 }
 
-this.DOMLocalization = DOMLocalization;
-var EXPORTED_SYMBOLS = ["DOMLocalization"];
+/* global L10nRegistry, Services */
+
+/**
+ * The default localization strategy for Gecko. It comabines locales
+ * available in L10nRegistry, with locales requested by the user to
+ * generate the iterator over FluentBundles.
+ *
+ * In the future, we may want to allow certain modules to override this
+ * with a different negotitation strategy to allow for the module to
+ * be localized into a different language - for example DevTools.
+ */
+function defaultGenerateBundles(resourceIds) {
+  const requestedLocales = Services.locale.getRequestedLocales();
+  const availableLocales = L10nRegistry.getAvailableLocales();
+  const defaultLocale = Services.locale.defaultLocale;
+  const locales = Services.locale.negotiateLanguages(
+    requestedLocales, availableLocales, defaultLocale,
+  );
+  return L10nRegistry.generateContexts(locales, resourceIds);
+}
+
+
+class GeckoDOMLocalization extends DOMLocalization {
+  constructor(
+    windowElement,
+    resourceIds,
+    generateBundles = defaultGenerateBundles
+  ) {
+    super(windowElement, resourceIds, generateBundles);
+  }
+}
+
+this.DOMLocalization = GeckoDOMLocalization;
+this.EXPORTED_SYMBOLS = ["DOMLocalization"];
diff --git a/intl/l10n/Fluent.jsm b/intl/l10n/Fluent.jsm
--- a/intl/l10n/Fluent.jsm
+++ b/intl/l10n/Fluent.jsm
@@ -16,7 +16,7 @@
  */
 
 
-/* fluent@0.10.0 */
+/* fluent-dom@0.4.0 */
 
 /* global Intl */
 
@@ -139,53 +139,7 @@ function values(opts) {
   return unwrapped;
 }
 
-/**
- * @overview
- *
- * 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 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
- * bundle'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
- * translation as possible.  In rare situations where the resolver didn't know
- * how to recover from an error it will return an instance of `FluentNone`.
- *
- * `MessageReference`, `VariantExpression`, `AttributeExpression` and
- * `SelectExpression` resolve to raw Runtime Entries objects and the result of
- * the resolution needs to be passed into `Type` to get their real value.
- * This is useful for composing expressions.  Consider:
- *
- *     brand-name[nominative]
- *
- * which is a `VariantExpression` with properties `id: MessageReference` and
- * `key: Keyword`.  If `MessageReference` was resolved eagerly, it would
- * instantly resolve to the value of the `brand-name` message.  Instead, we
- * want to get the message object and look for its `nominative` variant.
- *
- * All other expressions (except for `FunctionReference` which is only used in
- * `CallExpression`) resolve to an instance of `FluentType`.  The caller should
- * use the `toString` method to convert the instance to a native value.
- *
- *
- * All functions in this file pass around a special object called `env`.
- * This object stores a set of elements used by all resolve functions:
- *
- *  * {FluentBundle} bundle
- *      bundle for which the given resolution is happening
- *  * {Object} args
- *      list of developer provided arguments that can be used
- *  * {Array} errors
- *      list of errors collected while resolving
- *  * {WeakSet} dirty
- *      Set of patterns already encountered during this resolution.
- *      This is used to prevent cyclic resolutions.
- */
+/* global Intl */
 
 // Prevent expansion of too long placeables.
 const MAX_PLACEABLE_LENGTH = 2500;
@@ -514,7 +468,7 @@ function Pattern(env, ptn) {
  */
 function resolve(bundle, args, message, errors = []) {
   const env = {
-    bundle, args, errors, dirty: new WeakSet(),
+    bundle, args, errors, dirty: new WeakSet()
   };
   return Type(env, message).toString(bundle);
 }
@@ -1064,7 +1018,7 @@ class FluentBundle {
   constructor(locales, {
     functions = {},
     useIsolating = true,
-    transform = v => v,
+    transform = v => v
   } = {}) {
     this.locales = Array.isArray(locales) ? locales : [locales];
 
@@ -1235,6 +1189,14 @@ class FluentBundle {
   }
 }
 
+/*
+ * @module fluent
+ * @overview
+ *
+ * `fluent` is a JavaScript implementation of Project Fluent, a localization
+ * framework designed to unleash the expressive power of the natural language.
+ *
+ */
+
 this.FluentBundle = FluentBundle;
-this.FluentResource = FluentResource;
-var EXPORTED_SYMBOLS = ["FluentBundle", "FluentResource"];
+this.EXPORTED_SYMBOLS = ["FluentBundle"];
diff --git a/intl/l10n/Localization.jsm b/intl/l10n/Localization.jsm
--- a/intl/l10n/Localization.jsm
+++ b/intl/l10n/Localization.jsm
@@ -16,34 +16,27 @@
  */
 
 
-/* fluent-dom@fa25466f (October 12, 2018) */
-
-/* eslint no-console: ["error", { allow: ["warn", "error"] }] */
-/* global console */
-
-const { L10nRegistry } = ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm", {});
-const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
-const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm", {});
+/* fluent-dom@0.4.0 */
 
 /*
  * Base CachedIterable class.
  */
 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;
+    /**
+     * 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);
     }
-
-    return new this(iterable);
-  }
 }
 
 /*
@@ -53,80 +46,88 @@ class CachedIterable extends Array {
  * iterable.
  */
 class CachedAsyncIterable extends CachedIterable {
-  /**
-   * Create an `CachedAsyncIterable` instance.
-   *
-   * @param {Iterable} iterable
-   * @returns {CachedAsyncIterable}
-   */
-  constructor(iterable) {
-    super();
+    /**
+     * Create an `CachedAsyncIterable` instance.
+     *
+     * @param {Iterable} iterable
+     * @returns {CachedAsyncIterable}
+     */
+    constructor(iterable) {
+        super();
+
+        if (Symbol.asyncIterator in Object(iterable)) {
+            this.iterator = iterable[Symbol.asyncIterator]();
+        } else if (Symbol.iterator in Object(iterable)) {
+            this.iterator = iterable[Symbol.iterator]();
+        } else {
+            throw new TypeError("Argument must implement the iteration protocol.");
+        }
+    }
 
-    if (Symbol.asyncIterator in Object(iterable)) {
-      this.iterator = iterable[Symbol.asyncIterator]();
-    } else if (Symbol.iterator in Object(iterable)) {
-      this.iterator = iterable[Symbol.iterator]();
-    } else {
-      throw new TypeError("Argument must implement the iteration protocol.");
-    }
-  }
+    /**
+     * 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;
 
-  /**
-   * 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 cached = this;
-    let cur = 0;
+        return {
+            next() {
+                if (cached.length === cur) {
+                    return {value: undefined, done: true};
+                }
+                return cached[cur++];
+            }
+        };
+    }
 
-    return {
-      async next() {
-        if (cached.length <= cur) {
-          cached.push(cached.iterator.next());
-        }
-        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 cached = this;
+        let cur = 0;
 
-  /**
-   * This method allows user to consume the next element from the iterator
-   * into the cache.
-   *
-   * @param {number} count - number of elements to consume
-   */
-  async touchNext(count = 1) {
-    let idx = 0;
-    while (idx++ < count) {
-      const last = this[this.length - 1];
-      if (last && (await last).done) {
-        break;
-      }
-      this.push(this.iterator.next());
+        return {
+            async next() {
+                if (cached.length <= cur) {
+                    cached.push(await cached.iterator.next());
+                }
+                return cached[cur++];
+            }
+        };
     }
-    // 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];
-  }
+
+    /**
+     * This method allows user to consume the next element from the iterator
+     * into the cache.
+     *
+     * @param {number} count - number of elements to consume
+     */
+    async touchNext(count = 1) {
+        let idx = 0;
+        while (idx++ < count) {
+            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];
+    }
 }
 
-/**
- * The default localization strategy for Gecko. It comabines locales
- * available in L10nRegistry, with locales requested by the user to
- * generate the iterator over FluentBundles.
- *
- * In the future, we may want to allow certain modules to override this
- * with a different negotitation strategy to allow for the module to
- * be localized into a different language - for example DevTools.
- */
-function defaultGenerateBundles(resourceIds) {
-  const appLocales = Services.locale.appLocalesAsBCP47;
-  return L10nRegistry.generateBundles(appLocales, resourceIds);
-}
+/* eslint no-console: ["error", { allow: ["warn", "error"] }] */
 
 /**
  * The `Localization` class is a central high-level API for vanilla
@@ -142,21 +143,16 @@ class Localization {
    *
    * @returns {Localization}
    */
-  constructor(resourceIds = [], generateBundles = defaultGenerateBundles) {
+  constructor(resourceIds = [], generateBundles) {
     this.resourceIds = resourceIds;
     this.generateBundles = generateBundles;
     this.bundles = CachedAsyncIterable.from(
       this.generateBundles(this.resourceIds));
   }
 
-  /**
-   * @param {Array<String>} resourceIds - List of resource IDs
-   * @param {bool}                eager - whether the I/O for new context should
-   *                                      begin eagerly
-   */
-  addResourceIds(resourceIds, eager = false) {
+  addResourceIds(resourceIds) {
     this.resourceIds.push(...resourceIds);
-    this.onChange(eager);
+    this.onChange();
     return this.resourceIds.length;
   }
 
@@ -188,12 +184,9 @@ class Localization {
         break;
       }
 
-      if (AppConstants.NIGHTLY_BUILD || Cu.isInAutomation) {
+      if (typeof console !== "undefined") {
         const locale = bundle.locales[0];
         const ids = Array.from(missingIds).join(", ");
-        if (Cu.isInAutomation) {
-          throw new Error(`Missing translations in ${locale}: ${ids}`);
-        }
         console.warn(`Missing translations in ${locale}: ${ids}`);
       }
     }
@@ -281,64 +274,21 @@ class Localization {
     return val;
   }
 
-  /**
-   * Register weak observers on events that will trigger cache invalidation
-   */
-  registerObservers() {
-    Services.obs.addObserver(this, "intl:app-locales-changed", true);
-    Services.prefs.addObserver("intl.l10n.pseudo", this, true);
-  }
-
-  /**
-   * Default observer handler method.
-   *
-   * @param {String} subject
-   * @param {String} topic
-   * @param {Object} data
-   */
-  observe(subject, topic, data) {
-    switch (topic) {
-      case "intl:app-locales-changed":
-        this.onChange();
-        break;
-      case "nsPref:changed":
-        switch (data) {
-          case "intl.l10n.pseudo":
-            this.onChange();
-        }
-        break;
-      default:
-        break;
-    }
+  handleEvent() {
+    this.onChange();
   }
 
   /**
    * This method should be called when there's a reason to believe
    * that language negotiation or available resources changed.
-   *
-   * @param {bool} eager - whether the I/O for new context should begin eagerly
    */
-  onChange(eager = false) {
+  onChange() {
     this.bundles = CachedAsyncIterable.from(
       this.generateBundles(this.resourceIds));
-    if (eager) {
-      // If the first app locale is the same as last fallback
-      // it means that we have all resources in this locale, and
-      // we want to eagerly fetch just that one.
-      // Otherwise, we're in a scenario where the first locale may
-      // be partial and we want to eagerly fetch a fallback as well.
-      const appLocale = Services.locale.appLocaleAsBCP47;
-      const lastFallback = Services.locale.lastFallbackLocale;
-      const prefetchCount = appLocale === lastFallback ? 1 : 2;
-      this.bundles.touchNext(prefetchCount);
-    }
+    this.bundles.touchNext(2);
   }
 }
 
-Localization.prototype.QueryInterface = ChromeUtils.generateQI([
-  Ci.nsISupportsWeakReference,
-]);
-
 /**
  * Format the value of a message into a string.
  *
@@ -430,7 +380,7 @@ function messageFromBundle(bundle, error
  * See `Localization.formatWithFallback` for more info on how this is used.
  *
  * @param {Function}       method
- * @param {FluentBundle}   bundle
+ * @param {FluentBundle} bundle
  * @param {Array<string>}  keys
  * @param {{Array<{value: string, attributes: Object}>}} translations
  *
@@ -458,5 +408,44 @@ function keysFromBundle(method, bundle, 
   return missingIds;
 }
 
-this.Localization = Localization;
-var EXPORTED_SYMBOLS = ["Localization"];
+/* global Components */
+/* eslint no-unused-vars: 0 */
+
+const Cu = Components.utils;
+const Cc = Components.classes;
+const Ci = Components.interfaces;
+
+const { L10nRegistry } =
+  Cu.import("resource://gre/modules/L10nRegistry.jsm", {});
+const ObserverService =
+  Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
+const { Services } =
+  Cu.import("resource://gre/modules/Services.jsm", {});
+
+/**
+ * The default localization strategy for Gecko. It comabines locales
+ * available in L10nRegistry, with locales requested by the user to
+ * generate the iterator over FluentBundles.
+ *
+ * In the future, we may want to allow certain modules to override this
+ * with a different negotitation strategy to allow for the module to
+ * be localized into a different language - for example DevTools.
+ */
+function defaultGenerateBundles(resourceIds) {
+  const requestedLocales = Services.locale.getRequestedLocales();
+  const availableLocales = L10nRegistry.getAvailableLocales();
+  const defaultLocale = Services.locale.defaultLocale;
+  const locales = Services.locale.negotiateLanguages(
+    requestedLocales, availableLocales, defaultLocale,
+  );
+  return L10nRegistry.generateContexts(locales, resourceIds);
+}
+
+class GeckoLocalization extends Localization {
+  constructor(resourceIds, generateBundles = defaultGenerateBundles) {
+    super(resourceIds, generateBundles);
+  }
+}
+
+this.Localization = GeckoLocalization;
+this.EXPORTED_SYMBOLS = ["Localization"];
diff --git a/intl/l10n/fluent.js.patch b/intl/l10n/fluent.js.patch
--- a/intl/l10n/fluent.js.patch
+++ b/intl/l10n/fluent.js.patch
@@ -1,736 +0,0 @@
---- ./dist/Fluent.jsm	2018-10-19 08:40:36.557032837 -0600
-+++ /home/zbraniecki/projects/mozilla-unified/intl/l10n/Fluent.jsm	2018-10-19 21:22:35.174315857 -0600
-@@ -16,7 +16,7 @@
-  */
- 
- 
--/* fluent-dom@0.4.0 */
-+/* fluent@fa25466f (October 12, 2018) */
- 
- /* global Intl */
- 
-@@ -139,7 +139,53 @@
-   return unwrapped;
- }
- 
--/* global Intl */
-+/**
-+ * @overview
-+ *
-+ * 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 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
-+ * bundle'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
-+ * translation as possible.  In rare situations where the resolver didn't know
-+ * how to recover from an error it will return an instance of `FluentNone`.
-+ *
-+ * `MessageReference`, `VariantExpression`, `AttributeExpression` and
-+ * `SelectExpression` resolve to raw Runtime Entries objects and the result of
-+ * the resolution needs to be passed into `Type` to get their real value.
-+ * This is useful for composing expressions.  Consider:
-+ *
-+ *     brand-name[nominative]
-+ *
-+ * which is a `VariantExpression` with properties `id: MessageReference` and
-+ * `key: Keyword`.  If `MessageReference` was resolved eagerly, it would
-+ * instantly resolve to the value of the `brand-name` message.  Instead, we
-+ * want to get the message object and look for its `nominative` variant.
-+ *
-+ * All other expressions (except for `FunctionReference` which is only used in
-+ * `CallExpression`) resolve to an instance of `FluentType`.  The caller should
-+ * use the `toString` method to convert the instance to a native value.
-+ *
-+ *
-+ * All functions in this file pass around a special object called `env`.
-+ * This object stores a set of elements used by all resolve functions:
-+ *
-+ *  * {FluentBundle} bundle
-+ *      bundle for which the given resolution is happening
-+ *  * {Object} args
-+ *      list of developer provided arguments that can be used
-+ *  * {Array} errors
-+ *      list of errors collected while resolving
-+ *  * {WeakSet} dirty
-+ *      Set of patterns already encountered during this resolution.
-+ *      This is used to prevent cyclic resolutions.
-+ */
- 
- // Prevent expansion of too long placeables.
- const MAX_PLACEABLE_LENGTH = 2500;
-@@ -1319,14 +1365,6 @@
-   }
- }
- 
--/*
-- * @module fluent
-- * @overview
-- *
-- * `fluent` is a JavaScript implementation of Project Fluent, a localization
-- * framework designed to unleash the expressive power of the natural language.
-- *
-- */
--
- this.FluentBundle = FluentBundle;
--this.EXPORTED_SYMBOLS = ["FluentBundle"];
-+this.FluentResource = FluentResource;
-+var EXPORTED_SYMBOLS = ["FluentBundle", "FluentResource"];
---- ./dist/Localization.jsm	2018-10-19 08:40:36.773712561 -0600
-+++ /home/zbraniecki/projects/mozilla-unified/intl/l10n/Localization.jsm	2018-10-19 21:20:57.295233460 -0600
-@@ -16,27 +16,34 @@
-  */
- 
- 
--/* fluent-dom@0.4.0 */
-+/* fluent-dom@fa25466f (October 12, 2018) */
-+
-+/* eslint no-console: ["error", { allow: ["warn", "error"] }] */
-+/* global console */
-+
-+const { L10nRegistry } = ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm", {});
-+const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
-+const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm", {});
- 
- /*
-  * Base CachedIterable class.
-  */
- 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);
-+  /**
-+   * 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);
-+  }
- }
- 
- /*
-@@ -46,88 +53,100 @@
-  * iterable.
-  */
- class CachedAsyncIterable extends CachedIterable {
--    /**
--     * Create an `CachedAsyncIterable` instance.
--     *
--     * @param {Iterable} iterable
--     * @returns {CachedAsyncIterable}
--     */
--    constructor(iterable) {
--        super();
--
--        if (Symbol.asyncIterator in Object(iterable)) {
--            this.iterator = iterable[Symbol.asyncIterator]();
--        } else if (Symbol.iterator in Object(iterable)) {
--            this.iterator = iterable[Symbol.iterator]();
--        } else {
--            throw new TypeError("Argument must implement the iteration protocol.");
--        }
--    }
-+  /**
-+   * Create an `CachedAsyncIterable` instance.
-+   *
-+   * @param {Iterable} iterable
-+   * @returns {CachedAsyncIterable}
-+   */
-+  constructor(iterable) {
-+    super();
- 
--    /**
--     * 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++];
--            }
--        };
-+    if (Symbol.asyncIterator in Object(iterable)) {
-+      this.iterator = iterable[Symbol.asyncIterator]();
-+    } else if (Symbol.iterator in Object(iterable)) {
-+      this.iterator = iterable[Symbol.iterator]();
-+    } else {
-+      throw new TypeError("Argument must implement the iteration protocol.");
-     }
-+  }
- 
--    /**
--     * 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 cached = this;
--        let cur = 0;
--
--        return {
--            async next() {
--                if (cached.length <= cur) {
--                    cached.push(await cached.iterator.next());
--                }
--                return cached[cur++];
--            }
--        };
--    }
-+  /**
-+   * 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++];
-+      }
-+    };
-+  }
- 
--    /**
--     * This method allows user to consume the next element from the iterator
--     * into the cache.
--     *
--     * @param {number} count - number of elements to consume
--     */
--    async touchNext(count = 1) {
--        let idx = 0;
--        while (idx++ < count) {
--            const last = this[this.length - 1];
--            if (last && last.done) {
--                break;
--            }
--            this.push(await this.iterator.next());
-+  /**
-+   * 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 cached = this;
-+    let cur = 0;
-+
-+    return {
-+      async next() {
-+        if (cached.length <= cur) {
-+          cached.push(await cached.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];
-+        return cached[cur++];
-+      }
-+    };
-+  }
-+
-+  /**
-+   * This method allows user to consume the next element from the iterator
-+   * into the cache.
-+   *
-+   * @param {number} count - number of elements to consume
-+   */
-+  async touchNext(count = 1) {
-+    let idx = 0;
-+    while (idx++ < count) {
-+      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];
-+  }
- }
- 
--/* eslint no-console: ["error", { allow: ["warn", "error"] }] */
-+/**
-+ * The default localization strategy for Gecko. It comabines locales
-+ * available in L10nRegistry, with locales requested by the user to
-+ * generate the iterator over FluentBundles.
-+ *
-+ * In the future, we may want to allow certain modules to override this
-+ * with a different negotitation strategy to allow for the module to
-+ * be localized into a different language - for example DevTools.
-+ */
-+function defaultGenerateBundles(resourceIds) {
-+  const appLocales = Services.locale.appLocalesAsBCP47;
-+  return L10nRegistry.generateContexts(appLocales, resourceIds);
-+}
- 
- /**
-  * The `Localization` class is a central high-level API for vanilla
-@@ -143,16 +162,21 @@
-    *
-    * @returns {Localization}
-    */
--  constructor(resourceIds = [], generateBundles) {
-+  constructor(resourceIds = [], generateBundles = defaultGenerateBundles) {
-     this.resourceIds = resourceIds;
-     this.generateBundles = generateBundles;
-     this.bundles = CachedAsyncIterable.from(
-       this.generateBundles(this.resourceIds));
-   }
- 
--  addResourceIds(resourceIds) {
-+  /**
-+   * @param {Array<String>} resourceIds - List of resource IDs
-+   * @param {bool}                eager - whether the I/O for new context should
-+   *                                      begin eagerly
-+   */
-+  addResourceIds(resourceIds, eager = false) {
-     this.resourceIds.push(...resourceIds);
--    this.onChange();
-+    this.onChange(eager);
-     return this.resourceIds.length;
-   }
- 
-@@ -184,9 +208,12 @@
-         break;
-       }
- 
--      if (typeof console !== "undefined") {
-+      if (AppConstants.NIGHTLY_BUILD || Cu.isInAutomation) {
-         const locale = bundle.locales[0];
-         const ids = Array.from(missingIds).join(", ");
-+        if (Cu.isInAutomation) {
-+          throw new Error(`Missing translations in ${locale}: ${ids}`);
-+        }
-         console.warn(`Missing translations in ${locale}: ${ids}`);
-       }
-     }
-@@ -274,21 +301,64 @@
-     return val;
-   }
- 
--  handleEvent() {
--    this.onChange();
-+  /**
-+   * Register weak observers on events that will trigger cache invalidation
-+   */
-+  registerObservers() {
-+    Services.obs.addObserver(this, "intl:app-locales-changed", true);
-+    Services.prefs.addObserver("intl.l10n.pseudo", this, true);
-+  }
-+
-+  /**
-+   * Default observer handler method.
-+   *
-+   * @param {String} subject
-+   * @param {String} topic
-+   * @param {Object} data
-+   */
-+  observe(subject, topic, data) {
-+    switch (topic) {
-+      case "intl:app-locales-changed":
-+        this.onChange();
-+        break;
-+      case "nsPref:changed":
-+        switch (data) {
-+          case "intl.l10n.pseudo":
-+            this.onChange();
-+        }
-+        break;
-+      default:
-+        break;
-+    }
-   }
- 
-   /**
-    * This method should be called when there's a reason to believe
-    * that language negotiation or available resources changed.
-+   *
-+   * @param {bool} eager - whether the I/O for new context should begin eagerly
-    */
--  onChange() {
-+  onChange(eager = false) {
-     this.bundles = CachedAsyncIterable.from(
-       this.generateBundles(this.resourceIds));
--    this.bundles.touchNext(2);
-+    if (eager) {
-+      // If the first app locale is the same as last fallback
-+      // it means that we have all resources in this locale, and
-+      // we want to eagerly fetch just that one.
-+      // Otherwise, we're in a scenario where the first locale may
-+      // be partial and we want to eagerly fetch a fallback as well.
-+      const appLocale = Services.locale.appLocaleAsBCP47;
-+      const lastFallback = Services.locale.lastFallbackLocale;
-+      const prefetchCount = appLocale === lastFallback ? 1 : 2;
-+      this.bundles.touchNext(prefetchCount);
-+    }
-   }
- }
- 
-+Localization.prototype.QueryInterface = ChromeUtils.generateQI([
-+  Ci.nsISupportsWeakReference
-+]);
-+
- /**
-  * Format the value of a message into a string.
-  *
-@@ -380,7 +450,7 @@
-  * See `Localization.formatWithFallback` for more info on how this is used.
-  *
-  * @param {Function}       method
-- * @param {FluentBundle} bundle
-+ * @param {FluentBundle}   bundle
-  * @param {Array<string>}  keys
-  * @param {{Array<{value: string, attributes: Object}>}} translations
-  *
-@@ -408,44 +478,5 @@
-   return missingIds;
- }
- 
--/* global Components */
--/* eslint no-unused-vars: 0 */
--
--const Cu = Components.utils;
--const Cc = Components.classes;
--const Ci = Components.interfaces;
--
--const { L10nRegistry } =
--  Cu.import("resource://gre/modules/L10nRegistry.jsm", {});
--const ObserverService =
--  Cc["@mozilla.org/observer-service;1"].getService(Ci.nsIObserverService);
--const { Services } =
--  Cu.import("resource://gre/modules/Services.jsm", {});
--
--/**
-- * The default localization strategy for Gecko. It comabines locales
-- * available in L10nRegistry, with locales requested by the user to
-- * generate the iterator over FluentBundles.
-- *
-- * In the future, we may want to allow certain modules to override this
-- * with a different negotitation strategy to allow for the module to
-- * be localized into a different language - for example DevTools.
-- */
--function defaultGenerateBundles(resourceIds) {
--  const requestedLocales = Services.locale.getRequestedLocales();
--  const availableLocales = L10nRegistry.getAvailableLocales();
--  const defaultLocale = Services.locale.defaultLocale;
--  const locales = Services.locale.negotiateLanguages(
--    requestedLocales, availableLocales, defaultLocale,
--  );
--  return L10nRegistry.generateContexts(locales, resourceIds);
--}
--
--class GeckoLocalization extends Localization {
--  constructor(resourceIds, generateBundles = defaultGenerateBundles) {
--    super(resourceIds, generateBundles);
--  }
--}
--
--this.Localization = GeckoLocalization;
--this.EXPORTED_SYMBOLS = ["Localization"];
-+this.Localization = Localization;
-+var EXPORTED_SYMBOLS = ["Localization"];
---- ./dist/DOMLocalization.jsm	2018-10-19 08:40:37.000392886 -0600
-+++ /home/zbraniecki/projects/mozilla-unified/intl/l10n/DOMLocalization.jsm	2018-10-19 21:38:25.963726161 -0600
-@@ -15,13 +15,12 @@
-  * limitations under the License.
-  */
- 
-+/* fluent-dom@fa25466f (October 12, 2018) */
- 
--/* fluent-dom@0.4.0 */
--
--import Localization from '../../fluent-dom/src/localization.js';
--
--/* eslint no-console: ["error", {allow: ["warn"]}] */
--/* global console */
-+const { Localization } =
-+  ChromeUtils.import("resource://gre/modules/Localization.jsm", {});
-+const { Services } =
-+  ChromeUtils.import("resource://gre/modules/Services.jsm", {});
- 
- // Match the opening angle bracket (<) in HTML tags, and HTML entities like
- // &amp;, &#0038;, &#x0026;.
-@@ -61,11 +60,12 @@
-   },
-   "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",
-+      "title", "tooltiptext"],
-+    description: ["value"],
-     key: ["key", "keycode"],
-+    label: ["value"],
-     textbox: ["placeholder"],
--    toolbarbutton: ["tooltiptext"],
-   }
- };
- 
-@@ -96,6 +96,7 @@
-       const templateElement = element.ownerDocument.createElementNS(
-         "http://www.w3.org/1999/xhtml", "template"
-       );
-+      // eslint-disable-next-line no-unsanitized/property
-       templateElement.innerHTML = value;
-       overlayChildNodes(templateElement.content, element);
-     }
-@@ -349,6 +350,46 @@
-   return toElement;
- }
- 
-+/**
-+ * Sanitizes a translation before passing them to Node.localize API.
-+ *
-+ * It returns `false` if the translation contains DOM Overlays and should
-+ * not go into Node.localize.
-+ *
-+ * Note: There's a third item of work that JS DOM Overlays do - removal
-+ * of attributes from the previous translation.
-+ * This is not trivial to implement for Node.localize scenario, so
-+ * at the moment it is not supported.
-+ *
-+ * @param {{
-+ *          localName: string,
-+ *          namespaceURI: string,
-+ *          type: string || null
-+ *          l10nId: string,
-+ *          l10nArgs: Array<Object> || null,
-+ *          l10nAttrs: string ||null,
-+ *        }}                                     l10nItems
-+ * @param {{value: string, attrs: Object}} translations
-+ * @returns boolean
-+ * @private
-+ */
-+function sanitizeTranslationForNodeLocalize(l10nItem, translation) {
-+  if (reOverlay.test(translation.value)) {
-+    return false;
-+  }
-+
-+  if (translation.attributes) {
-+    const explicitlyAllowed = l10nItem.l10nAttrs === null ? null :
-+      l10nItem.l10nAttrs.split(",").map(i => i.trim());
-+    for (const [j, {name}] of translation.attributes.entries()) {
-+      if (!isAttrNameLocalizable(name, l10nItem, explicitlyAllowed)) {
-+        translation.attributes.splice(j, 1);
-+      }
-+    }
-+  }
-+  return true;
-+}
-+
- const L10NID_ATTR_NAME = "data-l10n-id";
- const L10NARGS_ATTR_NAME = "data-l10n-args";
- 
-@@ -390,8 +431,8 @@
-     };
-   }
- 
--  onChange() {
--    super.onChange();
-+  onChange(eager = false) {
-+    super.onChange(eager);
-     this.translateRoots();
-   }
- 
-@@ -478,18 +519,17 @@
-     }
- 
-     if (this.windowElement) {
--      if (this.windowElement !== newRoot.ownerDocument.defaultView) {
-+      if (this.windowElement !== newRoot.ownerGlobal) {
-         throw new Error(`Cannot connect a root:
-           DOMLocalization already has a root from a different window.`);
-       }
-     } else {
--      this.windowElement = newRoot.ownerDocument.defaultView;
-+      this.windowElement = newRoot.ownerGlobal;
-       this.mutationObserver = new this.windowElement.MutationObserver(
-         mutations => this.translateMutations(mutations)
-       );
-     }
- 
--
-     this.roots.add(newRoot);
-     this.mutationObserver.observe(newRoot, this.observerConfig);
-   }
-@@ -532,7 +572,20 @@
-   translateRoots() {
-     const roots = Array.from(this.roots);
-     return Promise.all(
--      roots.map(root => this.translateFragment(root))
-+      roots.map(async root => {
-+        // We want to first retranslate the UI, and
-+        // then (potentially) flip the directionality.
-+        //
-+        // This means that the DOM alternations and directionality
-+        // are set in the same microtask.
-+        await this.translateFragment(root);
-+        let primaryLocale = Services.locale.appLocaleAsBCP47;
-+        let direction = Services.locale.isAppLocaleRTL ? "rtl" : "ltr";
-+        root.setAttribute("lang", primaryLocale);
-+        root.setAttribute(root.namespaceURI ===
-+          "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
-+          ? "localedir" : "dir", direction);
-+      })
-     );
-   }
- 
-@@ -599,7 +652,10 @@
-     if (this.pendingElements.size > 0) {
-       if (this.pendingrAF === null) {
-         this.pendingrAF = this.windowElement.requestAnimationFrame(() => {
--          this.translateElements(Array.from(this.pendingElements));
-+          // We need to filter for elements that lost their l10n-id while
-+          // waiting for the animation frame.
-+          this.translateElements(Array.from(this.pendingElements)
-+            .filter(elem => elem.hasAttribute("data-l10n-id")));
-           this.pendingElements.clear();
-           this.pendingrAF = null;
-         });
-@@ -621,6 +677,63 @@
-    * @returns {Promise}
-    */
-   translateFragment(frag) {
-+    if (frag.localize) {
-+      // This is a temporary fast-path offered by Gecko to workaround performance
-+      // issues coming from Fluent and XBL+Stylo performing unnecesary
-+      // operations during startup.
-+      // For details see bug 1441037, bug 1442262, and bug 1363862.
-+
-+      // A sparse array which will store translations separated out from
-+      // all translations that is needed for DOM Overlay.
-+      const overlayTranslations = [];
-+
-+      const getTranslationsForItems = async l10nItems => {
-+        const keys = l10nItems.map(
-+          l10nItem => ({id: l10nItem.l10nId, args: l10nItem.l10nArgs}));
-+        const translations = await this.formatMessages(keys);
-+
-+        // Here we want to separate out elements that require DOM Overlays.
-+        // Those elements will have to be translated using our JS
-+        // implementation, while everything else is going to use the fast-path.
-+        for (const [i, translation] of translations.entries()) {
-+          if (translation === undefined) {
-+            continue;
-+          }
-+
-+          const hasOnlyText =
-+            sanitizeTranslationForNodeLocalize(l10nItems[i], translation);
-+          if (!hasOnlyText) {
-+            // Removing from translations to make Node.localize skip it.
-+            // We will translate it below using JS DOM Overlays.
-+            overlayTranslations[i] = translations[i];
-+            translations[i] = undefined;
-+          }
-+        }
-+
-+        // We pause translation observing here because Node.localize
-+        // will translate the whole DOM next, using the `translations`.
-+        //
-+        // The observer will be resumed after DOM Overlays are localized
-+        // in the next microtask.
-+        this.pauseObserving();
-+        return translations;
-+      };
-+
-+      return frag.localize(getTranslationsForItems.bind(this))
-+        .then(untranslatedElements => {
-+          for (let i = 0; i < overlayTranslations.length; i++) {
-+            if (overlayTranslations[i] !== undefined &&
-+                untranslatedElements[i] !== undefined) {
-+              translateElement(untranslatedElements[i], overlayTranslations[i]);
-+            }
-+          }
-+          this.resumeObserving();
-+        })
-+        .catch(e => {
-+          this.resumeObserving();
-+          throw e;
-+        });
-+    }
-     return this.translateElements(this.getTranslatables(frag));
-   }
- 
-@@ -700,37 +813,5 @@
-   }
- }
- 
--/* global L10nRegistry, Services */
--
--/**
-- * The default localization strategy for Gecko. It comabines locales
-- * available in L10nRegistry, with locales requested by the user to
-- * generate the iterator over FluentBundles.
-- *
-- * In the future, we may want to allow certain modules to override this
-- * with a different negotitation strategy to allow for the module to
-- * be localized into a different language - for example DevTools.
-- */
--function defaultGenerateBundles(resourceIds) {
--  const requestedLocales = Services.locale.getRequestedLocales();
--  const availableLocales = L10nRegistry.getAvailableLocales();
--  const defaultLocale = Services.locale.defaultLocale;
--  const locales = Services.locale.negotiateLanguages(
--    requestedLocales, availableLocales, defaultLocale,
--  );
--  return L10nRegistry.generateContexts(locales, resourceIds);
--}
--
--
--class GeckoDOMLocalization extends DOMLocalization {
--  constructor(
--    windowElement,
--    resourceIds,
--    generateBundles = defaultGenerateBundles
--  ) {
--    super(windowElement, resourceIds, generateBundles);
--  }
--}
--
--this.DOMLocalization = GeckoDOMLocalization;
--this.EXPORTED_SYMBOLS = ["DOMLocalization"];
-+this.DOMLocalization = DOMLocalization;
-+var EXPORTED_SYMBOLS = ["DOMLocalization"];