diff --git a/fluent-dom/package.json b/fluent-dom/package.json index 315a853a..e1132ce6 100644 --- a/fluent-dom/package.json +++ b/fluent-dom/package.json @@ -43,6 +43,9 @@ "devDependencies": { "@fluent/bundle": "^0.19.0" }, + "peerDependencies": { + "@fluent/bundle": ">=0.16.0" + }, "dependencies": { "cached-iterable": "^0.3" } diff --git a/fluent-dom/src/dom_localization.js b/fluent-dom/src/dom_localization.js index dc7e410e..241486df 100644 --- a/fluent-dom/src/dom_localization.js +++ b/fluent-dom/src/dom_localization.js @@ -16,9 +16,9 @@ const L10N_ELEMENT_QUERY = `[${L10NID_ATTR_NAME}]`; */ export default class DOMLocalization extends Localization { /** - * @param {Array} resourceIds - List of resource IDs - * @param {Function} generateBundles - Function that returns a - * generator over FluentBundles + * @param {string[]} resourceIds - List of resource IDs + * @param {import("./localization.js").GenerateBundles} generateBundles + * - Function that returns an iterable over FluentBundles * @returns {DOMLocalization} */ constructor(resourceIds, generateBundles) { @@ -42,6 +42,12 @@ export default class DOMLocalization extends Localization { }; } + /** + * Regenerate the bundles and retranslate all connected roots. + * + * @param {boolean} [eager] - Whether to start fetching the first bundles + * right away + */ onChange(eager = false) { super.onChange(eager); if (this.roots) { diff --git a/fluent-dom/src/index.js b/fluent-dom/src/index.js index d5575ad4..14d9607a 100644 --- a/fluent-dom/src/index.js +++ b/fluent-dom/src/index.js @@ -1,2 +1,6 @@ export { default as DOMLocalization } from "./dom_localization.js"; export { default as Localization } from "./localization.js"; + +/** @typedef {import("./localization.js").GenerateBundles} GenerateBundles */ +/** @typedef {import("./localization.js").L10nKey} L10nKey */ +/** @typedef {import("./localization.js").L10nMessage} L10nMessage */ diff --git a/fluent-dom/src/localization.js b/fluent-dom/src/localization.js index 8977165f..75de5016 100644 --- a/fluent-dom/src/localization.js +++ b/fluent-dom/src/localization.js @@ -3,6 +3,29 @@ import { CachedAsyncIterable } from "cached-iterable"; +/** @typedef {import("@fluent/bundle").FluentBundle} FluentBundle */ +/** @typedef {import("@fluent/bundle").FluentVariable} FluentVariable */ + +/** + * A translation key: a message identifier and optional variables. + * + * @typedef {{ id: string, args?: Record }} L10nKey + */ + +/** + * A formatted message: its value and the list of its attributes, either of + * which may be `null` if the message doesn't define them. + * + * @typedef {{ value: string | null, attributes: Array<{ name: string, value: string }> | null }} L10nMessage + */ + +/** + * A function returning an iterable (sync or async) of `FluentBundle`s to use + * for the given resource IDs, ordered from the most to the least preferred. + * + * @typedef {(resourceIds: string[]) => Iterable | AsyncIterable} GenerateBundles + */ + /** * The `Localization` class is a central high-level API for vanilla * JavaScript use of Fluent. @@ -11,9 +34,9 @@ import { CachedAsyncIterable } from "cached-iterable"; */ export default class Localization { /** - * @param {Array} resourceIds - List of resource IDs - * @param {Function} generateBundles - Function that returns a - * generator over FluentBundles + * @param {string[]} resourceIds - List of resource IDs + * @param {GenerateBundles} generateBundles - Function that returns an + * iterable over FluentBundles * * @returns {Localization} */ @@ -23,12 +46,26 @@ export default class Localization { this.onChange(true); } + /** + * Add resource IDs and regenerate the bundles. + * + * @param {string[]} resourceIds - Resource IDs to add + * @param {boolean} [eager] - Whether to start fetching the first + * bundles right away + * @returns {number} The new number of resource IDs + */ addResourceIds(resourceIds, eager = false) { this.resourceIds.push(...resourceIds); this.onChange(eager); return this.resourceIds.length; } + /** + * Remove resource IDs and regenerate the bundles. + * + * @param {string[]} resourceIds - Resource IDs to remove + * @returns {number} The new number of resource IDs + */ removeResourceIds(resourceIds) { this.resourceIds = this.resourceIds.filter(r => !resourceIds.includes(r)); this.onChange(); @@ -100,9 +137,9 @@ export default class Localization { * // ] * ``` * - * @param {Array} keys - * @returns {Promise>} - * @private + * @param {L10nKey[]} keys + * @returns {Promise>} + * @protected */ formatMessages(keys) { return this.formatWithFallback(keys, messageFromBundle); @@ -127,7 +164,7 @@ export default class Localization { * // ['Hello, Mary!', 'Hello, John!', 'Welcome!'] * ``` * - * @param {Array} keys + * @param {L10nKey[]} keys * @returns {Promise>} */ formatValues(keys) { @@ -171,6 +208,9 @@ export default class Localization { /** * This method should be called when there's a reason to believe * that language negotiation or available resources changed. + * + * @param {boolean} [eager] - Whether to start fetching the first bundles + * right away */ onChange(eager = false) { this.bundles = CachedAsyncIterable.from( diff --git a/package-lock.json b/package-lock.json index b640ec3d..215bf536 100644 --- a/package-lock.json +++ b/package-lock.json @@ -66,6 +66,9 @@ }, "engines": { "node": "^20.19 || ^22.12 || >=24" + }, + "peerDependencies": { + "@fluent/bundle": ">=0.16.0" } }, "fluent-gecko": {