@fluent/dom
Advanced tools
Comparing version 0.5.0 to 0.6.0
# Changelog | ||
## @fluent/dom 0.6.0 (August 21, 2019) | ||
- Update `@fluent/dom` to work with `@fluent/bundle` 0.14 | ||
## @fluent/dom 0.5.0 (July 25, 2019) | ||
@@ -4,0 +8,0 @@ |
240
compat.js
@@ -1,2 +0,2 @@ | ||
/* @fluent/dom@0.5.0 */ | ||
/* @fluent/dom@0.6.0 */ | ||
(function (global, factory) { | ||
@@ -105,3 +105,3 @@ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('cached-iterable')) : | ||
// &, &, &. | ||
const reOverlay = /<|&#?\w+;/; | ||
var reOverlay = /<|&#?\w+;/; | ||
/** | ||
@@ -116,6 +116,6 @@ * Elements allowed in translations even if they are not present in the source | ||
const TEXT_LEVEL_ELEMENTS = { | ||
var TEXT_LEVEL_ELEMENTS = { | ||
"http://www.w3.org/1999/xhtml": ["em", "strong", "small", "s", "cite", "q", "dfn", "abbr", "data", "time", "code", "var", "samp", "kbd", "sub", "sup", "i", "b", "u", "mark", "bdi", "bdo", "span", "br", "wbr"] | ||
}; | ||
const LOCALIZABLE_ATTRIBUTES = { | ||
var LOCALIZABLE_ATTRIBUTES = { | ||
"http://www.w3.org/1999/xhtml": { | ||
@@ -159,3 +159,3 @@ global: ["title", "aria-label", "aria-valuetext"], | ||
function translateElement(element, translation) { | ||
const value = translation.value; | ||
var value = translation.value; | ||
@@ -169,3 +169,3 @@ if (typeof value === "string") { | ||
// sanitize it and replace the element's content. | ||
const templateElement = element.ownerDocument.createElementNS("http://www.w3.org/1999/xhtml", "template"); | ||
var templateElement = element.ownerDocument.createElementNS("http://www.w3.org/1999/xhtml", "template"); | ||
templateElement.innerHTML = value; | ||
@@ -199,3 +199,3 @@ overlayChildNodes(templateElement.content, element); | ||
for (var _iterator = fromFragment.childNodes[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
const childNode = _step.value; | ||
var childNode = _step.value; | ||
@@ -208,3 +208,3 @@ if (childNode.nodeType === childNode.TEXT_NODE) { | ||
if (childNode.hasAttribute("data-l10n-name")) { | ||
const sanitized = getNodeForNamedElement(toElement, childNode); | ||
var sanitized = getNodeForNamedElement(toElement, childNode); | ||
fromFragment.replaceChild(sanitized, childNode); | ||
@@ -215,4 +215,5 @@ continue; | ||
if (isElementAllowed(childNode)) { | ||
const sanitized = createSanitizedElement(childNode); | ||
fromFragment.replaceChild(sanitized, childNode); | ||
var _sanitized = createSanitizedElement(childNode); | ||
fromFragment.replaceChild(_sanitized, childNode); | ||
continue; | ||
@@ -255,3 +256,3 @@ } | ||
for (var _iterator2 = attributes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
let attr = _step2.value; | ||
var attr = _step2.value; | ||
@@ -292,7 +293,7 @@ if (attr.name === name) { | ||
function overlayAttributes(fromElement, toElement) { | ||
const explicitlyAllowed = toElement.hasAttribute("data-l10n-attrs") ? toElement.getAttribute("data-l10n-attrs").split(",").map(i => i.trim()) : null; // Remove existing localizable attributes if they | ||
var explicitlyAllowed = toElement.hasAttribute("data-l10n-attrs") ? toElement.getAttribute("data-l10n-attrs").split(",").map(i => i.trim()) : null; // Remove existing localizable attributes if they | ||
// will not be used in the new translation. | ||
for (var _i = 0, _Array$from = Array.from(toElement.attributes); _i < _Array$from.length; _i++) { | ||
const attr = _Array$from[_i]; | ||
var attr = _Array$from[_i]; | ||
@@ -313,6 +314,6 @@ if (isAttrNameLocalizable(attr.name, toElement, explicitlyAllowed) && !hasAttribute(fromElement.attributes, attr.name)) { | ||
for (var _i2 = 0, _Array$from2 = Array.from(fromElement.attributes); _i2 < _Array$from2.length; _i2++) { | ||
const attr = _Array$from2[_i2]; | ||
var _attr = _Array$from2[_i2]; | ||
if (isAttrNameLocalizable(attr.name, toElement, explicitlyAllowed) && toElement.getAttribute(attr.name) !== attr.value) { | ||
toElement.setAttribute(attr.name, attr.value); | ||
if (isAttrNameLocalizable(_attr.name, toElement, explicitlyAllowed) && toElement.getAttribute(_attr.name) !== _attr.value) { | ||
toElement.setAttribute(_attr.name, _attr.value); | ||
} | ||
@@ -336,4 +337,4 @@ } | ||
function getNodeForNamedElement(sourceElement, translatedChild) { | ||
const childName = translatedChild.getAttribute("data-l10n-name"); | ||
const sourceChild = sourceElement.querySelector("[data-l10n-name=\"".concat(childName, "\"]")); | ||
var childName = translatedChild.getAttribute("data-l10n-name"); | ||
var sourceChild = sourceElement.querySelector("[data-l10n-name=\"".concat(childName, "\"]")); | ||
@@ -360,3 +361,3 @@ if (!sourceChild) { | ||
const clone = sourceChild.cloneNode(false); | ||
var clone = sourceChild.cloneNode(false); | ||
return shallowPopulateUsing(translatedChild, clone); | ||
@@ -379,3 +380,3 @@ } | ||
// and non-localizable attributes defined by the translation. | ||
const clone = element.ownerDocument.createElement(element.localName); | ||
var clone = element.ownerDocument.createElement(element.localName); | ||
return shallowPopulateUsing(element, clone); | ||
@@ -408,3 +409,3 @@ } | ||
function isElementAllowed(element) { | ||
const allowed = TEXT_LEVEL_ELEMENTS[element.namespaceURI]; | ||
var allowed = TEXT_LEVEL_ELEMENTS[element.namespaceURI]; | ||
return allowed && allowed.includes(element.localName); | ||
@@ -431,3 +432,3 @@ } | ||
function isAttrNameLocalizable(name, element) { | ||
let explicitlyAllowed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; | ||
var explicitlyAllowed = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null; | ||
@@ -438,3 +439,3 @@ if (explicitlyAllowed && explicitlyAllowed.includes(name)) { | ||
const allowed = LOCALIZABLE_ATTRIBUTES[element.namespaceURI]; | ||
var allowed = LOCALIZABLE_ATTRIBUTES[element.namespaceURI]; | ||
@@ -445,4 +446,4 @@ if (!allowed) { | ||
const attrName = name.toLowerCase(); | ||
const elemName = element.localName; // Is it a globally safe attribute? | ||
var attrName = name.toLowerCase(); | ||
var elemName = element.localName; // Is it a globally safe attribute? | ||
@@ -465,3 +466,3 @@ if (allowed.global.includes(attrName)) { | ||
if (element.namespaceURI === "http://www.w3.org/1999/xhtml" && elemName === "input" && attrName === "value") { | ||
const type = element.type.toLowerCase(); | ||
var type = element.type.toLowerCase(); | ||
@@ -507,12 +508,13 @@ if (type === "submit" || type === "button" || type === "reset") { | ||
constructor() { | ||
let resourceIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; | ||
let generateBundles = arguments.length > 1 ? arguments[1] : undefined; | ||
var resourceIds = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : []; | ||
var generateBundles = arguments.length > 1 ? arguments[1] : undefined; | ||
this.resourceIds = resourceIds; | ||
this.generateBundles = generateBundles; | ||
this.bundles = cachedIterable.CachedAsyncIterable.from(this.generateBundles(this.resourceIds)); | ||
this.onChange(true); | ||
} | ||
addResourceIds(resourceIds) { | ||
var eager = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false; | ||
this.resourceIds.push(...resourceIds); | ||
this.onChange(); | ||
this.onChange(eager); | ||
return this.resourceIds.length; | ||
@@ -544,3 +546,4 @@ } | ||
return _asyncToGenerator(function* () { | ||
const translations = []; | ||
var translations = []; | ||
var hasAtLeastOneBundle = false; | ||
var _iteratorNormalCompletion = true; | ||
@@ -553,4 +556,5 @@ var _didIteratorError = false; | ||
for (var _iterator = _asyncIterator(_this.bundles), _step, _value; _step = yield _iterator.next(), _iteratorNormalCompletion = _step.done, _value = yield _step.value, !_iteratorNormalCompletion; _iteratorNormalCompletion = true) { | ||
const bundle = _value; | ||
const missingIds = keysFromBundle(method, bundle, keys, translations); | ||
var bundle = _value; | ||
hasAtLeastOneBundle = true; | ||
var missingIds = keysFromBundle(method, bundle, keys, translations); | ||
@@ -562,5 +566,5 @@ if (missingIds.size === 0) { | ||
if (typeof console !== "undefined") { | ||
const locale = bundle.locales[0]; | ||
const ids = Array.from(missingIds).join(", "); | ||
console.warn("Missing translations in ".concat(locale, ": ").concat(ids)); | ||
var locale = bundle.locales[0]; | ||
var ids = Array.from(missingIds).join(", "); | ||
console.warn("[fluent] Missing translations in ".concat(locale, ": ").concat(ids)); | ||
} | ||
@@ -583,2 +587,7 @@ } | ||
if (!hasAtLeastOneBundle && typeof console !== "undefined") { | ||
// eslint-disable-next-line max-len | ||
console.warn("[fluent] Request for keys failed because no resource bundles got generated.\n keys: ".concat(JSON.stringify(keys), ".\n resourceIds: ").concat(JSON.stringify(_this.resourceIds), ".")); | ||
} | ||
return translations; | ||
@@ -590,5 +599,5 @@ })(); | ||
* | ||
* The fallback logic is the same as in `formatValues` but the argument type | ||
* is stricter (an array of arrays) and it returns {value, attributes} | ||
* objects which are suitable for the translation of DOM elements. | ||
* The fallback logic is the same as in `formatValues` but it returns {value, | ||
* attributes} objects which are suitable for the translation of DOM | ||
* elements. | ||
* | ||
@@ -622,4 +631,4 @@ * docL10n.formatMessages([ | ||
* | ||
* A generalized version of `DOMLocalization.formatValue`. Keys can | ||
* either be simple string identifiers or `[id, args]` arrays. | ||
* A generalized version of `DOMLocalization.formatValue`. Keys must | ||
* be `{id, args}` objects. | ||
* | ||
@@ -672,8 +681,8 @@ * docL10n.formatValues([ | ||
return _asyncToGenerator(function* () { | ||
const _ref = yield _this2.formatValues([{ | ||
var _ref = yield _this2.formatValues([{ | ||
id, | ||
args | ||
}]), | ||
_ref2 = _slicedToArray(_ref, 1), | ||
val = _ref2[0]; | ||
_ref2 = _slicedToArray(_ref, 1), | ||
val = _ref2[0]; | ||
@@ -694,4 +703,8 @@ return val; | ||
onChange() { | ||
var eager = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; | ||
this.bundles = cachedIterable.CachedAsyncIterable.from(this.generateBundles(this.resourceIds)); | ||
this.bundles.touchNext(2); | ||
if (eager) { | ||
this.bundles.touchNext(2); | ||
} | ||
} | ||
@@ -701,3 +714,3 @@ | ||
/** | ||
* Format the value of a message into a string. | ||
* Format the value of a message into a string or `null`. | ||
* | ||
@@ -707,18 +720,18 @@ * This function is passed as a method to `keysFromBundle` and resolve | ||
* | ||
* If the function fails to retrieve the entity, it will return an ID of it. | ||
* If formatting fails, it will return a partially resolved entity. | ||
* If the message doesn't have a value, return `null`. | ||
* | ||
* In both cases, an error is being added to the errors array. | ||
* | ||
* @param {FluentBundle} bundle | ||
* @param {Array<Error>} errors | ||
* @param {string} id | ||
* @param {Object} args | ||
* @returns {string} | ||
* @param {Array<Error>} errors | ||
* @param {Object} message | ||
* @param {Object} args | ||
* @returns {string|null} | ||
* @private | ||
*/ | ||
function valueFromBundle(bundle, errors, id, args) { | ||
const msg = bundle.getMessage(id); | ||
return bundle.format(msg, args, errors); | ||
function valueFromBundle(bundle, errors, message, args) { | ||
if (message.value) { | ||
return bundle.formatPattern(message.value, args, errors); | ||
} | ||
return null; | ||
} | ||
@@ -734,12 +747,6 @@ /** | ||
* | ||
* If the function fails to retrieve the entity, the value is set to the ID of | ||
* an entity, and attributes to `null`. If formatting fails, it will return | ||
* a partially resolved value and attributes. | ||
* | ||
* In both cases, an error is being added to the errors array. | ||
* | ||
* @param {FluentBundle} bundle | ||
* @param {Array<Error>} errors | ||
* @param {String} id | ||
* @param {Object} args | ||
* @param {Array<Error>} errors | ||
* @param {Object} message | ||
* @param {Object} args | ||
* @returns {Object} | ||
@@ -750,25 +757,45 @@ * @private | ||
function messageFromBundle(bundle, errors, id, args) { | ||
const msg = bundle.getMessage(id); | ||
const formatted = { | ||
value: bundle.format(msg, args, errors), | ||
function messageFromBundle(bundle, errors, message, args) { | ||
var formatted = { | ||
value: null, | ||
attributes: null | ||
}; | ||
if (msg.attrs) { | ||
formatted.attributes = []; | ||
if (message.value) { | ||
formatted.value = bundle.formatPattern(message.value, args, errors); | ||
} | ||
for (var _i = 0, _Object$entries = Object.entries(msg.attrs); _i < _Object$entries.length; _i++) { | ||
const _Object$entries$_i = _slicedToArray(_Object$entries[_i], 2), | ||
name = _Object$entries$_i[0], | ||
attr = _Object$entries$_i[1]; | ||
var attrNames = Object.keys(message.attributes); | ||
const value = bundle.format(attr, args, errors); | ||
if (attrNames.length > 0) { | ||
formatted.attributes = new Array(attrNames.length); | ||
var _iteratorNormalCompletion2 = true; | ||
var _didIteratorError2 = false; | ||
var _iteratorError2 = undefined; | ||
if (value !== null) { | ||
formatted.attributes.push({ | ||
try { | ||
for (var _iterator2 = attrNames.entries()[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
var _step2$value = _slicedToArray(_step2.value, 2), | ||
i = _step2$value[0], | ||
name = _step2$value[1]; | ||
var value = bundle.formatPattern(message.attributes[name], args, errors); | ||
formatted.attributes[i] = { | ||
name, | ||
value | ||
}); | ||
}; | ||
} | ||
} catch (err) { | ||
_didIteratorError2 = true; | ||
_iteratorError2 = err; | ||
} finally { | ||
try { | ||
if (!_iteratorNormalCompletion2 && _iterator2.return != null) { | ||
_iterator2.return(); | ||
} | ||
} finally { | ||
if (_didIteratorError2) { | ||
throw _iteratorError2; | ||
} | ||
} | ||
} | ||
@@ -814,6 +841,6 @@ } | ||
function keysFromBundle(method, bundle, keys, translations) { | ||
const messageErrors = []; | ||
const missingIds = new Set(); | ||
var messageErrors = []; | ||
var missingIds = new Set(); | ||
keys.forEach((_ref3, i) => { | ||
let id = _ref3.id, | ||
var id = _ref3.id, | ||
args = _ref3.args; | ||
@@ -825,5 +852,14 @@ | ||
if (bundle.hasMessage(id)) { | ||
var message = bundle.getMessage(id); | ||
if (message) { | ||
messageErrors.length = 0; | ||
translations[i] = method(bundle, messageErrors, id, args); // XXX: Report resolver errors | ||
translations[i] = method(bundle, messageErrors, message, args); | ||
if (messageErrors.length > 0 && typeof console !== "undefined") { | ||
var locale = bundle.locales[0]; | ||
var errors = messageErrors.join(", "); // eslint-disable-next-line max-len | ||
console.warn("[fluent][resolver] errors in ".concat(locale, "/").concat(id, ": ").concat(errors, ".")); | ||
} | ||
} else { | ||
@@ -836,5 +872,5 @@ missingIds.add(id); | ||
const L10NID_ATTR_NAME = "data-l10n-id"; | ||
const L10NARGS_ATTR_NAME = "data-l10n-args"; | ||
const L10N_ELEMENT_QUERY = "[".concat(L10NID_ATTR_NAME, "]"); | ||
var L10NID_ATTR_NAME = "data-l10n-id"; | ||
var L10NARGS_ATTR_NAME = "data-l10n-args"; | ||
var L10N_ELEMENT_QUERY = "[".concat(L10NID_ATTR_NAME, "]"); | ||
/** | ||
@@ -876,4 +912,8 @@ * The `DOMLocalization` class is responsible for fetching resources and | ||
onChange() { | ||
super.onChange(); | ||
this.translateRoots(); | ||
var eager = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : false; | ||
super.onChange(eager); | ||
if (this.roots) { | ||
this.translateRoots(); | ||
} | ||
} | ||
@@ -966,3 +1006,3 @@ /** | ||
for (var _iterator = this.roots[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { | ||
const root = _step.value; | ||
var root = _step.value; | ||
@@ -1039,3 +1079,3 @@ if (root === newRoot || root.contains(newRoot) || newRoot.contains(root)) { | ||
translateRoots() { | ||
const roots = Array.from(this.roots); | ||
var roots = Array.from(this.roots); | ||
return Promise.all(roots.map(root => this.translateFragment(root))); | ||
@@ -1076,3 +1116,3 @@ } | ||
for (var _iterator2 = this.roots[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) { | ||
const root = _step2.value; | ||
var root = _step2.value; | ||
this.mutationObserver.observe(root, this.observerConfig); | ||
@@ -1109,3 +1149,3 @@ } | ||
for (var _iterator3 = mutations[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) { | ||
const mutation = _step3.value; | ||
var mutation = _step3.value; | ||
@@ -1127,3 +1167,3 @@ switch (mutation.type) { | ||
for (var _iterator4 = mutation.addedNodes[Symbol.iterator](), _step4; !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done); _iteratorNormalCompletion4 = true) { | ||
const addedNode = _step4.value; | ||
var addedNode = _step4.value; | ||
@@ -1138,3 +1178,3 @@ if (addedNode.nodeType === addedNode.ELEMENT_NODE) { | ||
for (var _iterator5 = this.getTranslatables(addedNode)[Symbol.iterator](), _step5; !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done); _iteratorNormalCompletion5 = true) { | ||
const element = _step5.value; | ||
var element = _step5.value; | ||
this.pendingElements.add(element); | ||
@@ -1247,4 +1287,4 @@ } | ||
const keys = elements.map(_this.getKeysForElement); | ||
const translations = yield _this.formatMessages(keys); | ||
var keys = elements.map(_this.getKeysForElement); | ||
var translations = yield _this.formatMessages(keys); | ||
return _this.applyTranslations(elements, translations); | ||
@@ -1265,3 +1305,3 @@ })(); | ||
for (let i = 0; i < elements.length; i++) { | ||
for (var i = 0; i < elements.length; i++) { | ||
if (translations[i] !== undefined) { | ||
@@ -1284,3 +1324,3 @@ translateElement(elements[i], translations[i]); | ||
getTranslatables(element) { | ||
const nodes = Array.from(element.querySelectorAll(L10N_ELEMENT_QUERY)); | ||
var nodes = Array.from(element.querySelectorAll(L10N_ELEMENT_QUERY)); | ||
@@ -1287,0 +1327,0 @@ if (typeof element.hasAttribute === "function" && element.hasAttribute(L10NID_ATTR_NAME)) { |
115
index.js
@@ -1,2 +0,2 @@ | ||
/* @fluent/dom@0.5.0 */ | ||
/* @fluent/dom@0.6.0 */ | ||
(function (global, factory) { | ||
@@ -374,9 +374,8 @@ typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('cached-iterable')) : | ||
this.generateBundles = generateBundles; | ||
this.bundles = cachedIterable.CachedAsyncIterable.from( | ||
this.generateBundles(this.resourceIds)); | ||
this.onChange(true); | ||
} | ||
addResourceIds(resourceIds) { | ||
addResourceIds(resourceIds, eager = false) { | ||
this.resourceIds.push(...resourceIds); | ||
this.onChange(); | ||
this.onChange(eager); | ||
return this.resourceIds.length; | ||
@@ -405,4 +404,6 @@ } | ||
const translations = []; | ||
let hasAtLeastOneBundle = false; | ||
for await (const bundle of this.bundles) { | ||
hasAtLeastOneBundle = true; | ||
const missingIds = keysFromBundle(method, bundle, keys, translations); | ||
@@ -417,6 +418,13 @@ | ||
const ids = Array.from(missingIds).join(", "); | ||
console.warn(`Missing translations in ${locale}: ${ids}`); | ||
console.warn(`[fluent] Missing translations in ${locale}: ${ids}`); | ||
} | ||
} | ||
if (!hasAtLeastOneBundle && typeof console !== "undefined") { | ||
// eslint-disable-next-line max-len | ||
console.warn(`[fluent] Request for keys failed because no resource bundles got generated. | ||
keys: ${JSON.stringify(keys)}. | ||
resourceIds: ${JSON.stringify(this.resourceIds)}.`); | ||
} | ||
return translations; | ||
@@ -428,5 +436,5 @@ } | ||
* | ||
* The fallback logic is the same as in `formatValues` but the argument type | ||
* is stricter (an array of arrays) and it returns {value, attributes} | ||
* objects which are suitable for the translation of DOM elements. | ||
* The fallback logic is the same as in `formatValues` but it returns {value, | ||
* attributes} objects which are suitable for the translation of DOM | ||
* elements. | ||
* | ||
@@ -459,4 +467,4 @@ * docL10n.formatMessages([ | ||
* | ||
* A generalized version of `DOMLocalization.formatValue`. Keys can | ||
* either be simple string identifiers or `[id, args]` arrays. | ||
* A generalized version of `DOMLocalization.formatValue`. Keys must | ||
* be `{id, args}` objects. | ||
* | ||
@@ -515,6 +523,8 @@ * docL10n.formatValues([ | ||
*/ | ||
onChange() { | ||
onChange(eager = false) { | ||
this.bundles = cachedIterable.CachedAsyncIterable.from( | ||
this.generateBundles(this.resourceIds)); | ||
this.bundles.touchNext(2); | ||
if (eager) { | ||
this.bundles.touchNext(2); | ||
} | ||
} | ||
@@ -524,3 +534,3 @@ } | ||
/** | ||
* Format the value of a message into a string. | ||
* Format the value of a message into a string or `null`. | ||
* | ||
@@ -530,17 +540,17 @@ * This function is passed as a method to `keysFromBundle` and resolve | ||
* | ||
* If the function fails to retrieve the entity, it will return an ID of it. | ||
* If formatting fails, it will return a partially resolved entity. | ||
* If the message doesn't have a value, return `null`. | ||
* | ||
* In both cases, an error is being added to the errors array. | ||
* | ||
* @param {FluentBundle} bundle | ||
* @param {Array<Error>} errors | ||
* @param {string} id | ||
* @param {Object} args | ||
* @returns {string} | ||
* @param {Array<Error>} errors | ||
* @param {Object} message | ||
* @param {Object} args | ||
* @returns {string|null} | ||
* @private | ||
*/ | ||
function valueFromBundle(bundle, errors, id, args) { | ||
const msg = bundle.getMessage(id); | ||
return bundle.format(msg, args, errors); | ||
function valueFromBundle(bundle, errors, message, args) { | ||
if (message.value) { | ||
return bundle.formatPattern(message.value, args, errors); | ||
} | ||
return null; | ||
} | ||
@@ -557,30 +567,25 @@ | ||
* | ||
* If the function fails to retrieve the entity, the value is set to the ID of | ||
* an entity, and attributes to `null`. If formatting fails, it will return | ||
* a partially resolved value and attributes. | ||
* | ||
* In both cases, an error is being added to the errors array. | ||
* | ||
* @param {FluentBundle} bundle | ||
* @param {Array<Error>} errors | ||
* @param {String} id | ||
* @param {Object} args | ||
* @param {Array<Error>} errors | ||
* @param {Object} message | ||
* @param {Object} args | ||
* @returns {Object} | ||
* @private | ||
*/ | ||
function messageFromBundle(bundle, errors, id, args) { | ||
const msg = bundle.getMessage(id); | ||
function messageFromBundle(bundle, errors, message, args) { | ||
const formatted = { | ||
value: bundle.format(msg, args, errors), | ||
value: null, | ||
attributes: null, | ||
}; | ||
if (msg.attrs) { | ||
formatted.attributes = []; | ||
for (const [name, attr] of Object.entries(msg.attrs)) { | ||
const value = bundle.format(attr, args, errors); | ||
if (value !== null) { | ||
formatted.attributes.push({name, value}); | ||
} | ||
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}; | ||
} | ||
@@ -633,6 +638,12 @@ } | ||
if (bundle.hasMessage(id)) { | ||
let message = bundle.getMessage(id); | ||
if (message) { | ||
messageErrors.length = 0; | ||
translations[i] = method(bundle, messageErrors, id, args); | ||
// XXX: Report resolver errors | ||
translations[i] = method(bundle, messageErrors, message, args); | ||
if (messageErrors.length > 0 && typeof console !== "undefined") { | ||
const locale = bundle.locales[0]; | ||
const errors = messageErrors.join(", "); | ||
// eslint-disable-next-line max-len | ||
console.warn(`[fluent][resolver] errors in ${locale}/${id}: ${errors}.`); | ||
} | ||
} else { | ||
@@ -687,5 +698,7 @@ missingIds.add(id); | ||
onChange() { | ||
super.onChange(); | ||
this.translateRoots(); | ||
onChange(eager = false) { | ||
super.onChange(eager); | ||
if (this.roots) { | ||
this.translateRoots(); | ||
} | ||
} | ||
@@ -692,0 +705,0 @@ |
{ | ||
"name": "@fluent/dom", | ||
"version": "0.5.0", | ||
"version": "0.6.0", | ||
"description": "Fluent bindings for DOM", | ||
@@ -5,0 +5,0 @@ "repository": { |
@@ -73,3 +73,3 @@ # @fluent/dom | ||
[projectfluent.org]: http://projectfluent.org | ||
[FTL]: http://projectfluent.org/fluent/guide/ | ||
[projectfluent.org]: https://projectfluent.org | ||
[FTL]: https://projectfluent.org/fluent/guide/ |
@@ -45,5 +45,7 @@ import translateElement from "./overlay"; | ||
onChange() { | ||
super.onChange(); | ||
this.translateRoots(); | ||
onChange(eager = false) { | ||
super.onChange(eager); | ||
if (this.roots) { | ||
this.translateRoots(); | ||
} | ||
} | ||
@@ -50,0 +52,0 @@ |
@@ -23,9 +23,8 @@ /* eslint no-console: ["error", { allow: ["warn", "error"] }] */ | ||
this.generateBundles = generateBundles; | ||
this.bundles = CachedAsyncIterable.from( | ||
this.generateBundles(this.resourceIds)); | ||
this.onChange(true); | ||
} | ||
addResourceIds(resourceIds) { | ||
addResourceIds(resourceIds, eager = false) { | ||
this.resourceIds.push(...resourceIds); | ||
this.onChange(); | ||
this.onChange(eager); | ||
return this.resourceIds.length; | ||
@@ -54,4 +53,6 @@ } | ||
const translations = []; | ||
let hasAtLeastOneBundle = false; | ||
for await (const bundle of this.bundles) { | ||
hasAtLeastOneBundle = true; | ||
const missingIds = keysFromBundle(method, bundle, keys, translations); | ||
@@ -66,6 +67,13 @@ | ||
const ids = Array.from(missingIds).join(", "); | ||
console.warn(`Missing translations in ${locale}: ${ids}`); | ||
console.warn(`[fluent] Missing translations in ${locale}: ${ids}`); | ||
} | ||
} | ||
if (!hasAtLeastOneBundle && typeof console !== "undefined") { | ||
// eslint-disable-next-line max-len | ||
console.warn(`[fluent] Request for keys failed because no resource bundles got generated. | ||
keys: ${JSON.stringify(keys)}. | ||
resourceIds: ${JSON.stringify(this.resourceIds)}.`); | ||
} | ||
return translations; | ||
@@ -77,5 +85,5 @@ } | ||
* | ||
* The fallback logic is the same as in `formatValues` but the argument type | ||
* is stricter (an array of arrays) and it returns {value, attributes} | ||
* objects which are suitable for the translation of DOM elements. | ||
* The fallback logic is the same as in `formatValues` but it returns {value, | ||
* attributes} objects which are suitable for the translation of DOM | ||
* elements. | ||
* | ||
@@ -108,4 +116,4 @@ * docL10n.formatMessages([ | ||
* | ||
* A generalized version of `DOMLocalization.formatValue`. Keys can | ||
* either be simple string identifiers or `[id, args]` arrays. | ||
* A generalized version of `DOMLocalization.formatValue`. Keys must | ||
* be `{id, args}` objects. | ||
* | ||
@@ -164,6 +172,8 @@ * docL10n.formatValues([ | ||
*/ | ||
onChange() { | ||
onChange(eager = false) { | ||
this.bundles = CachedAsyncIterable.from( | ||
this.generateBundles(this.resourceIds)); | ||
this.bundles.touchNext(2); | ||
if (eager) { | ||
this.bundles.touchNext(2); | ||
} | ||
} | ||
@@ -173,3 +183,3 @@ } | ||
/** | ||
* Format the value of a message into a string. | ||
* Format the value of a message into a string or `null`. | ||
* | ||
@@ -179,17 +189,17 @@ * This function is passed as a method to `keysFromBundle` and resolve | ||
* | ||
* If the function fails to retrieve the entity, it will return an ID of it. | ||
* If formatting fails, it will return a partially resolved entity. | ||
* If the message doesn't have a value, return `null`. | ||
* | ||
* In both cases, an error is being added to the errors array. | ||
* | ||
* @param {FluentBundle} bundle | ||
* @param {Array<Error>} errors | ||
* @param {string} id | ||
* @param {Object} args | ||
* @returns {string} | ||
* @param {Array<Error>} errors | ||
* @param {Object} message | ||
* @param {Object} args | ||
* @returns {string|null} | ||
* @private | ||
*/ | ||
function valueFromBundle(bundle, errors, id, args) { | ||
const msg = bundle.getMessage(id); | ||
return bundle.format(msg, args, errors); | ||
function valueFromBundle(bundle, errors, message, args) { | ||
if (message.value) { | ||
return bundle.formatPattern(message.value, args, errors); | ||
} | ||
return null; | ||
} | ||
@@ -206,30 +216,25 @@ | ||
* | ||
* If the function fails to retrieve the entity, the value is set to the ID of | ||
* an entity, and attributes to `null`. If formatting fails, it will return | ||
* a partially resolved value and attributes. | ||
* | ||
* In both cases, an error is being added to the errors array. | ||
* | ||
* @param {FluentBundle} bundle | ||
* @param {Array<Error>} errors | ||
* @param {String} id | ||
* @param {Object} args | ||
* @param {Array<Error>} errors | ||
* @param {Object} message | ||
* @param {Object} args | ||
* @returns {Object} | ||
* @private | ||
*/ | ||
function messageFromBundle(bundle, errors, id, args) { | ||
const msg = bundle.getMessage(id); | ||
function messageFromBundle(bundle, errors, message, args) { | ||
const formatted = { | ||
value: bundle.format(msg, args, errors), | ||
value: null, | ||
attributes: null, | ||
}; | ||
if (msg.attrs) { | ||
formatted.attributes = []; | ||
for (const [name, attr] of Object.entries(msg.attrs)) { | ||
const value = bundle.format(attr, args, errors); | ||
if (value !== null) { | ||
formatted.attributes.push({name, value}); | ||
} | ||
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}; | ||
} | ||
@@ -282,6 +287,12 @@ } | ||
if (bundle.hasMessage(id)) { | ||
let message = bundle.getMessage(id); | ||
if (message) { | ||
messageErrors.length = 0; | ||
translations[i] = method(bundle, messageErrors, id, args); | ||
// XXX: Report resolver errors | ||
translations[i] = method(bundle, messageErrors, message, args); | ||
if (messageErrors.length > 0 && typeof console !== "undefined") { | ||
const locale = bundle.locales[0]; | ||
const errors = messageErrors.join(", "); | ||
// eslint-disable-next-line max-len | ||
console.warn(`[fluent][resolver] errors in ${locale}/${id}: ${errors}.`); | ||
} | ||
} else { | ||
@@ -288,0 +299,0 @@ missingIds.add(id); |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
110604
2959
1