forked from mirrors/gecko-dev
Bug 1558602 - Allow DocumentL10n to use LocalizationSync. r=smaug,Pike
Differential Revision: https://phabricator.services.mozilla.com/D34584 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
3cc10e36db
commit
b0ba25f8b9
21 changed files with 510 additions and 84 deletions
|
|
@ -9,8 +9,6 @@ var EXPORTED_SYMBOLS = ["AboutLoginsParent"];
|
||||||
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
const {XPCOMUtils} = ChromeUtils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
ChromeUtils.defineModuleGetter(this, "E10SUtils",
|
ChromeUtils.defineModuleGetter(this, "E10SUtils",
|
||||||
"resource://gre/modules/E10SUtils.jsm");
|
"resource://gre/modules/E10SUtils.jsm");
|
||||||
ChromeUtils.defineModuleGetter(this, "Localization",
|
|
||||||
"resource://gre/modules/Localization.jsm");
|
|
||||||
ChromeUtils.defineModuleGetter(this, "LoginHelper",
|
ChromeUtils.defineModuleGetter(this, "LoginHelper",
|
||||||
"resource://gre/modules/LoginHelper.jsm");
|
"resource://gre/modules/LoginHelper.jsm");
|
||||||
ChromeUtils.defineModuleGetter(this, "MigrationUtils",
|
ChromeUtils.defineModuleGetter(this, "MigrationUtils",
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
"use strict";
|
"use strict";
|
||||||
const {Localization} = ChromeUtils.import("resource://gre/modules/Localization.jsm");
|
|
||||||
const {FxAccountsConfig} = ChromeUtils.import("resource://gre/modules/FxAccountsConfig.jsm");
|
const {FxAccountsConfig} = ChromeUtils.import("resource://gre/modules/FxAccountsConfig.jsm");
|
||||||
const {AttributionCode} = ChromeUtils.import("resource:///modules/AttributionCode.jsm");
|
const {AttributionCode} = ChromeUtils.import("resource:///modules/AttributionCode.jsm");
|
||||||
const {AddonRepository} = ChromeUtils.import("resource://gre/modules/addons/AddonRepository.jsm");
|
const {AddonRepository} = ChromeUtils.import("resource://gre/modules/addons/AddonRepository.jsm");
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,6 @@ var {FileUtils} = ChromeUtils.import("resource://gre/modules/FileUtils.jsm");
|
||||||
var {TransientPrefs} = ChromeUtils.import("resource:///modules/TransientPrefs.jsm");
|
var {TransientPrefs} = ChromeUtils.import("resource:///modules/TransientPrefs.jsm");
|
||||||
var {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
var {AppConstants} = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||||
var {L10nRegistry} = ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm");
|
var {L10nRegistry} = ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm");
|
||||||
var {Localization} = ChromeUtils.import("resource://gre/modules/Localization.jsm");
|
|
||||||
var {HomePage} = ChromeUtils.import("resource:///modules/HomePage.jsm");
|
var {HomePage} = ChromeUtils.import("resource:///modules/HomePage.jsm");
|
||||||
ChromeUtils.defineModuleGetter(this, "CloudStorage",
|
ChromeUtils.defineModuleGetter(this, "CloudStorage",
|
||||||
"resource://gre/modules/CloudStorage.jsm");
|
"resource://gre/modules/CloudStorage.jsm");
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,6 @@ XPCOMUtils.defineLazyModuleGetters(this, {
|
||||||
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
PrivateBrowsingUtils: "resource://gre/modules/PrivateBrowsingUtils.jsm",
|
||||||
Services: "resource://gre/modules/Services.jsm",
|
Services: "resource://gre/modules/Services.jsm",
|
||||||
AppConstants: "resource://gre/modules/AppConstants.jsm",
|
AppConstants: "resource://gre/modules/AppConstants.jsm",
|
||||||
Localization: "resource://gre/modules/Localization.jsm",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
||||||
|
|
@ -309,13 +309,54 @@ already_AddRefed<Promise> DOMLocalization::TranslateElements(
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<Promise> callbackResult = FormatMessages(cx, l10nKeys, aRv);
|
if (mIsSync) {
|
||||||
if (NS_WARN_IF(aRv.Failed())) {
|
nsTArray<JS::Value> jsKeys;
|
||||||
return nullptr;
|
SequenceRooter<JS::Value> keysRooter(cx, &jsKeys);
|
||||||
}
|
for (auto& key : l10nKeys) {
|
||||||
|
JS::RootedValue jsKey(cx);
|
||||||
|
if (!ToJSValue(cx, key, &jsKey)) {
|
||||||
|
aRv.NoteJSContextException(cx);
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
jsKeys.AppendElement(jsKey);
|
||||||
|
}
|
||||||
|
|
||||||
nativeHandler->SetReturnValuePromise(promise);
|
nsTArray<JS::Value> messages;
|
||||||
callbackResult->AppendNativeHandler(nativeHandler);
|
SequenceRooter<JS::Value> messagesRooter(cx, &messages);
|
||||||
|
mLocalization->FormatMessagesSync(jsKeys, messages);
|
||||||
|
nsTArray<L10nMessage> l10nData;
|
||||||
|
SequenceRooter<L10nMessage> l10nDataRooter(cx, &l10nData);
|
||||||
|
|
||||||
|
for (auto& msg : messages) {
|
||||||
|
JS::Rooted<JS::Value> rootedMsg(cx);
|
||||||
|
rootedMsg.set(msg);
|
||||||
|
L10nMessage* slotPtr = l10nData.AppendElement(mozilla::fallible);
|
||||||
|
if (!slotPtr) {
|
||||||
|
promise->MaybeRejectWithUndefined();
|
||||||
|
return MaybeWrapPromise(promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!slotPtr->Init(cx, rootedMsg)) {
|
||||||
|
promise->MaybeRejectWithUndefined();
|
||||||
|
return MaybeWrapPromise(promise);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ApplyTranslations(domElements, l10nData, aRv);
|
||||||
|
if (NS_WARN_IF(aRv.Failed())) {
|
||||||
|
promise->MaybeRejectWithUndefined();
|
||||||
|
return MaybeWrapPromise(promise);
|
||||||
|
}
|
||||||
|
|
||||||
|
promise->MaybeResolveWithUndefined();
|
||||||
|
} else {
|
||||||
|
RefPtr<Promise> callbackResult = FormatMessages(cx, l10nKeys, aRv);
|
||||||
|
if (NS_WARN_IF(aRv.Failed())) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
nativeHandler->SetReturnValuePromise(promise);
|
||||||
|
callbackResult->AppendNativeHandler(nativeHandler);
|
||||||
|
}
|
||||||
|
|
||||||
return MaybeWrapPromise(promise);
|
return MaybeWrapPromise(promise);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,11 @@ DocumentL10n::DocumentL10n(Document* aDocument)
|
||||||
mDocument(aDocument),
|
mDocument(aDocument),
|
||||||
mState(DocumentL10nState::Initialized) {
|
mState(DocumentL10nState::Initialized) {
|
||||||
mContentSink = do_QueryInterface(aDocument->GetCurrentContentSink());
|
mContentSink = do_QueryInterface(aDocument->GetCurrentContentSink());
|
||||||
|
|
||||||
|
Element* elem = mDocument->GetDocumentElement();
|
||||||
|
if (elem) {
|
||||||
|
mIsSync = elem->HasAttr(kNameSpaceID_None, nsGkAtoms::datal10nsync);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DocumentL10n::Init(nsTArray<nsString>& aResourceIds, ErrorResult& aRv) {
|
void DocumentL10n::Init(nsTArray<nsString>& aResourceIds, ErrorResult& aRv) {
|
||||||
|
|
@ -133,6 +138,14 @@ void DocumentL10n::InitialDocumentTranslationCompleted() {
|
||||||
if (mContentSink) {
|
if (mContentSink) {
|
||||||
mContentSink->InitialDocumentTranslationCompleted();
|
mContentSink->InitialDocumentTranslationCompleted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If sync was true, we want to change the state of
|
||||||
|
// mozILocalization to async now.
|
||||||
|
if (mIsSync) {
|
||||||
|
mIsSync = false;
|
||||||
|
|
||||||
|
mLocalization->SetIsSync(mIsSync);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise* DocumentL10n::Ready() { return mReady; }
|
Promise* DocumentL10n::Ready() { return mReady; }
|
||||||
|
|
|
||||||
|
|
@ -36,5 +36,6 @@
|
||||||
[document_l10n/test_docl10n_initialize_after_parse.xul]
|
[document_l10n/test_docl10n_initialize_after_parse.xul]
|
||||||
[document_l10n/test_docl10n.xhtml]
|
[document_l10n/test_docl10n.xhtml]
|
||||||
[document_l10n/test_docl10n.html]
|
[document_l10n/test_docl10n.html]
|
||||||
|
[document_l10n/test_docl10n_sync.html]
|
||||||
[document_l10n/test_docl10n_ready_rejected.html]
|
[document_l10n/test_docl10n_ready_rejected.html]
|
||||||
[document_l10n/test_docl10n_removeResourceIds.html]
|
[document_l10n/test_docl10n_removeResourceIds.html]
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html data-l10n-sync>
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<title>Test DocumentL10n in HTML environment</title>
|
||||||
|
<script src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||||
|
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
|
||||||
|
<link rel="localization" href="crashreporter/aboutcrashes.ftl"/>
|
||||||
|
<script type="application/javascript">
|
||||||
|
"use strict";
|
||||||
|
SimpleTest.waitForExplicitFinish();
|
||||||
|
|
||||||
|
document.addEventListener("DOMContentLoaded", async function() {
|
||||||
|
await document.l10n.ready;
|
||||||
|
|
||||||
|
// Test for initial localization applied.
|
||||||
|
let desc = document.getElementById("main-desc");
|
||||||
|
is(desc.textContent.length > 0, true);
|
||||||
|
|
||||||
|
// Test for manual value formatting.
|
||||||
|
let msg = await document.l10n.formatValue("id-heading");
|
||||||
|
is(msg.length > 0, true);
|
||||||
|
|
||||||
|
// Test for mutations applied.
|
||||||
|
let verifyL10n = () => {
|
||||||
|
if (label.textContent.length > 0) {
|
||||||
|
window.removeEventListener("MozAfterPaint", verifyL10n);
|
||||||
|
SimpleTest.finish();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
window.addEventListener("MozAfterPaint", verifyL10n);
|
||||||
|
|
||||||
|
let label = document.getElementById("label1");
|
||||||
|
document.l10n.setAttributes(
|
||||||
|
label,
|
||||||
|
"date-crashed-heading",
|
||||||
|
{
|
||||||
|
name: "John",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
// Test for l10n.getAttributes
|
||||||
|
let l10nArgs = document.l10n.getAttributes(label);
|
||||||
|
is(l10nArgs.id, "date-crashed-heading");
|
||||||
|
is(l10nArgs.args.name, "John");
|
||||||
|
}, { once: true});
|
||||||
|
</script>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<h1 id="main-desc" data-l10n-id="crash-reports-title"></h1>
|
||||||
|
|
||||||
|
<p id="label1"></p>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
@ -81,7 +81,7 @@ const isParentProcess = appinfo.processType === appinfo.PROCESS_TYPE_DEFAULT;
|
||||||
*
|
*
|
||||||
* Notice: L10nRegistry is primarily an asynchronous API, but
|
* Notice: L10nRegistry is primarily an asynchronous API, but
|
||||||
* it does provide a synchronous version of it's main method
|
* it does provide a synchronous version of it's main method
|
||||||
* for use by the `LocalizationSync` class.
|
* for use by the `Localization` class when in `sync` state.
|
||||||
* This API should be only used in very specialized cases and
|
* This API should be only used in very specialized cases and
|
||||||
* the uses should be reviewed by the toolkit owner/peer.
|
* the uses should be reviewed by the toolkit owner/peer.
|
||||||
*/
|
*/
|
||||||
|
|
|
||||||
|
|
@ -34,14 +34,16 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(Localization)
|
||||||
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
|
||||||
NS_INTERFACE_MAP_END
|
NS_INTERFACE_MAP_END
|
||||||
|
|
||||||
Localization::Localization(nsIGlobalObject* aGlobal) : mGlobal(aGlobal) {}
|
Localization::Localization(nsIGlobalObject* aGlobal)
|
||||||
|
: mGlobal(aGlobal), mIsSync(false) {}
|
||||||
|
|
||||||
void Localization::Init(nsTArray<nsString>& aResourceIds, ErrorResult& aRv) {
|
void Localization::Init(nsTArray<nsString>& aResourceIds, ErrorResult& aRv) {
|
||||||
nsCOMPtr<mozILocalizationJSM> jsm =
|
nsCOMPtr<mozILocalizationJSM> jsm =
|
||||||
do_ImportModule("resource://gre/modules/Localization.jsm");
|
do_ImportModule("resource://gre/modules/Localization.jsm");
|
||||||
MOZ_RELEASE_ASSERT(jsm);
|
MOZ_RELEASE_ASSERT(jsm);
|
||||||
|
|
||||||
Unused << jsm->GetLocalization(aResourceIds, getter_AddRefs(mLocalization));
|
Unused << jsm->GetLocalization(aResourceIds, mIsSync,
|
||||||
|
getter_AddRefs(mLocalization));
|
||||||
MOZ_RELEASE_ASSERT(mLocalization);
|
MOZ_RELEASE_ASSERT(mLocalization);
|
||||||
|
|
||||||
RegisterObservers();
|
RegisterObservers();
|
||||||
|
|
|
||||||
|
|
@ -74,6 +74,7 @@ class Localization : public nsIObserver,
|
||||||
|
|
||||||
nsCOMPtr<nsIGlobalObject> mGlobal;
|
nsCOMPtr<nsIGlobalObject> mGlobal;
|
||||||
nsCOMPtr<mozILocalization> mLocalization;
|
nsCOMPtr<mozILocalization> mLocalization;
|
||||||
|
bool mIsSync;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace intl
|
} // namespace intl
|
||||||
|
|
|
||||||
|
|
@ -211,20 +211,28 @@ function maybeReportErrorToGecko(error) {
|
||||||
*/
|
*/
|
||||||
class Localization {
|
class Localization {
|
||||||
/**
|
/**
|
||||||
* @param {Array<String>} resourceIds - List of resource IDs
|
* @param {Array<String>} resourceIds - List of resource IDs
|
||||||
* @param {Function} generateBundles - Function that returns a
|
* @param {Function} generateBundles - Function that returns an async
|
||||||
* generator over FluentBundles
|
* generator over FluentBundles
|
||||||
|
* @param {Function} generateBundlesSync - Function that returns a sync
|
||||||
|
* generator over FluentBundles
|
||||||
*
|
*
|
||||||
* @returns {Localization}
|
* @returns {Localization}
|
||||||
*/
|
*/
|
||||||
constructor(resourceIds = [], generateBundles = defaultGenerateBundles) {
|
constructor(resourceIds = [], sync = false, generateBundles = defaultGenerateBundles, generateBundlesSync = defaultGenerateBundlesSync) {
|
||||||
|
this.isSync = sync;
|
||||||
this.resourceIds = resourceIds;
|
this.resourceIds = resourceIds;
|
||||||
this.generateBundles = generateBundles;
|
this.generateBundles = generateBundles;
|
||||||
|
this.generateBundlesSync = generateBundlesSync;
|
||||||
this.onChange(true);
|
this.onChange(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
cached(iterable) {
|
cached(iterable) {
|
||||||
return CachedAsyncIterable.from(iterable);
|
if (this.isSync) {
|
||||||
|
return CachedSyncIterable.from(iterable);
|
||||||
|
} else {
|
||||||
|
return CachedAsyncIterable.from(iterable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -280,6 +288,46 @@ class Localization {
|
||||||
return translations;
|
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<Object>} keys - Translation keys to format.
|
||||||
|
* @param {Function} method - Formatting function.
|
||||||
|
* @returns {Array<string|Object>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
formatWithFallbackSync(keys, method) {
|
||||||
|
if (!this.isSync) {
|
||||||
|
throw new Error("Can't use sync formatWithFallback when state is async.");
|
||||||
|
}
|
||||||
|
const translations = new Array(keys.length);
|
||||||
|
let hasAtLeastOneBundle = false;
|
||||||
|
|
||||||
|
for (const bundle of this.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(this.resourceIds)}.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return translations;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format translations into {value, attributes} objects.
|
* Format translations into {value, attributes} objects.
|
||||||
*
|
*
|
||||||
|
|
@ -300,7 +348,7 @@ class Localization {
|
||||||
* // }
|
* // }
|
||||||
* // ]
|
* // ]
|
||||||
*
|
*
|
||||||
* Returns a Promise resolving to an array of the translation strings.
|
* Returns a Promise resolving to an array of the translation messages.
|
||||||
*
|
*
|
||||||
* @param {Array<Object>} keys
|
* @param {Array<Object>} keys
|
||||||
* @returns {Promise<Array<{value: string, attributes: Object}>>}
|
* @returns {Promise<Array<{value: string, attributes: Object}>>}
|
||||||
|
|
@ -310,6 +358,19 @@ class Localization {
|
||||||
return this.formatWithFallback(keys, messageFromBundle);
|
return this.formatWithFallback(keys, messageFromBundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync version of `formatMessages`.
|
||||||
|
*
|
||||||
|
* Returns an array of the translation messages.
|
||||||
|
*
|
||||||
|
* @param {Array<Object>} keys
|
||||||
|
* @returns {Array<{value: string, attributes: Object}>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
formatMessagesSync(keys) {
|
||||||
|
return this.formatWithFallbackSync(keys, messageFromBundle);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve translations corresponding to the passed keys.
|
* Retrieve translations corresponding to the passed keys.
|
||||||
*
|
*
|
||||||
|
|
@ -333,6 +394,19 @@ class Localization {
|
||||||
return this.formatWithFallback(keys, valueFromBundle);
|
return this.formatWithFallback(keys, valueFromBundle);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync version of `formatValues`.
|
||||||
|
*
|
||||||
|
* Returns an array of the translation strings.
|
||||||
|
*
|
||||||
|
* @param {Array<Object>} keys
|
||||||
|
* @returns {Array<string>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
formatValuesSync(keys) {
|
||||||
|
return this.formatWithFallbackSync(keys, valueFromBundle);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieve the translation corresponding to the `id` identifier.
|
* Retrieve the translation corresponding to the `id` identifier.
|
||||||
*
|
*
|
||||||
|
|
@ -345,7 +419,7 @@ class Localization {
|
||||||
*
|
*
|
||||||
* // 'Hello, world!'
|
* // 'Hello, world!'
|
||||||
*
|
*
|
||||||
* Returns a Promise resolving to the translation string.
|
* Returns a Promise resolving to a translation string.
|
||||||
*
|
*
|
||||||
* Use this sparingly for one-off messages which don't need to be
|
* Use this sparingly for one-off messages which don't need to be
|
||||||
* retranslated when the user changes their language preferences, e.g. in
|
* retranslated when the user changes their language preferences, e.g. in
|
||||||
|
|
@ -360,6 +434,20 @@ class Localization {
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sync version of `formatValue`.
|
||||||
|
*
|
||||||
|
* Returns a translation string.
|
||||||
|
*
|
||||||
|
* @param {Array<Object>} keys
|
||||||
|
* @returns {string>}
|
||||||
|
* @private
|
||||||
|
*/
|
||||||
|
formatValueSync(id, args) {
|
||||||
|
const [val] = this.formatValuesSync([{id, args}]);
|
||||||
|
return val;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register weak observers on events that will trigger cache invalidation
|
* Register weak observers on events that will trigger cache invalidation
|
||||||
*/
|
*/
|
||||||
|
|
@ -398,8 +486,8 @@ class Localization {
|
||||||
* @param {bool} eager - whether the I/O for new context should begin eagerly
|
* @param {bool} eager - whether the I/O for new context should begin eagerly
|
||||||
*/
|
*/
|
||||||
onChange(eager = false) {
|
onChange(eager = false) {
|
||||||
this.bundles = this.cached(
|
let generateMessages = this.isSync ? this.generateBundlesSync : this.generateBundles;
|
||||||
this.generateBundles(this.resourceIds));
|
this.bundles = this.cached(generateMessages(this.resourceIds));
|
||||||
if (eager) {
|
if (eager) {
|
||||||
// If the first app locale is the same as last fallback
|
// If the first app locale is the same as last fallback
|
||||||
// it means that we have all resources in this locale, and
|
// it means that we have all resources in this locale, and
|
||||||
|
|
@ -412,51 +500,17 @@ class Localization {
|
||||||
this.bundles.touchNext(prefetchCount);
|
this.bundles.touchNext(prefetchCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setIsSync(isSync) {
|
||||||
|
this.isSync = isSync;
|
||||||
|
this.onChange();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Localization.prototype.QueryInterface = ChromeUtils.generateQI([
|
Localization.prototype.QueryInterface = ChromeUtils.generateQI([
|
||||||
Ci.nsISupportsWeakReference,
|
Ci.nsISupportsWeakReference,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
class LocalizationSync extends Localization {
|
|
||||||
constructor(resourceIds = [], generateBundles = defaultGenerateBundlesSync) {
|
|
||||||
super(resourceIds, generateBundles);
|
|
||||||
}
|
|
||||||
|
|
||||||
cached(iterable) {
|
|
||||||
return CachedSyncIterable.from(iterable);
|
|
||||||
}
|
|
||||||
|
|
||||||
formatWithFallback(keys, method) {
|
|
||||||
const translations = new Array(keys.length);
|
|
||||||
let hasAtLeastOneBundle = false;
|
|
||||||
|
|
||||||
for (const bundle of this.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(this.resourceIds)}.`);
|
|
||||||
}
|
|
||||||
|
|
||||||
return translations;
|
|
||||||
}
|
|
||||||
|
|
||||||
formatValue(id, args) {
|
|
||||||
const [val] = this.formatValues([{id, args}]);
|
|
||||||
return val;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Format the value of a message into a string.
|
* Format the value of a message into a string.
|
||||||
*
|
*
|
||||||
|
|
@ -577,7 +631,6 @@ function keysFromBundle(method, bundle, keys, translations) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return missingIds;
|
return missingIds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -585,14 +638,13 @@ function keysFromBundle(method, bundle, keys, translations) {
|
||||||
* Helper function which allows us to construct a new
|
* Helper function which allows us to construct a new
|
||||||
* Localization from Localization.
|
* Localization from Localization.
|
||||||
*/
|
*/
|
||||||
var getLocalization = (resourceIds) => {
|
var getLocalization = (resourceIds, sync = false) => {
|
||||||
return new Localization(resourceIds);
|
return new Localization(resourceIds, sync);
|
||||||
};
|
};
|
||||||
|
|
||||||
var getLocalizationWithCustomGenerateMessages = (resourceIds, generateMessages) => {
|
var getLocalizationWithCustomGenerateMessages = (resourceIds, generateMessages) => {
|
||||||
return new Localization(resourceIds, generateMessages);
|
return new Localization(resourceIds, false, generateMessages);
|
||||||
};
|
};
|
||||||
|
|
||||||
this.Localization = Localization;
|
this.Localization = Localization;
|
||||||
this.LocalizationSync = LocalizationSync;
|
var EXPORTED_SYMBOLS = ["Localization", "getLocalization", "getLocalizationWithCustomGenerateMessages"];
|
||||||
var EXPORTED_SYMBOLS = ["Localization", "LocalizationSync", "getLocalization", "getLocalizationWithCustomGenerateMessages"];
|
|
||||||
|
|
|
||||||
|
|
@ -540,16 +540,16 @@ contexts manually using the `Localization` class:
|
||||||
|
|
||||||
const { Localization } =
|
const { Localization } =
|
||||||
ChromeUtils.import("resource://gre/modules/Localization.jsm", {});
|
ChromeUtils.import("resource://gre/modules/Localization.jsm", {});
|
||||||
|
|
||||||
|
|
||||||
const myL10n = new Localization([
|
const myL10n = new Localization([
|
||||||
"branding/brand.ftl",
|
"branding/brand.ftl",
|
||||||
"browser/preferences/preferences.ftl"
|
"browser/preferences/preferences.ftl"
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
|
||||||
let [isDefaultMsg, isNotDefaultMsg] =
|
let [isDefaultMsg, isNotDefaultMsg] =
|
||||||
myL10n.formatValues({id: "is-default"}, {id: "is-not-default"});
|
await myL10n.formatValues({id: "is-default"}, {id: "is-not-default"});
|
||||||
|
|
||||||
|
|
||||||
.. admonition:: Example
|
.. admonition:: Example
|
||||||
|
|
@ -563,6 +563,32 @@ contexts manually using the `Localization` class:
|
||||||
A developer may create manually a new context with the same resources as the main one,
|
A developer may create manually a new context with the same resources as the main one,
|
||||||
but hardcode it to `en-US` and then build the search index using both contexts.
|
but hardcode it to `en-US` and then build the search index using both contexts.
|
||||||
|
|
||||||
|
|
||||||
|
By default, all `Localization` contexts are asynchronous. It is possible to create a synchronous
|
||||||
|
one by passing an `sync = false` argument to the constructor, or calling the `SetIsSync(bool)` method
|
||||||
|
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"
|
||||||
|
], false);
|
||||||
|
|
||||||
|
|
||||||
|
let [isDefaultMsg, isNotDefaultMsg] =
|
||||||
|
myL10n.formatValuesSync({id: "is-default"}, {id: "is-not-default"});
|
||||||
|
|
||||||
|
|
||||||
|
Synchronous contexts should be always avoided as they require synchronous I/O. If you think your use case
|
||||||
|
requires a synchronous localization context, please consult Gecko, Performance and L10n Drivers teams.
|
||||||
|
|
||||||
|
|
||||||
Designing Localizable APIs
|
Designing Localizable APIs
|
||||||
==========================
|
==========================
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,12 @@
|
||||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an internal XPIDL used to expose a JS based Localization class to be used
|
||||||
|
* by its C++ wrapper.
|
||||||
|
*
|
||||||
|
* Consumers should use the WebIDL Localization API instead of this one.
|
||||||
|
*/
|
||||||
#include "nsISupports.idl"
|
#include "nsISupports.idl"
|
||||||
|
|
||||||
[scriptable, uuid(7d468600-551f-4fe0-98c9-92a53b63ec8d)]
|
[scriptable, uuid(7d468600-551f-4fe0-98c9-92a53b63ec8d)]
|
||||||
|
|
@ -15,11 +21,14 @@ interface mozILocalization : nsISupports
|
||||||
Promise formatMessages(in Array<jsval> aKeys);
|
Promise formatMessages(in Array<jsval> aKeys);
|
||||||
Promise formatValues(in Array<jsval> aKeys);
|
Promise formatValues(in Array<jsval> aKeys);
|
||||||
Promise formatValue(in AString aId, [optional] in jsval aArgs);
|
Promise formatValue(in AString aId, [optional] in jsval aArgs);
|
||||||
|
|
||||||
|
Array<jsval> formatMessagesSync(in Array<jsval> aKeys);
|
||||||
|
void setIsSync(in boolean isSync);
|
||||||
};
|
};
|
||||||
|
|
||||||
[scriptable, uuid(96632d26-1422-12e9-b1ce-9bb586acd241)]
|
[scriptable, uuid(96632d26-1422-12e9-b1ce-9bb586acd241)]
|
||||||
interface mozILocalizationJSM : nsISupports
|
interface mozILocalizationJSM : nsISupports
|
||||||
{
|
{
|
||||||
mozILocalization getLocalization(in Array<AString> resourceIds);
|
mozILocalization getLocalization(in Array<AString> resourceIds, in bool sync);
|
||||||
mozILocalization getLocalizationWithCustomGenerateMessages(in Array<AString> resourceIds, in jsval generateMessages);
|
mozILocalization getLocalizationWithCustomGenerateMessages(in Array<AString> resourceIds, in jsval generateMessages);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,7 @@ add_task(async function test_methods_calling() {
|
||||||
|
|
||||||
const l10n = new Localization([
|
const l10n = new Localization([
|
||||||
"/browser/menu.ftl",
|
"/browser/menu.ftl",
|
||||||
], generateMessages);
|
], false, generateMessages);
|
||||||
|
|
||||||
let values = await l10n.formatValues([{id: "key"}, {id: "key2"}]);
|
let values = await l10n.formatValues([{id: "key"}, {id: "key2"}]);
|
||||||
|
|
||||||
|
|
@ -81,7 +81,7 @@ key = { PLATFORM() ->
|
||||||
|
|
||||||
const l10n = new Localization([
|
const l10n = new Localization([
|
||||||
"/test.ftl",
|
"/test.ftl",
|
||||||
], generateMessages);
|
], false, generateMessages);
|
||||||
|
|
||||||
let values = await l10n.formatValues([{id: "key"}]);
|
let values = await l10n.formatValues([{id: "key"}]);
|
||||||
|
|
||||||
|
|
@ -114,7 +114,7 @@ add_task(async function test_add_remove_resourceIds() {
|
||||||
yield * await L10nRegistry.generateBundles(["en-US"], resIds);
|
yield * await L10nRegistry.generateBundles(["en-US"], resIds);
|
||||||
}
|
}
|
||||||
|
|
||||||
const l10n = new Localization(["/browser/menu.ftl"], generateMessages);
|
const l10n = new Localization(["/browser/menu.ftl"], false, generateMessages);
|
||||||
|
|
||||||
let values = await l10n.formatValues([{id: "key1"}, {id: "key2"}]);
|
let values = await l10n.formatValues([{id: "key1"}, {id: "key2"}]);
|
||||||
|
|
||||||
|
|
@ -139,3 +139,81 @@ add_task(async function test_add_remove_resourceIds() {
|
||||||
L10nRegistry.load = originalLoad;
|
L10nRegistry.load = originalLoad;
|
||||||
Services.locale.requestedLocales = originalRequested;
|
Services.locale.requestedLocales = originalRequested;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
add_task(async function test_switch_to_async() {
|
||||||
|
const { L10nRegistry, FileSource } =
|
||||||
|
ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm");
|
||||||
|
|
||||||
|
const fs = {
|
||||||
|
"/localization/en-US/browser/menu.ftl": "key1 = Value1",
|
||||||
|
"/localization/en-US/toolkit/menu.ftl": "key2 = Value2",
|
||||||
|
};
|
||||||
|
const originalLoad = L10nRegistry.load;
|
||||||
|
const originalLoadSync = L10nRegistry.loadSync;
|
||||||
|
const originalRequested = Services.locale.requestedLocales;
|
||||||
|
|
||||||
|
let syncLoads = 0;
|
||||||
|
let asyncLoads = 0;
|
||||||
|
|
||||||
|
L10nRegistry.load = async function(url) {
|
||||||
|
asyncLoads += 1;
|
||||||
|
return fs[url];
|
||||||
|
};
|
||||||
|
|
||||||
|
L10nRegistry.loadSync = function(url) {
|
||||||
|
syncLoads += 1;
|
||||||
|
return fs[url];
|
||||||
|
};
|
||||||
|
|
||||||
|
const source = new FileSource("test", ["en-US"], "/localization/{locale}");
|
||||||
|
L10nRegistry.registerSource(source);
|
||||||
|
|
||||||
|
async function* generateMessages(resIds) {
|
||||||
|
yield * await L10nRegistry.generateBundles(["en-US"], resIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
function* generateMessagesSync(resIds) {
|
||||||
|
yield * L10nRegistry.generateBundlesSync(["en-US"], resIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
const l10n = new Localization(["/browser/menu.ftl"], false, generateMessages, generateMessagesSync);
|
||||||
|
|
||||||
|
let values = await l10n.formatValues([{id: "key1"}, {id: "key2"}]);
|
||||||
|
|
||||||
|
equal(values[0], "Value1");
|
||||||
|
equal(values[1], undefined);
|
||||||
|
equal(syncLoads, 0);
|
||||||
|
equal(asyncLoads, 1);
|
||||||
|
|
||||||
|
l10n.setIsSync(true);
|
||||||
|
|
||||||
|
l10n.addResourceIds(["/toolkit/menu.ftl"]);
|
||||||
|
|
||||||
|
// Nothing happens when we switch, because
|
||||||
|
// the next load is lazy.
|
||||||
|
equal(syncLoads, 0);
|
||||||
|
equal(asyncLoads, 1);
|
||||||
|
|
||||||
|
values = l10n.formatValuesSync([{id: "key1"}, {id: "key2"}]);
|
||||||
|
let values2 = await l10n.formatValues([{id: "key1"}, {id: "key2"}]);
|
||||||
|
|
||||||
|
deepEqual(values, values2);
|
||||||
|
equal(values[0], "Value1");
|
||||||
|
equal(values[1], "Value2");
|
||||||
|
equal(syncLoads, 1);
|
||||||
|
equal(asyncLoads, 1);
|
||||||
|
|
||||||
|
l10n.removeResourceIds(["/browser/menu.ftl"]);
|
||||||
|
|
||||||
|
values = await l10n.formatValues([{id: "key1"}, {id: "key2"}]);
|
||||||
|
|
||||||
|
equal(values[0], undefined);
|
||||||
|
equal(values[1], "Value2");
|
||||||
|
equal(syncLoads, 1);
|
||||||
|
equal(asyncLoads, 1);
|
||||||
|
|
||||||
|
L10nRegistry.sources.clear();
|
||||||
|
L10nRegistry.load = originalLoad;
|
||||||
|
L10nRegistry.loadSync = originalLoadSync;
|
||||||
|
Services.locale.requestedLocales = originalRequested;
|
||||||
|
});
|
||||||
|
|
|
||||||
151
intl/l10n/test/test_localization_sync.js
Normal file
151
intl/l10n/test/test_localization_sync.js
Normal file
|
|
@ -0,0 +1,151 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
const { AppConstants } = ChromeUtils.import("resource://gre/modules/AppConstants.jsm");
|
||||||
|
const { Localization } = ChromeUtils.import("resource://gre/modules/Localization.jsm");
|
||||||
|
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
add_task(function test_methods_calling() {
|
||||||
|
const { L10nRegistry, FileSource } =
|
||||||
|
ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm");
|
||||||
|
|
||||||
|
const fs = {
|
||||||
|
"/localization/de/browser/menu.ftl": "key = [de] Value2",
|
||||||
|
"/localization/en-US/browser/menu.ftl": "key = [en] Value2\nkey2 = [en] Value3",
|
||||||
|
};
|
||||||
|
const originalLoadSync = L10nRegistry.loadSync;
|
||||||
|
const originalRequested = Services.locale.requestedLocales;
|
||||||
|
|
||||||
|
L10nRegistry.loadSync = function(url) {
|
||||||
|
return fs[url];
|
||||||
|
};
|
||||||
|
|
||||||
|
const source = new FileSource("test", ["de", "en-US"], "/localization/{locale}");
|
||||||
|
L10nRegistry.registerSource(source);
|
||||||
|
|
||||||
|
function* generateMessagesSync(resIds) {
|
||||||
|
yield * L10nRegistry.generateBundlesSync(["de", "en-US"], resIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
const l10n = new Localization([
|
||||||
|
"/browser/menu.ftl",
|
||||||
|
], true, null, generateMessagesSync);
|
||||||
|
|
||||||
|
let values = l10n.formatValuesSync([{id: "key"}, {id: "key2"}]);
|
||||||
|
|
||||||
|
equal(values[0], "[de] Value2");
|
||||||
|
equal(values[1], "[en] Value3");
|
||||||
|
|
||||||
|
L10nRegistry.sources.clear();
|
||||||
|
L10nRegistry.loadSync = originalLoadSync;
|
||||||
|
Services.locale.requestedLocales = originalRequested;
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function test_builtins() {
|
||||||
|
const { L10nRegistry, FileSource } =
|
||||||
|
ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm");
|
||||||
|
|
||||||
|
const known_platforms = {
|
||||||
|
"linux": "linux",
|
||||||
|
"win": "windows",
|
||||||
|
"macosx": "macos",
|
||||||
|
"android": "android",
|
||||||
|
};
|
||||||
|
|
||||||
|
const fs = {
|
||||||
|
"/localization/en-US/test.ftl": `
|
||||||
|
key = { PLATFORM() ->
|
||||||
|
${ Object.values(known_platforms).map(
|
||||||
|
name => ` [${ name }] ${ name.toUpperCase() } Value\n`).join("") }
|
||||||
|
*[other] OTHER Value
|
||||||
|
}`,
|
||||||
|
};
|
||||||
|
const originalLoadSync = L10nRegistry.loadSync;
|
||||||
|
|
||||||
|
L10nRegistry.loadSync = function(url) {
|
||||||
|
return fs[url];
|
||||||
|
};
|
||||||
|
|
||||||
|
const source = new FileSource("test", ["en-US"], "/localization/{locale}");
|
||||||
|
L10nRegistry.registerSource(source);
|
||||||
|
|
||||||
|
function* generateMessagesSync(resIds) {
|
||||||
|
yield * L10nRegistry.generateBundlesSync(["en-US"], resIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
const l10n = new Localization([
|
||||||
|
"/test.ftl",
|
||||||
|
], true, null, generateMessagesSync);
|
||||||
|
|
||||||
|
let values = l10n.formatValuesSync([{id: "key"}]);
|
||||||
|
|
||||||
|
ok(values[0].includes(
|
||||||
|
`${ known_platforms[AppConstants.platform].toUpperCase() } Value`));
|
||||||
|
|
||||||
|
L10nRegistry.sources.clear();
|
||||||
|
L10nRegistry.loadSync = originalLoadSync;
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function test_add_remove_resourceIds() {
|
||||||
|
const { L10nRegistry, FileSource } =
|
||||||
|
ChromeUtils.import("resource://gre/modules/L10nRegistry.jsm");
|
||||||
|
|
||||||
|
const fs = {
|
||||||
|
"/localization/en-US/browser/menu.ftl": "key1 = Value1",
|
||||||
|
"/localization/en-US/toolkit/menu.ftl": "key2 = Value2",
|
||||||
|
};
|
||||||
|
const originalLoadSync = L10nRegistry.loadSYnc;
|
||||||
|
const originalRequested = Services.locale.requestedLocales;
|
||||||
|
|
||||||
|
L10nRegistry.loadSync = function(url) {
|
||||||
|
return fs[url];
|
||||||
|
};
|
||||||
|
|
||||||
|
const source = new FileSource("test", ["en-US"], "/localization/{locale}");
|
||||||
|
L10nRegistry.registerSource(source);
|
||||||
|
|
||||||
|
function* generateMessagesSync(resIds) {
|
||||||
|
yield * L10nRegistry.generateBundlesSync(["en-US"], resIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
const l10n = new Localization(["/browser/menu.ftl"], true, null, generateMessagesSync);
|
||||||
|
|
||||||
|
let values = l10n.formatValuesSync([{id: "key1"}, {id: "key2"}]);
|
||||||
|
|
||||||
|
equal(values[0], "Value1");
|
||||||
|
equal(values[1], undefined);
|
||||||
|
|
||||||
|
l10n.addResourceIds(["/toolkit/menu.ftl"]);
|
||||||
|
|
||||||
|
values = l10n.formatValuesSync([{id: "key1"}, {id: "key2"}]);
|
||||||
|
|
||||||
|
equal(values[0], "Value1");
|
||||||
|
equal(values[1], "Value2");
|
||||||
|
|
||||||
|
l10n.removeResourceIds(["/browser/menu.ftl"]);
|
||||||
|
|
||||||
|
values = l10n.formatValuesSync([{id: "key1"}, {id: "key2"}]);
|
||||||
|
|
||||||
|
equal(values[0], undefined);
|
||||||
|
equal(values[1], "Value2");
|
||||||
|
|
||||||
|
L10nRegistry.sources.clear();
|
||||||
|
L10nRegistry.loadSync = originalLoadSync;
|
||||||
|
Services.locale.requestedLocales = originalRequested;
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function test_calling_sync_methods_in_async_mode_fails() {
|
||||||
|
const l10n = new Localization(["/browser/menu.ftl"], false);
|
||||||
|
|
||||||
|
Assert.throws(() => {
|
||||||
|
l10n.formatValuesSync([{ id: "key1" }, { id: "key2" }]);
|
||||||
|
}, /Can't use sync formatWithFallback when state is async./);
|
||||||
|
|
||||||
|
Assert.throws(() => {
|
||||||
|
l10n.formatValueSync("key1");
|
||||||
|
}, /Can't use sync formatWithFallback when state is async./);
|
||||||
|
|
||||||
|
Assert.throws(() => {
|
||||||
|
l10n.formatMessagesSync([{ id: "key1"}]);
|
||||||
|
}, /Can't use sync formatWithFallback when state is async./);
|
||||||
|
});
|
||||||
|
|
@ -46,7 +46,7 @@ add_task(async function test_accented_works() {
|
||||||
|
|
||||||
const l10n = new Localization([
|
const l10n = new Localization([
|
||||||
"/browser/menu.ftl",
|
"/browser/menu.ftl",
|
||||||
], generateMessages);
|
], false, generateMessages);
|
||||||
l10n.registerObservers();
|
l10n.registerObservers();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
@ -108,7 +108,7 @@ add_task(async function test_unavailable_strategy_works() {
|
||||||
|
|
||||||
const l10n = new Localization([
|
const l10n = new Localization([
|
||||||
"/browser/menu.ftl",
|
"/browser/menu.ftl",
|
||||||
], generateMessages);
|
], false, generateMessages);
|
||||||
l10n.registerObservers();
|
l10n.registerObservers();
|
||||||
|
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -4,5 +4,6 @@ head =
|
||||||
[test_l10nregistry.js]
|
[test_l10nregistry.js]
|
||||||
[test_l10nregistry_sync.js]
|
[test_l10nregistry_sync.js]
|
||||||
[test_localization.js]
|
[test_localization.js]
|
||||||
|
[test_localization_sync.js]
|
||||||
[test_messagecontext.js]
|
[test_messagecontext.js]
|
||||||
[test_pseudo.js]
|
[test_pseudo.js]
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,7 @@
|
||||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
const {Services} = ChromeUtils.import("resource://gre/modules/Services.jsm");
|
||||||
const { LocalizationSync } = ChromeUtils.import("resource://gre/modules/Localization.jsm", null);
|
const { Localization } = ChromeUtils.import("resource://gre/modules/Localization.jsm", null);
|
||||||
|
|
||||||
const mozIntlHelper =
|
const mozIntlHelper =
|
||||||
Cc["@mozilla.org/mozintlhelper;1"].getService(Ci.mozIMozIntlHelper);
|
Cc["@mozilla.org/mozintlhelper;1"].getService(Ci.mozIMozIntlHelper);
|
||||||
|
|
@ -269,7 +269,7 @@ class MozIntl {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._cache.hasOwnProperty("languageLocalization")) {
|
if (!this._cache.hasOwnProperty("languageLocalization")) {
|
||||||
const loc = new LocalizationSync(["toolkit/intl/languageNames.ftl"]);
|
const loc = new Localization(["toolkit/intl/languageNames.ftl"], true);
|
||||||
this._cache.languageLocalization = loc;
|
this._cache.languageLocalization = loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -281,7 +281,7 @@ class MozIntl {
|
||||||
}
|
}
|
||||||
let lcLangCode = langCode.toLowerCase();
|
let lcLangCode = langCode.toLowerCase();
|
||||||
if (availableLocaleDisplayNames.language.has(lcLangCode)) {
|
if (availableLocaleDisplayNames.language.has(lcLangCode)) {
|
||||||
const value = loc.formatValue(`language-name-${lcLangCode}`);
|
const value = loc.formatValueSync(`language-name-${lcLangCode}`);
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
@ -296,7 +296,7 @@ class MozIntl {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._cache.hasOwnProperty("regionLocalization")) {
|
if (!this._cache.hasOwnProperty("regionLocalization")) {
|
||||||
const loc = new LocalizationSync(["toolkit/intl/regionNames.ftl"]);
|
const loc = new Localization(["toolkit/intl/regionNames.ftl"], true);
|
||||||
this._cache.regionLocalization = loc;
|
this._cache.regionLocalization = loc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -308,7 +308,7 @@ class MozIntl {
|
||||||
}
|
}
|
||||||
let lcRegionCode = regionCode.toLowerCase();
|
let lcRegionCode = regionCode.toLowerCase();
|
||||||
if (availableLocaleDisplayNames.region.has(lcRegionCode)) {
|
if (availableLocaleDisplayNames.region.has(lcRegionCode)) {
|
||||||
const value = loc.formatValue(`region-name-${lcRegionCode}`);
|
const value = loc.formatValueSync(`region-name-${lcRegionCode}`);
|
||||||
if (value !== undefined) {
|
if (value !== undefined) {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -312,6 +312,7 @@ module.exports = {
|
||||||
"KeyEvent": false,
|
"KeyEvent": false,
|
||||||
"KeyboardEvent": false,
|
"KeyboardEvent": false,
|
||||||
"KeyframeEffect": false,
|
"KeyframeEffect": false,
|
||||||
|
"Localization": false,
|
||||||
"Location": false,
|
"Location": false,
|
||||||
"MIDIAccess": false,
|
"MIDIAccess": false,
|
||||||
"MIDIConnectionEvent": false,
|
"MIDIConnectionEvent": false,
|
||||||
|
|
|
||||||
|
|
@ -289,6 +289,7 @@ STATIC_ATOMS = [
|
||||||
Atom("datal10nargs", "data-l10n-args"),
|
Atom("datal10nargs", "data-l10n-args"),
|
||||||
Atom("datal10nattrs", "data-l10n-attrs"),
|
Atom("datal10nattrs", "data-l10n-attrs"),
|
||||||
Atom("datal10nname", "data-l10n-name"),
|
Atom("datal10nname", "data-l10n-name"),
|
||||||
|
Atom("datal10nsync", "data-l10n-sync"),
|
||||||
Atom("dataType", "data-type"),
|
Atom("dataType", "data-type"),
|
||||||
Atom("dateTime", "date-time"),
|
Atom("dateTime", "date-time"),
|
||||||
Atom("date", "date"),
|
Atom("date", "date"),
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue