[Editor] Utilize Fluent "better" when localizing the AltText

Currently we manually localize and update the DOM-elements of the AltText-button, and it seems nicer to utilize Fluent "properly" for that task.
This can be achieved by introducing an explicit `span`-element on the AltText-button (similar to e.g. the regular toolbar-buttons), and adding a few more l10n-strings, since that allows just setting the `data-l10n-id`-attribute on all the relevant DOM-elements.

Finally, note how we no longer need to localize any strings eagerly when initializing the various editors.
This commit is contained in:
Jonas Jenwald 2024-10-28 14:34:12 +01:00
parent 9108848743
commit ee812b5df2
4 changed files with 70 additions and 58 deletions

View File

@ -360,9 +360,12 @@ pdfjs-ink-canvas =
## Alt-text dialog ## Alt-text dialog
# Alternative text (alt text) helps when people can't see the image. # Alternative text (alt text) helps when people can't see the image.
pdfjs-editor-alt-text-button =
.aria-label = Alt text
pdfjs-editor-alt-text-button-label = Alt text pdfjs-editor-alt-text-button-label = Alt text
pdfjs-editor-alt-text-edit-button-label = Edit alt text pdfjs-editor-alt-text-edit-button =
.aria-label = Edit alt text
pdfjs-editor-alt-text-dialog-label = Choose an option pdfjs-editor-alt-text-dialog-label = Choose an option
pdfjs-editor-alt-text-dialog-description = Alt text (alternative text) helps when people cant see the image or when it doesnt load. pdfjs-editor-alt-text-dialog-description = Alt text (alternative text) helps when people cant see the image or when it doesnt load.
pdfjs-editor-alt-text-add-description-label = Add a description pdfjs-editor-alt-text-add-description-label = Add a description
@ -457,12 +460,18 @@ pdfjs-editor-new-alt-text-ai-model-downloading-progress = Downloading alt text A
.aria-valuetext = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB) .aria-valuetext = Downloading alt text AI model ({ $downloadedSize } of { $totalSize } MB)
# This is a button that users can click to edit the alt text they have already added. # This is a button that users can click to edit the alt text they have already added.
pdfjs-editor-new-alt-text-added-button =
.aria-label = Alt text added
pdfjs-editor-new-alt-text-added-button-label = Alt text added pdfjs-editor-new-alt-text-added-button-label = Alt text added
# This is a button that users can click to open the alt text editor and add alt text when it is not present. # This is a button that users can click to open the alt text editor and add alt text when it is not present.
pdfjs-editor-new-alt-text-missing-button =
.aria-label = Missing alt text
pdfjs-editor-new-alt-text-missing-button-label = Missing alt text pdfjs-editor-new-alt-text-missing-button-label = Missing alt text
# This is a button that opens up the alt text modal where users should review the alt text that was automatically generated. # This is a button that opens up the alt text modal where users should review the alt text that was automatically generated.
pdfjs-editor-new-alt-text-to-review-button =
.aria-label = Review alt text
pdfjs-editor-new-alt-text-to-review-button-label = Review alt text pdfjs-editor-new-alt-text-to-review-button-label = Review alt text
# "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear. # "Created automatically" is a prefix that will be added to the beginning of any alt text that has been automatically generated. After the colon, the user will see/hear the actual alt text description. If the alt text has been edited by a human, this prefix will not appear.

View File

