mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-26 14:22:01 +00:00
Bug 1560038 - Switch uses of FluentBundle to use fluent-rs. r=jfkthame
Differential Revision: https://phabricator.services.mozilla.com/D57403 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
8334ed0644
commit
44eca5f333
@ -7,9 +7,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
bundle.addResource(new FluentResource(`
|
||||
|
@ -7,9 +7,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
bundle.addResource(new FluentResource(`
|
||||
|
@ -44,9 +44,6 @@
|
||||
</script>
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
bundle.addResource(new FluentResource(`
|
||||
|
@ -7,9 +7,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
bundle.addResource(new FluentResource(`
|
||||
|
@ -10,9 +10,6 @@
|
||||
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js" />
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function * generateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
bundle.addResource(new FluentResource(`
|
||||
|
@ -7,9 +7,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
bundle.addResource(new FluentResource("title = Hello World"));
|
||||
|
@ -7,9 +7,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
bundle.addResource(new FluentResource("title = <strong>Hello</strong> World"));
|
||||
|
@ -7,9 +7,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
// No translations!
|
||||
|
@ -7,9 +7,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
bundle.addResource(new FluentResource(`title = Visit <a data-l10n-name="mozilla-link">Mozilla</a> or <a data-l10n-name="firefox-link">Firefox</a> website!`));
|
||||
|
@ -7,9 +7,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
bundle.addResource(new FluentResource(`title = Visit <a data-l10n-name="mozilla-link">Mozilla</a> or <a data-l10n-name="firefox-link">Firefox</a> website!`));
|
||||
|
@ -7,9 +7,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
bundle.addResource(new FluentResource(`
|
||||
|
@ -7,9 +7,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
bundle.addResource(new FluentResource(`
|
||||
|
@ -7,9 +7,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
bundle.addResource(new FluentResource("title = Hello World"));
|
||||
|
@ -7,9 +7,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
bundle.addResource(new FluentResource("title = Hello World"));
|
||||
|
@ -7,9 +7,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US");
|
||||
bundle.addResource(new FluentResource("title = Hello World"));
|
||||
|
1222
intl/l10n/Fluent.jsm
1222
intl/l10n/Fluent.jsm
File diff suppressed because it is too large
Load Diff
@ -2,7 +2,6 @@ const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants
|
||||
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||
// eslint-disable-next-line mozilla/use-services
|
||||
const appinfo = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
|
||||
const { FluentBundle, FluentResource } = ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
|
||||
ChromeUtils.defineModuleGetter(
|
||||
@ -136,12 +135,12 @@ class L10nRegistryService {
|
||||
async* generateBundles(requestedLangs, resourceIds) {
|
||||
const resourceIdsDedup = Array.from(new Set(resourceIds));
|
||||
const sourcesOrder = Array.from(this.sources.keys()).reverse();
|
||||
const pseudoNameFromPref = Services.prefs.getStringPref("intl.l10n.pseudo", "");
|
||||
const pseudoStrategy = Services.prefs.getStringPref("intl.l10n.pseudo", "");
|
||||
for (const locale of requestedLangs) {
|
||||
for await (const dataSets of generateResourceSetsForLocale(locale, sourcesOrder, resourceIdsDedup)) {
|
||||
const bundle = new FluentBundle(locale, {
|
||||
...MSG_CONTEXT_OPTIONS,
|
||||
transform: PSEUDO_STRATEGIES[pseudoNameFromPref],
|
||||
pseudoStrategy,
|
||||
});
|
||||
for (const data of dataSets) {
|
||||
if (data === null) {
|
||||
@ -169,12 +168,12 @@ class L10nRegistryService {
|
||||
* generateBundlesSync(requestedLangs, resourceIds) {
|
||||
const resourceIdsDedup = Array.from(new Set(resourceIds));
|
||||
const sourcesOrder = Array.from(this.sources.keys()).reverse();
|
||||
const pseudoNameFromPref = Services.prefs.getStringPref("intl.l10n.pseudo", "");
|
||||
const pseudoStrategy = Services.prefs.getStringPref("intl.l10n.pseudo", "");
|
||||
for (const locale of requestedLangs) {
|
||||
for (const dataSets of generateResourceSetsForLocaleSync(locale, sourcesOrder, resourceIdsDedup)) {
|
||||
const bundle = new FluentBundle(locale, {
|
||||
...MSG_CONTEXT_OPTIONS,
|
||||
transform: PSEUDO_STRATEGIES[pseudoNameFromPref],
|
||||
pseudoStrategy
|
||||
});
|
||||
for (const data of dataSets) {
|
||||
if (data === null) {
|
||||
@ -427,112 +426,6 @@ const MSG_CONTEXT_OPTIONS = {
|
||||
// Temporarily disable bidi isolation due to Microsoft not supporting FSI/PDI.
|
||||
// See bug 1439018 for details.
|
||||
useIsolating: Services.prefs.getBoolPref("intl.l10n.enable-bidi-marks", false),
|
||||
functions: {
|
||||
/**
|
||||
* PLATFORM is a built-in allowing localizers to differentiate message
|
||||
* variants depending on the target platform.
|
||||
*/
|
||||
PLATFORM: () => {
|
||||
switch (AppConstants.platform) {
|
||||
case "linux":
|
||||
case "android":
|
||||
return AppConstants.platform;
|
||||
case "win":
|
||||
return "windows";
|
||||
case "macosx":
|
||||
return "macos";
|
||||
default:
|
||||
return "other";
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Pseudolocalizations
|
||||
*
|
||||
* PSEUDO_STRATEGIES is a dict of strategies to be used to modify a
|
||||
* context in order to create pseudolocalizations. These can be used by
|
||||
* developers to test the localizability of their code without having to
|
||||
* actually speak a foreign language.
|
||||
*
|
||||
* Currently, the following pseudolocales are supported:
|
||||
*
|
||||
* accented - Ȧȧƈƈḗḗƞŧḗḗḓ Ḗḗƞɠŀīīşħ
|
||||
*
|
||||
* In Accented English all Latin letters are replaced by accented
|
||||
* Unicode counterparts which don't impair the readability of the content.
|
||||
* This allows developers to quickly test if any given string is being
|
||||
* correctly displayed in its 'translated' form. Additionally, simple
|
||||
* heuristics are used to make certain words longer to better simulate the
|
||||
* experience of international users.
|
||||
*
|
||||
* bidi - ɥsıʅƃuƎ ıpıԐ
|
||||
*
|
||||
* Bidi English is a fake RTL locale. All words are surrounded by
|
||||
* Unicode formatting marks forcing the RTL directionality of characters.
|
||||
* In addition, to make the reversed text easier to read, individual
|
||||
* letters are flipped.
|
||||
*
|
||||
* Note: The name above is hardcoded to be RTL in case code editors have
|
||||
* trouble with the RLO and PDF Unicode marks. In reality, it should be
|
||||
* surrounded by those marks as well.
|
||||
*
|
||||
* See https://bugzil.la/1450781 for more information.
|
||||
*
|
||||
* In this implementation we use code points instead of inline unicode characters
|
||||
* because the encoding of JSM files mangles them otherwise.
|
||||
*/
|
||||
|
||||
const ACCENTED_MAP = {
|
||||
// ȦƁƇḒḖƑƓĦĪĴĶĿḾȠǾƤɊŘŞŦŬṼẆẊẎẐ
|
||||
"caps": [550, 385, 391, 7698, 7702, 401, 403, 294, 298, 308, 310, 319, 7742, 544, 510, 420, 586, 344, 350, 358, 364, 7804, 7814, 7818, 7822, 7824],
|
||||
// ȧƀƈḓḗƒɠħīĵķŀḿƞǿƥɋřşŧŭṽẇẋẏẑ
|
||||
"small": [551, 384, 392, 7699, 7703, 402, 608, 295, 299, 309, 311, 320, 7743, 414, 511, 421, 587, 345, 351, 359, 365, 7805, 7815, 7819, 7823, 7825],
|
||||
};
|
||||
|
||||
const FLIPPED_MAP = {
|
||||
// ∀ԐↃᗡƎℲ⅁HIſӼ⅂WNOԀÒᴚS⊥∩ɅMX⅄Z
|
||||
"caps": [8704, 1296, 8579, 5601, 398, 8498, 8513, 72, 73, 383, 1276, 8514, 87, 78, 79, 1280, 210, 7450, 83, 8869, 8745, 581, 77, 88, 8516, 90],
|
||||
// ɐqɔpǝɟƃɥıɾʞʅɯuodbɹsʇnʌʍxʎz
|
||||
"small": [592, 113, 596, 112, 477, 607, 387, 613, 305, 638, 670, 645, 623, 117, 111, 100, 98, 633, 115, 647, 110, 652, 653, 120, 654, 122],
|
||||
};
|
||||
|
||||
function transformString(map, elongate = false, prefix = "", postfix = "", msg) {
|
||||
// Exclude access-keys and other single-char messages
|
||||
if (msg.length === 1) {
|
||||
return msg;
|
||||
}
|
||||
// XML entities (‪) and XML tags.
|
||||
const reExcluded = /(&[#\w]+;|<\s*.+?\s*>)/;
|
||||
|
||||
const parts = msg.split(reExcluded);
|
||||
const modified = parts.map((part) => {
|
||||
if (reExcluded.test(part)) {
|
||||
return part;
|
||||
}
|
||||
return prefix + part.replace(/[a-z]/ig, (ch) => {
|
||||
let cc = ch.charCodeAt(0);
|
||||
if (cc >= 97 && cc <= 122) {
|
||||
const newChar = String.fromCodePoint(map.small[cc - 97]);
|
||||
// duplicate "a", "e", "o" and "u" to emulate ~30% longer text
|
||||
if (elongate && (cc === 97 || cc === 101 || cc === 111 || cc === 117)) {
|
||||
return newChar + newChar;
|
||||
}
|
||||
return newChar;
|
||||
}
|
||||
if (cc >= 65 && cc <= 90) {
|
||||
return String.fromCodePoint(map.caps[cc - 65]);
|
||||
}
|
||||
return ch;
|
||||
}) + postfix;
|
||||
});
|
||||
return modified.join("");
|
||||
}
|
||||
|
||||
const PSEUDO_STRATEGIES = {
|
||||
"accented": transformString.bind(null, ACCENTED_MAP, true, "", ""),
|
||||
"bidi": transformString.bind(null, FLIPPED_MAP, false, "\u202e", "\u202c"),
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1,15 +1,8 @@
|
||||
The content of this directory is partially sourced from the fluent.js project.
|
||||
|
||||
The following files are affected:
|
||||
- Fluent.jsm
|
||||
- Localization.jsm
|
||||
|
||||
At the moment, the tool used to produce those files in fluent.js repository, doesn't
|
||||
fully align with how the code is structured here, so we perform a manual adjustments
|
||||
mostly around header and footer.
|
||||
|
||||
The result difference is stored in `./fluent.js.patch` file which can be used to
|
||||
approximate the changes needed to be applied on the output of the
|
||||
fluent.js/fluent-gecko's make.
|
||||
|
||||
In b.m.o. bug 1434434 we will try to reduce this difference to zero.
|
||||
|
@ -1,475 +0,0 @@
|
||||
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"];
|
@ -18,7 +18,6 @@ UNIFIED_SOURCES += [
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
'Fluent.jsm',
|
||||
'L10nRegistry.jsm',
|
||||
'Localization.jsm',
|
||||
]
|
||||
|
@ -7,8 +7,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US", {
|
||||
|
@ -7,9 +7,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US", {
|
||||
useIsolating: false,
|
||||
|
@ -7,9 +7,6 @@
|
||||
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||
<script type="application/javascript">
|
||||
"use strict";
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
async function* mockGenerateMessages(resourceIds) {
|
||||
const bundle = new FluentBundle("en-US",
|
||||
{
|
||||
|
@ -2,9 +2,6 @@
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
function run_test() {
|
||||
const { FluentBundle, FluentResource } =
|
||||
ChromeUtils.import("resource://gre/modules/Fluent.jsm");
|
||||
|
||||
test_methods_presence(FluentBundle);
|
||||
test_methods_calling(FluentBundle, FluentResource);
|
||||
|
||||
|
@ -173,6 +173,8 @@ module.exports = {
|
||||
Flex: false,
|
||||
FlexItemValues: false,
|
||||
FlexLineValues: false,
|
||||
FluentBundle: false,
|
||||
FluentResource: false,
|
||||
FocusEvent: false,
|
||||
FontFace: false,
|
||||
FontFaceSet: false,
|
||||
|
@ -68,7 +68,6 @@
|
||||
"file_url.jsm": ["checkFromJSM"],
|
||||
"file_worker_url.jsm": ["checkFromJSM"],
|
||||
"Finder.jsm": ["Finder", "GetClipboardSearchString"],
|
||||
"Fluent.jsm": ["FluentBundle", "FluentResource"],
|
||||
"format.js": ["pprint", "truncate"],
|
||||
"formautofill.jsm": ["Address", "CreditCard", "DumpAddresses", "DumpCreditCards"],
|
||||
"FormAutofillHeuristics.jsm": ["FormAutofillHeuristics", "LabelUtils"],
|
||||
|
Loading…
Reference in New Issue
Block a user