[api-minor] Use "data-l10n-id"/"data-l10n-args", rather than manually updating DOM-elements, to trigger translation (PR 17146 follow-up)

This patch changes almost all viewer-components[1] to use "data-l10n-id"/"data-l10n-args" for localization, which means that in many cases we no longer need to pass around the `L10n`-instance any more.

One part of the code-base where the `L10n`-instance is still being used "directly" is the AnnotationEditors, however while it might be possible to convert (most of) that code as well that's not attempted in this patch.

---
[1] The one exception is the `PDFDocumentProperties` dialog, since the way it's currently implemented makes that less straightforward to fix without a lot of code changes.
This commit is contained in:
Jonas Jenwald 2023-10-19 16:30:57 +02:00
parent 898cc2e399
commit 17af706070
17 changed files with 117 additions and 149 deletions

View File

@ -218,11 +218,13 @@ pdfjs-additional-layers = Additional Layers
# Variables: # Variables:
# $page (Number) - the page number # $page (Number) - the page number
pdfjs-thumb-page-title = Page { $page } pdfjs-thumb-page-title =
.title = Page { $page }
# Variables: # Variables:
# $page (Number) - the page number # $page (Number) - the page number
pdfjs-thumb-page-canvas = Thumbnail of Page { $page } pdfjs-thumb-page-canvas =
.aria-label = Thumbnail of Page { $page }
## Find panel button title and messages ## Find panel button title and messages
@ -276,7 +278,8 @@ pdfjs-page-scale-percent = { $scale }%
# Variables: # Variables:
# $page (Number) - the page number # $page (Number) - the page number
pdfjs-page-landmark = Page { $page } pdfjs-page-landmark =
.aria-label = Page { $page }
## Loading indicator messages ## Loading indicator messages

View File