@ -22,6 +22,8 @@ class AltText {
#altTextButton = null; #altTextButton = null;
#altTextButtonLabel = null;
#altTextTooltip = null; #altTextTooltip = null;
#altTextTooltipTimeout = null; #altTextTooltipTimeout = null;
@ -40,38 +42,46 @@ class AltText {
static #l10nNewButton = null; static #l10nNewButton = null;
static _l10nPromise = null; static _l10n = null;
constructor(editor) { constructor(editor) {
this.#editor = editor; this.#editor = editor;
this.#useNewAltTextFlow = editor._uiManager.useNewAltTextFlow; this.#useNewAltTextFlow = editor._uiManager.useNewAltTextFlow;
AltText.#l10nNewButton ||= Object.freeze({ AltText.#l10nNewButton ||= Object.freeze({
added: "pdfjs-editor-new-alt-text-added-button-label", added: "pdfjs-editor-new-alt-text-added-button",
missing: "pdfjs-editor-new-alt-text-missing-button-label", "added-label": "pdfjs-editor-new-alt-text-added-button-label",
review: "pdfjs-editor-new-alt-text-to-review-button-label", missing: "pdfjs-editor-new-alt-text-missing-button",
"missing-label": "pdfjs-editor-new-alt-text-missing-button-label",
review: "pdfjs-editor-new-alt-text-to-review-button",
"review-label": "pdfjs-editor-new-alt-text-to-review-button-label",
}); });
} }
static initialize(l10nPromise) { static initialize(l10n) {
AltText._l10nPromise ||= l10nPromise; AltText._l10n ??= l10n;
} }
async render() { async render() {
const altText = (this.#altTextButton = document.createElement("button")); const altText = (this.#altTextButton = document.createElement("button"));
altText.className = "altText"; altText.className = "altText";
let msg; altText.tabIndex = "0";
const label = (this.#altTextButtonLabel = document.createElement("span"));
altText.append(label);
if (this.#useNewAltTextFlow) { if (this.#useNewAltTextFlow) {
altText.classList.add("new"); altText.classList.add("new");
msg = await AltText._l10nPromise.get(AltText.#l10nNewButton.missing); altText.setAttribute("data-l10n-id", AltText.#l10nNewButton.missing);
} else { label.setAttribute(
msg = await AltText._l10nPromise.get( "data-l10n-id",
"pdfjs-editor-alt-text-button-label" AltText.#l10nNewButton["missing-label"]
); );
} else {
altText.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-button");
label.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-button-label");
} }
altText.textContent = msg;
altText.setAttribute("aria-label", msg);
altText.tabIndex = "0";
const signal = this.#editor._uiManager._signal; const signal = this.#editor._uiManager._signal;
altText.addEventListener("contextmenu", noContextMenu, { signal }); altText.addEventListener("contextmenu", noContextMenu, { signal });
altText.addEventListener("pointerdown", event => event.stopPropagation(), { altText.addEventListener("pointerdown", event => event.stopPropagation(), {
@ -144,9 +154,10 @@ class AltText {
return; return;
} }
this.#guessedText = guessedText; this.#guessedText = guessedText;
this.#textWithDisclaimer = await AltText._l10nPromise.get( this.#textWithDisclaimer = await AltText._l10n.get(
"pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer" "pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer",
)({ generatedAltText: guessedText }); { generatedAltText: guessedText }
);
this.#setState(); this.#setState();
} }
@ -229,6 +240,7 @@ class AltText {
destroy() { destroy() {
this.#altTextButton?.remove(); this.#altTextButton?.remove();
this.#altTextButton = null; this.#altTextButton = null;
this.#altTextButtonLabel = null;
this.#altTextTooltip = null; this.#altTextTooltip = null;
this.#badge?.remove(); this.#badge?.remove();
this.#badge = null; this.#badge = null;
@ -242,19 +254,12 @@ class AltText {
if (this.#useNewAltTextFlow) { if (this.#useNewAltTextFlow) {
button.classList.toggle("done", !!this.#altText); button.classList.toggle("done", !!this.#altText);
AltText._l10nPromise button.setAttribute("data-l10n-id", AltText.#l10nNewButton[this.#label]);
.get(AltText.#l10nNewButton[this.#label])
.then(msg => { this.#altTextButtonLabel?.setAttribute(
button.setAttribute("aria-label", msg); "data-l10n-id",
// We can't just use button.textContent here, because it would remove AltText.#l10nNewButton[`${this.#label}-label`]
// the existing tooltip element. );
for (const child of button.childNodes) {
if (child.nodeType === Node.TEXT_NODE) {
child.textContent = msg;
break;
}
}
});
if (!this.#altText) { if (!this.#altText) {
this.#altTextTooltip?.remove(); this.#altTextTooltip?.remove();
return; return;
@ -266,11 +271,7 @@ class AltText {
return; return;
} }
button.classList.add("done"); button.classList.add("done");
AltText._l10nPromise button.setAttribute("data-l10n-id", "pdfjs-editor-alt-text-edit-button");
.get("pdfjs-editor-alt-text-edit-button-label")
.then(msg => {
button.setAttribute("aria-label", msg);
});
} }
let tooltip = this.#altTextTooltip; let tooltip = this.#altTextTooltip;
@ -315,11 +316,15 @@ class AltText {
{ signal } { signal }
); );
} }
tooltip.innerText = this.#altTextDecorative if (this.#altTextDecorative) {
? await AltText._l10nPromise.get( tooltip.setAttribute(
"data-l10n-id",
"pdfjs-editor-alt-text-decorative-tooltip" "pdfjs-editor-alt-text-decorative-tooltip"
) );
: this.#altText; } else {
tooltip.removeAttribute("data-l10n-id");
tooltip.textContent = this.#altText;
}
if (!tooltip.parentNode) { if (!tooltip.parentNode) {
button.append(tooltip); button.append(tooltip);

View File

@ -88,7 +88,7 @@ class AnnotationEditor {
_focusEventsAllowed = true; _focusEventsAllowed = true;
static _l10nPromise = null; static _l10n = null;
static _l10nResizer = null; static _l10nResizer = null;
@ -210,6 +210,8 @@ class AnnotationEditor {
* @param {Object} l10n * @param {Object} l10n
*/ */
static initialize(l10n, _uiManager) { static initialize(l10n, _uiManager) {
AnnotationEditor._l10n ??= l10n;
AnnotationEditor._l10nResizer ||= Object.freeze({ AnnotationEditor._l10nResizer ||= Object.freeze({
topLeft: "pdfjs-editor-resizer-top-left", topLeft: "pdfjs-editor-resizer-top-left",
topMiddle: "pdfjs-editor-resizer-top-middle", topMiddle: "pdfjs-editor-resizer-top-middle",
@ -221,21 +223,6 @@ class AnnotationEditor {
middleLeft: "pdfjs-editor-resizer-middle-left", middleLeft: "pdfjs-editor-resizer-middle-left",
}); });
AnnotationEditor._l10nPromise ||= new Map([
...[
"pdfjs-editor-alt-text-button-label",
"pdfjs-editor-alt-text-edit-button-label",
"pdfjs-editor-alt-text-decorative-tooltip",
"pdfjs-editor-new-alt-text-added-button-label",
"pdfjs-editor-new-alt-text-missing-button-label",
"pdfjs-editor-new-alt-text-to-review-button-label",
].map(str => [str, l10n.get(str)]),
...[
// Strings that need l10n-arguments.
"pdfjs-editor-new-alt-text-generated-alt-text-with-disclaimer",
].map(str => [str, l10n.get.bind(l10n, str)]),
]);
if (AnnotationEditor._borderLineWidth !== -1) { if (AnnotationEditor._borderLineWidth !== -1) {
return; return;
} }
@ -1003,7 +990,7 @@ class AnnotationEditor {
if (this.#altText) { if (this.#altText) {
return; return;
} }
AltText.initialize(AnnotationEditor._l10nPromise); AltText.initialize(AnnotationEditor._l10n);
this.#altText = new AltText(this); this.#altText = new AltText(this);
if (this.#accessibilityData) { if (this.#accessibilityData) {
this.#altText.data = this.#accessibilityData; this.#altText.data = this.#accessibilityData;

View File

@ -94,6 +94,14 @@ const copyImage = async (page, imagePath, number) => {
await waitForImage(page, getEditorSelector(number)); await waitForImage(page, getEditorSelector(number));
}; };
async function waitForTranslation(page) {
return page.evaluate(async () => {
await new Promise(resolve => {
window.requestAnimationFrame(resolve);
});
});
}
const switchToStamp = switchToEditor.bind(null, "Stamp"); const switchToStamp = switchToEditor.bind(null, "Stamp");
describe("Stamp Editor", () => { describe("Stamp Editor", () => {
@ -987,6 +995,7 @@ describe("Stamp Editor", () => {
const buttonSelector = `${editorSelector} button.altText.new`; const buttonSelector = `${editorSelector} button.altText.new`;
await page.waitForSelector(buttonSelector, { visible: true }); await page.waitForSelector(buttonSelector, { visible: true });
await waitForTranslation(page);
// Check the text in the button. // Check the text in the button.
let text = await page.evaluate( let text = await page.evaluate(
sel => document.querySelector(sel).textContent, sel => document.querySelector(sel).textContent,
@ -1036,6 +1045,7 @@ describe("Stamp Editor", () => {
await waitForSelectedEditor(page, editorSelector); await waitForSelectedEditor(page, editorSelector);
await page.waitForSelector(buttonSelector, { visible: true }); await page.waitForSelector(buttonSelector, { visible: true });
await waitForTranslation(page);
// Check the text in the button. // Check the text in the button.
text = await page.evaluate( text = await page.evaluate(
sel => document.querySelector(sel).textContent, sel => document.querySelector(sel).textContent,
@ -1078,6 +1088,7 @@ describe("Stamp Editor", () => {
await page.click("#newAltTextSave"); await page.click("#newAltTextSave");
await page.waitForSelector("#newAltTextDialog", { visible: false }); await page.waitForSelector("#newAltTextDialog", { visible: false });
await waitForTranslation(page);
// Check the text in the button. // Check the text in the button.
text = await page.evaluate( text = await page.evaluate(
sel => document.querySelector(sel).firstChild.textContent, sel => document.querySelector(sel).firstChild.textContent,