--- a/intl/l10n/L10nRegistry.jsm
+++ b/intl/l10n/L10nRegistry.jsm
@@ -1,14 +1,16 @@
const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm", {});
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm", {});
const { FluentBundle, FluentResource } = ChromeUtils.import("resource://gre/modules/Fluent.jsm", {});
ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyGlobalGetters(this, ["fetch"]);
+const isParentProcess =
+ Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_DEFAULT;
/**
* L10nRegistry is a localization resource management system for Gecko.
*
* It manages the list of resource sources provided with the app and allows
* for additional sources to be added and updated.
*
* It's primary purpose is to allow for building an iterator over FluentBundle objects
* that will be utilized by a localization API.
@@ -71,32 +73,43 @@ XPCOMUtils.defineLazyGlobalGetters(this,
*
* This allows the localization API to consume the FluentBundle and lazily fallback
* on the next in case of a missing string or error.
*
* If during the life-cycle of the app a new source is added, the generator can be called again
* and will produce a new set of permutations placing the language pack provided resources
* at the top.
*/
-const L10nRegistry = {
- sources: new Map(),
- bootstrap: null,
+class L10nRegistryService {
+ constructor() {
+ this.sources = new Map();
+
+ if (!isParentProcess) {
+ this._setSourcesFromSharedData();
+ Services.cpmm.sharedData.addEventListener("change", this);
+ }
+ }
+
+ handleEvent(event) {
+ if (event.type === "change") {
+ if (event.changedKeys.includes("L10nRegistry:Sources")) {
+ this._setSourcesFromSharedData();
+ }
+ }
+ }
/**
* Based on the list of requested languages and resource Ids,
* this function returns an lazy iterator over message context permutations.
*
* @param {Array} requestedLangs
* @param {Array} resourceIds
* @returns {AsyncIterator<FluentBundle>}
*/
async* generateBundles(requestedLangs, resourceIds) {
- if (this.bootstrap !== null) {
- await this.bootstrap;
- }
const sourcesOrder = Array.from(this.sources.keys()).reverse();
const pseudoNameFromPref = Services.prefs.getStringPref("intl.l10n.pseudo", "");
for (const locale of requestedLangs) {
for await (const dataSets of generateResourceSetsForLocale(locale, sourcesOrder, resourceIds)) {
const bundle = new FluentBundle(locale, {
...MSG_CONTEXT_OPTIONS,
transform: PSEUDO_STRATEGIES[pseudoNameFromPref],
});
@@ -104,74 +117,114 @@ const L10nRegistry = {
if (data === null) {
return;
}
bundle.addResource(data);
}
yield bundle;
}
}
- },
+ }
/**
* Adds a new resource source to the L10nRegistry.
*
* @param {FileSource} source
*/
registerSource(source) {
if (this.sources.has(source.name)) {
throw new Error(`Source with name "${source.name}" already registered.`);
}
this.sources.set(source.name, source);
- Services.locale.availableLocales = this.getAvailableLocales();
- },
+
+ if (isParentProcess) {
+ this._synchronizeSharedData();
+ Services.locale.availableLocales = this.getAvailableLocales();
+ }
+ }
/**
* Updates an existing source in the L10nRegistry
*
* That will usually happen when a new version of a source becomes
* available (for example, an updated version of a language pack).
*
* @param {FileSource} source
*/
updateSource(source) {
if (!this.sources.has(source.name)) {
throw new Error(`Source with name "${source.name}" is not registered.`);
}
this.sources.set(source.name, source);
- Services.locale.availableLocales = this.getAvailableLocales();
- },
+ if (isParentProcess) {
+ this._synchronizeSharedData();
+ Services.locale.availableLocales = this.getAvailableLocales();
+ }
+ }
/**
* Removes a source from the L10nRegistry.
*
* @param {String} sourceId
*/
removeSource(sourceName) {
this.sources.delete(sourceName);
- Services.locale.availableLocales = this.getAvailableLocales();
- },
+ if (isParentProcess) {
+ this._synchronizeSharedData();
+ Services.locale.availableLocales = this.getAvailableLocales();
+ }
+ }
+
+ _synchronizeSharedData() {
+ const sources = new Map();
+ for (const [name, source] of this.sources.entries()) {
+ if (source.indexed) {
+ continue;
+ }
+ sources.set(name, {
+ locales: source.locales,
+ prePath: source.prePath,
+ });
+ }
+ Services.ppmm.sharedData.set("L10nRegistry:Sources", sources);
+ Services.ppmm.sharedData.flush();
+ }
+
+ _setSourcesFromSharedData() {
+ let sources = Services.cpmm.sharedData.get("L10nRegistry:Sources");
+ for (let [name, data] of sources.entries()) {
+ if (!this.sources.has(name)) {
+ const source = new FileSource(name, data.locales, data.prePath);
+ this.registerSource(source);
+ }
+ }
+ for (let name of this.sources.keys()) {
+ if (!sources.has(name)) {
+ this.removeSource(name);
+ }
+ }
+ }
/**
* Returns a list of locales for which at least one source
* has resources.
*
* @returns {Array<String>}
*/
getAvailableLocales() {
const locales = new Set();
for (const source of this.sources.values()) {
for (const locale of source.locales) {
locales.add(locale);
}
}
return Array.from(locales);
- },
-};
+ }
+}
/**
* This function generates an iterator over FluentBundles for a single locale
* for a given list of resourceIds for all possible combinations of sources.
*
* This function is called recursively to generate all possible permutations
* and uses the last, optional parameter, to pass the already resolved
* sources order.
@@ -479,32 +532,33 @@ class IndexedFileSource extends FileSour
super(name, locales, prePath);
this.indexed = true;
for (const path of paths) {
this.cache[path] = true;
}
}
}
+this.L10nRegistry = new L10nRegistryService();
+
/**
* The low level wrapper around Fetch API. It unifies the error scenarios to
* always produce a promise rejection.
*
* We keep it as a method to make it easier to override for testing purposes.
*
* @param {string} url
*
* @returns {Promise<string>}
*/
-L10nRegistry.load = function(url) {
+this.L10nRegistry.load = function(url) {
return fetch(url).then(response => {
if (!response.ok) {
return Promise.reject(response.statusText);
}
return response.text();
});
};
-this.L10nRegistry = L10nRegistry;
this.FileSource = FileSource;
this.IndexedFileSource = IndexedFileSource;
var EXPORTED_SYMBOLS = ["L10nRegistry", "FileSource", "IndexedFileSource"];