зеркало из https://github.com/mozilla/gecko-dev.git
567 строки
18 KiB
Diff
567 строки
18 KiB
Diff
diff -uN -x README -x moz.build -x L10nRegistry.jsm -x jar.mn -x fluent.js.patch ./intl/l10n/DOMLocalization.jsm /home/zbraniecki/projects/fluent/fluent.js/fluent-gecko/dist/DOMLocalization.jsm
|
|
--- ./intl/l10n/DOMLocalization.jsm 2018-08-03 13:25:20.275840905 -0700
|
|
+++ /home/zbraniecki/projects/fluent/fluent.js/fluent-gecko/dist/DOMLocalization.jsm 2018-08-01 09:15:58.916763182 -0700
|
|
@@ -16,10 +16,12 @@
|
|
*/
|
|
|
|
|
|
-/* fluent-dom@cab517f (July 31, 2018) */
|
|
+/* fluent-dom@0.3.0 */
|
|
|
|
-const { Localization } =
|
|
- ChromeUtils.import("resource://gre/modules/Localization.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
|
|
// &, &, &.
|
|
@@ -61,9 +63,7 @@
|
|
global: [
|
|
"accesskey", "aria-label", "aria-valuetext", "aria-moz-hint", "label"
|
|
],
|
|
- description: ["value"],
|
|
key: ["key", "keycode"],
|
|
- label: ["value"],
|
|
textbox: ["placeholder"],
|
|
toolbarbutton: ["tooltiptext"],
|
|
}
|
|
@@ -96,7 +96,6 @@
|
|
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 @@
|
|
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";
|
|
|
|
@@ -530,6 +489,7 @@
|
|
);
|
|
}
|
|
|
|
+
|
|
this.roots.add(newRoot);
|
|
this.mutationObserver.observe(newRoot, this.observerConfig);
|
|
}
|
|
@@ -639,10 +599,7 @@
|
|
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;
|
|
});
|
|
@@ -664,63 +621,6 @@
|
|
* @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));
|
|
}
|
|
|
|
@@ -800,5 +700,37 @@
|
|
}
|
|
}
|
|
|
|
-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 MessageContexts.
|
|
+ *
|
|
+ * 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 defaultGenerateMessages(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,
|
|
+ generateMessages = defaultGenerateMessages
|
|
+ ) {
|
|
+ super(windowElement, resourceIds, generateMessages);
|
|
+ }
|
|
+}
|
|
+
|
|
+this.DOMLocalization = GeckoDOMLocalization;
|
|
+this.EXPORTED_SYMBOLS = ["DOMLocalization"];
|
|
diff -uN -x README -x moz.build -x L10nRegistry.jsm -x jar.mn -x fluent.js.patch ./intl/l10n/l10n.js /home/zbraniecki/projects/fluent/fluent.js/fluent-gecko/dist/l10n.js
|
|
--- ./intl/l10n/l10n.js 2018-08-03 13:26:42.691527746 -0700
|
|
+++ /home/zbraniecki/projects/fluent/fluent.js/fluent-gecko/dist/l10n.js 2018-08-01 09:15:59.253432348 -0700
|
|
@@ -1,6 +1,7 @@
|
|
+/* global Components, document, window */
|
|
{
|
|
const { DOMLocalization } =
|
|
- ChromeUtils.import("resource://gre/modules/DOMLocalization.jsm", {});
|
|
+ Components.utils.import("resource://gre/modules/DOMLocalization.jsm");
|
|
|
|
/**
|
|
* Polyfill for document.ready polyfill.
|
|
@@ -44,13 +45,16 @@
|
|
|
|
const resourceIds = getResourceLinks(document.head || document);
|
|
|
|
- document.l10n = new DOMLocalization(resourceIds);
|
|
+ document.l10n = new DOMLocalization(window, resourceIds);
|
|
|
|
- // Trigger the first two contexts to be loaded eagerly.
|
|
- document.l10n.ctxs.touchNext(2);
|
|
+ // trigger first context to be fetched eagerly
|
|
+ document.l10n.ctxs.touchNext();
|
|
|
|
document.l10n.ready = documentReady().then(() => {
|
|
document.l10n.registerObservers();
|
|
+ window.addEventListener("unload", () => {
|
|
+ document.l10n.unregisterObservers();
|
|
+ });
|
|
document.l10n.connectRoot(document.documentElement);
|
|
return document.l10n.translateRoots();
|
|
});
|
|
diff -uN -x README -x moz.build -x L10nRegistry.jsm -x jar.mn -x fluent.js.patch ./intl/l10n/Localization.jsm /home/zbraniecki/projects/fluent/fluent.js/fluent-gecko/dist/Localization.jsm
|
|
--- ./intl/l10n/Localization.jsm 2018-08-03 13:20:57.417703171 -0700
|
|
+++ /home/zbraniecki/projects/fluent/fluent.js/fluent-gecko/dist/Localization.jsm 2018-08-01 09:15:58.546760435 -0700
|
|
@@ -16,128 +16,11 @@
|
|
*/
|
|
|
|
|
|
-/* fluent-dom@cab517f (July 31, 2018) */
|
|
+/* fluent-dom@0.3.0 */
|
|
|
|
-/* 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", {});
|
|
+import { CachedAsyncIterable } from 'cached-iterable';
|
|
|
|
-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.
|
|
- *
|
|
- * @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.");
|
|
- }
|
|
- }
|
|
-
|
|
- /**
|
|
- * 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 cached = this;
|
|
- let cur = 0;
|
|
-
|
|
- return {
|
|
- async next() {
|
|
- if (cached.length <= cur) {
|
|
- cached.push(await cached.iterator.next());
|
|
- }
|
|
- 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];
|
|
- }
|
|
-}
|
|
-
|
|
-/**
|
|
- * The default localization strategy for Gecko. It comabines locales
|
|
- * available in L10nRegistry, with locales requested by the user to
|
|
- * generate the iterator over MessageContexts.
|
|
- *
|
|
- * 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 defaultGenerateMessages(resourceIds) {
|
|
- const appLocales = Services.locale.getAppLocalesAsBCP47();
|
|
- return L10nRegistry.generateContexts(appLocales, resourceIds);
|
|
-}
|
|
+/* eslint no-console: ["error", { allow: ["warn", "error"] }] */
|
|
|
|
/**
|
|
* The `Localization` class is a central high-level API for vanilla
|
|
@@ -153,7 +36,7 @@
|
|
*
|
|
* @returns {Localization}
|
|
*/
|
|
- constructor(resourceIds = [], generateMessages = defaultGenerateMessages) {
|
|
+ constructor(resourceIds = [], generateMessages) {
|
|
this.resourceIds = resourceIds;
|
|
this.generateMessages = generateMessages;
|
|
this.ctxs = CachedAsyncIterable.from(
|
|
@@ -194,12 +77,9 @@
|
|
break;
|
|
}
|
|
|
|
- if (AppConstants.NIGHTLY_BUILD || Cu.isInAutomation) {
|
|
+ if (typeof console !== "undefined") {
|
|
const locale = ctx.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}`);
|
|
}
|
|
}
|
|
@@ -284,35 +164,8 @@
|
|
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();
|
|
}
|
|
|
|
/**
|
|
@@ -326,10 +179,6 @@
|
|
}
|
|
}
|
|
|
|
-Localization.prototype.QueryInterface = ChromeUtils.generateQI([
|
|
- Ci.nsISupportsWeakReference
|
|
-]);
|
|
-
|
|
/**
|
|
* Format the value of a message into a string.
|
|
*
|
|
@@ -449,5 +298,44 @@
|
|
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 MessageContexts.
|
|
+ *
|
|
+ * 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 defaultGenerateMessages(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, generateMessages = defaultGenerateMessages) {
|
|
+ super(resourceIds, generateMessages);
|
|
+ }
|
|
+}
|
|
+
|
|
+this.Localization = GeckoLocalization;
|
|
+this.EXPORTED_SYMBOLS = ["Localization"];
|
|
diff -uN -x README -x moz.build -x L10nRegistry.jsm -x jar.mn -x fluent.js.patch ./intl/l10n/MessageContext.jsm /home/zbraniecki/projects/fluent/fluent.js/fluent-gecko/dist/MessageContext.jsm
|
|
--- ./intl/l10n/MessageContext.jsm 2018-08-03 13:11:36.949029757 -0700
|
|
+++ /home/zbraniecki/projects/fluent/fluent.js/fluent-gecko/dist/MessageContext.jsm 2018-08-01 09:15:58.176757688 -0700
|
|
@@ -16,7 +16,7 @@
|
|
*/
|
|
|
|
|
|
-/* fluent@0.6.0 */
|
|
+/* fluent-dom@0.3.0 */
|
|
|
|
/* eslint no-magic-numbers: [0] */
|
|
|
|
@@ -1930,6 +1930,57 @@
|
|
}
|
|
}
|
|
|
|
+/*
|
|
+ * @overview
|
|
+ *
|
|
+ * Functions for managing ordered sequences of MessageContexts.
|
|
+ *
|
|
+ * An ordered iterable of MessageContext instances can represent the current
|
|
+ * negotiated fallback chain of languages. This iterable can be used to find
|
|
+ * the best existing translation for a given identifier.
|
|
+ *
|
|
+ * The mapContext* methods can be used to find the first MessageContext in the
|
|
+ * given iterable which contains the translation with the given identifier. If
|
|
+ * the iterable is ordered according to the result of a language negotiation
|
|
+ * the returned MessageContext contains the best available translation.
|
|
+ *
|
|
+ * A simple function which formats translations based on the identifier might
|
|
+ * be implemented as follows:
|
|
+ *
|
|
+ * formatString(id, args) {
|
|
+ * const ctx = mapContextSync(contexts, id);
|
|
+ *
|
|
+ * if (ctx === null) {
|
|
+ * return id;
|
|
+ * }
|
|
+ *
|
|
+ * const msg = ctx.getMessage(id);
|
|
+ * return ctx.format(msg, args);
|
|
+ * }
|
|
+ *
|
|
+ * In order to pass an iterator to mapContext*, wrap it in
|
|
+ * Cached{Sync|Async}Iterable.
|
|
+ * This allows multiple calls to mapContext* without advancing and eventually
|
|
+ * depleting the iterator.
|
|
+ *
|
|
+ * function *generateMessages() {
|
|
+ * // Some lazy logic for yielding MessageContexts.
|
|
+ * yield *[ctx1, ctx2];
|
|
+ * }
|
|
+ *
|
|
+ * const contexts = new CachedSyncIterable(generateMessages());
|
|
+ * const ctx = mapContextSync(contexts, id);
|
|
+ *
|
|
+ */
|
|
+
|
|
+/*
|
|
+ * @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.MessageContext = MessageContext;
|
|
-this.FluentResource = FluentResource;
|
|
-var EXPORTED_SYMBOLS = ["MessageContext", "FluentResource"];
|
|
+this.EXPORTED_SYMBOLS = ["MessageContext"];
|