diff --git a/components/menu/constants.js b/components/menu/constants.js index 995280c23..12647497b 100644 --- a/components/menu/constants.js +++ b/components/menu/constants.js @@ -1,8 +1,6 @@ import missingDocs from "./missing-docs.json" with { type: "json" }; -import tabs from "./tabs.json" with { type: "json" }; -/** @type {Readonly}*/ -export const TABS = Object.freeze(tabs); +export { TABS } from "./tabs.js"; /** * Lists untranslated menu pages per locale. diff --git a/components/menu/l10n.js b/components/menu/l10n.js new file mode 100644 index 000000000..90fad59f0 --- /dev/null +++ b/components/menu/l10n.js @@ -0,0 +1,45 @@ +import { TABS } from "./tabs.js"; + +/** + * All localizable menu strings, derived from tabs.js. + * Used by the l10n extractor only — not imported at runtime. + * + * @type {Map} + */ +export const strings = new Map( + TABS.flatMap((tab) => { + /** @type {[string, string][]} */ + const entries = []; + + if (typeof tab.buttonText === "string") { + entries.push([/** @type {string} */ (tab.buttonL10nId), tab.buttonText]); + } else { + const buttonL10nId = /** @type {{ long: string; short: string }} */ ( + tab.buttonL10nId + ); + entries.push( + [buttonL10nId.long, tab.buttonText.long], + [buttonL10nId.short, tab.buttonText.short], + ); + } + + if (!("panelTitle" in tab)) return entries; + + entries.push([tab.panelTitle.l10nId, tab.panelTitle.text]); + + for (const group of tab.panelGroups) { + if (group.titleL10nId) { + entries.push([group.titleL10nId, /** @type {string} */ (group.title)]); + } + for (const item of group.items) { + if (!("l10nId" in item)) continue; + entries.push([item.l10nId, item.text]); + if (item.labelL10nId) { + entries.push([item.labelL10nId, /** @type {string} */ (item.label)]); + } + } + } + + return entries; + }), +); diff --git a/components/menu/server.js b/components/menu/server.js index fb4cc6a05..02dc9b848 100644 --- a/components/menu/server.js +++ b/components/menu/server.js @@ -33,9 +33,9 @@ export class Menu extends ServerComponent { * Renders a link to a page. * * @param {string} slug - The link slug (the part after `/en-US/docs/`!). - * @param {string} text - The link text. + * @param {import("../../types/fluent.js").L10nTag} text - The link text. * @param {object} [options] - * @param {string} [options.label] - The title and aria-label of the link. + * @param {import("../../types/fluent.js").L10nTag} [options.label] - The title and aria-label of the link. * @param {boolean} [options.primary] - Whether this is the primary link (in the panel title). */ const link = (slug, text, { label, primary = false } = {}) => { @@ -69,27 +69,29 @@ export class Menu extends ServerComponent { class="menu__tab-link" href=${tab.href} data-glean-id=${`menu_click_link: top-level -> ${tab.href}`} - >${tab.buttonText}${context.l10n(tab.buttonL10nId)}` : html`