зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1613705 - [localization] part10: Remove Localization.jsm. r=platform-i18n-reviewers,dminor
Depends on D113692 Differential Revision: https://phabricator.services.mozilla.com/D105586
This commit is contained in:
Родитель
92183f248d
Коммит
31f47cc980
|
@ -38,7 +38,7 @@
|
|||
document.l10n.setAttributes(inputElem, "about-telemetry-filter-all-placeholder");
|
||||
|
||||
// Due to the async iteractions between nsINode.localize
|
||||
// and DOMLocalization.jsm, we'll need to wait two frames
|
||||
// and DOMLocalization, we'll need to wait two frames
|
||||
// to verify that no mutations happened.
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
document.l10n.setAttributes(inputElem, "about-telemetry-filter-placeholder", {selectedTitle: "Test"});
|
||||
|
||||
// Due to the async iteractions between nsINode.localize
|
||||
// and DOMLocalization.jsm, we'll need to wait two frames
|
||||
// and DOMLocalization, we'll need to wait two frames
|
||||
// to verify that no mutations happened.
|
||||
requestAnimationFrame(() => {
|
||||
requestAnimationFrame(() => {
|
||||
|
|
|
@ -1,607 +0,0 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80 filetype=javascript: */
|
||||
|
||||
/* Copyright 2017 Mozilla Foundation and others
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
/* fluent-dom@fa25466f (October 12, 2018) */
|
||||
|
||||
/* eslint no-console: ["error", { allow: ["warn", "error"] }] */
|
||||
/* global console */
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* CachedAsyncIterable caches the elements yielded by an async iterable.
|
||||
*
|
||||
* It can be used to iterate over an iterable many times without depleting the
|
||||
* 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.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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(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 && (await last).done) {
|
||||
break;
|
||||
}
|
||||
this.push(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];
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* CachedSyncIterable caches the elements yielded by an iterable.
|
||||
*
|
||||
* It can be used to iterate over an iterable many times without depleting the
|
||||
* iterable.
|
||||
*/
|
||||
class CachedSyncIterable extends CachedIterable {
|
||||
/**
|
||||
* Create an `CachedSyncIterable` instance.
|
||||
*
|
||||
* @param {Iterable} iterable
|
||||
* @returns {CachedSyncIterable}
|
||||
*/
|
||||
constructor(iterable) {
|
||||
super();
|
||||
|
||||
if (Symbol.iterator in Object(iterable)) {
|
||||
this.iterator = iterable[Symbol.iterator]();
|
||||
} else {
|
||||
throw new TypeError("Argument must implement the iteration protocol.");
|
||||
}
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
const cached = this;
|
||||
let cur = 0;
|
||||
|
||||
return {
|
||||
next() {
|
||||
if (cached.length <= cur) {
|
||||
cached.push(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
|
||||
*/
|
||||
touchNext(count = 1) {
|
||||
let idx = 0;
|
||||
while (idx++ < count) {
|
||||
const last = this[this.length - 1];
|
||||
if (last && last.done) {
|
||||
break;
|
||||
}
|
||||
this.push(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.getInstance().generateBundles(appLocales, resourceIds);
|
||||
}
|
||||
|
||||
function defaultGenerateBundlesSync(resourceIds) {
|
||||
const appLocales = Services.locale.appLocalesAsBCP47;
|
||||
return L10nRegistry.getInstance().generateBundlesSync(appLocales, resourceIds);
|
||||
}
|
||||
|
||||
function maybeReportErrorToGecko(error) {
|
||||
if (AppConstants.NIGHTLY_BUILD || Cu.isInAutomation) {
|
||||
if (Cu.isInAutomation) {
|
||||
// We throw a string, rather than Error
|
||||
// to allow the C++ Promise handler
|
||||
// to clone it
|
||||
throw error;
|
||||
}
|
||||
console.warn(error);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The `Localization` class is a central high-level API for vanilla
|
||||
* JavaScript use of Fluent.
|
||||
* It combines language negotiation, FluentBundle and I/O to
|
||||
* provide a scriptable API to format translations.
|
||||
*/
|
||||
const Localization = {
|
||||
cached(iterable, isSync) {
|
||||
if (isSync) {
|
||||
return CachedSyncIterable.from(iterable);
|
||||
} else {
|
||||
return CachedAsyncIterable.from(iterable);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Format translations and handle fallback if needed.
|
||||
*
|
||||
* Format translations for `keys` from `FluentBundle` instances on this
|
||||
* Localization. In case of errors, fetch the next context in the
|
||||
* fallback chain.
|
||||
*
|
||||
* @param {Array<String>} resourceIds - List of resource ids used by this
|
||||
* localization.
|
||||
* @param {Iter<FluentBundle>} bundles - Iterator over bundles.
|
||||
* @param {Array<string|Object>} keys - Translation keys to format.
|
||||
* @param {Function} method - Formatting function.
|
||||
* @returns {Promise<Array<string?|Object?>>}
|
||||
* @private
|
||||
*/
|
||||
async formatWithFallback(resourceIds, bundles, keys, method) {
|
||||
if (!bundles) {
|
||||
throw new Error("Attempt to format on an uninitialized instance.");
|
||||
}
|
||||
|
||||
const translations = new Array(keys.length).fill(null);
|
||||
let hasAtLeastOneBundle = false;
|
||||
|
||||
for await (const bundle of bundles) {
|
||||
hasAtLeastOneBundle = true;
|
||||
const missingIds = keysFromBundle(method, bundle, keys, translations);
|
||||
|
||||
if (missingIds.size === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
const locale = bundle.locales[0];
|
||||
const ids = Array.from(missingIds).join(", ");
|
||||
maybeReportErrorToGecko(`[fluent] Missing translations in ${locale}: ${ids}.`);
|
||||
}
|
||||
|
||||
if (!hasAtLeastOneBundle) {
|
||||
maybeReportErrorToGecko(`[fluent] Request for keys failed because no resource bundles got generated.\n keys: ${JSON.stringify(keys)}.\n resourceIds: ${JSON.stringify(resourceIds)}.`);
|
||||
}
|
||||
|
||||
return translations;
|
||||
},
|
||||
|
||||
/**
|
||||
* Format translations and handle fallback if needed.
|
||||
*
|
||||
* Format translations for `keys` from `FluentBundle` instances on this
|
||||
* Localization. In case of errors, fetch the next context in the
|
||||
* fallback chain.
|
||||
*
|
||||
* @param {Array<String>} resourceIds - List of resource ids used by this
|
||||
* localization.
|
||||
* @param {Iter<FluentBundle>} bundles - Iterator over bundles.
|
||||
* @param {Array<string|Object>} keys - Translation keys to format.
|
||||
* @param {Function} method - Formatting function.
|
||||
* @returns {Array<string|Object>}
|
||||
* @private
|
||||
*/
|
||||
formatWithFallbackSync(resourceIds, bundles, keys, method) {
|
||||
if (!bundles) {
|
||||
throw new Error("Attempt to format on an uninitialized instance.");
|
||||
}
|
||||
|
||||
const translations = new Array(keys.length).fill(null);
|
||||
let hasAtLeastOneBundle = false;
|
||||
|
||||
for (const bundle of bundles) {
|
||||
hasAtLeastOneBundle = true;
|
||||
const missingIds = keysFromBundle(method, bundle, keys, translations);
|
||||
|
||||
if (missingIds.size === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
const locale = bundle.locales[0];
|
||||
const ids = Array.from(missingIds).join(", ");
|
||||
maybeReportErrorToGecko(`[fluent] Missing translations in ${locale}: ${ids}.`);
|
||||
}
|
||||
|
||||
if (!hasAtLeastOneBundle) {
|
||||
maybeReportErrorToGecko(`[fluent] Request for keys failed because no resource bundles got generated.\n keys: ${JSON.stringify(keys)}.\n resourceIds: ${JSON.stringify(resourceIds)}.`);
|
||||
}
|
||||
|
||||
return translations;
|
||||
},
|
||||
|
||||
|
||||
/**
|
||||
* Format translations into {value, attributes} objects.
|
||||
*
|
||||
* The fallback logic is the same as in `formatValues` but it returns {value,
|
||||
* attributes} objects which are suitable for the translation of DOM
|
||||
* elements.
|
||||
*
|
||||
* docL10n.formatMessages([
|
||||
* {id: 'hello', args: { who: 'Mary' }},
|
||||
* {id: 'welcome'}
|
||||
* ]).then(console.log);
|
||||
*
|
||||
* // [
|
||||
* // { value: 'Hello, Mary!', attributes: null },
|
||||
* // {
|
||||
* // value: 'Welcome!',
|
||||
* // attributes: [ { name: "title", value: 'Hello' } ]
|
||||
* // }
|
||||
* // ]
|
||||
*
|
||||
* Returns a Promise resolving to an array of the translation messages.
|
||||
*
|
||||
* @param {Array<String>} resourceIds - List of resource ids used by this
|
||||
* localization.
|
||||
* @param {Iter<FluentBundle>} bundles - Iterator over bundles.
|
||||
* @param {Array<string|Object>} keys - Translation keys to format.
|
||||
* @returns {Promise<Array<{value: string, attributes: Object}?>>}
|
||||
* @private
|
||||
*/
|
||||
formatMessages(resourceIds, bundles, keys) {
|
||||
return this.formatWithFallback(resourceIds, bundles, keys, messageFromBundle);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sync version of `formatMessages`.
|
||||
*
|
||||
* Returns an array of the translation messages.
|
||||
*
|
||||
* @param {Array<String>} resourceIds - List of resource ids used by this
|
||||
* localization.
|
||||
* @param {Iter<FluentBundle>} bundles - Iterator over bundles.
|
||||
* @param {Array<string|Object>} keys - Translation keys to format.
|
||||
* @returns {Array<{value: string, attributes: Object}?>}
|
||||
* @private
|
||||
*/
|
||||
formatMessagesSync(resourceIds, bundles, keys) {
|
||||
return this.formatWithFallbackSync(resourceIds, bundles, keys, messageFromBundle);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve translations corresponding to the passed keys.
|
||||
*
|
||||
* A generalized version of `Localization.formatValue`. Keys must
|
||||
* be `{id, args}` objects.
|
||||
*
|
||||
* docL10n.formatValues([
|
||||
* {id: 'hello', args: { who: 'Mary' }},
|
||||
* {id: 'hello', args: { who: 'John' }},
|
||||
* {id: 'welcome'}
|
||||
* ]).then(console.log);
|
||||
*
|
||||
* // ['Hello, Mary!', 'Hello, John!', 'Welcome!']
|
||||
*
|
||||
* Returns a Promise resolving to an array of the translation strings.
|
||||
*
|
||||
* @param {Array<String>} resourceIds - List of resource ids used by this
|
||||
* localization.
|
||||
* @param {Iter<FluentBundle>} bundles - Iterator over bundles.
|
||||
* @param {Array<string|Object>} keys - Translation keys to format.
|
||||
* @returns {Promise<Array<string?>>}
|
||||
*/
|
||||
formatValues(resourceIds, bundles, keys) {
|
||||
return this.formatWithFallback(resourceIds, bundles, keys, valueFromBundle);
|
||||
},
|
||||
|
||||
/**
|
||||
* Sync version of `formatValues`.
|
||||
*
|
||||
* Returns an array of the translation strings.
|
||||
*
|
||||
* @param {Array<String>} resourceIds - List of resource ids used by this
|
||||
* localization.
|
||||
* @param {Iter<FluentBundle>} bundles - Iterator over bundles.
|
||||
* @param {Array<string|Object>} keys - Translation keys to format.
|
||||
* @returns {Array<string?>}
|
||||
* @private
|
||||
*/
|
||||
formatValuesSync(resourceIds, bundles, keys) {
|
||||
return this.formatWithFallbackSync(resourceIds, bundles, keys, valueFromBundle);
|
||||
},
|
||||
|
||||
/**
|
||||
* Retrieve the translation corresponding to the `id` identifier.
|
||||
*
|
||||
* If passed, `args` is a simple hash object with a list of variables that
|
||||
* will be interpolated in the value of the translation.
|
||||
*
|
||||
* docL10n.formatValue(
|
||||
* 'hello', { who: 'world' }
|
||||
* ).then(console.log);
|
||||
*
|
||||
* // 'Hello, world!'
|
||||
*
|
||||
* Returns a Promise resolving to a translation string.
|
||||
*
|
||||
* Use this sparingly for one-off messages which don't need to be
|
||||
* retranslated when the user changes their language preferences, e.g. in
|
||||
* notifications.
|
||||
*
|
||||
* @param {Array<String>} resourceIds - List of resource ids used by this
|
||||
* localization.
|
||||
* @param {Iter<FluentBundle>} bundles - Iterator over bundles.
|
||||
* @param {string} id - Identifier of the translation to format
|
||||
* @param {Object} [args] - Optional external arguments
|
||||
* @returns {Promise<string?>}
|
||||
*/
|
||||
async formatValue(resourceIds, bundles, id, args) {
|
||||
const [val] = await this.formatValues(resourceIds, bundles, [{id, args}]);
|
||||
return val;
|
||||
},
|
||||
|
||||
/**
|
||||
* Sync version of `formatValue`.
|
||||
*
|
||||
* Returns a translation string.
|
||||
*
|
||||
* @param {Array<String>} resourceIds - List of resource ids used by this
|
||||
* localization.
|
||||
* @param {Iter<FluentBundle>} bundles - Iterator over bundles.
|
||||
* @param {string} id - Identifier of the translation to format
|
||||
* @param {Object} [args] - Optional external arguments
|
||||
* @returns {string?}
|
||||
* @private
|
||||
*/
|
||||
formatValueSync(resourceIds, bundles, id, args) {
|
||||
const [val] = this.formatValuesSync(resourceIds, bundles, [{id, args}]);
|
||||
return val;
|
||||
},
|
||||
|
||||
/**
|
||||
* This method should be called when there's a reason to believe
|
||||
* that language negotiation or available resources changed.
|
||||
*
|
||||
* @param {Array<String>} resourceIds - List of resource ids used by this
|
||||
* localization.
|
||||
* @param {bool} isSync - Whether the instance should be
|
||||
* synchronous.
|
||||
* @param {bool} eager - whether the I/O for new context should begin eagerly
|
||||
* @param {Function} generateBundles - Custom FluentBundle asynchronous generator.
|
||||
* @param {Function} generateBundlesSync - Custom FluentBundle generator.
|
||||
* @returns {Iter<FluentBundle>}
|
||||
*/
|
||||
generateBundles(resourceIds, isSync, eager = false, generateBundles = defaultGenerateBundles, generateBundlesSync = defaultGenerateBundlesSync) {
|
||||
// Store for error reporting from `formatWithFallback`.
|
||||
let generateMessages = isSync ? generateBundlesSync : generateBundles;
|
||||
let bundles = this.cached(generateMessages(resourceIds), isSync);
|
||||
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;
|
||||
bundles.touchNext(prefetchCount);
|
||||
}
|
||||
return bundles;
|
||||
},
|
||||
}
|
||||
|
||||
/**
|
||||
* Format the value of a message into a string or `null`.
|
||||
*
|
||||
* This function is passed as a method to `keysFromBundle` and resolve
|
||||
* a value of a single L10n Entity using provided `FluentBundle`.
|
||||
|
||||
* If the message doesn't have a value, return `null`.
|
||||
*
|
||||
* @param {FluentBundle} bundle
|
||||
* @param {Array<Error>} errors
|
||||
* @param {Object} message
|
||||
* @param {Object} args
|
||||
* @returns {string?}
|
||||
* @private
|
||||
*/
|
||||
function valueFromBundle(bundle, errors, message, args) {
|
||||
if (message.value) {
|
||||
return bundle.formatPattern(message.value, args, errors);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Format all public values of a message into a {value, attributes} object.
|
||||
*
|
||||
* This function is passed as a method to `keysFromBundle` and resolve
|
||||
* a single L10n Entity using provided `FluentBundle`.
|
||||
*
|
||||
* The function will return an object with a value and attributes of the
|
||||
* entity.
|
||||
*
|
||||
* @param {FluentBundle} bundle
|
||||
* @param {Array<Error>} errors
|
||||
* @param {Object} message
|
||||
* @param {Object} args
|
||||
* @returns {Object}
|
||||
* @private
|
||||
*/
|
||||
function messageFromBundle(bundle, errors, message, args) {
|
||||
const formatted = {
|
||||
value: null,
|
||||
attributes: null,
|
||||
};
|
||||
|
||||
if (message.value) {
|
||||
formatted.value = bundle.formatPattern(message.value, args, errors);
|
||||
}
|
||||
|
||||
let attrNames = Object.keys(message.attributes);
|
||||
if (attrNames.length > 0) {
|
||||
formatted.attributes = new Array(attrNames.length);
|
||||
for (let [i, name] of attrNames.entries()) {
|
||||
let value = bundle.formatPattern(message.attributes[name], args, errors);
|
||||
formatted.attributes[i] = {name, value};
|
||||
}
|
||||
}
|
||||
|
||||
return formatted;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function is an inner function for `Localization.formatWithFallback`.
|
||||
*
|
||||
* It takes a `FluentBundle`, list of l10n-ids and a method to be used for
|
||||
* key resolution (either `valueFromBundle` or `messageFromBundle`) and
|
||||
* optionally a value returned from `keysFromBundle` executed against
|
||||
* another `FluentBundle`.
|
||||
*
|
||||
* The idea here is that if the previous `FluentBundle` did not resolve
|
||||
* all keys, we're calling this function with the next context to resolve
|
||||
* the remaining ones.
|
||||
*
|
||||
* In the function, we loop over `keys` and check if we have the `prev`
|
||||
* passed and if it has an error entry for the position we're in.
|
||||
*
|
||||
* If it doesn't, it means that we have a good translation for this key and
|
||||
* we return it. If it does, we'll try to resolve the key using the passed
|
||||
* `FluentBundle`.
|
||||
*
|
||||
* In the end, we fill the translations array, and return the Set with
|
||||
* missing ids.
|
||||
*
|
||||
* See `Localization.formatWithFallback` for more info on how this is used.
|
||||
*
|
||||
* @param {Function} method
|
||||
* @param {FluentBundle} bundle
|
||||
* @param {Array<string|Object>} keys
|
||||
* @param {{Array<{value: string, attributes: Object}>}} translations
|
||||
*
|
||||
* @returns {Set<string>}
|
||||
* @private
|
||||
*/
|
||||
function keysFromBundle(method, bundle, keys, translations) {
|
||||
const messageErrors = [];
|
||||
const missingIds = new Set();
|
||||
|
||||
keys.forEach((key, i) => {
|
||||
let id;
|
||||
let args = undefined;
|
||||
if (typeof key == "object" && "id" in key) {
|
||||
id = String(key.id);
|
||||
args = key.args;
|
||||
} else {
|
||||
id = String(key);
|
||||
}
|
||||
|
||||
if (translations[i] !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
let message = bundle.getMessage(id);
|
||||
if (message) {
|
||||
messageErrors.length = 0;
|
||||
translations[i] = method(bundle, messageErrors, message, args);
|
||||
if (messageErrors.length > 0) {
|
||||
const locale = bundle.locales[0];
|
||||
const errors = messageErrors.join(", ");
|
||||
maybeReportErrorToGecko(`[fluent][resolver] errors in ${locale}/${id}: ${errors}.`);
|
||||
}
|
||||
} else {
|
||||
missingIds.add(id);
|
||||
}
|
||||
});
|
||||
|
||||
return missingIds;
|
||||
}
|
||||
|
||||
this.Localization = Localization;
|
||||
var EXPORTED_SYMBOLS = ["Localization"];
|
|
@ -1,7 +1,7 @@
|
|||
The content of this directory is partially sourced from the fluent.js project.
|
||||
|
||||
The following files are affected:
|
||||
- Localization.jsm
|
||||
- FluentSyntax.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
|
||||
|
|
|
@ -495,10 +495,6 @@ Localization object manually using the `Localization` class:
|
|||
|
||||
.. code-block:: javascript
|
||||
|
||||
const { Localization } =
|
||||
ChromeUtils.import("resource://gre/modules/Localization.jsm", {});
|
||||
|
||||
|
||||
const myL10n = new Localization([
|
||||
"branding/brand.ftl",
|
||||
"browser/preferences/preferences.ftl"
|
||||
|
@ -528,10 +524,6 @@ on the class.
|
|||
|
||||
.. code-block:: javascript
|
||||
|
||||
const { Localization } =
|
||||
ChromeUtils.import("resource://gre/modules/Localization.jsm", {});
|
||||
|
||||
|
||||
const myL10n = new Localization([
|
||||
"branding/brand.ftl",
|
||||
"browser/preferences/preferences.ftl"
|
||||
|
|
|
@ -23,10 +23,6 @@ UNIFIED_SOURCES += [
|
|||
"Localization.cpp",
|
||||
]
|
||||
|
||||
EXTRA_JS_MODULES += [
|
||||
"Localization.jsm",
|
||||
]
|
||||
|
||||
TESTING_JS_MODULES += [
|
||||
"FluentSyntax.jsm",
|
||||
]
|
||||
|
|
Загрузка…
Ссылка в новой задаче