diff --git a/web/annotation_layer_builder.js b/web/annotation_layer_builder.js index de56682b8..0f7ce4060 100644 --- a/web/annotation_layer_builder.js +++ b/web/annotation_layer_builder.js @@ -14,7 +14,7 @@ */ import { AnnotationLayer } from "pdfjs-lib"; -import { NullL10n } from "./ui_utils.js"; +import { NullL10n } from "./l10n_utils.js"; import { SimpleLinkService } from "./pdf_link_service.js"; /** diff --git a/web/app.js b/web/app.js index b425acb90..8cefd7e56 100644 --- a/web/app.js +++ b/web/app.js @@ -260,28 +260,6 @@ const PDFViewerApplication = { _scriptingInstance: null, _mouseState: Object.create(null), - _localizeMessage(key, args = null) { - const DEFAULT_L10N_STRINGS = { - error_file: "File: {{file}}", - error_line: "Line: {{line}}", - error_message: "Message: {{message}}", - error_stack: "Stack: {{stack}}", - error_version_info: "PDF.js v{{version}} (build: {{build}})", - invalid_file_error: "Invalid or corrupted PDF file.", - loading_error: "An error occurred while loading the PDF.", - missing_file_error: "Missing PDF file.", - printing_not_ready: "Warning: The PDF is not fully loaded for printing.", - printing_not_supported: - "Warning: Printing is not fully supported by this browser.", - rendering_error: "An error occurred while rendering the page.", - unexpected_response_error: "Unexpected server response.", - web_fonts_disabled: - "Web fonts are disabled: unable to use embedded PDF fonts.", - }; - - return this.l10n.get(key || "", args, DEFAULT_L10N_STRINGS[key]); - }, - // Called once when the document is loaded. async initialize(appConfig) { this.preferences = this.externalServices.createPreferences(); @@ -741,7 +719,7 @@ const PDFViewerApplication = { this.open(file, args); }, onError: err => { - this._localizeMessage("loading_error").then(msg => { + this.l10n.get("loading_error").then(msg => { this._documentError(msg, err); }); }, @@ -973,7 +951,7 @@ const PDFViewerApplication = { } else if (exception instanceof UnexpectedResponseException) { key = "unexpected_response_error"; } - return this._localizeMessage(key).then(msg => { + return this.l10n.get(key).then(msg => { this._documentError(msg, { message: exception?.message }); throw exception; }); @@ -1128,28 +1106,28 @@ const PDFViewerApplication = { */ _otherError(message, moreInfo = null) { const moreInfoText = [ - this._localizeMessage("error_version_info", { + this.l10n.get("error_version_info", { version: version || "?", build: build || "?", }), ]; if (moreInfo) { moreInfoText.push( - this._localizeMessage("error_message", { message: moreInfo.message }) + this.l10n.get("error_message", { message: moreInfo.message }) ); if (moreInfo.stack) { moreInfoText.push( - this._localizeMessage("error_stack", { stack: moreInfo.stack }) + this.l10n.get("error_stack", { stack: moreInfo.stack }) ); } else { if (moreInfo.filename) { moreInfoText.push( - this._localizeMessage("error_file", { file: moreInfo.filename }) + this.l10n.get("error_file", { file: moreInfo.filename }) ); } if (moreInfo.lineNumber) { moreInfoText.push( - this._localizeMessage("error_line", { line: moreInfo.lineNumber }) + this.l10n.get("error_line", { line: moreInfo.lineNumber }) ); } } @@ -2021,7 +1999,7 @@ const PDFViewerApplication = { } if (!this.supportsPrinting) { - this._localizeMessage("printing_not_supported").then(msg => { + this.l10n.get("printing_not_supported").then(msg => { this._otherError(msg); }); return; @@ -2030,7 +2008,7 @@ const PDFViewerApplication = { // The beforePrint is a sync method and we need to know layout before // returning from this method. Ensure that we can get sizes of the pages. if (!this.pdfViewer.pageViewsReady) { - this._localizeMessage("printing_not_ready").then(msg => { + this.l10n.get("printing_not_ready").then(msg => { // eslint-disable-next-line no-alert window.alert(msg); }); @@ -2354,7 +2332,7 @@ if (typeof PDFJSDev === "undefined" || PDFJSDev.test("GENERIC")) { throw new Error("file origin does not match viewer's"); } } catch (ex) { - PDFViewerApplication._localizeMessage("loading_error").then(msg => { + PDFViewerApplication.l10n.get("loading_error").then(msg => { PDFViewerApplication._documentError(msg, { message: ex?.message }); }); throw ex; @@ -2465,7 +2443,7 @@ function webViewerInitialized() { if (!PDFViewerApplication.supportsDocumentFonts) { AppOptions.set("disableFontFace", true); - PDFViewerApplication._localizeMessage("web_fonts_disabled").then(msg => { + PDFViewerApplication.l10n.get("web_fonts_disabled").then(msg => { console.warn(msg); }); } @@ -2497,7 +2475,7 @@ function webViewerInitialized() { try { webViewerOpenFileViaURL(file); } catch (reason) { - PDFViewerApplication._localizeMessage("loading_error").then(msg => { + PDFViewerApplication.l10n.get("loading_error").then(msg => { PDFViewerApplication._documentError(msg, reason); }); } @@ -2568,7 +2546,7 @@ function webViewerPageRendered({ pageNumber, timestamp, error }) { } if (error) { - PDFViewerApplication._localizeMessage("rendering_error").then(msg => { + PDFViewerApplication.l10n.get("rendering_error").then(msg => { PDFViewerApplication._otherError(msg, error); }); } diff --git a/web/base_viewer.js b/web/base_viewer.js index 893be50d3..52e298881 100644 --- a/web/base_viewer.js +++ b/web/base_viewer.js @@ -25,7 +25,6 @@ import { isValidSpreadMode, MAX_AUTO_SCALE, moveToEndOfArray, - NullL10n, PresentationModeState, RendererType, SCROLLBAR_PADDING, @@ -39,6 +38,7 @@ import { } from "./ui_utils.js"; import { PDFRenderingQueue, RenderingStates } from "./pdf_rendering_queue.js"; import { AnnotationLayerBuilder } from "./annotation_layer_builder.js"; +import { NullL10n } from "./l10n_utils.js"; import { PDFPageView } from "./pdf_page_view.js"; import { SimpleLinkService } from "./pdf_link_service.js"; import { TextLayerBuilder } from "./text_layer_builder.js"; diff --git a/web/firefoxcom.js b/web/firefoxcom.js index 9d9a5d78f..129190a74 100644 --- a/web/firefoxcom.js +++ b/web/firefoxcom.js @@ -18,6 +18,7 @@ import { DefaultExternalServices, PDFViewerApplication } from "./app.js"; import { isPdfFile, PDFDataRangeTransport, shadow } from "pdfjs-lib"; import { BasePreferences } from "./preferences.js"; import { DEFAULT_SCALE_VALUE } from "./ui_utils.js"; +import { getL10nFallback } from "./l10n_utils.js"; if (typeof PDFJSDev === "undefined" || !PDFJSDev.test("MOZCENTRAL")) { throw new Error( @@ -200,8 +201,8 @@ class MozL10n { return this.mozL10n.getDirection(); } - async get(property, args, fallback) { - return this.mozL10n.get(property, args, fallback); + async get(key, args = null, fallback = getL10nFallback(key, args)) { + return this.mozL10n.get(key, args, fallback); } async translate(element) { diff --git a/web/genericl10n.js b/web/genericl10n.js index d260e9be2..432a84534 100644 --- a/web/genericl10n.js +++ b/web/genericl10n.js @@ -14,6 +14,7 @@ */ import "../external/webL10n/l10n.js"; +import { getL10nFallback } from "./l10n_utils.js"; const webL10n = document.webL10n; @@ -37,9 +38,9 @@ class GenericL10n { return l10n.getDirection(); } - async get(property, args, fallback) { + async get(key, args = null, fallback = getL10nFallback(key, args)) { const l10n = await this._ready; - return l10n.get(property, args, fallback); + return l10n.get(key, args, fallback); } async translate(element) { diff --git a/web/l10n_utils.js b/web/l10n_utils.js new file mode 100644 index 000000000..117cd4175 --- /dev/null +++ b/web/l10n_utils.js @@ -0,0 +1,127 @@ +/* Copyright 2021 Mozilla Foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +/** + * A subset of the l10n strings in the `l10n/en-US/viewer.properties` file. + */ +const DEFAULT_L10N_STRINGS = { + of_pages: "of {{pagesCount}}", + page_of_pages: "({{pageNumber}} of {{pagesCount}})", + + document_properties_kb: "{{size_kb}} KB ({{size_b}} bytes)", + document_properties_mb: "{{size_mb}} MB ({{size_b}} bytes)", + document_properties_date_string: "{{date}}, {{time}}", + document_properties_page_size_unit_inches: "in", + document_properties_page_size_unit_millimeters: "mm", + document_properties_page_size_orientation_portrait: "portrait", + document_properties_page_size_orientation_landscape: "landscape", + document_properties_page_size_name_a3: "A3", + document_properties_page_size_name_a4: "A4", + document_properties_page_size_name_letter: "Letter", + document_properties_page_size_name_legal: "Legal", + document_properties_page_size_dimension_string: + "{{width}} × {{height}} {{unit}} ({{orientation}})", + document_properties_page_size_dimension_name_string: + "{{width}} × {{height}} {{unit}} ({{name}}, {{orientation}})", + document_properties_linearized_yes: "Yes", + document_properties_linearized_no: "No", + + print_progress_percent: "{{progress}}%", + + "toggle_sidebar.title": "Toggle Sidebar", + "toggle_sidebar_notification2.title": + "Toggle Sidebar (document contains outline/attachments/layers)", + + additional_layers: "Additional Layers", + page_canvas: "Page {{page}}", + thumb_page_title: "Page {{page}}", + thumb_page_canvas: "Thumbnail of Page {{page}}", + + find_reached_top: "Reached top of document, continued from bottom", + find_reached_bottom: "Reached end of document, continued from top", + "find_match_count[one]": "{{current}} of {{total}} match", + "find_match_count[other]": "{{current}} of {{total}} matches", + "find_match_count_limit[one]": "More than {{limit}} match", + "find_match_count_limit[other]": "More than {{limit}} matches", + find_not_found: "Phrase not found", + + error_version_info: "PDF.js v{{version}} (build: {{build}})", + error_message: "Message: {{message}}", + error_stack: "Stack: {{stack}}", + error_file: "File: {{file}}", + error_line: "Line: {{line}}", + rendering_error: "An error occurred while rendering the page.", + + page_scale_width: "Page Width", + page_scale_fit: "Page Fit", + page_scale_auto: "Automatic Zoom", + page_scale_actual: "Actual Size", + page_scale_percent: "{{scale}}%", + + loading_error: "An error occurred while loading the PDF.", + invalid_file_error: "Invalid or corrupted PDF file.", + missing_file_error: "Missing PDF file.", + unexpected_response_error: "Unexpected server response.", + + printing_not_supported: + "Warning: Printing is not fully supported by this browser.", + printing_not_ready: "Warning: The PDF is not fully loaded for printing.", + web_fonts_disabled: + "Web fonts are disabled: unable to use embedded PDF fonts.", +}; + +function getL10nFallback(key, args) { + switch (key) { + case "find_match_count": + key = `find_match_count[${args.total === 1 ? "one" : "other"}]`; + break; + case "find_match_count_limit": + key = `find_match_count_limit[${args.limit === 1 ? "one" : "other"}]`; + break; + } + return DEFAULT_L10N_STRINGS[key] || ""; +} + +// Replaces {{arguments}} with their values. +function formatL10nValue(text, args) { + if (!args) { + return text; + } + return text.replace(/\{\{\s*(\w+)\s*\}\}/g, (all, name) => { + return name in args ? args[name] : "{{" + name + "}}"; + }); +} + +/** + * No-op implementation of the localization service. + * @implements {IL10n} + */ +const NullL10n = { + async getLanguage() { + return "en-us"; + }, + + async getDirection() { + return "ltr"; + }, + + async get(key, args = null, fallback = getL10nFallback(key, args)) { + return formatL10nValue(fallback, args); + }, + + async translate(element) {}, +}; + +export { getL10nFallback, NullL10n }; diff --git a/web/password_prompt.js b/web/password_prompt.js index 2af0c47a6..583a13a68 100644 --- a/web/password_prompt.js +++ b/web/password_prompt.js @@ -67,34 +67,18 @@ class PasswordPrompt { ); } - open() { - this.overlayManager.open(this.overlayName).then(() => { - if ( - !this._isViewerEmbedded || - this.reason === PasswordResponses.INCORRECT_PASSWORD - ) { - this.input.focus(); - } + async open() { + await this.overlayManager.open(this.overlayName); - let promptString; - if (this.reason === PasswordResponses.INCORRECT_PASSWORD) { - promptString = this.l10n.get( - "password_invalid", - null, - "Invalid password. Please try again." - ); - } else { - promptString = this.l10n.get( - "password_label", - null, - "Enter the password to open this PDF file." - ); - } + const passwordIncorrect = + this.reason === PasswordResponses.INCORRECT_PASSWORD; - promptString.then(msg => { - this.label.textContent = msg; - }); - }); + if (!this._isViewerEmbedded || passwordIncorrect) { + this.input.focus(); + } + this.label.textContent = await this.l10n.get( + `password_${passwordIncorrect ? "invalid" : "label"}` + ); } close() { diff --git a/web/pdf_document_properties.js b/web/pdf_document_properties.js index 5a427f309..9ecbb2c3a 100644 --- a/web/pdf_document_properties.js +++ b/web/pdf_document_properties.js @@ -255,27 +255,16 @@ class PDFDocumentProperties { * @private */ async _parseFileSize(fileSize = 0) { - const kb = fileSize / 1024; + const kb = fileSize / 1024, + mb = kb / 1024; if (!kb) { return undefined; - } else if (kb < 1024) { - return this.l10n.get( - "document_properties_kb", - { - size_kb: (+kb.toPrecision(3)).toLocaleString(), - size_b: fileSize.toLocaleString(), - }, - "{{size_kb}} KB ({{size_b}} bytes)" - ); } - return this.l10n.get( - "document_properties_mb", - { - size_mb: (+(kb / 1024).toPrecision(3)).toLocaleString(), - size_b: fileSize.toLocaleString(), - }, - "{{size_mb}} MB ({{size_b}} bytes)" - ); + return this.l10n.get(`document_properties_${mb >= 1 ? "mb" : "kb"}`, { + size_mb: mb >= 1 && (+mb.toPrecision(3)).toLocaleString(), + size_kb: (+kb.toPrecision(3)).toLocaleString(), + size_b: fileSize.toLocaleString(), + }); } /** @@ -304,7 +293,6 @@ class PDFDocumentProperties { height: Math.round(pageSizeInches.height * 25.4 * 10) / 10, }; - let pageName = null; let rawName = getPageName(sizeInches, isPortrait, US_PAGE_NAMES) || getPageName(sizeMillimeters, isPortrait, METRIC_PAGE_NAMES); @@ -345,46 +333,35 @@ class PDFDocumentProperties { } } } - if (rawName) { - pageName = this.l10n.get( - "document_properties_page_size_name_" + rawName.toLowerCase(), - null, - rawName - ); - } - return Promise.all([ + const [{ width, height }, unit, name, orientation] = await Promise.all([ this._isNonMetricLocale ? sizeInches : sizeMillimeters, this.l10n.get( - "document_properties_page_size_unit_" + - (this._isNonMetricLocale ? "inches" : "millimeters"), - null, - this._isNonMetricLocale ? "in" : "mm" + `document_properties_page_size_unit_${ + this._isNonMetricLocale ? "inches" : "millimeters" + }` ), - pageName, + rawName && + this.l10n.get( + `document_properties_page_size_name_${rawName.toLowerCase()}` + ), this.l10n.get( - "document_properties_page_size_orientation_" + - (isPortrait ? "portrait" : "landscape"), - null, - isPortrait ? "portrait" : "landscape" + `document_properties_page_size_orientation_${ + isPortrait ? "portrait" : "landscape" + }` ), - ]).then(([{ width, height }, unit, name, orientation]) => { - return this.l10n.get( - "document_properties_page_size_dimension_" + - (name ? "name_" : "") + - "string", - { - width: width.toLocaleString(), - height: height.toLocaleString(), - unit, - name, - orientation, - }, - "{{width}} × {{height}} {{unit}} (" + - (name ? "{{name}}, " : "") + - "{{orientation}})" - ); - }); + ]); + + return this.l10n.get( + `document_properties_page_size_dimension_${name ? "name_" : ""}string`, + { + width: width.toLocaleString(), + height: height.toLocaleString(), + unit, + name, + orientation, + } + ); } /** @@ -395,14 +372,10 @@ class PDFDocumentProperties { if (!dateObject) { return undefined; } - return this.l10n.get( - "document_properties_date_string", - { - date: dateObject.toLocaleDateString(), - time: dateObject.toLocaleTimeString(), - }, - "{{date}}, {{time}}" - ); + return this.l10n.get("document_properties_date_string", { + date: dateObject.toLocaleDateString(), + time: dateObject.toLocaleTimeString(), + }); } /** @@ -410,9 +383,7 @@ class PDFDocumentProperties { */ _parseLinearization(isLinearized) { return this.l10n.get( - "document_properties_linearized_" + (isLinearized ? "yes" : "no"), - null, - isLinearized ? "Yes" : "No" + `document_properties_linearized_${isLinearized ? "yes" : "no"}` ); } } diff --git a/web/pdf_find_bar.js b/web/pdf_find_bar.js index 94bc0c954..3388711d9 100644 --- a/web/pdf_find_bar.js +++ b/web/pdf_find_bar.js @@ -103,41 +103,26 @@ class PDFFindBar { } updateUIState(state, previous, matchesCount) { - let findMsg = ""; + let findMsg = Promise.resolve(""); let status = ""; switch (state) { case FindState.FOUND: break; - case FindState.PENDING: status = "pending"; break; - case FindState.NOT_FOUND: - findMsg = this.l10n.get("find_not_found", null, "Phrase not found"); + findMsg = this.l10n.get("find_not_found"); status = "notFound"; break; - case FindState.WRAPPED: - if (previous) { - findMsg = this.l10n.get( - "find_reached_top", - null, - "Reached top of document, continued from bottom" - ); - } else { - findMsg = this.l10n.get( - "find_reached_bottom", - null, - "Reached end of document, continued from top" - ); - } + findMsg = this.l10n.get(`find_reached_${previous ? "top" : "bottom"}`); break; } this.findField.setAttribute("data-status", status); - Promise.resolve(findMsg).then(msg => { + findMsg.then(msg => { this.findMsg.textContent = msg; this._adjustWidth(); }); @@ -147,54 +132,30 @@ class PDFFindBar { updateResultsCount({ current = 0, total = 0 } = {}) { const limit = MATCHES_COUNT_LIMIT; - let matchesCountMsg = ""; + let matchCountMsg = Promise.resolve(""); if (total > 0) { if (total > limit) { + let key = "find_match_count_limit"; + if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { // TODO: Remove this hard-coded `[other]` form once plural support has // been implemented in the mozilla-central specific `l10n.js` file. - matchesCountMsg = this.l10n.get( - "find_match_count_limit[other]", - { - limit, - }, - "More than {{limit}} matches" - ); - } else { - matchesCountMsg = this.l10n.get( - "find_match_count_limit", - { - limit, - }, - "More than {{limit}} match" + (limit !== 1 ? "es" : "") - ); + key += "[other]"; } + matchCountMsg = this.l10n.get(key, { limit }); } else { + let key = "find_match_count"; + if (typeof PDFJSDev !== "undefined" && PDFJSDev.test("MOZCENTRAL")) { // TODO: Remove this hard-coded `[other]` form once plural support has // been implemented in the mozilla-central specific `l10n.js` file. - matchesCountMsg = this.l10n.get( - "find_match_count[other]", - { - current, - total, - }, - "{{current}} of {{total}} matches" - ); - } else { - matchesCountMsg = this.l10n.get( - "find_match_count", - { - current, - total, - }, - "{{current}} of {{total}} match" + (total !== 1 ? "es" : "") - ); + key += "[other]"; } + matchCountMsg = this.l10n.get(key, { current, total }); } } - Promise.resolve(matchesCountMsg).then(msg => { + matchCountMsg.then(msg => { this.findResultsCount.textContent = msg; this.findResultsCount.classList.toggle("hidden", !total); // Since `updateResultsCount` may be called from `PDFFindController`, diff --git a/web/pdf_layer_viewer.js b/web/pdf_layer_viewer.js index dc06625e8..837ceecb6 100644 --- a/web/pdf_layer_viewer.js +++ b/web/pdf_layer_viewer.js @@ -87,11 +87,7 @@ class PDFLayerViewer extends BaseTreeViewer { element.textContent = this._normalizeTextContent(name); return; } - element.textContent = await this.l10n.get( - "additional_layers", - null, - "Additional Layers" - ); + element.textContent = await this.l10n.get("additional_layers"); element.style.fontStyle = "italic"; } diff --git a/web/pdf_page_view.js b/web/pdf_page_view.js index 05eacee65..3503ab602 100644 --- a/web/pdf_page_view.js +++ b/web/pdf_page_view.js @@ -18,7 +18,6 @@ import { CSS_UNITS, DEFAULT_SCALE, getOutputScale, - NullL10n, RendererType, roundToDivide, TextLayerMode, @@ -28,6 +27,7 @@ import { RenderingCancelledException, SVGGraphics, } from "pdfjs-lib"; +import { NullL10n } from "./l10n_utils.js"; import { RenderingStates } from "./pdf_rendering_queue.js"; import { viewerCompatibilityParams } from "./viewer_compatibility.js"; @@ -576,11 +576,9 @@ class PDFPageView { const viewport = this.viewport; const canvas = document.createElement("canvas"); - this.l10n - .get("page_canvas", { page: this.id }, "Page {{page}}") - .then(msg => { - canvas.setAttribute("aria-label", msg); - }); + this.l10n.get("page_canvas", { page: this.id }).then(msg => { + canvas.setAttribute("aria-label", msg); + }); // Keep the canvas hidden until the first draw callback, or until drawing // is complete when `!this.renderingQueue`, to prevent black flickering. diff --git a/web/pdf_print_service.js b/web/pdf_print_service.js index 8ac57ccf3..e384aaafe 100644 --- a/web/pdf_print_service.js +++ b/web/pdf_print_service.js @@ -308,7 +308,7 @@ function renderProgress(index, total, l10n) { const progressBar = progressContainer.querySelector("progress"); const progressPerc = progressContainer.querySelector(".relative-progress"); progressBar.value = progress; - l10n.get("print_progress_percent", { progress }, progress + "%").then(msg => { + l10n.get("print_progress_percent", { progress }).then(msg => { progressPerc.textContent = msg; }); } diff --git a/web/pdf_sidebar.js b/web/pdf_sidebar.js index 37e26a499..4faaa4da3 100644 --- a/web/pdf_sidebar.js +++ b/web/pdf_sidebar.js @@ -339,15 +339,9 @@ class PDFSidebar { * @private */ _showUINotification() { - this.l10n - .get( - "toggle_sidebar_notification2.title", - null, - "Toggle Sidebar (document contains outline/attachments/layers)" - ) - .then(msg => { - this.toggleButton.title = msg; - }); + this.l10n.get("toggle_sidebar_notification2.title").then(msg => { + this.toggleButton.title = msg; + }); if (!this.isOpen) { // Only show the notification on the `toggleButton` if the sidebar is @@ -367,11 +361,9 @@ class PDFSidebar { } if (reset) { - this.l10n - .get("toggle_sidebar.title", null, "Toggle Sidebar") - .then(msg => { - this.toggleButton.title = msg; - }); + this.l10n.get("toggle_sidebar.title").then(msg => { + this.toggleButton.title = msg; + }); } } diff --git a/web/pdf_thumbnail_view.js b/web/pdf_thumbnail_view.js index a990032fe..ac8899089 100644 --- a/web/pdf_thumbnail_view.js +++ b/web/pdf_thumbnail_view.js @@ -467,19 +467,15 @@ class PDFThumbnailView { } get _thumbPageTitle() { - return this.l10n.get( - "thumb_page_title", - { page: this.pageLabel ?? this.id }, - "Page {{page}}" - ); + return this.l10n.get("thumb_page_title", { + page: this.pageLabel ?? this.id, + }); } get _thumbPageCanvas() { - return this.l10n.get( - "thumb_page_canvas", - { page: this.pageLabel ?? this.id }, - "Thumbnail of Page {{page}}" - ); + return this.l10n.get("thumb_page_canvas", { + page: this.pageLabel ?? this.id, + }); } /** diff --git a/web/pdf_viewer.component.js b/web/pdf_viewer.component.js index 38d944f15..1042188c1 100644 --- a/web/pdf_viewer.component.js +++ b/web/pdf_viewer.component.js @@ -21,10 +21,11 @@ import { DefaultTextLayerFactory, TextLayerBuilder, } from "./text_layer_builder.js"; -import { EventBus, NullL10n, ProgressBar } from "./ui_utils.js"; +import { EventBus, ProgressBar } from "./ui_utils.js"; import { PDFLinkService, SimpleLinkService } from "./pdf_link_service.js"; import { DownloadManager } from "./download_manager.js"; import { GenericL10n } from "./genericl10n.js"; +import { NullL10n } from "./l10n_utils.js"; import { PDFFindController } from "./pdf_find_controller.js"; import { PDFHistory } from "./pdf_history.js"; import { PDFPageView } from "./pdf_page_view.js"; diff --git a/web/toolbar.js b/web/toolbar.js index 116b6e401..6b6353dbc 100644 --- a/web/toolbar.js +++ b/web/toolbar.js @@ -177,26 +177,18 @@ class Toolbar { items.pageNumber.type = "text"; } else { items.pageNumber.type = "number"; - this.l10n - .get("of_pages", { pagesCount }, "of {{pagesCount}}") - .then(msg => { - items.numPages.textContent = msg; - }); + this.l10n.get("of_pages", { pagesCount }).then(msg => { + items.numPages.textContent = msg; + }); } items.pageNumber.max = pagesCount; } if (this.hasPageLabels) { items.pageNumber.value = this.pageLabel; - this.l10n - .get( - "page_of_pages", - { pageNumber, pagesCount }, - "({{pageNumber}} of {{pagesCount}})" - ) - .then(msg => { - items.numPages.textContent = msg; - }); + this.l10n.get("page_of_pages", { pageNumber, pagesCount }).then(msg => { + items.numPages.textContent = msg; + }); } else { items.pageNumber.value = pageNumber; } @@ -207,9 +199,8 @@ class Toolbar { items.zoomOut.disabled = pageScale <= MIN_SCALE; items.zoomIn.disabled = pageScale >= MAX_SCALE; - const customScale = Math.round(pageScale * 10000) / 100; this.l10n - .get("page_scale_percent", { scale: customScale }, "{{scale}}%") + .get("page_scale_percent", { scale: Math.round(pageScale * 10000) / 100 }) .then(msg => { let predefinedValueFound = false; for (const option of items.scaleSelect.options) { @@ -242,10 +233,10 @@ class Toolbar { const { items, l10n } = this; const predefinedValuesPromise = Promise.all([ - l10n.get("page_scale_auto", null, "Automatic Zoom"), - l10n.get("page_scale_actual", null, "Actual Size"), - l10n.get("page_scale_fit", null, "Page Fit"), - l10n.get("page_scale_width", null, "Page Width"), + l10n.get("page_scale_auto"), + l10n.get("page_scale_actual"), + l10n.get("page_scale_fit"), + l10n.get("page_scale_width"), ]); // The temporary canvas is used to measure text length in the DOM. diff --git a/web/ui_utils.js b/web/ui_utils.js index 0c5eda291..addfb90f9 100644 --- a/web/ui_utils.js +++ b/web/ui_utils.js @@ -69,36 +69,6 @@ const SpreadMode = { // Used by `PDFViewerApplication`, and by the API unit-tests. const AutoPrintRegExp = /\bprint\s*\(/; -// Replaces {{arguments}} with their values. -function formatL10nValue(text, args) { - if (!args) { - return text; - } - return text.replace(/\{\{\s*(\w+)\s*\}\}/g, (all, name) => { - return name in args ? args[name] : "{{" + name + "}}"; - }); -} - -/** - * No-op implementation of the localization service. - * @implements {IL10n} - */ -const NullL10n = { - async getLanguage() { - return "en-us"; - }, - - async getDirection() { - return "ltr"; - }, - - async get(property, args, fallback) { - return formatL10nValue(fallback, args); - }, - - async translate(element) {}, -}; - /** * Returns scale factor for the canvas. It makes sense for the HiDPI displays. * @returns {Object} The object with horizontal (sx) and vertical (sy) @@ -1057,7 +1027,6 @@ export { noContextMenuHandler, normalizeWheelEventDelta, normalizeWheelEventDirection, - NullL10n, parseQueryString, PresentationModeState, ProgressBar,