@ -983,8 +983,11 @@ class TextAnnotationElement extends AnnotationElement {
"annotation-" + "annotation-" +
this.data.name.toLowerCase() + this.data.name.toLowerCase() +
".svg"; ".svg";
image.dataset.l10nId = "pdfjs-text-annotation-type"; image.setAttribute("data-l10n-id", "pdfjs-text-annotation-type");
image.dataset.l10nArgs = JSON.stringify({ type: this.data.name }); image.setAttribute(
"data-l10n-args",
JSON.stringify({ type: this.data.name })
);
if (!this.data.popupRef && this.hasPopupData) { if (!this.data.popupRef && this.hasPopupData) {
this._createPopup(); this._createPopup();
@ -2158,11 +2161,17 @@ class PopupElement {
if (this.#dateObj) { if (this.#dateObj) {
const modificationDate = document.createElement("span"); const modificationDate = document.createElement("span");
modificationDate.classList.add("popupDate"); modificationDate.classList.add("popupDate");
modificationDate.dataset.l10nId = "pdfjs-annotation-date-string"; modificationDate.setAttribute(
modificationDate.dataset.l10nArgs = JSON.stringify({ "data-l10n-id",
"pdfjs-annotation-date-string"
);
modificationDate.setAttribute(
"data-l10n-args",
JSON.stringify({
date: this.#dateObj.toLocaleDateString(), date: this.#dateObj.toLocaleDateString(),
time: this.#dateObj.toLocaleTimeString(), time: this.#dateObj.toLocaleTimeString(),
}); })
);
header.append(modificationDate); header.append(modificationDate);
} }
@ -2889,14 +2898,12 @@ class AnnotationLayer {
div, div,
accessibilityManager, accessibilityManager,
annotationCanvasMap, annotationCanvasMap,
l10n,
page, page,
viewport, viewport,
}) { }) {
this.div = div; this.div = div;
this.#accessibilityManager = accessibilityManager; this.#accessibilityManager = accessibilityManager;
this.#annotationCanvasMap = annotationCanvasMap; this.#annotationCanvasMap = annotationCanvasMap;
this.l10n = l10n;
this.page = page; this.page = page;
this.viewport = viewport; this.viewport = viewport;
this.zIndex = 0; this.zIndex = 0;

View File

@ -25,8 +25,7 @@ const {
shadow, shadow,
XfaLayer, XfaLayer,
} = pdfjsLib; } = pdfjsLib;
const { GenericL10n, NullL10n, parseQueryString, SimpleLinkService } = const { GenericL10n, parseQueryString, SimpleLinkService } = pdfjsViewer;
pdfjsViewer;
const WAITING_TIME = 100; // ms const WAITING_TIME = 100; // ms
const CMAP_URL = "/build/generic/web/cmaps/"; const CMAP_URL = "/build/generic/web/cmaps/";
@ -215,8 +214,7 @@ class Rasterize {
annotationCanvasMap, annotationCanvasMap,
page, page,
imageResourcesPath, imageResourcesPath,
renderForms = false, renderForms = false
l10n = NullL10n
) { ) {
try { try {
const { svg, foreignObject, style, div } = this.createContainer(viewport); const { svg, foreignObject, style, div } = this.createContainer(viewport);
@ -248,7 +246,6 @@ class Rasterize {
div, div,
annotationCanvasMap: annotationImageMap, annotationCanvasMap: annotationImageMap,
page, page,
l10n,
viewport: annotationViewport, viewport: annotationViewport,
}); });
await annotationLayer.render(parameters); await annotationLayer.render(parameters);
@ -356,6 +353,8 @@ class Driver {
// Configure the global worker options. // Configure the global worker options.
GlobalWorkerOptions.workerSrc = WORKER_SRC; GlobalWorkerOptions.workerSrc = WORKER_SRC;
// We only need to initialize the `L10n`-instance here, since translation is
// triggered by a `MutationObserver`; see e.g. `Rasterize.annotationLayer`.
this._l10n = new GenericL10n(VIEWER_LOCALE); this._l10n = new GenericL10n(VIEWER_LOCALE);
// Set the passed options // Set the passed options
@ -879,8 +878,7 @@ class Driver {
annotationCanvasMap, annotationCanvasMap,
page, page,
IMAGE_RESOURCES_PATH, IMAGE_RESOURCES_PATH,
renderForms, renderForms
this._l10n
).then(() => { ).then(() => {
completeRender(false); completeRender(false);
}); });

View File

@ -19,13 +19,11 @@
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
/** @typedef {import("../src/display/annotation_storage").AnnotationStorage} AnnotationStorage */ /** @typedef {import("../src/display/annotation_storage").AnnotationStorage} AnnotationStorage */
/** @typedef {import("./interfaces").IDownloadManager} IDownloadManager */ /** @typedef {import("./interfaces").IDownloadManager} IDownloadManager */
/** @typedef {import("./interfaces").IL10n} IL10n */
/** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */ /** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
/** @typedef {import("./text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */ /** @typedef {import("./text_accessibility.js").TextAccessibilityManager} TextAccessibilityManager */
import { AnnotationLayer } from "pdfjs-lib"; import { AnnotationLayer } from "pdfjs-lib";
import { NullL10n } from "web-l10n_utils";
import { PresentationModeState } from "./ui_utils.js"; import { PresentationModeState } from "./ui_utils.js";
/** /**
@ -38,7 +36,6 @@ import { PresentationModeState } from "./ui_utils.js";
* @property {boolean} renderForms * @property {boolean} renderForms
* @property {IPDFLinkService} linkService * @property {IPDFLinkService} linkService
* @property {IDownloadManager} [downloadManager] * @property {IDownloadManager} [downloadManager]
* @property {IL10n} l10n - Localization service.
* @property {boolean} [enableScripting] * @property {boolean} [enableScripting]
* @property {Promise<boolean>} [hasJSActionsPromise] * @property {Promise<boolean>} [hasJSActionsPromise]
* @property {Promise<Object<string, Array<Object>> | null>} * @property {Promise<Object<string, Array<Object>> | null>}
@ -61,7 +58,6 @@ class AnnotationLayerBuilder {
annotationStorage = null, annotationStorage = null,
imageResourcesPath = "", imageResourcesPath = "",
renderForms = true, renderForms = true,
l10n = NullL10n,
enableScripting = false, enableScripting = false,
hasJSActionsPromise = null, hasJSActionsPromise = null,
fieldObjectsPromise = null, fieldObjectsPromise = null,
@ -74,7 +70,6 @@ class AnnotationLayerBuilder {
this.downloadManager = downloadManager; this.downloadManager = downloadManager;
this.imageResourcesPath = imageResourcesPath; this.imageResourcesPath = imageResourcesPath;
this.renderForms = renderForms; this.renderForms = renderForms;
this.l10n = l10n;
this.annotationStorage = annotationStorage; this.annotationStorage = annotationStorage;
this.enableScripting = enableScripting; this.enableScripting = enableScripting;
this._hasJSActionsPromise = hasJSActionsPromise || Promise.resolve(false); this._hasJSActionsPromise = hasJSActionsPromise || Promise.resolve(false);
@ -131,7 +126,6 @@ class AnnotationLayerBuilder {
div, div,
accessibilityManager: this._accessibilityManager, accessibilityManager: this._accessibilityManager,
annotationCanvasMap: this._annotationCanvasMap, annotationCanvasMap: this._annotationCanvasMap,
l10n: this.l10n,
page: this.pdfPage, page: this.pdfPage,
viewport: viewport.clone({ dontFlip: true }), viewport: viewport.clone({ dontFlip: true }),
}); });

View File

@ -521,7 +521,6 @@ const PDFViewerApplication = {
eventBus, eventBus,
renderingQueue: pdfRenderingQueue, renderingQueue: pdfRenderingQueue,
linkService: pdfLinkService, linkService: pdfLinkService,
l10n,
pageColors, pageColors,
}); });
pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer); pdfRenderingQueue.setThumbnailViewer(this.pdfThumbnailViewer);
@ -538,7 +537,7 @@ const PDFViewerApplication = {
} }
if (!this.supportsIntegratedFind && appConfig.findBar) { if (!this.supportsIntegratedFind && appConfig.findBar) {
this.findBar = new PDFFindBar(appConfig.findBar, eventBus, l10n); this.findBar = new PDFFindBar(appConfig.findBar, eventBus);
} }
if (appConfig.annotationEditorParams) { if (appConfig.annotationEditorParams) {
@ -587,11 +586,10 @@ const PDFViewerApplication = {
this.toolbar = new Toolbar( this.toolbar = new Toolbar(
appConfig.toolbar, appConfig.toolbar,
eventBus, eventBus,
l10n,
await this._nimbusDataPromise await this._nimbusDataPromise
); );
} else { } else {
this.toolbar = new Toolbar(appConfig.toolbar, eventBus, l10n); this.toolbar = new Toolbar(appConfig.toolbar, eventBus);
} }
} }
@ -617,7 +615,6 @@ const PDFViewerApplication = {
this.passwordPrompt = new PasswordPrompt( this.passwordPrompt = new PasswordPrompt(
appConfig.passwordOverlay, appConfig.passwordOverlay,
this.overlayManager, this.overlayManager,
l10n,
this.isViewerEmbedded this.isViewerEmbedded
); );
} }
@ -643,7 +640,6 @@ const PDFViewerApplication = {
this.pdfLayerViewer = new PDFLayerViewer({ this.pdfLayerViewer = new PDFLayerViewer({
container: appConfig.sidebar.layersView, container: appConfig.sidebar.layersView,
eventBus, eventBus,
l10n,
}); });
} }
@ -1848,8 +1844,7 @@ const PDFViewerApplication = {
printContainer, printContainer,
printResolution, printResolution,
optionalContentConfigPromise, optionalContentConfigPromise,
this._printAnnotationStoragePromise, this._printAnnotationStoragePromise
this.l10n
); );
this.printService = printService; this.printService = printService;
this.forceRendering(); this.forceRendering();

View File

@ -18,13 +18,12 @@
import { FluentBundle, FluentResource } from "fluent-bundle"; import { FluentBundle, FluentResource } from "fluent-bundle";
import { DOMLocalization } from "fluent-dom"; import { DOMLocalization } from "fluent-dom";
import { L10n } from "./l10n.js"; import { L10n } from "./l10n.js";
import { shadow } from "pdfjs-lib";
/** /**
* @implements {IL10n} * @implements {IL10n}
*/ */
class ConstL10n extends L10n { class ConstL10n extends L10n {
static #instance;
constructor(lang) { constructor(lang) {
super({ lang }); super({ lang });
this.setL10n( this.setL10n(
@ -51,7 +50,7 @@ class ConstL10n extends L10n {
} }
static get instance() { static get instance() {
return (this.#instance ||= new ConstL10n("en-US")); return shadow(this, "instance", new ConstL10n("en-US"));
} }
} }

View File

@ -37,18 +37,16 @@ class PasswordPrompt {
/** /**
* @param {PasswordPromptOptions} options * @param {PasswordPromptOptions} options
* @param {OverlayManager} overlayManager - Manager for the viewer overlays. * @param {OverlayManager} overlayManager - Manager for the viewer overlays.
* @param {IL10n} l10n - Localization service.
* @param {boolean} [isViewerEmbedded] - If the viewer is embedded, in e.g. * @param {boolean} [isViewerEmbedded] - If the viewer is embedded, in e.g.
* an <iframe> or an <object>. The default value is `false`. * an <iframe> or an <object>. The default value is `false`.
*/ */
constructor(options, overlayManager, l10n, isViewerEmbedded = false) { constructor(options, overlayManager, isViewerEmbedded = false) {
this.dialog = options.dialog; this.dialog = options.dialog;
this.label = options.label; this.label = options.label;
this.input = options.input; this.input = options.input;
this.submitButton = options.submitButton; this.submitButton = options.submitButton;
this.cancelButton = options.cancelButton; this.cancelButton = options.cancelButton;
this.overlayManager = overlayManager; this.overlayManager = overlayManager;
this.l10n = l10n;
this._isViewerEmbedded = isViewerEmbedded; this._isViewerEmbedded = isViewerEmbedded;
// Attach the event listeners. // Attach the event listeners.
@ -84,7 +82,8 @@ class PasswordPrompt {
if (!this._isViewerEmbedded || passwordIncorrect) { if (!this._isViewerEmbedded || passwordIncorrect) {
this.input.focus(); this.input.focus();
} }
this.label.textContent = await this.l10n.get( this.label.setAttribute(
"data-l10n-id",
`pdfjs-password-${passwordIncorrect ? "invalid" : "label"}` `pdfjs-password-${passwordIncorrect ? "invalid" : "label"}`
); );
} }

View File

@ -27,7 +27,7 @@ const MATCHES_COUNT_LIMIT = 1000;
class PDFFindBar { class PDFFindBar {
#resizeObserver = new ResizeObserver(this.#resizeObserverCallback.bind(this)); #resizeObserver = new ResizeObserver(this.#resizeObserverCallback.bind(this));
constructor(options, eventBus, l10n) { constructor(options, eventBus) {
this.opened = false; this.opened = false;
this.bar = options.bar; this.bar = options.bar;
@ -42,7 +42,6 @@ class PDFFindBar {
this.findPreviousButton = options.findPreviousButton; this.findPreviousButton = options.findPreviousButton;
this.findNextButton = options.findNextButton; this.findNextButton = options.findNextButton;
this.eventBus = eventBus; this.eventBus = eventBus;
this.l10n = l10n;
// Add event listeners to the DOM elements. // Add event listeners to the DOM elements.
this.toggleButton.addEventListener("click", () => { this.toggleButton.addEventListener("click", () => {
@ -109,8 +108,9 @@ class PDFFindBar {
} }
updateUIState(state, previous, matchesCount) { updateUIState(state, previous, matchesCount) {
let findMsg = Promise.resolve(""); const { findField, findMsg } = this;
let status = ""; let findMsgId = "",
status = "";
switch (state) { switch (state) {
case FindState.FOUND: case FindState.FOUND:
@ -119,39 +119,46 @@ class PDFFindBar {
status = "pending"; status = "pending";
break; break;
case FindState.NOT_FOUND: case FindState.NOT_FOUND:
findMsg = this.l10n.get("pdfjs-find-not-found"); findMsgId = "pdfjs-find-not-found";
status = "notFound"; status = "notFound";
break; break;
case FindState.WRAPPED: case FindState.WRAPPED:
findMsg = this.l10n.get( findMsgId = `pdfjs-find-reached-${previous ? "top" : "bottom"}`;
`pdfjs-find-reached-${previous ? "top" : "bottom"}`
);
break; break;
} }
this.findField.setAttribute("data-status", status); findField.setAttribute("data-status", status);
this.findField.setAttribute("aria-invalid", state === FindState.NOT_FOUND); findField.setAttribute("aria-invalid", state === FindState.NOT_FOUND);
findMsg.then(msg => { findMsg.setAttribute("data-status", status);
this.findMsg.setAttribute("data-status", status); if (findMsgId) {
this.findMsg.textContent = msg; findMsg.setAttribute("data-l10n-id", findMsgId);
}); } else {
findMsg.removeAttribute("data-l10n-id");
findMsg.textContent = "";
}
this.updateResultsCount(matchesCount); this.updateResultsCount(matchesCount);
} }
updateResultsCount({ current = 0, total = 0 } = {}) { updateResultsCount({ current = 0, total = 0 } = {}) {
const limit = MATCHES_COUNT_LIMIT; const { findResultsCount } = this;
let matchCountMsg = Promise.resolve("");
if (total > 0) { if (total > 0) {
matchCountMsg = const limit = MATCHES_COUNT_LIMIT,
total > limit isLimited = total > limit;
? this.l10n.get("pdfjs-find-match-count-limit", { limit })
: this.l10n.get("pdfjs-find-match-count", { current, total }); findResultsCount.setAttribute(
"data-l10n-id",
`pdfjs-find-match-count${isLimited ? "-limit" : ""}`
);
findResultsCount.setAttribute(
"data-l10n-args",
JSON.stringify(isLimited ? { limit } : { current, total })
);
} else {
findResultsCount.removeAttribute("data-l10n-id");
findResultsCount.textContent = "";
} }
matchCountMsg.then(msg => {
this.findResultsCount.textContent = msg;
});
} }
open() { open() {

View File

@ -19,7 +19,6 @@ import { BaseTreeViewer } from "./base_tree_viewer.js";
* @typedef {Object} PDFLayerViewerOptions * @typedef {Object} PDFLayerViewerOptions
* @property {HTMLDivElement} container - The viewer element. * @property {HTMLDivElement} container - The viewer element.
* @property {EventBus} eventBus - The application event bus. * @property {EventBus} eventBus - The application event bus.
* @property {IL10n} l10n - Localization service.
*/ */
/** /**
@ -32,7 +31,6 @@ import { BaseTreeViewer } from "./base_tree_viewer.js";
class PDFLayerViewer extends BaseTreeViewer { class PDFLayerViewer extends BaseTreeViewer {
constructor(options) { constructor(options) {
super(options); super(options);
this.l10n = options.l10n;
this.eventBus._on("optionalcontentconfigchanged", evt => { this.eventBus._on("optionalcontentconfigchanged", evt => {
this.#updateLayers(evt.promise); this.#updateLayers(evt.promise);
@ -89,7 +87,7 @@ class PDFLayerViewer extends BaseTreeViewer {
/** /**
* @private * @private
*/ */
async _setNestedName(element, { name = null }) { _setNestedName(element, { name = null }) {
if (typeof name === "string") { if (typeof name === "string") {
element.textContent = this._normalizeTextContent(name); element.textContent = this._normalizeTextContent(name);
return; return;

View File

@ -182,9 +182,8 @@ class PDFPageView {
div.className = "page"; div.className = "page";
div.setAttribute("data-page-number", this.id); div.setAttribute("data-page-number", this.id);
div.setAttribute("role", "region"); div.setAttribute("role", "region");
this.l10n.get("pdfjs-page-landmark", { page: this.id }).then(msg => { div.setAttribute("data-l10n-id", "pdfjs-page-landmark");
div.setAttribute("aria-label", msg); div.setAttribute("data-l10n-args", JSON.stringify({ page: this.id }));
});
this.div = div; this.div = div;
this.#setDimensions(); this.#setDimensions();
@ -878,7 +877,6 @@ class PDFPageView {
renderForms: this.#annotationMode === AnnotationMode.ENABLE_FORMS, renderForms: this.#annotationMode === AnnotationMode.ENABLE_FORMS,
linkService, linkService,
downloadManager, downloadManager,
l10n,
enableScripting, enableScripting,
hasJSActionsPromise, hasJSActionsPromise,
fieldObjectsPromise, fieldObjectsPromise,

View File

@ -69,8 +69,7 @@ class PDFPrintService {
printContainer, printContainer,
printResolution, printResolution,
optionalContentConfigPromise = null, optionalContentConfigPromise = null,
printAnnotationStoragePromise = null, printAnnotationStoragePromise = null
l10n
) { ) {
this.pdfDocument = pdfDocument; this.pdfDocument = pdfDocument;
this.pagesOverview = pagesOverview; this.pagesOverview = pagesOverview;
@ -80,7 +79,6 @@ class PDFPrintService {
optionalContentConfigPromise || pdfDocument.getOptionalContentConfig(); optionalContentConfigPromise || pdfDocument.getOptionalContentConfig();
this._printAnnotationStoragePromise = this._printAnnotationStoragePromise =
printAnnotationStoragePromise || Promise.resolve(); printAnnotationStoragePromise || Promise.resolve();
this.l10n = l10n;
this.currentPage = -1; this.currentPage = -1;
// The temporary canvas where renderPage paints one page at a time. // The temporary canvas where renderPage paints one page at a time.
this.scratchCanvas = document.createElement("canvas"); this.scratchCanvas = document.createElement("canvas");
@ -151,12 +149,12 @@ class PDFPrintService {
const renderNextPage = (resolve, reject) => { const renderNextPage = (resolve, reject) => {
this.throwIfInactive(); this.throwIfInactive();
if (++this.currentPage >= pageCount) { if (++this.currentPage >= pageCount) {
renderProgress(pageCount, pageCount, this.l10n); renderProgress(pageCount, pageCount);
resolve(); resolve();
return; return;
} }
const index = this.currentPage; const index = this.currentPage;
renderProgress(index, pageCount, this.l10n); renderProgress(index, pageCount);
renderPage( renderPage(
this, this,
this.pdfDocument, this.pdfDocument,
@ -288,7 +286,7 @@ function abort() {
} }
} }
function renderProgress(index, total, l10n) { function renderProgress(index, total) {
if (typeof PDFJSDev === "undefined" && window.isGECKOVIEW) { if (typeof PDFJSDev === "undefined" && window.isGECKOVIEW) {
return; return;
} }
@ -297,9 +295,7 @@ function renderProgress(index, total, l10n) {
const progressBar = dialog.querySelector("progress"); const progressBar = dialog.querySelector("progress");
const progressPerc = dialog.querySelector(".relative-progress"); const progressPerc = dialog.querySelector(".relative-progress");
progressBar.value = progress; progressBar.value = progress;
l10n.get("pdfjs-print-progress-percent", { progress }).then(msg => { progressPerc.setAttribute("data-l10n-args", JSON.stringify({ progress }));
progressPerc.textContent = msg;
});
} }
window.addEventListener( window.addEventListener(
@ -368,8 +364,7 @@ PDFPrintServiceFactory.instance = {
printContainer, printContainer,
printResolution, printResolution,
optionalContentConfigPromise, optionalContentConfigPromise,
printAnnotationStoragePromise, printAnnotationStoragePromise
l10n
) { ) {
if (activeService) { if (activeService) {
throw new Error("The print service is created and active."); throw new Error("The print service is created and active.");
@ -380,8 +375,7 @@ PDFPrintServiceFactory.instance = {
printContainer, printContainer,
printResolution, printResolution,
optionalContentConfigPromise, optionalContentConfigPromise,
printAnnotationStoragePromise, printAnnotationStoragePromise
l10n
); );
return activeService; return activeService;
}, },

View File

@ -111,7 +111,6 @@ class PDFSidebar {
this._currentOutlineItemButton = elements.currentOutlineItemButton; this._currentOutlineItemButton = elements.currentOutlineItemButton;
this.eventBus = eventBus; this.eventBus = eventBus;
this.l10n = l10n;
this.#isRTL = l10n.getDirection() === "rtl"; this.#isRTL = l10n.getDirection() === "rtl";
this.#addEventListeners(); this.#addEventListeners();

View File

@ -18,7 +18,6 @@
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
/** @typedef {import("../src/display/display_utils").PageViewport} PageViewport */ /** @typedef {import("../src/display/display_utils").PageViewport} PageViewport */
/** @typedef {import("./event_utils").EventBus} EventBus */ /** @typedef {import("./event_utils").EventBus} EventBus */
/** @typedef {import("./interfaces").IL10n} IL10n */
/** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */ /** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */
/** @typedef {import("./interfaces").IRenderableView} IRenderableView */ /** @typedef {import("./interfaces").IRenderableView} IRenderableView */
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
@ -42,7 +41,6 @@ const THUMBNAIL_WIDTH = 98; // px
* The default value is `null`. * The default value is `null`.
* @property {IPDFLinkService} linkService - The navigation/linking service. * @property {IPDFLinkService} linkService - The navigation/linking service.
* @property {PDFRenderingQueue} renderingQueue - The rendering queue object. * @property {PDFRenderingQueue} renderingQueue - The rendering queue object.
* @property {IL10n} l10n - Localization service.
* @property {Object} [pageColors] - Overwrites background and foreground colors * @property {Object} [pageColors] - Overwrites background and foreground colors
* with user defined ones in order to improve readability in high contrast * with user defined ones in order to improve readability in high contrast
* mode. * mode.
@ -93,7 +91,6 @@ class PDFThumbnailView {
optionalContentConfigPromise, optionalContentConfigPromise,
linkService, linkService,
renderingQueue, renderingQueue,
l10n,
pageColors, pageColors,
}) { }) {
this.id = id; this.id = id;
@ -114,13 +111,11 @@ class PDFThumbnailView {
this.renderTask = null; this.renderTask = null;
this.renderingState = RenderingStates.INITIAL; this.renderingState = RenderingStates.INITIAL;
this.resume = null; this.resume = null;
this.l10n = l10n;
const anchor = document.createElement("a"); const anchor = document.createElement("a");
anchor.href = linkService.getAnchorUrl("#page=" + id); anchor.href = linkService.getAnchorUrl("#page=" + id);
this._thumbPageTitle.then(msg => { anchor.setAttribute("data-l10n-id", "pdfjs-thumb-page-title");
anchor.title = msg; anchor.setAttribute("data-l10n-args", this.#pageL10nArgs);
});
anchor.onclick = function () { anchor.onclick = function () {
linkService.goToPage(id); linkService.goToPage(id);
return false; return false;
@ -232,9 +227,8 @@ class PDFThumbnailView {
const image = document.createElement("img"); const image = document.createElement("img");
image.className = "thumbnailImage"; image.className = "thumbnailImage";
this._thumbPageCanvas.then(msg => { image.setAttribute("data-l10n-id", "pdfjs-thumb-page-canvas");
image.setAttribute("aria-label", msg); image.setAttribute("data-l10n-args", this.#pageL10nArgs);
});
image.src = reducedCanvas.toDataURL(); image.src = reducedCanvas.toDataURL();
this.image = image; this.image = image;
@ -423,16 +417,8 @@ class PDFThumbnailView {
return canvas; return canvas;
} }
get _thumbPageTitle() { get #pageL10nArgs() {
return this.l10n.get("pdfjs-thumb-page-title", { return JSON.stringify({ page: this.pageLabel ?? this.id });
page: this.pageLabel ?? this.id,
});
}
get _thumbPageCanvas() {
return this.l10n.get("pdfjs-thumb-page-canvas", {
page: this.pageLabel ?? this.id,
});
} }
/** /**
@ -441,17 +427,12 @@ class PDFThumbnailView {
setPageLabel(label) { setPageLabel(label) {
this.pageLabel = typeof label === "string" ? label : null; this.pageLabel = typeof label === "string" ? label : null;
this._thumbPageTitle.then(msg => { this.anchor.setAttribute("data-l10n-args", this.#pageL10nArgs);
this.anchor.title = msg;
});
if (this.renderingState !== RenderingStates.FINISHED) { if (this.renderingState !== RenderingStates.FINISHED) {
return; return;
} }
this.image?.setAttribute("data-l10n-args", this.#pageL10nArgs);
this._thumbPageCanvas.then(msg => {
this.image?.setAttribute("aria-label", msg);
});
} }
} }

View File

@ -16,7 +16,6 @@
/** @typedef {import("../src/display/api").PDFDocumentProxy} PDFDocumentProxy */ /** @typedef {import("../src/display/api").PDFDocumentProxy} PDFDocumentProxy */
/** @typedef {import("../src/display/api").PDFPageProxy} PDFPageProxy */ /** @typedef {import("../src/display/api").PDFPageProxy} PDFPageProxy */
/** @typedef {import("./event_utils").EventBus} EventBus */ /** @typedef {import("./event_utils").EventBus} EventBus */
/** @typedef {import("./interfaces").IL10n} IL10n */
/** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */ /** @typedef {import("./interfaces").IPDFLinkService} IPDFLinkService */
// eslint-disable-next-line max-len // eslint-disable-next-line max-len
/** @typedef {import("./pdf_rendering_queue").PDFRenderingQueue} PDFRenderingQueue */ /** @typedef {import("./pdf_rendering_queue").PDFRenderingQueue} PDFRenderingQueue */
@ -40,7 +39,6 @@ const THUMBNAIL_SELECTED_CLASS = "selected";
* @property {EventBus} eventBus - The application event bus. * @property {EventBus} eventBus - The application event bus.
* @property {IPDFLinkService} linkService - The navigation/linking service. * @property {IPDFLinkService} linkService - The navigation/linking service.
* @property {PDFRenderingQueue} renderingQueue - The rendering queue object. * @property {PDFRenderingQueue} renderingQueue - The rendering queue object.
* @property {IL10n} l10n - Localization service.
* @property {Object} [pageColors] - Overwrites background and foreground colors * @property {Object} [pageColors] - Overwrites background and foreground colors
* with user defined ones in order to improve readability in high contrast * with user defined ones in order to improve readability in high contrast
* mode. * mode.
@ -58,14 +56,12 @@ class PDFThumbnailViewer {
eventBus, eventBus,
linkService, linkService,
renderingQueue, renderingQueue,
l10n,
pageColors, pageColors,
}) { }) {
this.container = container; this.container = container;
this.eventBus = eventBus; this.eventBus = eventBus;
this.linkService = linkService; this.linkService = linkService;
this.renderingQueue = renderingQueue; this.renderingQueue = renderingQueue;
this.l10n = l10n;
this.pageColors = pageColors || null; this.pageColors = pageColors || null;
this.scroll = watchScroll(this.container, this._scrollUpdated.bind(this)); this.scroll = watchScroll(this.container, this._scrollUpdated.bind(this));
@ -209,7 +205,6 @@ class PDFThumbnailViewer {
optionalContentConfigPromise, optionalContentConfigPromise,
linkService: this.linkService, linkService: this.linkService,
renderingQueue: this.renderingQueue, renderingQueue: this.renderingQueue,
l10n: this.l10n,
pageColors: this.pageColors, pageColors: this.pageColors,
}); });
this._thumbnails.push(thumbnail); this._thumbnails.push(thumbnail);

View File

@ -28,10 +28,9 @@ class Toolbar {
/** /**
* @param {ToolbarOptions} options * @param {ToolbarOptions} options
* @param {EventBus} eventBus * @param {EventBus} eventBus
* @param {IL10n} _l10n - Localization service.
* @param {Object} nimbusData - Nimbus configuration. * @param {Object} nimbusData - Nimbus configuration.
*/ */
constructor(options, eventBus, _l10n, nimbusData) { constructor(options, eventBus, nimbusData) {
this.#eventBus = eventBus; this.#eventBus = eventBus;
const buttons = [ const buttons = [
{ {

View File

@ -49,12 +49,10 @@ class Toolbar {
/** /**
* @param {ToolbarOptions} options * @param {ToolbarOptions} options
* @param {EventBus} eventBus * @param {EventBus} eventBus
* @param {IL10n} l10n - Localization service.
*/ */
constructor(options, eventBus, l10n) { constructor(options, eventBus) {
this.toolbar = options.container; this.toolbar = options.container;
this.eventBus = eventBus; this.eventBus = eventBus;
this.l10n = l10n;
this.buttons = [ this.buttons = [
{ element: options.previous, eventName: "previouspage" }, { element: options.previous, eventName: "previouspage" },
{ element: options.next, eventName: "nextpage" }, { element: options.next, eventName: "nextpage" },
@ -252,22 +250,27 @@ class Toolbar {
if (resetNumPages) { if (resetNumPages) {
if (this.hasPageLabels) { if (this.hasPageLabels) {
items.pageNumber.type = "text"; items.pageNumber.type = "text";
items.numPages.setAttribute("data-l10n-id", "pdfjs-page-of-pages");
} else { } else {
items.pageNumber.type = "number"; items.pageNumber.type = "number";
this.l10n.get("pdfjs-of-pages", { pagesCount }).then(msg => {
items.numPages.textContent = msg; items.numPages.setAttribute("data-l10n-id", "pdfjs-of-pages");
}); items.numPages.setAttribute(
"data-l10n-args",
JSON.stringify({ pagesCount })
);
} }
items.pageNumber.max = pagesCount; items.pageNumber.max = pagesCount;
} }
if (this.hasPageLabels) { if (this.hasPageLabels) {
items.pageNumber.value = this.pageLabel; items.pageNumber.value = this.pageLabel;
this.l10n
.get("pdfjs-page-of-pages", { pageNumber, pagesCount }) items.numPages.setAttribute(
.then(msg => { "data-l10n-args",
items.numPages.textContent = msg; JSON.stringify({ pageNumber, pagesCount })
}); );
} else { } else {
items.pageNumber.value = pageNumber; items.pageNumber.value = pageNumber;
} }
@ -278,11 +281,6 @@ class Toolbar {
items.zoomOut.disabled = pageScale <= MIN_SCALE; items.zoomOut.disabled = pageScale <= MIN_SCALE;
items.zoomIn.disabled = pageScale >= MAX_SCALE; items.zoomIn.disabled = pageScale >= MAX_SCALE;
this.l10n
.get("pdfjs-page-scale-percent", {
scale: Math.round(pageScale * 10000) / 100,
})
.then(msg => {
let predefinedValueFound = false; let predefinedValueFound = false;
for (const option of items.scaleSelect.options) { for (const option of items.scaleSelect.options) {
if (option.value !== pageScaleValue) { if (option.value !== pageScaleValue) {
@ -293,10 +291,14 @@ class Toolbar {
predefinedValueFound = true; predefinedValueFound = true;
} }
if (!predefinedValueFound) { if (!predefinedValueFound) {
items.customScaleOption.textContent = msg;
items.customScaleOption.selected = true; items.customScaleOption.selected = true;
items.customScaleOption.setAttribute(
"data-l10n-args",
JSON.stringify({
scale: Math.round(pageScale * 10000) / 100,
})
);
} }
});
} }
updateLoadingIndicatorState(loading = false) { updateLoadingIndicatorState(loading = false) {

View File

@ -379,7 +379,7 @@ See https://github.com/adobe-type-tools/cmap-resources
<option id="pageActualOption" title="" value="page-actual" data-l10n-id="pdfjs-page-scale-actual">Actual Size</option> <option id="pageActualOption" title="" value="page-actual" data-l10n-id="pdfjs-page-scale-actual">Actual Size</option>
<option id="pageFitOption" title="" value="page-fit" data-l10n-id="pdfjs-page-scale-fit">Page Fit</option> <option id="pageFitOption" title="" value="page-fit" data-l10n-id="pdfjs-page-scale-fit">Page Fit</option>
<option id="pageWidthOption" title="" value="page-width" data-l10n-id="pdfjs-page-scale-width">Page Width</option> <option id="pageWidthOption" title="" value="page-width" data-l10n-id="pdfjs-page-scale-width">Page Width</option>
<option id="customScaleOption" title="" value="custom" disabled="disabled" hidden="true"></option> <option id="customScaleOption" title="" value="custom" disabled="disabled" hidden="true" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 0 }'>0%</option>
<option title="" value="0.5" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 50 }'>50%</option> <option title="" value="0.5" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 50 }'>50%</option>
<option title="" value="0.75" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 75 }'>75%</option> <option title="" value="0.75" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 75 }'>75%</option>
<option title="" value="1" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 100 }'>100%</option> <option title="" value="1" data-l10n-id="pdfjs-page-scale-percent" data-l10n-args='{ "scale": 100 }'>100%</option>