From 038668bf8cbf73490e06a70b4d24364499ab484e Mon Sep 17 00:00:00 2001 From: Jonas Jenwald <jonas.jenwald@gmail.com> Date: Mon, 22 Feb 2021 12:43:16 +0100 Subject: [PATCH] Collect all l10n fallback strings, used in the viewer, in one helper function (PR 12981 follow-up) Rather than having to spell out the English fallback strings at *every* single `IL10n.get` call-site throughout the viewer, we can simplify things by collecting them in *one* central spot. This provides a much better overview of the fallback l10n strings used, which makes future changes easier and ensures that fallback strings occuring in multiple places cannot accidentally get out of sync. Furthermore, by making the `fallback` parameter of the `IL10n.get` method *optional*[1] many of the call-sites (and their surrounding code) become a lot less verbose. --- [1] It's obviously still possible to pass in a fallback string, it's just not required. --- web/annotation_layer_builder.js | 2 +- web/app.js | 48 ++++-------- web/base_viewer.js | 2 +- web/firefoxcom.js | 5 +- web/genericl10n.js | 5 +- web/l10n_utils.js | 127 ++++++++++++++++++++++++++++++++ web/password_prompt.js | 36 +++------ web/pdf_document_properties.js | 99 +++++++++---------------- web/pdf_find_bar.js | 67 ++++------------- web/pdf_layer_viewer.js | 6 +- web/pdf_page_view.js | 10 +-- web/pdf_print_service.js | 2 +- web/pdf_sidebar.js | 20 ++--- web/pdf_thumbnail_view.js | 16 ++-- web/pdf_viewer.component.js | 3 +- web/toolbar.js | 31 +++----- web/ui_utils.js | 31 -------- 17 files changed, 238 insertions(+), 272 deletions(-) create mode 100644 web/l10n_utils.js 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,