зеркало из https://github.com/mozilla/gecko-dev.git
586 строки
20 KiB
Diff
586 строки
20 KiB
Diff
diff -uNr ./dist/DOMLocalization.jsm /home/zbraniecki/projects/mozilla-unified/intl/l10n/DOMLocalization.jsm
|
|
--- ./dist/DOMLocalization.jsm 2018-04-13 08:25:21.143138950 -0700
|
|
+++ /home/zbraniecki/projects/mozilla-unified/intl/l10n/DOMLocalization.jsm 2018-04-13 08:27:11.658083766 -0700
|
|
@@ -18,10 +18,8 @@
|
|
|
|
/* fluent-dom@0.2.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", {});
|
|
|
|
// Match the opening angle bracket (<) in HTML tags, and HTML entities like
|
|
// &, &, &.
|
|
@@ -96,6 +94,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);
|
|
}
|
|
@@ -323,6 +322,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";
|
|
|
|
@@ -568,6 +607,59 @@
|
|
* @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 => [l10nItem.l10nId, 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(() => this.resumeObserving());
|
|
+ }
|
|
return this.translateElements(this.getTranslatables(frag));
|
|
}
|
|
|
|
@@ -647,37 +739,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 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"];
|
|
+this.DOMLocalization = DOMLocalization;
|
|
+var EXPORTED_SYMBOLS = ["DOMLocalization"];
|
|
diff -uNr ./dist/l10n.js /home/zbraniecki/projects/mozilla-unified/intl/l10n/l10n.js
|
|
--- ./dist/l10n.js 2018-04-13 08:25:21.307139138 -0700
|
|
+++ /home/zbraniecki/projects/mozilla-unified/intl/l10n/l10n.js 2018-04-13 08:27:25.230296529 -0700
|
|
@@ -1,20 +1,26 @@
|
|
-/* global Components, document, window */
|
|
{
|
|
const { DOMLocalization } =
|
|
- Components.utils.import("resource://gre/modules/DOMLocalization.jsm");
|
|
+ ChromeUtils.import("resource://gre/modules/DOMLocalization.jsm", {});
|
|
|
|
/**
|
|
* Polyfill for document.ready polyfill.
|
|
* See: https://github.com/whatwg/html/issues/127 for details.
|
|
*
|
|
+ * XXX: The callback is a temporary workaround for bug 1193394. Once Promises in Gecko
|
|
+ * start beeing a microtask and stop pushing translation post-layout, we can
|
|
+ * remove it and start using the returned Promise again.
|
|
+ *
|
|
+ * @param {Function} callback - function to be called when the document is ready.
|
|
* @returns {Promise}
|
|
*/
|
|
- function documentReady() {
|
|
+ function documentReady(callback) {
|
|
if (document.contentType === "application/vnd.mozilla.xul+xml") {
|
|
// XUL
|
|
return new Promise(
|
|
resolve => document.addEventListener(
|
|
- "MozBeforeInitialXULLayout", resolve, { once: true }
|
|
+ "MozBeforeInitialXULLayout", () => {
|
|
+ resolve(callback());
|
|
+ }, { once: true }
|
|
)
|
|
);
|
|
}
|
|
@@ -22,11 +28,13 @@
|
|
// HTML
|
|
const rs = document.readyState;
|
|
if (rs === "interactive" || rs === "completed") {
|
|
- return Promise.resolve();
|
|
+ return Promise.resolve(callback);
|
|
}
|
|
return new Promise(
|
|
resolve => document.addEventListener(
|
|
- "readystatechange", resolve, { once: true }
|
|
+ "readystatechange", () => {
|
|
+ resolve(callback());
|
|
+ }, { once: true }
|
|
)
|
|
);
|
|
}
|
|
@@ -50,11 +58,8 @@
|
|
// trigger first context to be fetched eagerly
|
|
document.l10n.ctxs.touchNext();
|
|
|
|
- document.l10n.ready = documentReady().then(() => {
|
|
+ document.l10n.ready = documentReady(() => {
|
|
document.l10n.registerObservers();
|
|
- window.addEventListener("unload", () => {
|
|
- document.l10n.unregisterObservers();
|
|
- });
|
|
document.l10n.connectRoot(document.documentElement);
|
|
return document.l10n.translateRoots();
|
|
});
|
|
diff -uNr ./dist/Localization.jsm /home/zbraniecki/projects/mozilla-unified/intl/l10n/Localization.jsm
|
|
--- ./dist/Localization.jsm 2018-04-13 08:25:20.946138732 -0700
|
|
+++ /home/zbraniecki/projects/mozilla-unified/intl/l10n/Localization.jsm 2018-04-13 08:27:16.396155987 -0700
|
|
@@ -18,70 +18,13 @@
|
|
|
|
/* fluent-dom@0.2.0 */
|
|
|
|
-/* eslint no-magic-numbers: [0] */
|
|
-
|
|
-/* global Intl */
|
|
-
|
|
-/**
|
|
- * @overview
|
|
- *
|
|
- * The FTL resolver ships with a number of functions built-in.
|
|
- *
|
|
- * Each function take two arguments:
|
|
- * - args - an array of positional args
|
|
- * - opts - an object of key-value args
|
|
- *
|
|
- * Arguments to functions are guaranteed to already be instances of
|
|
- * `FluentType`. Functions must return `FluentType` objects as well.
|
|
- */
|
|
+/* eslint no-console: ["error", { allow: ["warn", "error"] }] */
|
|
+/* global console */
|
|
|
|
-/**
|
|
- * @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 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.
|
|
- *
|
|
- * 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:
|
|
- *
|
|
- * * {MessageContext} ctx
|
|
- * context 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.
|
|
- */
|
|
+const { XPCOMUtils } = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm", {});
|
|
+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", {});
|
|
|
|
/*
|
|
* CachedIterable caches the elements yielded by an iterable.
|
|
@@ -148,58 +91,19 @@
|
|
}
|
|
}
|
|
|
|
-/*
|
|
- * @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 CachedIterable.
|
|
- * 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 CachedIterable(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.
|
|
+/**
|
|
+ * 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.
|
|
*/
|
|
-
|
|
-/* eslint no-console: ["error", { allow: ["warn", "error"] }] */
|
|
+function defaultGenerateMessages(resourceIds) {
|
|
+ const appLocales = Services.locale.getAppLocalesAsLangTags();
|
|
+ return L10nRegistry.generateContexts(appLocales, resourceIds);
|
|
+}
|
|
|
|
/**
|
|
* The `Localization` class is a central high-level API for vanilla
|
|
@@ -215,7 +119,7 @@
|
|
*
|
|
* @returns {Localization}
|
|
*/
|
|
- constructor(resourceIds, generateMessages) {
|
|
+ constructor(resourceIds, generateMessages = defaultGenerateMessages) {
|
|
this.resourceIds = resourceIds;
|
|
this.generateMessages = generateMessages;
|
|
this.ctxs = new CachedIterable(this.generateMessages(this.resourceIds));
|
|
@@ -236,7 +140,7 @@
|
|
async formatWithFallback(keys, method) {
|
|
const translations = [];
|
|
|
|
- for (let ctx of this.ctxs) {
|
|
+ for await (let ctx of this.ctxs) {
|
|
// This can operate on synchronous and asynchronous
|
|
// contexts coming from the iterator.
|
|
if (typeof ctx.then === "function") {
|
|
@@ -248,7 +152,7 @@
|
|
break;
|
|
}
|
|
|
|
- if (typeof console !== "undefined") {
|
|
+ if (AppConstants.NIGHTLY_BUILD) {
|
|
const locale = ctx.locales[0];
|
|
const ids = Array.from(missingIds).join(", ");
|
|
console.warn(`Missing translations in ${locale}: ${ids}`);
|
|
@@ -335,8 +239,28 @@
|
|
return val;
|
|
}
|
|
|
|
- handleEvent() {
|
|
- this.onLanguageChange();
|
|
+ /**
|
|
+ * Register weak observers on events that will trigger cache invalidation
|
|
+ */
|
|
+ registerObservers() {
|
|
+ Services.obs.addObserver(this, "intl:app-locales-changed", 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.onLanguageChange();
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
}
|
|
|
|
/**
|
|
@@ -348,6 +272,10 @@
|
|
}
|
|
}
|
|
|
|
+Localization.prototype.QueryInterface = XPCOMUtils.generateQI([
|
|
+ Ci.nsISupportsWeakReference
|
|
+]);
|
|
+
|
|
/**
|
|
* Format the value of a message into a string.
|
|
*
|
|
@@ -368,6 +296,7 @@
|
|
*/
|
|
function valueFromContext(ctx, errors, id, args) {
|
|
const msg = ctx.getMessage(id);
|
|
+
|
|
return ctx.format(msg, args, errors);
|
|
}
|
|
|
|
@@ -467,44 +396,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 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"];
|
|
+this.Localization = Localization;
|
|
+var EXPORTED_SYMBOLS = ["Localization"];
|
|
diff -uNr ./dist/MessageContext.jsm /home/zbraniecki/projects/mozilla-unified/intl/l10n/MessageContext.jsm
|
|
--- ./dist/MessageContext.jsm 2018-04-13 08:25:20.698138486 -0700
|
|
+++ /home/zbraniecki/projects/mozilla-unified/intl/l10n/MessageContext.jsm 2018-04-13 08:27:20.944227388 -0700
|
|
@@ -16,7 +16,7 @@
|
|
*/
|
|
|
|
|
|
-/* fluent-dom@0.2.0 */
|
|
+/* fluent@0.6.3 */
|
|
|
|
/* eslint no-magic-numbers: [0] */
|
|
|
|
@@ -1858,63 +1858,5 @@
|
|
}
|
|
}
|
|
|
|
-/*
|
|
- * CachedIterable caches the elements yielded by an iterable.
|
|
- *
|
|
- * It can be used to iterate over an iterable many times without depleting the
|
|
- * iterable.
|
|
- */
|
|
-
|
|
-/*
|
|
- * @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 CachedIterable.
|
|
- * 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 CachedIterable(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.EXPORTED_SYMBOLS = ["MessageContext"];
|
|
+var EXPORTED_SYMBOLS = ["MessageContext"];
|