From f1722c73344c64449ea9beea2e893cf8a24d06af Mon Sep 17 00:00:00 2001 From: "Louis Wicket (wil)" Date: Fri, 4 Aug 2023 11:28:59 +0000 Subject: [PATCH] [IMP] *: unify sprintf and gettext Improve gettext to directly handle value injection within translations, removing the need for sprintf. closes odoo/odoo#123932 Related: odoo/enterprise#45370 Signed-off-by: Samuel Degueldre (sad) --- .../analytic_distribution.js | 3 +-- .../static/src/password_meter.js | 7 ++---- .../static/src/password_policy.js | 7 +++--- .../static/src/js/password_meter.js | 9 +++---- .../static/src/import_action/import_action.js | 8 +++---- .../import_data_options.js | 3 +-- addons/base_import/static/src/import_model.js | 9 ++++--- .../static/src/add_to_board/add_to_board.js | 3 +-- .../forecast_kanban_column_quick_create.js | 3 +-- .../greeting_message/greeting_message.js | 3 +-- .../static/src/persona_service_patch.js | 3 +-- .../static/src/client_action/sign_invoice.js | 5 +--- addons/l10n_fr_pos_cert/static/src/js/pos.js | 3 +-- .../static/src/components/ke_proxy_hook.js | 12 +++++----- .../static/src/core/common/attachment_list.js | 3 +-- .../mail/static/src/core/common/composer.js | 21 +++++----------- .../src/core/common/message_reactions.js | 19 +++++++-------- .../static/src/core/common/message_service.js | 5 +--- .../src/core/common/out_of_focus_service.js | 6 ++--- .../static/src/core/common/thread_model.js | 3 +-- addons/mail/static/src/core/web/activity.js | 3 +-- .../core/web/activity_list_popover_item.js | 5 ++-- .../src/core/web/follower_subtype_dialog.js | 5 +--- .../static/src/core/web/messaging_menu.js | 3 +-- .../src/core/web/suggested_recipient.js | 6 +---- .../discuss/call/common/call_context_menu.js | 5 ++-- .../src/discuss/call/common/rtc_service.js | 16 +++++-------- .../common/discuss_core_common_service.js | 12 ++++------ .../core/web/discuss_core_web_service.js | 5 ++-- .../message_pin/common/message_pin_service.js | 8 +++---- .../src/discuss/typing/common/typing.js | 15 +++--------- .../voice_message/common/voice_recorder.js | 3 +-- .../views/web/activity/activity_controller.js | 3 +-- .../many2many_tags_email.js | 3 +-- .../static/src/js/mailing_portal.js | 12 ++++++---- .../static/src/js/payment_form_mixin.js | 4 ++-- .../navbar/cash_move_popup/cash_move_popup.js | 8 ++----- .../app/navbar/closing_popup/closing_popup.js | 9 +++---- .../app/screens/partner_list/partner_list.js | 9 ++----- .../screens/payment_screen/payment_screen.js | 5 ++-- .../product_list/product_list.js | 9 ++----- .../order_details/orderline_details.js | 5 ++-- .../screens/ticket_screen/ticket_screen.js | 13 ++++------ .../static/src/app/store/models.js | 11 ++++----- .../static/src/app/store/pos_store.js | 21 +++++++--------- .../portal/static/src/js/portal_composer.js | 5 ++-- addons/portal/static/src/js/portal_sidebar.js | 5 ++-- .../pos_adyen/static/src/app/payment_adyen.js | 6 ++--- .../discount_button/discount_button.js | 5 ++-- .../static/src/app/epson_printer.js | 7 ++---- .../components/partner_line/partner_line.js | 3 +-- .../product_screen/product_screen.js | 7 ++---- .../static/src/overrides/models/loyalty.js | 5 ++-- .../static/src/overrides/models/pos_store.js | 7 ++---- .../screens/payment_screen/payment_screen.js | 13 +++++++--- .../table_guests_button.js | 6 +---- .../src/app/floor_screen/floor_screen.js | 5 ++-- .../sale_order_management_screen.js | 10 ++++---- .../static/src/app/payment_stripe.js | 7 +++--- .../product_pricelist_report.js | 7 +++--- .../project_task_priority_switch_field.js | 3 +-- .../project_task_state_selection.js | 3 +-- .../fields/stock_move_line_x2_many_field.js | 3 +-- .../survey_question_trigger.js | 20 +++++++++------- .../domain_selector_autocomplete.js | 9 ++++--- .../static/src/core/domain_selector/utils.js | 3 +-- .../file_upload/file_upload_progress_bar.js | 6 +++-- .../file_upload_progress_record.js | 5 ++-- .../core/file_upload/file_upload_service.js | 3 +-- addons/web/static/src/core/l10n/dates.js | 3 +-- .../web/static/src/core/l10n/translation.js | 24 +++++++++++++++---- addons/web/static/src/legacy/js/core/dates.js | 5 ++-- addons/web/static/src/legacy/js/core/time.js | 5 ++-- .../static/src/legacy/js/core/translation.js | 10 +++++--- .../model/relational_model/dynamic_list.js | 9 ++++--- .../src/search/control_panel/control_panel.js | 3 +-- addons/web/static/src/search/search_model.js | 7 +++--- .../src/views/calendar/calendar_controller.js | 3 +-- .../filter_panel/calendar_filter_panel.js | 7 +++--- .../static/src/views/fields/file_handler.js | 10 +++----- .../web/static/src/views/fields/formatters.js | 4 ++-- .../views/fields/many2one/many2one_field.js | 3 +-- .../fields/properties/properties_field.js | 11 ++++----- .../fields/properties/property_definition.js | 5 ++-- .../views/fields/properties/property_tags.js | 3 +-- .../src/views/fields/relational_utils.js | 15 ++++++------ .../remaining_days/remaining_days_field.js | 5 ++-- .../state_selection/state_selection_field.js | 3 +-- .../views/fields/statusbar/statusbar_field.js | 6 ++--- .../src/views/fields/translation_dialog.js | 3 +-- .../src/views/fields/x2many/x2many_field.js | 3 +-- .../web/static/src/views/graph/graph_model.js | 3 +-- .../static/src/views/kanban/kanban_header.js | 3 +-- .../static/src/views/kanban/kanban_record.js | 7 ++---- .../src/views/kanban/kanban_renderer.js | 3 +-- .../src/webclient/actions/action_service.js | 6 +---- .../src/webclient/actions/client_actions.js | 5 +--- .../widgets/res_config_invite_users.js | 7 +++--- .../tests/core/l10n/translation_tests.js | 20 ++++++++++++++++ addons/web_editor/static/src/js/common/ace.js | 7 +++--- .../static/src/js/editor/snippets.editor.js | 14 +++++------ .../static/src/js/editor/snippets.options.js | 11 ++++----- .../components/media_dialog/image_selector.js | 5 ++-- .../website_preview/website_preview.js | 6 +---- .../static/src/components/views/page_list.js | 8 +------ .../static/src/js/editor/snippets.options.js | 4 ++-- .../website/static/src/js/tours/tour_utils.js | 9 ++++--- .../src/services/website_custom_menus.js | 3 +-- .../static/src/snippets/s_website_form/000.js | 20 +++++++++------- .../src/snippets/s_website_form/options.js | 6 ++--- .../static/src/systray_items/new_content.js | 4 +--- .../static/src/js/website_blog.js | 8 ++++--- .../static/src/js/crm_partner_assign.js | 3 +-- .../static/src/js/event_track_reminder.js | 3 +-- .../static/src/js/website_forum.js | 6 ++--- .../static/src/core/persona_model_patch.js | 3 +-- .../static/src/js/website_payment_form.js | 3 +-- .../static/src/snippets/s_donation/000.js | 3 +-- .../static/src/js/sale_variant_mixin.js | 3 +-- .../static/src/js/website_sale_reorder.js | 13 +++++----- .../static/src/js/portal_chatter.js | 3 +-- .../static/src/js/slides_course_tag_add.js | 3 +-- .../static/src/js/slides_upload.js | 13 +++++----- 123 files changed, 353 insertions(+), 495 deletions(-) diff --git a/addons/analytic/static/src/components/analytic_distribution/analytic_distribution.js b/addons/analytic/static/src/components/analytic_distribution/analytic_distribution.js index e9564114063..af21cafd195 100644 --- a/addons/analytic/static/src/components/analytic_distribution/analytic_distribution.js +++ b/addons/analytic/static/src/components/analytic_distribution/analytic_distribution.js @@ -7,7 +7,6 @@ import { getNextTabableElement, getPreviousTabableElement } from "@web/core/util import { usePosition } from "@web/core/position_hook"; import { getActiveHotkey } from "@web/core/hotkeys/hotkey_service"; import { shallowEqual } from "@web/core/utils/arrays"; -import { sprintf } from "@web/core/utils/strings"; import { _t } from "@web/core/l10n/translation"; import { AutoComplete } from "@web/core/autocomplete/autocomplete"; import { useRecordObserver } from "@web/model/relational_model/utils"; @@ -272,7 +271,7 @@ export class AnalyticDistribution extends Component { if (searchTerm.length) { dynamicFilters = [ { - description: sprintf(this.env._t("Quick search: %s"), searchTerm), + description: this.env._t("Quick search: %s", searchTerm), domain: this.searchAnalyticDomain(searchTerm), }, ]; diff --git a/addons/auth_password_policy/static/src/password_meter.js b/addons/auth_password_policy/static/src/password_meter.js index 46fd2e6b5c6..13e128a4f15 100644 --- a/addons/auth_password_policy/static/src/password_meter.js +++ b/addons/auth_password_policy/static/src/password_meter.js @@ -1,16 +1,13 @@ /** @odoo-module **/ -import { sprintf } from "@web/core/utils/strings"; import { computeScore } from "./password_policy"; const { Component, xml } = owl; export class Meter extends Component { get title() { - return sprintf( - this.env._t( - "Required: %s\n\nHint: to increase password strength, increase length, use multiple words, and use non-letter characters." - ), + return this.env._t( + "Required: %s\n\nHint: to increase password strength, increase length, use multiple words, and use non-letter characters.", String(this.props.required) || this.env._t("no requirements") ); } diff --git a/addons/auth_password_policy/static/src/password_policy.js b/addons/auth_password_policy/static/src/password_policy.js index 1b7429cfb7a..bde9d686b56 100644 --- a/addons/auth_password_policy/static/src/password_policy.js +++ b/addons/auth_password_policy/static/src/password_policy.js @@ -1,7 +1,6 @@ /** @odoo-module **/ import { _t } from "@web/core/l10n/translation"; -import { sprintf } from "@web/core/utils/strings"; export class Policy { /** @@ -27,13 +26,13 @@ export class ConcretePolicy extends Policy { toString() { const msgs = []; if (this.minlength > 1) { - msgs.push(sprintf(_t("at least %s characters"), this.minlength)); + msgs.push(_t("at least %s characters", this.minlength)); } if (this.minwords > 1) { - msgs.push(sprintf(_t("at least %s words"), this.minwords)); + msgs.push(_t("at least %s words", this.minwords)); } if (this.minclasses > 1) { - msgs.push(sprintf(_t("at least %s character classes"), this.minclasses)); + msgs.push(_t("at least %s character classes", this.minclasses)); } return msgs.join(", "); } diff --git a/addons/auth_password_policy_signup/static/src/js/password_meter.js b/addons/auth_password_policy_signup/static/src/js/password_meter.js index d47f94dfffa..bb66b1a5e5c 100644 --- a/addons/auth_password_policy_signup/static/src/js/password_meter.js +++ b/addons/auth_password_policy_signup/static/src/js/password_meter.js @@ -2,7 +2,6 @@ import Widget from "@web/legacy/js/core/widget"; import { computeScore } from "@auth_password_policy/password_policy"; -import { sprintf } from "@web/core/utils/strings"; import { translationIsReady, _t } from "@web/core/l10n/translation"; export default Widget.extend({ @@ -27,12 +26,10 @@ export default Widget.extend({ }, start() { var helpMessage = _t( - "Required: %s.\n\nHint: increase length, use multiple words and use non-letter characters to increase your password's strength." - ); - this.el.setAttribute( - "title", - sprintf(helpMessage, String(this._required) || _t("no requirements")) + "Required: %s.\n\nHint: increase length, use multiple words and use non-letter characters to increase your password's strength.", + String(this._required) || _t("no requirements") ); + this.el.setAttribute("title", helpMessage); return this._super().then(function () {}); }, /** diff --git a/addons/base_import/static/src/import_action/import_action.js b/addons/base_import/static/src/import_action/import_action.js index c9cddf052cc..599e35f44b9 100644 --- a/addons/base_import/static/src/import_action/import_action.js +++ b/addons/base_import/static/src/import_action/import_action.js @@ -3,7 +3,6 @@ import { Component, onWillStart, onMounted, useState } from "@odoo/owl"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { FileInput } from "@web/core/file_input/file_input"; import { useImportModel } from "../import_model"; import { ImportDataContent } from "../import_data_content/import_data_content"; @@ -171,10 +170,9 @@ export class ImportAction extends Component { } if (!isTest && res.ids.length) { - this.notification.add( - sprintf(this.env._t("%s records successfully imported"), res.ids.length), - { type: "success" } - ); + this.notification.add(this.env._t("%s records successfully imported", res.ids.length), { + type: "success", + }); this.exit(); } } diff --git a/addons/base_import/static/src/import_data_options/import_data_options.js b/addons/base_import/static/src/import_data_options/import_data_options.js index 40781839bdc..dcaa739a330 100644 --- a/addons/base_import/static/src/import_data_options/import_data_options.js +++ b/addons/base_import/static/src/import_data_options/import_data_options.js @@ -3,7 +3,6 @@ import { Component, useState, onWillStart } from "@odoo/owl"; import { _t } from "@web/core/l10n/translation"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; export class ImportDataOptions extends Component { static template = "ImportDataOptions"; @@ -45,7 +44,7 @@ export class ImportDataOptions extends Component { const fields = await this.orm.call(this.currentModel, "fields_get"); const selection = fields[this.props.fieldInfo.name].selection.map((opt) => [ opt[0], - sprintf(_t("Set to: %s"), opt[1]), + _t("Set to: %s", opt[1]), ]); options.push(...selection); } else { diff --git a/addons/base_import/static/src/import_model.js b/addons/base_import/static/src/import_model.js index 1bfbfccde45..863c74f6b11 100644 --- a/addons/base_import/static/src/import_model.js +++ b/addons/base_import/static/src/import_model.js @@ -5,7 +5,6 @@ import { registry } from "@web/core/registry"; import { pick } from "@web/core/utils/objects"; import { groupBy, sortBy } from "@web/core/utils/arrays"; import { memoize } from "@web/core/utils/functions"; -import { sprintf } from "@web/core/utils/strings"; import { useState } from "@odoo/owl"; import { ImportBlockUI } from "./import_block_ui"; @@ -281,8 +280,8 @@ export class BaseImportModel { if (!importRes.hasError) { if (importRes.nextrow) { this._addMessage("warning", [ - sprintf( - _t("Click 'Resume' to proceed with the import, resuming at line %s."), + _t( + "Click 'Resume' to proceed with the import, resuming at line %s.", importRes.nextrow + 1 ), _t("You can test or reload your file before resuming the import."), @@ -445,8 +444,8 @@ export class BaseImportModel { if (error.record !== undefined) { this._addMessage("danger", [ error.rows.from === error.rows.to - ? sprintf(_t('Error at row %s: "%s"'), error.record, error.message) - : sprintf(_t("%s at multiple rows"), error.message), + ? _t('Error at row %s: "%s"', error.record, error.message) + : _t("%s at multiple rows", error.message), ]); } // Handle global errors. diff --git a/addons/board/static/src/add_to_board/add_to_board.js b/addons/board/static/src/add_to_board/add_to_board.js index 2f3b3e48cb4..f581b83ed0c 100644 --- a/addons/board/static/src/add_to_board/add_to_board.js +++ b/addons/board/static/src/add_to_board/add_to_board.js @@ -3,7 +3,6 @@ import { Dropdown } from "@web/core/dropdown/dropdown"; import { registry } from "@web/core/registry"; import { useAutofocus, useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; const { Component, useState } = owl; const cogMenuRegistry = registry.category("cogMenu"); @@ -63,7 +62,7 @@ export class AddToBoard extends Component { this.notification.add( this.env._t("Please refresh your browser for the changes to take effect."), { - title: sprintf(this.env._t(`"%s" added to dashboard`), this.state.name), + title: this.env._t("“%s” added to dashboard", this.state.name), type: "warning", } ); diff --git a/addons/crm/static/src/views/forecast_kanban/forecast_kanban_column_quick_create.js b/addons/crm/static/src/views/forecast_kanban/forecast_kanban_column_quick_create.js index 081658758c8..279ace8fb54 100644 --- a/addons/crm/static/src/views/forecast_kanban/forecast_kanban_column_quick_create.js +++ b/addons/crm/static/src/views/forecast_kanban/forecast_kanban_column_quick_create.js @@ -1,7 +1,6 @@ /** @odoo-module **/ import { _t } from "@web/core/l10n/translation"; -import { sprintf } from "@web/core/utils/strings"; import { INTERVAL_OPTIONS } from "@web/search/utils/dates"; import { KanbanColumnQuickCreate } from "@web/views/kanban/kanban_column_quick_create"; @@ -12,7 +11,7 @@ export class ForecastKanbanColumnQuickCreate extends KanbanColumnQuickCreate { get relatedFieldName() { const { granularity = "month" } = this.props.groupByField; const { description } = INTERVAL_OPTIONS[granularity]; - return sprintf(_t("Add next %s"), description.toLocaleLowerCase()); + return _t("Add next %s", description.toLocaleLowerCase()); } /** * @override diff --git a/addons/hr_attendance/static/src/client_action/greeting_message/greeting_message.js b/addons/hr_attendance/static/src/client_action/greeting_message/greeting_message.js index 66f910b8720..60703fffcf8 100644 --- a/addons/hr_attendance/static/src/client_action/greeting_message/greeting_message.js +++ b/addons/hr_attendance/static/src/client_action/greeting_message/greeting_message.js @@ -5,7 +5,6 @@ import { Component, onWillStart, onWillUnmount, useState } from "@odoo/owl"; import { deserializeDateTime } from "@web/core/l10n/dates"; import { registry } from "@web/core/registry"; import { useBus, useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; const { Duration } = luxon; @@ -73,7 +72,7 @@ export class GreetingMessage extends Component { .toFormat("hh-mm") .split("-"); - this.hours_today = sprintf(this.env._t("%(hours)s hours, %(minutes)s minutes"), { + this.hours_today = this.env._t("%(hours)s hours, %(minutes)s minutes", { hours: duration[0], minutes: duration[1], }); diff --git a/addons/hr_holidays/static/src/persona_service_patch.js b/addons/hr_holidays/static/src/persona_service_patch.js index 6661c6e5066..865c37f6bae 100644 --- a/addons/hr_holidays/static/src/persona_service_patch.js +++ b/addons/hr_holidays/static/src/persona_service_patch.js @@ -4,7 +4,6 @@ import { personaService, PersonaService } from "@mail/core/common/persona_servic import { deserializeDateTime } from "@web/core/l10n/dates"; import { _t } from "@web/core/l10n/translation"; import { patch } from "@web/core/utils/patch"; -import { sprintf } from "@web/core/utils/strings"; const { DateTime } = luxon; @@ -35,6 +34,6 @@ patch(PersonaService.prototype, { // const fdate = date.toLocaleString(DateTime.TIME_SHORT); const fdate = date.toLocaleString(DateTime.DATE_MED); // const formattedDate = date.toLocaleDateString(localeCode, options); - return sprintf(_t("Out of office until %s"), fdate); + return _t("Out of office until %s", fdate); }, }); diff --git a/addons/l10n_eg_edi_eta/static/src/client_action/sign_invoice.js b/addons/l10n_eg_edi_eta/static/src/client_action/sign_invoice.js index a7c15906f01..ebd55b5a5b8 100644 --- a/addons/l10n_eg_edi_eta/static/src/client_action/sign_invoice.js +++ b/addons/l10n_eg_edi_eta/static/src/client_action/sign_invoice.js @@ -2,7 +2,6 @@ import { AlertDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; import { registry } from "@web/core/registry"; -import { sprintf } from "@web/core/utils/strings"; async function actionGetDrive(env, action, type) { const { drive_id, sign_host: host } = action.params; @@ -47,9 +46,7 @@ async function actionGetDrive(env, action, type) { unauthorized: _t("Unauthorized"), }; dialog.add(AlertDialog, { - body: - typeToErrorMessage[result.error] || - sprintf(_t(`Unexpected error: "%s"`), result.error), + body: typeToErrorMessage[result.error] || _t("Unexpected error: “%s”", result.error), }); } else if (result[key]) { await orm.call("l10n_eg_edi.thumb.drive", method, [[drive_id], result[key]]).catch(() => { diff --git a/addons/l10n_fr_pos_cert/static/src/js/pos.js b/addons/l10n_fr_pos_cert/static/src/js/pos.js index cbc3a915acc..ebe93775487 100644 --- a/addons/l10n_fr_pos_cert/static/src/js/pos.js +++ b/addons/l10n_fr_pos_cert/static/src/js/pos.js @@ -4,7 +4,6 @@ import { PosStore } from "@point_of_sale/app/store/pos_store"; import { Order, Orderline } from "@point_of_sale/app/store/models"; import { _t } from "@web/core/l10n/translation"; import { patch } from "@web/core/utils/patch"; -import { sprintf } from "@web/core/utils/strings"; import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup"; patch(PosStore.prototype, { @@ -13,7 +12,7 @@ patch(PosStore.prototype, { if (!this.company.country) { this.env.services.popup.add(ErrorPopup, { title: _t("Missing Country"), - body: sprintf(_t("The company %s doesn't have a country set."), this.company.name), + body: _t("The company %s doesn't have a country set.", this.company.name), }); return false; } diff --git a/addons/l10n_ke_edi_tremol/static/src/components/ke_proxy_hook.js b/addons/l10n_ke_edi_tremol/static/src/components/ke_proxy_hook.js index 7218c4e6610..42bf7da5041 100644 --- a/addons/l10n_ke_edi_tremol/static/src/components/ke_proxy_hook.js +++ b/addons/l10n_ke_edi_tremol/static/src/components/ke_proxy_hook.js @@ -1,5 +1,5 @@ /** @odoo-module **/ -import { sprintf } from "@web/core/utils/strings"; + import { useState } from "@odoo/owl"; import { useService } from "@web/core/utils/hooks"; import { _t } from "@web/core/l10n/translation"; @@ -37,7 +37,7 @@ export function useKEProxy({onAllSent}) { let progress; // keep track of when an error occurs for (const index in invoices) { let { move_id, messages, proxy_address, company_vat, name } = invoices[index]; - state.message = sprintf(_t("Posting invoice: %s"), name); + state.message = _t("Posting invoice: %s", name); try { progress = 'postToDevice'; let deviceResponse = await http.post( @@ -59,16 +59,16 @@ export function useKEProxy({onAllSent}) { state.error = true; switch (progress) { case 'postToDevice': - state.message = sprintf(_t("Error trying to connect to the middleware. Is the middleware running? \n Error message: %s"), e.message); + state.message = _t("Error trying to connect to the middleware. Is the middleware running? \n Error message: %s", e.message); break; case 'parseResponse': - state.message = sprintf(_t("Posting the invoice %s has failed with the message: \n %s"), name, e.message); + state.message = _t("Posting the invoice %s has failed with the message: \n %s", name, e.message); break; case 'updateInvoice': - state.message = sprintf(_t("Error trying to connect to Odoo. Check your internet connection. Error message: %s"), e.message); + state.message = _t("Error trying to connect to Odoo. Check your internet connection. Error message: %s", e.message); break; default: - state.message = sprintf(_t("Unexpected Error:\n %s"), e.message); + state.message = _t("Unexpected Error:\n %s", e.message); } break; } diff --git a/addons/mail/static/src/core/common/attachment_list.js b/addons/mail/static/src/core/common/attachment_list.js index 6ce82b6a850..b3fa6e7b5f2 100644 --- a/addons/mail/static/src/core/common/attachment_list.js +++ b/addons/mail/static/src/core/common/attachment_list.js @@ -6,7 +6,6 @@ import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_d import { useFileViewer } from "@web/core/file_viewer/file_viewer_hook"; import { _t } from "@web/core/l10n/translation"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { url } from "@web/core/utils/urls"; /** @@ -83,7 +82,7 @@ export class AttachmentList extends Component { return this.props.unlinkAttachment(attachment); } this.dialog.add(ConfirmationDialog, { - body: sprintf(_t('Do you really want to delete "%s"?'), attachment.filename), + body: _t('Do you really want to delete "%s"?', attachment.filename), cancel: () => {}, confirm: () => this.onConfirmUnlink(attachment), }); diff --git a/addons/mail/static/src/core/common/composer.js b/addons/mail/static/src/core/common/composer.js index 4a35055a827..fac5be3a908 100644 --- a/addons/mail/static/src/core/common/composer.js +++ b/addons/mail/static/src/core/common/composer.js @@ -27,7 +27,6 @@ import { import { browser } from "@web/core/browser/browser"; import { _t } from "@web/core/l10n/translation"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { FileUploader } from "@web/views/fields/file_handler"; const EDIT_CLICK_TYPE = { @@ -82,9 +81,7 @@ export class Composer extends Component { setup() { this.SEND_KEYBIND_TO_SEND = markup( - sprintf(_t("%(send_keybind)s to send"), { - send_keybind: this.sendKeybind, - }) + _t("%(send_keybind)s to send", { send_keybind: this.sendKeybind }) ); this.KEYBOARD = { NONE: "None", @@ -211,13 +208,9 @@ export class Composer extends Component { } if (this.thread) { if (this.thread.type === "channel") { - return sprintf(_t("Message #%(thread name)s…"), { - "thread name": this.thread.displayName, - }); + return _t("Message #%(thread name)s…", { "thread name": this.thread.displayName }); } - return sprintf(_t("Message %(thread name)s…"), { - "thread name": this.thread.displayName, - }); + return _t("Message %(thread name)s…", { "thread name": this.thread.displayName }); } return ""; } @@ -233,10 +226,8 @@ export class Composer extends Component { get CANCEL_OR_SAVE_EDIT_TEXT() { return markup( - sprintf( - _t( - "%(cancel_keybind)s to cancel, %(save_keybind)s to save" - ), + _t( + "%(cancel_keybind)s to cancel, %(save_keybind)s to save", { cancel_keybind: _t("Escape"), cancel_type: EDIT_CLICK_TYPE.CANCEL, @@ -556,7 +547,7 @@ export class Composer extends Component { const message = await this.threadService.post(this.thread, value, postData); if (this.props.composer.thread.type === "mailbox") { this.env.services.notification.add( - sprintf(_t('Message posted on "%s"'), message.originThread.displayName), + _t('Message posted on "%s"', message.originThread.displayName), { type: "info" } ); } diff --git a/addons/mail/static/src/core/common/message_reactions.js b/addons/mail/static/src/core/common/message_reactions.js index e712826f8a1..b0ef6a22960 100644 --- a/addons/mail/static/src/core/common/message_reactions.js +++ b/addons/mail/static/src/core/common/message_reactions.js @@ -6,7 +6,6 @@ import { Component, useState } from "@odoo/owl"; import { _t } from "@web/core/l10n/translation"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; export class MessageReactions extends Component { static props = ["message", "openReactionMenu"]; @@ -26,33 +25,33 @@ export class MessageReactions extends Component { ); switch (reaction.count) { case 1: - return sprintf(_t("%s has reacted with %s"), firstUserName, reaction.content); + return _t("%s has reacted with %s", firstUserName, reaction.content); case 2: - return sprintf( - _t("%s and %s have reacted with %s"), + return _t( + "%s and %s have reacted with %s", firstUserName, secondUserName, reaction.content ); case 3: - return sprintf( - _t("%s, %s, %s have reacted with %s"), + return _t( + "%s, %s, %s have reacted with %s", firstUserName, secondUserName, thirdUserName, reaction.content ); case 4: - return sprintf( - _t("%s, %s, %s and 1 other person have reacted with %s"), + return _t( + "%s, %s, %s and 1 other person have reacted with %s", firstUserName, secondUserName, thirdUserName, reaction.content ); default: - return sprintf( - _t("%s, %s, %s and %s other persons have reacted with %s"), + return _t( + "%s, %s, %s and %s other persons have reacted with %s", firstUserName, secondUserName, thirdUserName, diff --git a/addons/mail/static/src/core/common/message_service.js b/addons/mail/static/src/core/common/message_service.js index 177e8e80d73..d1d88fa513a 100644 --- a/addons/mail/static/src/core/common/message_service.js +++ b/addons/mail/static/src/core/common/message_service.js @@ -13,7 +13,6 @@ import { markup } from "@odoo/owl"; import { _t } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; -import { sprintf } from "@web/core/utils/strings"; const { DateTime } = luxon; @@ -161,9 +160,7 @@ export class MessageService { const thread = message.originThread; await this.env.services["mail.thread"].removeFollower(thread.selfFollower); this.env.services.notification.add( - sprintf(_t('You are no longer following "%(thread_name)s".'), { - thread_name: thread.name, - }), + _t('You are no longer following "%(thread_name)s".', { thread_name: thread.name }), { type: "success" } ); } diff --git a/addons/mail/static/src/core/common/out_of_focus_service.js b/addons/mail/static/src/core/common/out_of_focus_service.js index 7b4eb77390d..dc57092bad2 100644 --- a/addons/mail/static/src/core/common/out_of_focus_service.js +++ b/addons/mail/static/src/core/common/out_of_focus_service.js @@ -5,7 +5,6 @@ import { htmlToTextContentInline } from "@mail/utils/common/format"; import { browser } from "@web/core/browser/browser"; import { _t } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; -import { sprintf } from "@web/core/utils/strings"; import { url } from "@web/core/utils/urls"; const PREVIEW_MSG_MAX_SIZE = 350; // optimal for native English speakers @@ -43,7 +42,7 @@ export class OutOfFocusService { notificationTitle = _t("New message"); } else { if (channel.channel_type === "channel") { - notificationTitle = sprintf(_t("%(author name)s from %(channel name)s"), { + notificationTitle = _t("%(author name)s from %(channel name)s", { "author name": author.name, "channel name": channel.displayName, }); @@ -61,10 +60,9 @@ export class OutOfFocusService { type: "info", }); this.counter++; - const titlePattern = this.counter === 1 ? _t("%s Message") : _t("%s Messages"); this.busService.trigger("set_title_part", { part: "_chat", - title: sprintf(titlePattern, this.counter), + title: this.counter === 1 ? _t("1 Message") : _t("%s Messages", this.counter), }); } diff --git a/addons/mail/static/src/core/common/thread_model.js b/addons/mail/static/src/core/common/thread_model.js index 76727af49b9..aa676ad4439 100644 --- a/addons/mail/static/src/core/common/thread_model.js +++ b/addons/mail/static/src/core/common/thread_model.js @@ -5,7 +5,6 @@ import { createLocalId } from "@mail/utils/common/misc"; import { _t } from "@web/core/l10n/translation"; import { Deferred } from "@web/core/utils/concurrency"; -import { sprintf } from "@web/core/utils/strings"; /** * @typedef SeenInfo @@ -147,7 +146,7 @@ export class Thread { if (!this.authorizedGroupFullName) { return false; } - return sprintf(_t('Access restricted to group "%(groupFullName)s"'), { + return _t('Access restricted to group "%(groupFullName)s"', { groupFullName: this.authorizedGroupFullName, }); } diff --git a/addons/mail/static/src/core/web/activity.js b/addons/mail/static/src/core/web/activity.js index e0139eda984..1813d9d15d4 100644 --- a/addons/mail/static/src/core/web/activity.js +++ b/addons/mail/static/src/core/web/activity.js @@ -12,7 +12,6 @@ import { browser } from "@web/core/browser/browser"; import { _t } from "@web/core/l10n/translation"; import { usePopover } from "@web/core/popover/popover_hook"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { FileUploader } from "@web/views/fields/file_handler"; /** @@ -46,7 +45,7 @@ export class Activity extends Component { get displayName() { if (this.props.data.summary) { - return sprintf(_t("“%s”"), this.props.data.summary); + return _t("“%s”", this.props.data.summary); } return this.props.data.display_name; } diff --git a/addons/mail/static/src/core/web/activity_list_popover_item.js b/addons/mail/static/src/core/web/activity_list_popover_item.js index 67a741a05fd..a3741f451ad 100644 --- a/addons/mail/static/src/core/web/activity_list_popover_item.js +++ b/addons/mail/static/src/core/web/activity_list_popover_item.js @@ -9,7 +9,6 @@ import { Component, useState } from "@odoo/owl"; import { _t } from "@web/core/l10n/translation"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { url } from "@web/core/utils/urls"; import { FileUploader } from "@web/views/fields/file_handler"; @@ -56,11 +55,11 @@ export class ActivityListPopoverItem extends Component { } else if (diff === -1) { return _t("Yesterday"); } else if (diff < 0) { - return sprintf(_t("%s days overdue"), Math.round(Math.abs(diff))); + return _t("%s days overdue", Math.round(Math.abs(diff))); } else if (diff === 1) { return _t("Tomorrow"); } else { - return sprintf(_t("Due in %s days"), Math.round(Math.abs(diff))); + return _t("Due in %s days", Math.round(Math.abs(diff))); } } diff --git a/addons/mail/static/src/core/web/follower_subtype_dialog.js b/addons/mail/static/src/core/web/follower_subtype_dialog.js index a77ec381ef3..84ba26a4d18 100644 --- a/addons/mail/static/src/core/web/follower_subtype_dialog.js +++ b/addons/mail/static/src/core/web/follower_subtype_dialog.js @@ -5,7 +5,6 @@ import { Component, onWillStart, useState } from "@odoo/owl"; import { Dialog } from "@web/core/dialog/dialog"; import { _t } from "@web/core/l10n/translation"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; /** * @typedef {Object} SubtypeData @@ -71,8 +70,6 @@ export class FollowerSubtypeDialog extends Component { } get title() { - return sprintf(_t("Edit Subscription of %(name)s"), { - name: this.props.follower.partner.name, - }); + return _t("Edit Subscription of %(name)s", { name: this.props.follower.partner.name }); } } diff --git a/addons/mail/static/src/core/web/messaging_menu.js b/addons/mail/static/src/core/web/messaging_menu.js index daeb13597eb..ea04c76337a 100644 --- a/addons/mail/static/src/core/web/messaging_menu.js +++ b/addons/mail/static/src/core/web/messaging_menu.js @@ -13,7 +13,6 @@ import { Dropdown } from "@web/core/dropdown/dropdown"; import { _t } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; export class MessagingMenu extends Component { static components = { Dropdown, NotificationItem, ImStatus }; @@ -90,7 +89,7 @@ export class MessagingMenu extends Component { get notificationRequest() { return { body: _t("Enable desktop notifications to chat"), - displayName: sprintf(_t("%s has a request"), this.store.odoobot.name), + displayName: _t("%s has a request", this.store.odoobot.name), iconSrc: this.threadService.avatarUrl(this.store.odoobot), partner: this.store.odoobot, isLast: this.threads.length === 0 && this.store.notificationGroups.length === 0, diff --git a/addons/mail/static/src/core/web/suggested_recipient.js b/addons/mail/static/src/core/web/suggested_recipient.js index 1c1556a4577..2ed8a3402d1 100644 --- a/addons/mail/static/src/core/web/suggested_recipient.js +++ b/addons/mail/static/src/core/web/suggested_recipient.js @@ -4,7 +4,6 @@ import { Component } from "@odoo/owl"; import { _t } from "@web/core/l10n/translation"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { FormViewDialog } from "@web/views/view_dialogs/form_view_dialog"; /** @@ -23,10 +22,7 @@ export class SuggestedRecipient extends Component { } get titleText() { - return sprintf( - _t("Add as recipient and follower (reason: %s)"), - this.props.recipient.reason - ); + return _t("Add as recipient and follower (reason: %s)", this.props.recipient.reason); } onChangeCheckbox(ev) { diff --git a/addons/mail/static/src/discuss/call/common/call_context_menu.js b/addons/mail/static/src/discuss/call/common/call_context_menu.js index 37ee85d7e27..4163cf3b5ac 100644 --- a/addons/mail/static/src/discuss/call/common/call_context_menu.js +++ b/addons/mail/static/src/discuss/call/common/call_context_menu.js @@ -5,7 +5,6 @@ import { Component, onMounted, onWillUnmount, useState } from "@odoo/owl"; import { browser } from "@web/core/browser/browser"; import { _t } from "@web/core/l10n/translation"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; const PROTOCOLS_TEXT = { host: "HOST", srflx: "STUN", prflx: "STUN", relay: "TURN" }; @@ -34,7 +33,7 @@ export class CallContextMenu extends Component { if (!this.props.rtcSession.remoteCandidateType) { return _t("no connection"); } - return sprintf(_t("%(candidateType)s (%(protocol)s)"), { + return _t("%(candidateType)s (%(protocol)s)", { candidateType: this.props.rtcSession.remoteCandidateType, protocol: PROTOCOLS_TEXT[this.props.rtcSession.remoteCandidateType], }); @@ -44,7 +43,7 @@ export class CallContextMenu extends Component { if (!this.props.rtcSession.localCandidateType) { return _t("no connection"); } - return sprintf(_t("%(candidateType)s (%(protocol)s)"), { + return _t("%(candidateType)s (%(protocol)s)", { candidateType: this.props.rtcSession.localCandidateType, protocol: PROTOCOLS_TEXT[this.props.rtcSession.localCandidateType], }); diff --git a/addons/mail/static/src/discuss/call/common/rtc_service.js b/addons/mail/static/src/discuss/call/common/rtc_service.js index def9166b747..5565c521efe 100644 --- a/addons/mail/static/src/discuss/call/common/rtc_service.js +++ b/addons/mail/static/src/discuss/call/common/rtc_service.js @@ -11,7 +11,6 @@ import { reactive } from "@odoo/owl"; import { browser } from "@web/core/browser/browser"; import { _t } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; -import { sprintf } from "@web/core/utils/strings"; import { debounce } from "@web/core/utils/timing"; const ORDERED_TRANSCEIVER_NAMES = ["audio", "video"]; @@ -492,7 +491,7 @@ export class Rtc { if (session.raisingHand) { this.addCallNotification({ id: notificationId, - text: sprintf(_t("%s raised a hand"), session.name), + text: _t("%s raised a hand", session.name), }); } else { this.removeCallNotification(notificationId); @@ -1236,9 +1235,9 @@ export class Rtc { } catch { const str = type === "camera" - ? _t('%s" requires "camera" access') - : _t('%s" requires "screen recording" access'); - this.notification.add(sprintf(str, window.location.host), { type: "warning" }); + ? _t('%s" requires "camera" access', window.location.host) + : _t('%s" requires "screen recording" access', window.location.host); + this.notification.add(str, { type: "warning" }); stopVideo(); return; } @@ -1252,10 +1251,7 @@ export class Rtc { videoStream = await this.blurManager.stream; } catch (_e) { this.notification.add( - sprintf(_t("%(name)s: %(message)s)"), { - name: _e.name, - message: _e.message, - }), + _t("%(name)s: %(message)s)", { name: _e.name, message: _e.message }), { type: "warning" } ); this.userSettingsService.useBlur = false; @@ -1330,7 +1326,7 @@ export class Rtc { audioTrack = audioStream.getAudioTracks()[0]; } catch { this.notification.add( - sprintf(_t('"%(hostname)s" requires microphone access'), { + _t('"%(hostname)s" requires microphone access', { hostname: window.location.host, }), { type: "warning" } diff --git a/addons/mail/static/src/discuss/core/common/discuss_core_common_service.js b/addons/mail/static/src/discuss/core/common/discuss_core_common_service.js index 119dfc8f4d4..6bf521e7bbf 100644 --- a/addons/mail/static/src/discuss/core/common/discuss_core_common_service.js +++ b/addons/mail/static/src/discuss/core/common/discuss_core_common_service.js @@ -8,7 +8,6 @@ import { markup, reactive, useState } from "@odoo/owl"; import { _t } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; export class DiscussCoreCommon { /** @@ -45,7 +44,7 @@ export class DiscussCoreCommon { }); if (invitedByUserId && invitedByUserId !== this.store.user?.user?.id) { this.notificationService.add( - sprintf(_t("You have been invited to #%s"), thread.displayName), + _t("You have been invited to #%s", thread.displayName), { type: "info" } ); } @@ -69,10 +68,9 @@ export class DiscussCoreCommon { if (thread.localId === this.store.discuss.threadLocalId) { this.store.discuss.threadLocalId = undefined; } - this.notificationService.add( - sprintf(_t("You unsubscribed from %s."), thread.displayName), - { type: "info" } - ); + this.notificationService.add(_t("You unsubscribed from %s.", thread.displayName), { + type: "info", + }); }); this.busService.subscribe("discuss.channel/legacy_insert", (payload) => { this.threadService.insert({ @@ -112,7 +110,7 @@ export class DiscussCoreCommon { if (thread) { thread.is_pinned = false; this.notificationService.add( - sprintf(_t("You unpinned your conversation with %s"), thread.displayName), + _t("You unpinned your conversation with %s", thread.displayName), { type: "info" } ); } diff --git a/addons/mail/static/src/discuss/core/web/discuss_core_web_service.js b/addons/mail/static/src/discuss/core/web/discuss_core_web_service.js index c5e8fe6e83d..f2c4337a815 100644 --- a/addons/mail/static/src/discuss/core/web/discuss_core_web_service.js +++ b/addons/mail/static/src/discuss/core/web/discuss_core_web_service.js @@ -5,7 +5,6 @@ import { reactive } from "@odoo/owl"; import { _t } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; -import { sprintf } from "@web/core/utils/strings"; export class DiscussCoreWeb { /** @@ -73,8 +72,8 @@ export class DiscussCoreWeb { // If the current user invited a new user, and the new user is // connecting for the first time while the current user is present // then open a chat for the current user with the new user. - const notification = sprintf( - _t("%(user)s connected. This is their first connection. Wish them luck."), + const notification = _t( + "%(user)s connected. This is their first connection. Wish them luck.", { user: username } ); this.notificationService.add(notification, { type: "info" }); diff --git a/addons/mail/static/src/discuss/message_pin/common/message_pin_service.js b/addons/mail/static/src/discuss/message_pin/common/message_pin_service.js index 7c3a49d774a..46c5ea4cc3a 100644 --- a/addons/mail/static/src/discuss/message_pin/common/message_pin_service.js +++ b/addons/mail/static/src/discuss/message_pin/common/message_pin_service.js @@ -8,7 +8,6 @@ import { markup, reactive, useState } from "@odoo/owl"; import { _t } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; export const OTHER_LONG_TYPING = 60000; @@ -159,10 +158,9 @@ export class MessagePin { confirmText: _t("Yeah, pin it!"), message: message, messageComponent: Message, - prompt: sprintf( - _t("You sure want this message pinned to %(conversation)s forever and ever?"), - { conversation: thread.prefix + thread.displayName } - ), + prompt: _t("You sure want this message pinned to %(conversation)s forever and ever?", { + conversation: thread.prefix + thread.displayName, + }), size: "md", title: _t("Pin It"), onConfirm: () => this.setPin(message, true), diff --git a/addons/mail/static/src/discuss/typing/common/typing.js b/addons/mail/static/src/discuss/typing/common/typing.js index e7e8f5cce9f..e95d07d1e8a 100644 --- a/addons/mail/static/src/discuss/typing/common/typing.js +++ b/addons/mail/static/src/discuss/typing/common/typing.js @@ -6,7 +6,6 @@ import { useTypingService } from "@mail/discuss/typing/common/typing_service"; import { Component } from "@odoo/owl"; import { _t } from "@web/core/l10n/translation"; -import { sprintf } from "@web/core/utils/strings"; /** * @typedef {Object} Props @@ -34,19 +33,11 @@ export class Typing extends Component { .getTypingMembers(this.props.channel) .map(({ persona }) => this.props.channel.getMemberName(persona)); if (typingMemberNames.length === 1) { - return sprintf(_t("%s is typing..."), typingMemberNames[0]); + return _t("%s is typing...", typingMemberNames[0]); } if (typingMemberNames.length === 2) { - return sprintf( - _t("%s and %s are typing..."), - typingMemberNames[0], - typingMemberNames[1] - ); + return _t("%s and %s are typing...", typingMemberNames[0], typingMemberNames[1]); } - return sprintf( - _t("%s, %s and more are typing..."), - typingMemberNames[0], - typingMemberNames[1] - ); + return _t("%s, %s and more are typing...", typingMemberNames[0], typingMemberNames[1]); } } diff --git a/addons/mail/static/src/discuss/voice_message/common/voice_recorder.js b/addons/mail/static/src/discuss/voice_message/common/voice_recorder.js index b9f430e885e..6a527d5b8e3 100644 --- a/addons/mail/static/src/discuss/voice_message/common/voice_recorder.js +++ b/addons/mail/static/src/discuss/voice_message/common/voice_recorder.js @@ -1,7 +1,6 @@ /* @odoo-module */ import { Component, useState, onWillUnmount } from "@odoo/owl"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { _t } from "@web/core/l10n/translation"; import { browser } from "@web/core/browser/browser"; import { Mp3Encoder } from "./mp3_encoder"; @@ -85,7 +84,7 @@ export class VoiceRecorder extends Component { }); } catch { this.notification.add( - sprintf(_t('"%(hostname)s" needs to access your microphone'), { + _t('"%(hostname)s" needs to access your microphone', { hostname: window.location.host, }), { type: "warning" } diff --git a/addons/mail/static/src/views/web/activity/activity_controller.js b/addons/mail/static/src/views/web/activity/activity_controller.js index e6022da9d26..29177767d9d 100644 --- a/addons/mail/static/src/views/web/activity/activity_controller.js +++ b/addons/mail/static/src/views/web/activity/activity_controller.js @@ -5,7 +5,6 @@ import { useMessaging } from "@mail/core/common/messaging_hook"; import { Component, useState } from "@odoo/owl"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { useModel } from "@web/model/model"; import { extractFieldsFromArchInfo } from "@web/model/relational_model/utils"; import { CogMenu } from "@web/search/cog_menu/cog_menu"; @@ -48,7 +47,7 @@ export class ActivityController extends Component { resModel: this.props.resModel, searchViewId: this.env.searchModel.searchViewId, domain: this.model.originalDomain, - title: sprintf(this.env._t("Search: %s"), this.props.archInfo.title), + title: this.env._t("Search: %s", this.props.archInfo.title), multiSelect: false, context: this.props.context, onSelected: async (resIds) => { diff --git a/addons/mail/static/src/views/web/fields/many2many_tags_email/many2many_tags_email.js b/addons/mail/static/src/views/web/fields/many2many_tags_email/many2many_tags_email.js index d320c3b8f79..29d002a201a 100644 --- a/addons/mail/static/src/views/web/fields/many2many_tags_email/many2many_tags_email.js +++ b/addons/mail/static/src/views/web/fields/many2many_tags_email/many2many_tags_email.js @@ -5,7 +5,6 @@ import { onMounted } from "@odoo/owl"; import { _t } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; import { TagsList } from "@web/core/tags_list/tags_list"; -import { sprintf } from "@web/core/utils/strings"; import { Many2ManyTagsField, many2ManyTagsField, @@ -63,7 +62,7 @@ export class FieldMany2ManyTagsEmail extends Many2ManyTagsField { this.openMany2xRecord({ resId: record.resId, context: this.props.context, - title: sprintf(_t("Edit: %s"), record.data.display_name), + title: _t("Edit: %s", record.data.display_name), }) ); } diff --git a/addons/mass_mailing/static/src/js/mailing_portal.js b/addons/mass_mailing/static/src/js/mailing_portal.js index f5d38a3b1c5..6c899e406c3 100644 --- a/addons/mass_mailing/static/src/js/mailing_portal.js +++ b/addons/mass_mailing/static/src/js/mailing_portal.js @@ -1,7 +1,7 @@ /** @odoo-module **/ import ajax from "@web/legacy/js/core/ajax"; -import { sprintf, escape } from "@web/core/utils/strings"; +import { escape } from "@web/core/utils/strings"; import { whenReady } from "@odoo/owl"; import { _t } from "@web/core/l10n/translation"; import session from "web.session"; @@ -45,10 +45,12 @@ whenReady(() => { var unsubscribed_list = $("input[name='unsubscribed_list']").val(); if (unsubscribed_list){ - $('#subscription_info').html(sprintf( - _t("You have been successfully unsubscribed from %s."), - escape(unsubscribed_list) - )); + $("#subscription_info").html( + _t( + "You have been successfully unsubscribed from %s.", + escape(unsubscribed_list) + ) + ) } else{ $('#subscription_info').html(_t('You have been successfully unsubscribed.')); diff --git a/addons/payment/static/src/js/payment_form_mixin.js b/addons/payment/static/src/js/payment_form_mixin.js index 22b1849dbfc..072b678d728 100644 --- a/addons/payment/static/src/js/payment_form_mixin.js +++ b/addons/payment/static/src/js/payment_form_mixin.js @@ -1,6 +1,6 @@ /** @odoo-module **/ - import { escape, sprintf } from "@web/core/utils/strings"; + import { escape } from "@web/core/utils/strings"; import core from "@web/legacy/js/services/core"; import Dialog from "@web/legacy/js/core/dialog"; import { _t } from "@web/core/l10n/translation"; @@ -77,7 +77,7 @@ const $checkedRadios = this.$('input[name="o_payment_radio"]:checked'); if ($checkedRadios.length !== 1) { // Cannot find selected payment option, show dialog return new Dialog(null, { - title: sprintf(_t("Error: %s"), title), + title: _t("Error: %s", title), size: 'medium', $content: `

${escape(description) || ''}

`, buttons: [{text: _t("Ok"), close: true}] diff --git a/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_popup.js b/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_popup.js index 29faf4ecfdc..19a7f6c9008 100644 --- a/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_popup.js +++ b/addons/point_of_sale/static/src/app/navbar/cash_move_popup/cash_move_popup.js @@ -1,7 +1,6 @@ /** @odoo-module */ import { _t } from "@web/core/l10n/translation"; -import { sprintf } from "@web/core/utils/strings"; import { renderToElement } from "@web/core/utils/render"; import { useAutofocus, useService } from "@web/core/utils/hooks"; import { parseFloat, InvalidNumberError } from "@web/views/fields/parsers"; @@ -50,10 +49,7 @@ export class CashMovePopup extends AbstractAwaitablePopup { } const formattedAmount = this.env.utils.formatCurrency(amount); if (!amount) { - this.notification.add( - sprintf(_t("Cash in/out of %s is ignored."), formattedAmount), - 3000 - ); + this.notification.add(_t("Cash in/out of %s is ignored.", formattedAmount), 3000); return this.props.close(); } @@ -96,7 +92,7 @@ export class CashMovePopup extends AbstractAwaitablePopup { } this.props.close(); this.notification.add( - sprintf(this.env._t("Successfully made a cash %s of %s."), type, formattedAmount), + this.env._t("Successfully made a cash %s of %s.", type, formattedAmount), 3000 ); } diff --git a/addons/point_of_sale/static/src/app/navbar/closing_popup/closing_popup.js b/addons/point_of_sale/static/src/app/navbar/closing_popup/closing_popup.js index cbf21e0ba16..ce44bd997d1 100644 --- a/addons/point_of_sale/static/src/app/navbar/closing_popup/closing_popup.js +++ b/addons/point_of_sale/static/src/app/navbar/closing_popup/closing_popup.js @@ -12,7 +12,6 @@ import { ConnectionLostError } from "@web/core/network/rpc_service"; import { identifyError } from "@point_of_sale/app/errors/error_handlers"; import { _t } from "@web/core/l10n/translation"; import { usePos } from "@point_of_sale/app/store/pos_hook"; -import { sprintf } from "@web/core/utils/strings"; import { parseFloat } from "@web/views/fields/parsers"; import { useValidateCashInput } from "@point_of_sale/app/utils/hooks"; @@ -63,11 +62,9 @@ export class ClosePosPopup extends AbstractAwaitablePopup { } else { await this.popup.add(ConfirmPopup, { title: this.env._t("Payments Difference"), - body: sprintf( - this.env._t( - "The maximum difference allowed is %s.\n\ - Please contact your manager to accept the closing difference." - ), + body: this.env._t( + "The maximum difference allowed is %s.\n\ + Please contact your manager to accept the closing difference.", this.env.utils.formatCurrency(this.amountAuthorizedDiff) ), confirmText: this.env._t("OK"), diff --git a/addons/point_of_sale/static/src/app/screens/partner_list/partner_list.js b/addons/point_of_sale/static/src/app/screens/partner_list/partner_list.js index c26b301207b..ba9380de989 100644 --- a/addons/point_of_sale/static/src/app/screens/partner_list/partner_list.js +++ b/addons/point_of_sale/static/src/app/screens/partner_list/partner_list.js @@ -8,7 +8,6 @@ import { PartnerLine } from "@point_of_sale/app/screens/partner_list/partner_lin import { PartnerDetailsEdit } from "@point_of_sale/app/screens/partner_list/partner_editor/partner_editor"; import { usePos } from "@point_of_sale/app/store/pos_hook"; import { Component, onWillUnmount, useRef, useState } from "@odoo/owl"; -import { sprintf } from "@web/core/utils/strings"; /** * Render this screen using `showTempScreen` to select partner. @@ -128,16 +127,12 @@ export class PartnerListScreen extends Component { const result = await this.searchPartner(); if (result.length > 0) { this.notification.add( - sprintf( - this.env._t('%s customer(s) found for "%s".'), - result.length, - this.state.query - ), + this.env._t('%s customer(s) found for "%s".', result.length, this.state.query), 3000 ); } else { this.notification.add( - sprintf(this.env._t('No more customer found for "%s".'), this.state.query), + this.env._t('No more customer found for "%s".', this.state.query), 3000 ); } diff --git a/addons/point_of_sale/static/src/app/screens/payment_screen/payment_screen.js b/addons/point_of_sale/static/src/app/screens/payment_screen/payment_screen.js index 109ab599a26..0fb953f61ae 100644 --- a/addons/point_of_sale/static/src/app/screens/payment_screen/payment_screen.js +++ b/addons/point_of_sale/static/src/app/screens/payment_screen/payment_screen.js @@ -17,7 +17,6 @@ import { PaymentScreenPaymentLines } from "@point_of_sale/app/screens/payment_sc import { PaymentScreenStatus } from "@point_of_sale/app/screens/payment_screen/payment_status/payment_status"; import { usePos } from "@point_of_sale/app/store/pos_hook"; import { Component, useState } from "@odoo/owl"; -import { sprintf } from "@web/core/utils/strings"; export class PaymentScreen extends Component { static template = "point_of_sale.PaymentScreen"; @@ -343,8 +342,8 @@ export class PaymentScreen extends Component { const paymentMethod = splitPayments[0].payment_method; const { confirmed } = await this.popup.add(ConfirmPopup, { title: this.env._t("Customer Required"), - body: sprintf( - this.env._t("Customer is required for %s payment method."), + body: this.env._t( + "Customer is required for %s payment method.", paymentMethod.name ), }); diff --git a/addons/point_of_sale/static/src/app/screens/product_screen/product_list/product_list.js b/addons/point_of_sale/static/src/app/screens/product_screen/product_list/product_list.js index d27f2bbc3a5..f9a95ba5ccd 100644 --- a/addons/point_of_sale/static/src/app/screens/product_screen/product_list/product_list.js +++ b/addons/point_of_sale/static/src/app/screens/product_screen/product_list/product_list.js @@ -8,7 +8,6 @@ import { ConnectionLostError, ConnectionAbortedError } from "@web/core/network/r import { ProductItem } from "@point_of_sale/app/screens/product_screen/product/product"; import { ProductsWidgetControlPanel } from "@point_of_sale/app/screens/product_screen/product_list/control_panel/control_panel"; import { Component, useState } from "@odoo/owl"; -import { sprintf } from "@web/core/utils/strings"; import { OfflineErrorPopup } from "@point_of_sale/app/errors/popups/offline_error_popup"; import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup"; @@ -81,16 +80,12 @@ export class ProductsWidget extends Component { const result = await this.loadProductFromDB(); if (result.length > 0) { this.notification.add( - sprintf( - this.env._t('%s product(s) found for "%s".'), - result.length, - searchProductWord - ), + this.env._t('%s product(s) found for "%s".', result.length, searchProductWord), 3000 ); } else { this.notification.add( - sprintf(this.env._t('No more product found for "%s".'), searchProductWord), + this.env._t('No more product found for "%s".', searchProductWord), 3000 ); } diff --git a/addons/point_of_sale/static/src/app/screens/ticket_screen/order_details/orderline_details.js b/addons/point_of_sale/static/src/app/screens/ticket_screen/order_details/orderline_details.js index a8dd78207e2..7437dc3a79c 100644 --- a/addons/point_of_sale/static/src/app/screens/ticket_screen/order_details/orderline_details.js +++ b/addons/point_of_sale/static/src/app/screens/ticket_screen/order_details/orderline_details.js @@ -1,7 +1,6 @@ /** @odoo-module */ import { Component } from "@odoo/owl"; -import { sprintf } from "@web/core/utils/strings"; import { formatFloat } from "@web/views/fields/formatters"; import { roundPrecision as round_pr } from "@web/core/utils/numbers"; import { usePos } from "@point_of_sale/app/store/pos_hook"; @@ -72,9 +71,9 @@ export class OrderlineDetails extends Component { return this.env.utils.formatProductQty(toRefundDetail && toRefundDetail.qty); } getRefundingMessage() { - return sprintf(this.env._t("Refunding %s in "), this.getFormattedToRefundQty()); + return this.env._t("Refunding %s in ", this.getFormattedToRefundQty()); } getToRefundMessage() { - return sprintf(this.env._t("To Refund: %s"), this.getFormattedToRefundQty()); + return this.env._t("To Refund: %s", this.getFormattedToRefundQty()); } } diff --git a/addons/point_of_sale/static/src/app/screens/ticket_screen/ticket_screen.js b/addons/point_of_sale/static/src/app/screens/ticket_screen/ticket_screen.js index 8b45e71f03b..774c15e6cc4 100644 --- a/addons/point_of_sale/static/src/app/screens/ticket_screen/ticket_screen.js +++ b/addons/point_of_sale/static/src/app/screens/ticket_screen/ticket_screen.js @@ -18,7 +18,6 @@ import { ReprintReceiptButton } from "@point_of_sale/app/screens/ticket_screen/r import { SearchBar } from "@point_of_sale/app/screens/ticket_screen/search_bar/search_bar"; import { usePos } from "@point_of_sale/app/store/pos_hook"; import { Component, onMounted, useState } from "@odoo/owl"; -import { sprintf } from "@web/core/utils/strings"; const { DateTime } = luxon; @@ -128,10 +127,8 @@ export class TicketScreen extends Component { ) { const { confirmed } = await this.popup.add(ConfirmPopup, { title: this.env._t("Existing orderlines"), - body: sprintf( - this.env._t( - "%s has a total amount of %s, are you sure you want to delete this order?" - ), + body: this.env._t( + "%s has a total amount of %s, are you sure you want to delete this order?", order.name, this.getTotal(order) ), @@ -212,10 +209,8 @@ export class TicketScreen extends Component { this.numberBuffer.reset(); this.popup.add(ErrorPopup, { title: this.env._t("Maximum Exceeded"), - body: sprintf( - this.env._t( - "The requested quantity to be refunded is higher than the ordered quantity. %s is requested while only %s can be refunded." - ), + body: this.env._t( + "The requested quantity to be refunded is higher than the ordered quantity. %s is requested while only %s can be refunded.", quantity, refundableQty ), diff --git a/addons/point_of_sale/static/src/app/store/models.js b/addons/point_of_sale/static/src/app/store/models.js index 0a98b8af596..6cc521c9656 100644 --- a/addons/point_of_sale/static/src/app/store/models.js +++ b/addons/point_of_sale/static/src/app/store/models.js @@ -14,7 +14,6 @@ import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup"; import { ProductConfiguratorPopup } from "@point_of_sale/app/store/product_configurator_popup/product_configurator_popup"; import { EditListPopup } from "@point_of_sale/app/store/select_lot_popup/select_lot_popup"; import { ConfirmPopup } from "@point_of_sale/app/utils/confirm_popup/confirm_popup"; -import { sprintf } from "@web/core/utils/strings"; import { _t } from "@web/core/l10n/translation"; import { renderToElement } from "@web/core/utils/render"; @@ -573,10 +572,8 @@ export class Orderline extends PosModel { } else { this.env.services.popup.add(ErrorPopup, { title: _t("Greater than allowed"), - body: sprintf( - _t( - "The requested quantity to be refunded is higher than the refundable quantity of %s." - ), + body: _t( + "The requested quantity to be refunded is higher than the refundable quantity of %s.", this.env.utils.formatProductQty(maxQtyToRefund) ), }); @@ -1267,7 +1264,7 @@ export class Order extends PosModel { this.access_token = uuidv4(); // unique uuid used to identify the authenticity of the request from the QR code. this.ticketCode = this._generateTicketCode(); // 5-digits alphanum code shown on the receipt this.uid = this.generate_unique_id(); - this.name = sprintf(_t("Order %s"), this.uid); + this.name = _t("Order %s", this.uid); this.validation_date = undefined; this.fiscal_position = this.pos.fiscal_positions.find(function (fp) { return fp.id === self.pos.config.default_fiscal_position_id[0]; @@ -1314,7 +1311,7 @@ export class Order extends PosModel { if (json.name) { this.name = json.name; } else { - this.name = sprintf(_t("Order %s"), this.uid); + this.name = _t("Order %s", this.uid); } this.validation_date = json.creation_date; this.server_id = json.server_id || json.id || false; diff --git a/addons/point_of_sale/static/src/app/store/pos_store.js b/addons/point_of_sale/static/src/app/store/pos_store.js index 621d0143221..64b30794d7f 100644 --- a/addons/point_of_sale/static/src/app/store/pos_store.js +++ b/addons/point_of_sale/static/src/app/store/pos_store.js @@ -16,7 +16,6 @@ import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup"; import { ConnectionLostError } from "@web/core/network/rpc_service"; import { _t } from "@web/core/l10n/translation"; import { CashOpeningPopup } from "@point_of_sale/app/store/cash_opening_popup/cash_opening_popup"; -import { sprintf } from "@web/core/utils/strings"; import { PaymentScreen } from "@point_of_sale/app/screens/payment_screen/payment_screen"; import { ProductScreen } from "@point_of_sale/app/screens/product_screen/product_screen"; import { renderToString } from "@web/core/utils/render"; @@ -790,8 +789,8 @@ export class PosStore extends Reactive { const pricelistsNames = pricelistsJson.map((pricelist) => { return pricelist.display_name; }); - message = sprintf( - _t("%s fiscal position(s) added to the configuration."), + message = _t( + "%s fiscal position(s) added to the configuration.", pricelistsNames.join(", ") ); return message; @@ -830,8 +829,8 @@ export class PosStore extends Reactive { const fiscalPositionNames = fiscalPositionJson.map((fp) => { return fp.display_name; }); - message = sprintf( - _t("%s fiscal position(s) added to the configuration."), + message = _t( + "%s fiscal position(s) added to the configuration.", fiscalPositionNames.join(", ") ); return message; @@ -1704,10 +1703,8 @@ export class PosStore extends Reactive { // FIXME POSREF this looks like it's dead code. reject({ title: _t("HTTPS connection to IoT Box failed"), - body: sprintf( - _t( - "Make sure you are using IoT Box v18.12 or higher. Navigate to %s to accept the certificate of your IoT Box." - ), + body: _t( + "Make sure you are using IoT Box v18.12 or higher. Navigate to %s to accept the certificate of your IoT Box.", url ), popup: "alert", @@ -1774,10 +1771,8 @@ export class PosStore extends Reactive { if (currentPartner && currentOrder.getHasRefundLines()) { this.popup.add(ErrorPopup, { title: _t("Can't change customer"), - body: sprintf( - _t( - "This order already has refund lines for %s. We can't change the customer associated to it. Create a new order for the new customer." - ), + body: _t( + "This order already has refund lines for %s. We can't change the customer associated to it. Create a new order for the new customer.", currentPartner.name ), }); diff --git a/addons/portal/static/src/js/portal_composer.js b/addons/portal/static/src/js/portal_composer.js index 8381733a047..c3f9b079a5e 100644 --- a/addons/portal/static/src/js/portal_composer.js +++ b/addons/portal/static/src/js/portal_composer.js @@ -1,7 +1,7 @@ /** @odoo-module **/ -import { escape, sprintf } from "@web/core/utils/strings"; import { _t } from "@web/core/l10n/translation"; +import { escape } from "@web/core/utils/strings"; import ajax from "@web/legacy/js/core/ajax"; import core from "@web/legacy/js/services/core"; import publicWidget from "@web/legacy/js/public/public_widget"; @@ -125,8 +125,7 @@ var PortalComposer = publicWidget.Widget.extend({ resolve(); }).guardedCatch(function (error) { self.displayNotification({ - message: sprintf(_t("Could not save file %s"), - escape(file.name)), + message: _t("Could not save file %s", escape(file.name)), type: 'warning', sticky: true, }); diff --git a/addons/portal/static/src/js/portal_sidebar.js b/addons/portal/static/src/js/portal_sidebar.js index 2668f9dd0b2..6a4469e6645 100644 --- a/addons/portal/static/src/js/portal_sidebar.js +++ b/addons/portal/static/src/js/portal_sidebar.js @@ -3,7 +3,6 @@ import { _t } from "@web/core/l10n/translation"; import publicWidget from "@web/legacy/js/public/public_widget"; import session from "web.session"; -import { sprintf } from "@web/core/utils/strings"; import { deserializeDateTime } from "@web/core/l10n/dates"; const { DateTime } = luxon; @@ -41,9 +40,9 @@ var PortalSidebar = publicWidget.Widget.extend({ } else if (diff > 0) { // Workaround: force uniqueness of these two translations. We use %1d because the string // with %d is already used in mail and mail's translations are not sent to the frontend. - displayStr = sprintf(_t('Due in %s days'), Math.abs(diff).toFixed(1)); + displayStr = _t('Due in %s days', Math.abs(diff).toFixed(1)); } else { - displayStr = sprintf(_t('%s days overdue'), Math.abs(diff).toFixed(1)); + displayStr = _t('%s days overdue', Math.abs(diff).toFixed(1)); } $(el).text(displayStr); }); diff --git a/addons/pos_adyen/static/src/app/payment_adyen.js b/addons/pos_adyen/static/src/app/payment_adyen.js index aaf97b82792..1950729569d 100644 --- a/addons/pos_adyen/static/src/app/payment_adyen.js +++ b/addons/pos_adyen/static/src/app/payment_adyen.js @@ -269,7 +269,7 @@ export class PaymentAdyen extends PaymentInterface { resolve(true); } else { var message = additional_response.get("message"); - self._show_error(sprintf(_t("Message from Adyen: %s"), message)); + self._show_error(_t("Message from Adyen: %s", message)); // this means the transaction was cancelled by pressing the cancel button on the device if (message.startsWith("108 ")) { @@ -308,9 +308,7 @@ export class PaymentAdyen extends PaymentInterface { msg = params.get("message"); } - this._show_error( - sprintf(_t("An unexpected error occurred. Message from Adyen: %s"), msg) - ); + this._show_error(_t("An unexpected error occurred. Message from Adyen: %s", msg)); if (line) { line.set_payment_status("force_done"); } diff --git a/addons/pos_discount/static/src/overrides/components/discount_button/discount_button.js b/addons/pos_discount/static/src/overrides/components/discount_button/discount_button.js index 2578ca7c7ad..d587dc27dee 100644 --- a/addons/pos_discount/static/src/overrides/components/discount_button/discount_button.js +++ b/addons/pos_discount/static/src/overrides/components/discount_button/discount_button.js @@ -5,7 +5,6 @@ import { useService } from "@web/core/utils/hooks"; import { NumberPopup } from "@point_of_sale/app/utils/input_popups/number_popup"; import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup"; import { Component } from "@odoo/owl"; -import { sprintf } from "@web/core/utils/strings"; import { usePos } from "@point_of_sale/app/store/pos_hook"; export class DiscountButton extends Component { @@ -73,8 +72,8 @@ export class DiscountButton extends Component { description: `${pc}%, ` + (tax_ids_array.length - ? sprintf( - this.env._t("Tax: %s"), + ? this.env._t( + "Tax: %s", tax_ids_array .map((taxId) => this.pos.taxes_by_id[taxId].amount + "%") .join(", ") diff --git a/addons/pos_epson_printer/static/src/app/epson_printer.js b/addons/pos_epson_printer/static/src/app/epson_printer.js index 692c70be8c6..161531dfbe0 100644 --- a/addons/pos_epson_printer/static/src/app/epson_printer.js +++ b/addons/pos_epson_printer/static/src/app/epson_printer.js @@ -2,7 +2,6 @@ import { BasePrinter } from "@point_of_sale/app/printer/base_printer"; import { _t } from "@web/core/l10n/translation"; -import { sprintf } from "@web/core/utils/strings"; import { templates } from "@web/core/assets"; import { createElement, append, createTextNode } from "@web/core/utils/xml"; @@ -166,10 +165,8 @@ export class EpsonPrinter extends BasePrinter { }, }; if (window.location.protocol === "https:") { - printRes.message.body += sprintf( - _t( - "If you are on a secure server (HTTPS) please make sure you manually accepted the certificate by accessing %s" - ), + printRes.message.body += _t( + "If you are on a secure server (HTTPS) please make sure you manually accepted the certificate by accessing %s", this.url ); } diff --git a/addons/pos_loyalty/static/src/overrides/components/partner_line/partner_line.js b/addons/pos_loyalty/static/src/overrides/components/partner_line/partner_line.js index c4be6803620..99454479874 100644 --- a/addons/pos_loyalty/static/src/overrides/components/partner_line/partner_line.js +++ b/addons/pos_loyalty/static/src/overrides/components/partner_line/partner_line.js @@ -3,7 +3,6 @@ import { usePos } from "@point_of_sale/app/store/pos_hook"; import { PartnerLine } from "@point_of_sale/app/screens/partner_list/partner_line/partner_line"; import { patch } from "@web/core/utils/patch"; -import { sprintf } from "@web/core/utils/strings"; import { formatFloat } from "@web/views/fields/formatters"; patch(PartnerLine.prototype, { @@ -20,6 +19,6 @@ patch(PartnerLine.prototype, { if (program.portal_visible) { return `${balanceRepr} ${program.portal_point_name}`; } - return sprintf(this.env._t("%s Points"), balanceRepr); + return this.env._t("%s Points", balanceRepr); }, }); diff --git a/addons/pos_loyalty/static/src/overrides/components/product_screen/product_screen.js b/addons/pos_loyalty/static/src/overrides/components/product_screen/product_screen.js index fdb96b9b33c..7e8aad55433 100644 --- a/addons/pos_loyalty/static/src/overrides/components/product_screen/product_screen.js +++ b/addons/pos_loyalty/static/src/overrides/components/product_screen/product_screen.js @@ -5,7 +5,6 @@ import { useBarcodeReader } from "@point_of_sale/app/barcode/barcode_reader_hook import { patch } from "@web/core/utils/patch"; import { ConfirmPopup } from "@point_of_sale/app/utils/confirm_popup/confirm_popup"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; patch(ProductScreen.prototype, { setup() { @@ -43,10 +42,8 @@ patch(ProductScreen.prototype, { const reward = this.pos.reward_by_id[selectedLine.reward_id]; const { confirmed } = await this.popup.add(ConfirmPopup, { title: this.env._t("Deactivating reward"), - body: sprintf( - this.env._t( - "Are you sure you want to remove %s from this order?\n You will still be able to claim it through the reward button." - ), + body: this.env._t( + "Are you sure you want to remove %s from this order?\n You will still be able to claim it through the reward button.", reward.description ), cancelText: this.env._t("No"), diff --git a/addons/pos_loyalty/static/src/overrides/models/loyalty.js b/addons/pos_loyalty/static/src/overrides/models/loyalty.js index 16bdfefd33c..79501b67173 100644 --- a/addons/pos_loyalty/static/src/overrides/models/loyalty.js +++ b/addons/pos_loyalty/static/src/overrides/models/loyalty.js @@ -7,7 +7,6 @@ import { roundDecimals, roundPrecision } from "@web/core/utils/numbers"; import { _t } from "@web/core/l10n/translation"; import { patch } from "@web/core/utils/patch"; import { ConfirmPopup } from "@point_of_sale/app/utils/confirm_popup/confirm_popup"; -import { sprintf } from "@web/core/utils/strings"; // FIXME: Perhaps MutexedDropPrevious can be replaced by the new KeepLast. // > This might require thorough investigation on how _updateRewards work. @@ -1556,8 +1555,8 @@ patch(Order.prototype, { } } if (!rule && this.orderlines.length === 0 && coupon) { - return sprintf( - _t("Gift Card: %s\nBalance: %s"), + return _t( + "Gift Card: %s\nBalance: %s", code, this.env.utils.formatCurrency(coupon.balance) ); diff --git a/addons/pos_loyalty/static/src/overrides/models/pos_store.js b/addons/pos_loyalty/static/src/overrides/models/pos_store.js index 0ff41b3d8a7..c9b25a62224 100644 --- a/addons/pos_loyalty/static/src/overrides/models/pos_store.js +++ b/addons/pos_loyalty/static/src/overrides/models/pos_store.js @@ -7,7 +7,6 @@ import { SelectionPopup } from "@point_of_sale/app/utils/input_popups/selection_ import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup"; import { TextInputPopup } from "@point_of_sale/app/utils/input_popups/text_input_popup"; import { Domain, InvalidDomainError } from "@web/core/domain"; -import { sprintf } from "@web/core/utils/strings"; import { PosLoyaltyCard } from "@pos_loyalty/overrides/models/loyalty"; const COUPON_CACHE_MAX_SIZE = 4096; // Maximum coupon cache size, prevents long run memory issues and (to some extent) invalid data @@ -245,10 +244,8 @@ patch(PosStore.prototype, { if (index != -1) { this.env.services.popup.add(ErrorPopup, { title: _t("A reward could not be loaded"), - body: sprintf( - _t( - 'The reward "%s" contain an error in its domain, your domain must be compatible with the PoS client' - ), + body: _t( + 'The reward "%s" contain an error in its domain, your domain must be compatible with the PoS client', this.rewards[index].description ), }); diff --git a/addons/pos_online_payment/static/src/app/screens/payment_screen/payment_screen.js b/addons/pos_online_payment/static/src/app/screens/payment_screen/payment_screen.js index 260278ab43d..b03117221da 100644 --- a/addons/pos_online_payment/static/src/app/screens/payment_screen/payment_screen.js +++ b/addons/pos_online_payment/static/src/app/screens/payment_screen/payment_screen.js @@ -6,7 +6,6 @@ import { OnlinePaymentPopup } from "@pos_online_payment/app/utils/online_payment import { ConfirmPopup } from "@point_of_sale/app/utils/confirm_popup/confirm_popup"; import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup"; import { floatIsZero } from "@web/core/utils/numbers"; -import { sprintf } from "@web/core/utils/strings"; patch(PaymentScreen.prototype, { getRemainingOnlinePaymentLines() { @@ -23,7 +22,11 @@ patch(PaymentScreen.prototype, { if (amount <= 0) { this.popup.add(ErrorPopup, { title: this.env._t("Invalid online payment"), - body: sprintf(this.env._t("Online payments cannot have a negative amount (%s: %s)."), line.payment_method.name, this.env.utils.formatCurrency(amount)), + body: this.env._t( + "Online payments cannot have a negative amount (%s: %s).", + line.payment_method.name, + this.env.utils.formatCurrency(amount) + ), }); return false; } @@ -32,7 +35,11 @@ patch(PaymentScreen.prototype, { if (!floatIsZero(unpaidAmount - remainingAmount, this.pos.currency.decimal_places)) { this.popup.add(ErrorPopup, { title: this.env._t("Invalid online payments"), - body: sprintf(this.env._t("The total amount of remaining online payments to execute (%s) doesn't correspond to the remaining unpaid amount of the order (%s)."), this.env.utils.formatCurrency(remainingAmount), this.env.utils.formatCurrency(unpaidAmount)), + body: this.env._t( + "The total amount of remaining online payments to execute (%s) doesn't correspond to the remaining unpaid amount of the order (%s).", + this.env.utils.formatCurrency(remainingAmount), + this.env.utils.formatCurrency(unpaidAmount) + ), }); return false; } diff --git a/addons/pos_restaurant/static/src/app/control_buttons/table_guests_button/table_guests_button.js b/addons/pos_restaurant/static/src/app/control_buttons/table_guests_button/table_guests_button.js index 77287371e25..ebb513ab1f3 100644 --- a/addons/pos_restaurant/static/src/app/control_buttons/table_guests_button/table_guests_button.js +++ b/addons/pos_restaurant/static/src/app/control_buttons/table_guests_button/table_guests_button.js @@ -5,7 +5,6 @@ import { useService } from "@web/core/utils/hooks"; import { NumberPopup } from "@point_of_sale/app/utils/input_popups/number_popup"; import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup"; import { Component } from "@odoo/owl"; -import { sprintf } from "@web/core/utils/strings"; import { usePos } from "@point_of_sale/app/store/pos_hook"; export class TableGuestsButton extends Component { @@ -36,10 +35,7 @@ export class TableGuestsButton extends Component { if (guestCount > max_capacity) { await this.popup.add(ErrorPopup, { title: this.env._t("Blocked action"), - body: sprintf( - this.env._t("You cannot put a number that exceeds %s "), - max_capacity - ), + body: this.env._t("You cannot put a number that exceeds %s ", max_capacity), }); return; } diff --git a/addons/pos_restaurant/static/src/app/floor_screen/floor_screen.js b/addons/pos_restaurant/static/src/app/floor_screen/floor_screen.js index d258f431706..aa8cfd61203 100644 --- a/addons/pos_restaurant/static/src/app/floor_screen/floor_screen.js +++ b/addons/pos_restaurant/static/src/app/floor_screen/floor_screen.js @@ -23,7 +23,6 @@ import { useState, onWillStart, } from "@odoo/owl"; -import { sprintf } from "@web/core/utils/strings"; export class FloorScreen extends Component { static components = { EditableTable, EditBar, Table }; @@ -526,8 +525,8 @@ export class FloorScreen extends Component { if (this.selectedTables.length == 0) { const { confirmed } = await this.popup.add(ConfirmPopup, { title: `Removing floor ${this.activeFloor.name}`, - body: sprintf( - this.env._t("Removing a floor cannot be undone. Do you still wanna remove %s?"), + body: this.env._t( + "Removing a floor cannot be undone. Do you still wanna remove %s?", this.activeFloor.name ), }); diff --git a/addons/pos_sale/static/src/app/order_management_screen/sale_order_management_screen/sale_order_management_screen.js b/addons/pos_sale/static/src/app/order_management_screen/sale_order_management_screen/sale_order_management_screen.js index 07c21a6d8c7..ff0663759c6 100644 --- a/addons/pos_sale/static/src/app/order_management_screen/sale_order_management_screen/sale_order_management_screen.js +++ b/addons/pos_sale/static/src/app/order_management_screen/sale_order_management_screen/sale_order_management_screen.js @@ -141,8 +141,8 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(Component) { await this.pos._loadPartners([sale_order.partner_id[0]]); } catch { const title = this.env._t("Customer loading error"); - const body = sprintf( - this.env._t("There was a problem in loading the %s customer."), + const body = this.env._t( + "There was a problem in loading the %s customer.", sale_order.partner_id[1] ); await this.popup.add(ErrorPopup, { title, body }); @@ -335,10 +335,8 @@ export class SaleOrderManagementScreen extends ControlButtonsMixin(Component) { } if (down_payment > sale_order.amount_unpaid) { - const errorBody = sprintf( - this.env._t( - "You have tried to charge a down payment of %s but only %s remains to be paid, %s will be applied to the purchase order line." - ), + const errorBody = this.env._t( + "You have tried to charge a down payment of %s but only %s remains to be paid, %s will be applied to the purchase order line.", this.env.utils.formatCurrency(down_payment), this.env.utils.formatCurrency(sale_order.amount_unpaid), sale_order.amount_unpaid > 0 diff --git a/addons/pos_stripe/static/src/app/payment_stripe.js b/addons/pos_stripe/static/src/app/payment_stripe.js index ebc3e390d82..88bf3aca3cc 100644 --- a/addons/pos_stripe/static/src/app/payment_stripe.js +++ b/addons/pos_stripe/static/src/app/payment_stripe.js @@ -4,7 +4,6 @@ import { _t } from "@web/core/l10n/translation"; import { PaymentInterface } from "@point_of_sale/app/payment/payment_interface"; import { ErrorPopup } from "@point_of_sale/app/errors/popups/error_popup"; -import { sprintf } from "@web/core/utils/strings"; export class PaymentStripe extends PaymentInterface { setup() { @@ -38,7 +37,7 @@ export class PaymentStripe extends PaymentInterface { async discoverReaders() { const discoverResult = await this.terminal.discoverReaders({}); if (discoverResult.error) { - this._showError(sprintf(_t("Failed to discover: %s"), discoverResult.error)); + this._showError(_t("Failed to discover: %s", discoverResult.error)); } else if (discoverResult.discoveredReaders.length === 0) { this._showError(_t("No available Stripe readers.")); } else { @@ -108,8 +107,8 @@ export class PaymentStripe extends PaymentInterface { } } this._showError( - sprintf( - _t("Stripe readers %s not listed in your account"), + _t( + "Stripe readers %s not listed in your account", this.payment_method.stripe_serial_number ) ); diff --git a/addons/product/static/src/js/pricelist_report/product_pricelist_report.js b/addons/product/static/src/js/pricelist_report/product_pricelist_report.js index cc0f2271a02..815390d33b7 100644 --- a/addons/product/static/src/js/pricelist_report/product_pricelist_report.js +++ b/addons/product/static/src/js/pricelist_report/product_pricelist_report.js @@ -4,7 +4,6 @@ import { Component, markup, onRendered, onWillStart, useState } from "@odoo/owl"; import { Layout } from "@web/search/layout"; import { registry } from "@web/core/registry"; -import { sprintf } from "@web/core/utils/strings"; import { useService } from "@web/core/utils/hooks"; import { useSetupAction } from "@web/webclient/actions/action_hook"; @@ -123,8 +122,8 @@ export class ProductPricelistReport extends Component { ev.preventDefault(); // avoid automatic reloading of the page if (this.quantities.length >= this.MAX_QTY) { - let message = sprintf( - this.env._t("At most %s quantities can be displayed simultaneously. Remove a selected quantity to add others."), + let message = this.env._t( + "At most %s quantities can be displayed simultaneously. Remove a selected quantity to add others.", this.MAX_QTY ); await this.action.doAction(sendCustomNotification("warning", message)); @@ -139,7 +138,7 @@ export class ProductPricelistReport extends Component { this.quantities = this.quantities.sort((a, b) => a - b); this.renderHtml(); } else { - let message = sprintf(this.env._t("Quantity already present (%s)."), qty); + let message = this.env._t("Quantity already present (%s).", qty); await this.action.doAction(sendCustomNotification("info", message)); } } else { diff --git a/addons/project/static/src/components/project_task_priority_switch_field/project_task_priority_switch_field.js b/addons/project/static/src/components/project_task_priority_switch_field/project_task_priority_switch_field.js index 1910aa1f88b..a8db88cc98d 100644 --- a/addons/project/static/src/components/project_task_priority_switch_field/project_task_priority_switch_field.js +++ b/addons/project/static/src/components/project_task_priority_switch_field/project_task_priority_switch_field.js @@ -1,13 +1,12 @@ /** @odoo-module **/ -import { sprintf } from "@web/core/utils/strings"; import { registry } from "@web/core/registry"; import { PriorityField, priorityField } from "@web/views/fields/priority/priority_field"; export class PrioritySwitchField extends PriorityField { get commands() { return this.options.map(([id, name]) => [ - sprintf(this.env._t("Set priority as %s"), name), + this.env._t("Set priority as %s", name), () => this.updateRecord(id), { category: "smart_action", diff --git a/addons/project/static/src/components/project_task_state_selection/project_task_state_selection.js b/addons/project/static/src/components/project_task_state_selection/project_task_state_selection.js index ecd2c59c657..49d5732f856 100644 --- a/addons/project/static/src/components/project_task_state_selection/project_task_state_selection.js +++ b/addons/project/static/src/components/project_task_state_selection/project_task_state_selection.js @@ -4,7 +4,6 @@ import { StateSelectionField, stateSelectionField, } from "@web/views/fields/state_selection/state_selection_field"; -import { sprintf } from "@web/core/utils/strings"; import { useCommand } from "@web/core/commands/command_hook"; import { formatSelection } from "@web/views/fields/formatters"; @@ -44,7 +43,7 @@ export class ProjectTaskStateSelection extends StateSelectionField { if (this.props.viewType != 'form') { super.setup(); } else { - const commandName = sprintf(this.env._t(`Set state as...`)); + const commandName = this.env._t("Set state as..."); useCommand( commandName, () => { diff --git a/addons/stock/static/src/fields/stock_move_line_x2_many_field.js b/addons/stock/static/src/fields/stock_move_line_x2_many_field.js index 5db1fc24658..10ffd23ee25 100644 --- a/addons/stock/static/src/fields/stock_move_line_x2_many_field.js +++ b/addons/stock/static/src/fields/stock_move_line_x2_many_field.js @@ -2,7 +2,6 @@ import { registry } from "@web/core/registry"; import { X2ManyField, x2ManyField } from "@web/views/fields/x2many/x2many_field"; -import { sprintf } from "@web/core/utils/strings"; import { useSelectCreate, useOpenMany2XRecord} from "@web/views/fields/relational_utils"; export class SMLX2ManyField extends X2ManyField { setup() { @@ -35,7 +34,7 @@ export class SMLX2ManyField extends X2ManyField { tree_view_ref: "stock.view_stock_quant_tree_simple", }; const productName = this.props.record.data.product_id[1]; - const title = sprintf(this.env._t("Add line: %s"), productName); + const title = this.env._t("Add line: %s", productName); const alreadySelected = this.props.record.data.move_line_ids.records.filter((line) => line.data.quant_id?.[0]); const domain = [ ["product_id", "=", this.props.record.data.product_id[0]], diff --git a/addons/survey/static/src/views/widgets/survey_question_trigger/survey_question_trigger.js b/addons/survey/static/src/views/widgets/survey_question_trigger/survey_question_trigger.js index 84f15554b16..c24c976a2fd 100644 --- a/addons/survey/static/src/views/widgets/survey_question_trigger/survey_question_trigger.js +++ b/addons/survey/static/src/views/widgets/survey_question_trigger/survey_question_trigger.js @@ -2,7 +2,6 @@ import { _t } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; -import { sprintf } from '@web/core/utils/strings'; import { standardWidgetProps } from "@web/views/widgets/standard_widget_props"; const { Component, useEffect, useRef, useState } = owl; @@ -21,21 +20,26 @@ export class SurveyQuestionTriggerWidget extends Component { const triggerError = this.surveyQuestionTriggerError; if (triggerError === "MISPLACED_TRIGGER_WARNING") { this.state.surveyIconWarning = true; - this.state.triggerTooltip = sprintf( - '⚠ ' + _t('This question is positioned before its trigger ("%s") and will be skipped.'), - triggeringQuestionTitle); + this.state.triggerTooltip = _t( + '⚠ This question is positioned before its trigger ("%s") and will be skipped.', + triggeringQuestionTitle + ); } else if (triggerError === "WRONG_QUESTIONS_SELECTION_WARNING") { this.state.surveyIconWarning = true; - this.state.triggerTooltip = '⚠ ' + _t( - 'Conditional display is not available when questions are randomly picked.'); + this.state.triggerTooltip = _t( + "⚠ Conditional display is not available when questions are randomly picked." + ); } else if (triggerError === "MISSING_TRIGGER_ERROR") { // This case must be handled to not temporarily render the "normal" icon if previously // on an error state, which would cause a flicker as the trigger itself will be removed // at next save (auto on survey form and primary list view). } else { this.state.surveyIconWarning = false; - this.state.triggerTooltip = sprintf(_t('Displayed if "%s: %s"'), - triggeringQuestionTitle, this.props.record.data.triggering_answer_id[1]); + this.state.triggerTooltip = _t( + 'Displayed if "%s: %s"', + triggeringQuestionTitle, + this.props.record.data.triggering_answer_id[1] + ); } } else { this.state.surveyIconWarning = false; diff --git a/addons/web/static/src/core/domain_selector/domain_selector_autocomplete.js b/addons/web/static/src/core/domain_selector/domain_selector_autocomplete.js index 60c40ec0d9a..2cb81cb6eb7 100644 --- a/addons/web/static/src/core/domain_selector/domain_selector_autocomplete.js +++ b/addons/web/static/src/core/domain_selector/domain_selector_autocomplete.js @@ -7,7 +7,6 @@ import { formatAST, toPyValue } from "@web/core/py_js/py_utils"; import { registry } from "@web/core/registry"; import { TagsList } from "@web/core/tags_list/tags_list"; import { useOwnedDialogs, useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { Expression } from "@web/core/domain_tree"; const SEARCH_LIMIT = 7; @@ -89,7 +88,7 @@ class AutoCompleteWithSources extends Component { const dynamicFilters = ids.length ? [ { - description: sprintf(_t("Quick search: %s"), name), + description: _t("Quick search: %s", name), domain: [["id", operator, ids]], }, ] @@ -97,7 +96,7 @@ class AutoCompleteWithSources extends Component { // fine for now but we don't like this kind of dependence of core to views const SelectCreateDialog = registry.category("dialogs").get("select_create"); this.addDialog(SelectCreateDialog, { - title: sprintf(_t("Search: %s"), fieldString), + title: _t("Search: %s", fieldString), dynamicFilters, resModel, noCreate: true, @@ -140,13 +139,13 @@ const getFormat = (val, displayNames) => { text = typeof displayNames[val] === "string" ? displayNames[val] - : sprintf(_t(`Inaccessible/missing record ID: %s`), val); + : _t("Inaccessible/missing record ID: %s", val); colorIndex = typeof displayNames[val] === "string" ? 0 : 2; // 0 = grey, 2 = orange } else { text = val instanceof Expression ? String(val) - : sprintf(_t(`Invalid record ID: %s`), formatAST(toPyValue(val))); + : _t("Invalid record ID: %s", formatAST(toPyValue(val))); colorIndex = val instanceof Expression ? 2 : 1; // 1 = red } return { text, colorIndex }; diff --git a/addons/web/static/src/core/domain_selector/utils.js b/addons/web/static/src/core/domain_selector/utils.js index e608a822e98..b254b3eeb88 100644 --- a/addons/web/static/src/core/domain_selector/utils.js +++ b/addons/web/static/src/core/domain_selector/utils.js @@ -5,7 +5,6 @@ import { getDefaultValue, getDefaultOperator } from "./domain_selector_fields"; import { useService } from "@web/core/utils/hooks"; import { _t } from "@web/core/l10n/translation"; import { unique, zip } from "@web/core/utils/arrays"; -import { sprintf } from "@web/core/utils/strings"; import { Expression, toValue, @@ -183,7 +182,7 @@ function formatValue(val, disambiguate, fieldDef, displayNames) { if (typeof displayNames[val] === "string") { val = displayNames[val]; } else { - return sprintf(_t(`Inaccessible/missing record ID: %s`), val); + return _t("Inaccessible/missing record ID: %s", val); } } if (fieldDef?.type === "selection") { diff --git a/addons/web/static/src/core/file_upload/file_upload_progress_bar.js b/addons/web/static/src/core/file_upload/file_upload_progress_bar.js index 793bd330ca1..59a3d4ec0f0 100644 --- a/addons/web/static/src/core/file_upload/file_upload_progress_bar.js +++ b/addons/web/static/src/core/file_upload/file_upload_progress_bar.js @@ -1,7 +1,6 @@ /** @odoo-module **/ import { useService } from "../utils/hooks"; -import { sprintf } from "../utils/strings"; import { ConfirmationDialog } from "../confirmation_dialog/confirmation_dialog"; import { Component } from "@odoo/owl"; @@ -16,7 +15,10 @@ export class FileUploadProgressBar extends Component { return; } this.dialogService.add(ConfirmationDialog, { - body: sprintf(this.env._t("Do you really want to cancel the upload of %s?"), this.props.fileUpload.title), + body: this.env._t( + "Do you really want to cancel the upload of %s?", + this.props.fileUpload.title + ), confirm: () => { this.props.fileUpload.xhr.abort(); }, diff --git a/addons/web/static/src/core/file_upload/file_upload_progress_record.js b/addons/web/static/src/core/file_upload/file_upload_progress_record.js index 8bab4b89bc5..47ac33e5c2d 100644 --- a/addons/web/static/src/core/file_upload/file_upload_progress_record.js +++ b/addons/web/static/src/core/file_upload/file_upload_progress_record.js @@ -1,6 +1,5 @@ /** @odoo-module **/ -import { sprintf } from "../utils/strings"; import { FileUploadProgressBar } from "./file_upload_progress_bar"; import { Component } from "@odoo/owl"; @@ -18,8 +17,8 @@ export class FileUploadProgressRecord extends Component { const mbLoaded = Math.round(fileUpload.loaded / 1000000); const mbTotal = Math.round(fileUpload.total / 1000000); return { - left: sprintf(this.env._t("Uploading... (%s%)"), percent), - right: sprintf(this.env._t("(%s/%sMB)"), mbLoaded, mbTotal), + left: this.env._t("Uploading... (%s%)", percent), + right: this.env._t("(%s/%sMB)", mbLoaded, mbTotal), }; } } diff --git a/addons/web/static/src/core/file_upload/file_upload_service.js b/addons/web/static/src/core/file_upload/file_upload_service.js index a57b699930a..1ff5326c87d 100644 --- a/addons/web/static/src/core/file_upload/file_upload_service.js +++ b/addons/web/static/src/core/file_upload/file_upload_service.js @@ -1,7 +1,6 @@ /** @odoo-module **/ import { registry } from "../registry"; -import { sprintf } from "../utils/strings"; import { EventBus, reactive } from "@odoo/owl"; @@ -56,7 +55,7 @@ export const fileUploadService = { loaded: 0, total: 0, state: "pending", - title: files.length === 1 ? files[0].name : sprintf(env._t("%s Files"), files.length), + title: files.length === 1 ? files[0].name : env._t("%s Files", files.length), type: files.length === 1 ? files[0].type : undefined, }); uploads[upload.id] = upload; diff --git a/addons/web/static/src/core/l10n/dates.js b/addons/web/static/src/core/l10n/dates.js index 374172c3e48..f479f8690cd 100644 --- a/addons/web/static/src/core/l10n/dates.js +++ b/addons/web/static/src/core/l10n/dates.js @@ -3,7 +3,6 @@ import { localization } from "@web/core/l10n/localization"; import { _t } from "@web/core/l10n/translation"; import { memoize } from "@web/core/utils/functions"; -import { sprintf } from "@web/core/utils/strings"; import { ensureArray } from "../utils/arrays"; const { DateTime, Settings } = luxon; @@ -436,7 +435,7 @@ export function parseDateTime(value, options = {}) { // No working parsing methods: throw an error if (!isValidDate(result)) { - throw new ConversionError(sprintf(_t("'%s' is not a correct date or datetime"), value)); + throw new ConversionError(_t("'%s' is not a correct date or datetime", value)); } // Revert to original numbering system diff --git a/addons/web/static/src/core/l10n/translation.js b/addons/web/static/src/core/l10n/translation.js index e7e6fb25aa7..11293fbca5e 100644 --- a/addons/web/static/src/core/l10n/translation.js +++ b/addons/web/static/src/core/l10n/translation.js @@ -1,6 +1,8 @@ /** @odoo-module **/ import { Deferred } from "@web/core/utils/concurrency"; +import { sprintf } from "@web/core/utils/strings"; + export const translationLoaded = Symbol("translationLoaded"); export const translatedTerms = { [translationLoaded]: false, @@ -17,11 +19,15 @@ export const translationIsReady = new Deferred(); * @param {string} term * @returns {string} */ -export function _t(term) { +export function _t(term, ...values) { if (translatedTerms[translationLoaded]) { - return translatedTerms[term] || term; + const translation = translatedTerms[term] ?? term; + if (values.length === 0) { + return translation; + } + return sprintf(translation, ...values); } else { - return new LazyTranslatedString(term); + return new LazyTranslatedString(term, ...values); } } @@ -35,13 +41,21 @@ export function _t(term) { * @param {string} term * @returns {LazyTranslatedString} */ -export const _lt = (term) => _t(term); +export const _lt = (term, ...values) => _t(term, ...values); class LazyTranslatedString extends String { + constructor(term, ...values) { + super(term); + this.values = values; + } valueOf() { const term = super.valueOf(); if (translatedTerms[translationLoaded]) { - return translatedTerms[term] || term; + const translation = translatedTerms[term] ?? term; + if (this.values.length === 0) { + return translation; + } + return sprintf(translation, ...this.values); } else { throw new Error(`translation error`); } diff --git a/addons/web/static/src/legacy/js/core/dates.js b/addons/web/static/src/legacy/js/core/dates.js index fb637eb50e9..94a80254039 100644 --- a/addons/web/static/src/legacy/js/core/dates.js +++ b/addons/web/static/src/legacy/js/core/dates.js @@ -1,6 +1,5 @@ /** @odoo-module **/ -import { sprintf } from "@web/core/utils/strings"; import core from "@web/legacy/js/services/core"; import session from "web.session"; import time from "@web/legacy/js/core/time"; @@ -133,7 +132,7 @@ export function parseDate(value, field, options) { return date; } } - throw new Error(sprintf(core._t("'%s' is not a correct date"), value)); + throw new Error(core._t("'%s' is not a correct date", value)); } /** @@ -186,5 +185,5 @@ export function parseDateTime(value, field, options) { return datetime; } } - throw new Error(sprintf(core._t("'%s' is not a correct datetime"), value)); + throw new Error(core._t("'%s' is not a correct datetime", value)); } diff --git a/addons/web/static/src/legacy/js/core/time.js b/addons/web/static/src/legacy/js/core/time.js index 72411b025fd..7e682a9f4c1 100644 --- a/addons/web/static/src/legacy/js/core/time.js +++ b/addons/web/static/src/legacy/js/core/time.js @@ -1,6 +1,5 @@ /** @odoo-module **/ -import { sprintf } from "@web/core/utils/strings"; import translation from "@web/legacy/js/core/translation"; import utils from "@web/legacy/js/core/utils"; @@ -190,7 +189,7 @@ export function auto_str_to_date (value) { try { return str_to_time(value); } catch {} - throw new Error(sprintf(_t("'%s' is not a correct date, datetime nor time"), value)); + throw new Error(_t("'%s' is not a correct date, datetime nor time", value)); } export function auto_date_to_str (value, type) { @@ -202,7 +201,7 @@ export function auto_date_to_str (value, type) { case 'time': return time_to_str(value); default: - throw new Error(sprintf(_t("'%s' is not convertible to date, datetime nor time"), type)); + throw new Error(_t("'%s' is not convertible to date, datetime nor time", type)); } } diff --git a/addons/web/static/src/legacy/js/core/translation.js b/addons/web/static/src/legacy/js/core/translation.js index e518ed1a7b0..5c3c5b37578 100644 --- a/addons/web/static/src/legacy/js/core/translation.js +++ b/addons/web/static/src/legacy/js/core/translation.js @@ -1,6 +1,7 @@ /** @odoo-module **/ +import { sprintf } from "@web/core/utils/strings"; import Class from "@web/legacy/js/core/class"; var TranslationDataBase = Class.extend(/** @lends instance.TranslationDataBase# */{ @@ -39,9 +40,12 @@ var TranslationDataBase = Class.extend(/** @lends instance.TranslationDataBase# }, build_translation_function: function() { var self = this; - var fcnt = function(str) { - var tmp = self.get(str); - return tmp === undefined ? str : tmp; + var fcnt = function(term, ...values) { + const translation = self.get(term) ?? term; + if (values.length === 0) { + return translation; + } + return sprintf(translation, ...values); }; fcnt.database = this; return fcnt; diff --git a/addons/web/static/src/model/relational_model/dynamic_list.js b/addons/web/static/src/model/relational_model/dynamic_list.js index 15b80f56d67..b414ffcd731 100644 --- a/addons/web/static/src/model/relational_model/dynamic_list.js +++ b/addons/web/static/src/model/relational_model/dynamic_list.js @@ -2,7 +2,6 @@ import { AlertDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; import { _t } from "@web/core/l10n/translation"; -import { sprintf } from "@web/core/utils/strings"; import { DataPoint } from "./datapoint"; import { Record } from "./record"; @@ -200,8 +199,8 @@ export class DynamicList extends DataPoint { resIds.length === this.model.activeIdsLimit && resIds.length < this.count ) { - const msg = sprintf( - _t(`Only the first %s records have been deleted (out of %s selected)`), + const msg = _t( + `Only the first %s records have been deleted (out of %s selected)`, resIds.length, this.count ); @@ -366,8 +365,8 @@ export class DynamicList extends DataPoint { resIds.length === this.model.activeIdsLimit && resIds.length < this.count ) { - const msg = sprintf( - _t("Of the %s records selected, only the first %s have been archived/unarchived."), + const msg = _t( + "Of the %s records selected, only the first %s have been archived/unarchived.", resIds.length, this.count ); diff --git a/addons/web/static/src/search/control_panel/control_panel.js b/addons/web/static/src/search/control_panel/control_panel.js index 6b3aef48a31..3c3b5200bd1 100644 --- a/addons/web/static/src/search/control_panel/control_panel.js +++ b/addons/web/static/src/search/control_panel/control_panel.js @@ -7,7 +7,6 @@ import { useService } from "@web/core/utils/hooks"; import { SearchBar } from "../search_bar/search_bar"; import { Dropdown } from "@web/core/dropdown/dropdown"; import { useCommand } from "@web/core/commands/command_hook"; -import { sprintf } from "@web/core/utils/strings"; import { DropdownItem } from "@web/core/dropdown/dropdown_item"; import { Component, useState, onMounted, useExternalListener, useRef, useEffect } from "@odoo/owl"; @@ -35,7 +34,7 @@ export class ControlPanel extends Component { const { viewSwitcherEntries, viewType } = this.env.config; for (const view of viewSwitcherEntries || []) { useCommand( - sprintf(this.env._t("Show %s view"), view.name), + this.env._t("Show %s view", view.name), () => this.onViewClicked(view.type), { category: "view_switcher", diff --git a/addons/web/static/src/search/search_model.js b/addons/web/static/src/search/search_model.js index 70312c53549..94a90e4ebf5 100644 --- a/addons/web/static/src/search/search_model.js +++ b/addons/web/static/src/search/search_model.js @@ -5,7 +5,6 @@ import { Domain } from "@web/core/domain"; import { evaluateExpr } from "@web/core/py_js/py"; import { sortBy } from "@web/core/utils/arrays"; import { deepCopy } from "@web/core/utils/objects"; -import { sprintf } from "@web/core/utils/strings"; import { SearchArchParser } from "./search_arch_parser"; import { constructDateDomain, @@ -1501,7 +1500,7 @@ export class SearchModel extends EventBus { return context; } catch (error) { throw new Error( - sprintf(_t("Failed to evaluate the context: %(context)s.\n%(error)s"), { + _t("Failed to evaluate the context: %(context)s.\n%(error)s", { context, error: error.message, }) @@ -1589,7 +1588,7 @@ export class SearchModel extends EventBus { return params.raw ? domain : domain.toList(this.domainEvalContext); } catch (error) { throw new Error( - sprintf(_t("Failed to evaluate the domain: %(domain)s.\n%(error)s"), { + _t("Failed to evaluate the domain: %(domain)s.\n%(error)s", { domain: domain.toString(), error: error.message, }) @@ -1991,7 +1990,7 @@ export class SearchModel extends EventBus { } } catch (error) { throw new Error( - sprintf(_t("Failed to evaluate the context: %(context)s.\n%(error)s"), { + _t("Failed to evaluate the context: %(context)s.\n%(error)s", { context: searchItem.context, error: error.message, }) diff --git a/addons/web/static/src/views/calendar/calendar_controller.js b/addons/web/static/src/views/calendar/calendar_controller.js index ef1e61ca5e5..7fdc2c54056 100644 --- a/addons/web/static/src/views/calendar/calendar_controller.js +++ b/addons/web/static/src/views/calendar/calendar_controller.js @@ -3,7 +3,6 @@ import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; import { _t } from "@web/core/l10n/translation"; import { useOwnedDialogs, useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { Layout } from "@web/search/layout"; import { useModelWithSampleData } from "@web/model/model"; import { FormViewDialog } from "@web/views/view_dialogs/form_view_dialog"; @@ -175,7 +174,7 @@ export class CalendarController extends Component { resModel: this.model.resModel, resId: record.id || false, context, - title: record.id ? sprintf(_t("Open: %s"), record.title) : _t("New Event"), + title: record.id ? _t("Open: %s", record.title) : _t("New Event"), viewId: this.model.formViewId, onRecordSaved: () => this.model.load(), }, diff --git a/addons/web/static/src/views/calendar/filter_panel/calendar_filter_panel.js b/addons/web/static/src/views/calendar/filter_panel/calendar_filter_panel.js index 1fc765b6688..babb8e4cde7 100644 --- a/addons/web/static/src/views/calendar/filter_panel/calendar_filter_panel.js +++ b/addons/web/static/src/views/calendar/filter_panel/calendar_filter_panel.js @@ -4,7 +4,6 @@ import { _t } from "@web/core/l10n/translation"; import { AutoComplete } from "@web/core/autocomplete/autocomplete"; import { Transition } from "@web/core/transition"; import { useOwnedDialogs, useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { SelectCreateDialog } from "@web/views/view_dialogs/select_create_dialog"; import { getColor } from "../colors"; import { Component, useState } from "@odoo/owl"; @@ -29,7 +28,7 @@ export class CalendarFilterPanel extends Component { return { autoSelect: true, resetOnSelect: true, - placeholder: sprintf(_t("+ Add %s"), section.label), + placeholder: _t("+ Add %s", section.label), sources: [ { placeholder: _t("Loading..."), @@ -93,11 +92,11 @@ export class CalendarFilterPanel extends Component { context: {}, }); dynamicFilters.push({ - description: sprintf(_t("Quick search: %s"), request), + description: _t("Quick search: %s", request), domain: [["id", "in", nameGets.map((nameGet) => nameGet[0])]], }); } - const title = sprintf(_t("Search: %s"), section.label); + const title = _t("Search: %s", section.label); this.addDialog(SelectCreateDialog, { title, noCreate: true, diff --git a/addons/web/static/src/views/fields/file_handler.js b/addons/web/static/src/views/fields/file_handler.js index dfd0255c4be..5592128fe9c 100644 --- a/addons/web/static/src/views/fields/file_handler.js +++ b/addons/web/static/src/views/fields/file_handler.js @@ -1,7 +1,6 @@ /** @odoo-module **/ import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { getDataURLFromFile } from "@web/core/utils/urls"; import { session } from "@web/session"; import { formatFloat } from "./formatters"; @@ -32,14 +31,11 @@ export class FileUploader extends Component { for (const file of ev.target.files) { if (file.size > this.maxUploadSize) { this.notification.add( - sprintf( - this.env._t("The selected file exceed the maximum file size of %s."), + this.env._t( + "The selected file exceed the maximum file size of %s.", formatFloat(this.maxUploadSize, { humanReadable: true }) ), - { - title: this.env._t("File upload"), - type: "danger", - } + { title: this.env._t("File upload"), type: "danger" } ); } this.state.isUploading = true; diff --git a/addons/web/static/src/views/fields/formatters.js b/addons/web/static/src/views/fields/formatters.js index e82e351b4b0..d8c6e9dfe98 100644 --- a/addons/web/static/src/views/fields/formatters.js +++ b/addons/web/static/src/views/fields/formatters.js @@ -4,7 +4,7 @@ import { formatDate, formatDateTime } from "@web/core/l10n/dates"; import { localization as l10n } from "@web/core/l10n/localization"; import { _t } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; -import { escape, intersperse, nbsp, sprintf } from "@web/core/utils/strings"; +import { escape, intersperse, nbsp } from "@web/core/utils/strings"; import { isBinarySize } from "@web/core/utils/binary"; import { markup } from "@odoo/owl"; @@ -364,7 +364,7 @@ export function formatX2many(value) { } else if (count === 1) { return _t("1 record"); } else { - return sprintf(_t("%s records"), count); + return _t("%s records", count); } } diff --git a/addons/web/static/src/views/fields/many2one/many2one_field.js b/addons/web/static/src/views/fields/many2one/many2one_field.js index f87a201d0e0..896df28645b 100644 --- a/addons/web/static/src/views/fields/many2one/many2one_field.js +++ b/addons/web/static/src/views/fields/many2one/many2one_field.js @@ -8,7 +8,6 @@ import { _t } from "@web/core/l10n/translation"; import { evaluateExpr } from "@web/core/py_js/py"; import { registry } from "@web/core/registry"; import { useChildRef, useOwnedDialogs, useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { Many2XAutocomplete, useOpenMany2XRecord } from "@web/views/fields/relational_utils"; import * as BarcodeScanner from "@web/webclient/barcode/barcode_scanner"; import { standardFieldProps } from "../standard_field_props"; @@ -20,7 +19,7 @@ class CreateConfirmationDialog extends Component { static components = { Dialog }; get title() { - return sprintf(this.env._t("New: %s"), this.props.name); + return this.env._t("New: %s", this.props.name); } async onCreate() { diff --git a/addons/web/static/src/views/fields/properties/properties_field.js b/addons/web/static/src/views/fields/properties/properties_field.js index 1ce20e3fa33..2248f4bc1af 100644 --- a/addons/web/static/src/views/fields/properties/properties_field.js +++ b/addons/web/static/src/views/fields/properties/properties_field.js @@ -10,7 +10,6 @@ import { DropdownItem } from "@web/core/dropdown/dropdown_item"; import { PropertyValue } from "./property_value"; import { useService } from "@web/core/utils/hooks"; import { usePopover } from "@web/core/popover/popover_hook"; -import { sprintf } from "@web/core/utils/strings"; import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog"; import { reposition } from "@web/core/position_hook"; import { archParseBoolean } from "@web/views/utils"; @@ -278,10 +277,8 @@ export class PropertiesField extends Component { this.popover.close(); const dialogProps = { title: _t("Delete Property Field"), - body: sprintf( - _t( - 'Are you sure you want to delete this property field? It will be removed for everyone using the "%s" %s.' - ), + body: _t( + 'Are you sure you want to delete this property field? It will be removed for everyone using the "%s" %s.', this.parentName, this.parentString ), @@ -325,7 +322,7 @@ export class PropertiesField extends Component { propertiesDefinitions.push({ name: uuid(), - string: sprintf(_t("Property %s"), propertiesDefinitions.length + 1), + string: _t("Property %s", propertiesDefinitions.length + 1), type: "char", definition_changed: true, }); @@ -583,7 +580,7 @@ async function actionAddProperty(env) { if (addProperty) { addProperty.click(); } else { - const message = sprintf(env._t("You can not create a new property.")); + const message = env._t("You can not create a new property."); env.services.notification.add(message, { type: "danger" }); } } diff --git a/addons/web/static/src/views/fields/properties/property_definition.js b/addons/web/static/src/views/fields/properties/property_definition.js index b26517f7c8d..ad5c8f1e2a3 100644 --- a/addons/web/static/src/views/fields/properties/property_definition.js +++ b/addons/web/static/src/views/fields/properties/property_definition.js @@ -12,7 +12,6 @@ import { Many2XAutocomplete } from "@web/views/fields/relational_utils"; import { useService, useOwnedDialogs } from "@web/core/utils/hooks"; import { PropertyDefinitionSelection } from "./property_definition_selection"; import { PropertyTags } from "./property_tags"; -import { sprintf } from "@web/core/utils/strings"; import { SelectCreateDialog } from "@web/views/view_dialogs/select_create_dialog"; import { uuid } from "../../utils"; @@ -341,8 +340,8 @@ export class PropertyDefinition extends Component { this.state.resModelDescription = result[0].display_name; } catch { // can not read the ir.model - this.state.resModelDescription = sprintf( - _t('You do not have access to the model "%s".'), + this.state.resModelDescription = _t( + 'You do not have access to the model "%s".', newModel ); } diff --git a/addons/web/static/src/views/fields/properties/property_tags.js b/addons/web/static/src/views/fields/properties/property_tags.js index c6e07a85a50..60e2ebf8cb6 100644 --- a/addons/web/static/src/views/fields/properties/property_tags.js +++ b/addons/web/static/src/views/fields/properties/property_tags.js @@ -6,7 +6,6 @@ import { _t } from "@web/core/l10n/translation"; import { usePopover } from "@web/core/popover/popover_hook"; import { registry } from "@web/core/registry"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { TagsList } from "@web/core/tags_list/tags_list"; import { Component } from "@odoo/owl"; @@ -139,7 +138,7 @@ export class PropertyTags extends Component { return [ { value: { toCreate: true, value: request }, - label: sprintf(_t('Create "%s"'), request), + label: _t('Create "%s"', request), classList: "o_field_property_dropdown_add", }, ]; diff --git a/addons/web/static/src/views/fields/relational_utils.js b/addons/web/static/src/views/fields/relational_utils.js index ded01cd476d..82735eab8b1 100644 --- a/addons/web/static/src/views/fields/relational_utils.js +++ b/addons/web/static/src/views/fields/relational_utils.js @@ -13,7 +13,6 @@ import { useOwnedDialogs, useService, } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { createElement } from "@web/core/utils/xml"; import { FormArchParser } from "@web/views/form/form_arch_parser"; import { loadSubViews } from "@web/views/form/form_controller"; @@ -283,7 +282,7 @@ export class Many2XAutocomplete extends Component { if (this.props.quickCreate && request.length) { options.push({ - label: sprintf(this.env._t(`Create "%s"`), request), + label: this.env._t('Create "%s"', request), classList: "o_m2o_dropdown_option o_m2o_dropdown_option_create", action: async (params) => { try { @@ -363,13 +362,13 @@ export class Many2XAutocomplete extends Component { dynamicFilters = [ { - description: sprintf(this.env._t("Quick search: %s"), request), + description: this.env._t("Quick search: %s", request), domain: [["id", "in", nameGets.map((nameGet) => nameGet[0])]], }, ]; } - const title = sprintf(this.env._t("Search: %s"), fieldString); + const title = this.env._t("Search: %s", fieldString); this.selectCreate({ domain, context, @@ -461,8 +460,7 @@ export function useOpenMany2XRecord({ let resolve = () => {}; if (!title) { - title = resId ? env._t("Open: %s") : env._t("Create %s"); - title = sprintf(title, fieldString); + title = resId ? env._t("Open: %s", fieldString) : env._t("Create %s", fieldString); } const { create: canCreate, write: canWrite } = activeActions; @@ -708,8 +706,9 @@ export function useOpenX2ManyRecord({ async function openRecord({ record, mode, context, title, onClose }) { if (!title) { - title = record ? env._t("Open: %s") : env._t("Create %s"); - title = sprintf(title, activeField.string); + title = record + ? env._t("Open: %s", activeField.string) + : env._t("Create %s", activeField.string); } const list = getList(); const { archInfo, fields: _fields } = await getFormViewInfo({ diff --git a/addons/web/static/src/views/fields/remaining_days/remaining_days_field.js b/addons/web/static/src/views/fields/remaining_days/remaining_days_field.js index cdfc17a58e3..d35b436ca72 100644 --- a/addons/web/static/src/views/fields/remaining_days/remaining_days_field.js +++ b/addons/web/static/src/views/fields/remaining_days/remaining_days_field.js @@ -5,7 +5,6 @@ import { formatDate, formatDateTime } from "@web/core/l10n/dates"; import { localization } from "@web/core/l10n/localization"; import { _t } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; -import { sprintf } from "@web/core/utils/strings"; import { DateTimeField } from "../datetime/datetime_field"; import { standardFieldProps } from "../standard_field_props"; @@ -45,9 +44,9 @@ export class RemainingDaysField extends Component { return this.formattedValue; } if (this.diffDays < 0) { - return sprintf(_t("%s days ago"), -this.diffDays); + return _t("%s days ago", -this.diffDays); } - return sprintf(_t("In %s days"), this.diffDays); + return _t("In %s days", this.diffDays); } get formattedValue() { diff --git a/addons/web/static/src/views/fields/state_selection/state_selection_field.js b/addons/web/static/src/views/fields/state_selection/state_selection_field.js index 1d1062e3491..b6b0a6d3d6c 100644 --- a/addons/web/static/src/views/fields/state_selection/state_selection_field.js +++ b/addons/web/static/src/views/fields/state_selection/state_selection_field.js @@ -6,7 +6,6 @@ import { Dropdown } from "@web/core/dropdown/dropdown"; import { DropdownItem } from "@web/core/dropdown/dropdown_item"; import { _t } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; -import { sprintf } from "@web/core/utils/strings"; import { formatSelection } from "../formatters"; import { standardFieldProps } from "../standard_field_props"; @@ -36,7 +35,7 @@ export class StateSelectionField extends Component { const hotkeys = ["D", "F", "G"]; for (const [index, [value, label]] of this.options.entries()) { useCommand( - sprintf(this.env._t("Set kanban state as %s"), label), + this.env._t("Set kanban state as %s", label), () => { this.updateRecord(value); }, diff --git a/addons/web/static/src/views/fields/statusbar/statusbar_field.js b/addons/web/static/src/views/fields/statusbar/statusbar_field.js index 2318dc1f5c4..7c83446b7b7 100644 --- a/addons/web/static/src/views/fields/statusbar/statusbar_field.js +++ b/addons/web/static/src/views/fields/statusbar/statusbar_field.js @@ -8,7 +8,7 @@ import { DropdownItem } from "@web/core/dropdown/dropdown_item"; import { _t } from "@web/core/l10n/translation"; import { registry } from "@web/core/registry"; import { groupBy } from "@web/core/utils/arrays"; -import { escape, sprintf } from "@web/core/utils/strings"; +import { escape } from "@web/core/utils/strings"; import { useSpecialData } from "@web/views/fields/relational_utils"; import { standardFieldProps } from "../standard_field_props"; @@ -35,7 +35,7 @@ export class StatusBarField extends Component { setup() { if (this.props.withCommand) { - const commandName = sprintf(this.env._t(`Move to %s...`), escape(this.displayName)); + const commandName = this.env._t("Move to %s...", escape(this.displayName)); useCommand( commandName, () => { @@ -61,7 +61,7 @@ export class StatusBarField extends Component { } ); useCommand( - sprintf(this.env._t(`Move to next %s`), this.displayName), + this.env._t("Move to next %s", this.displayName), () => { const options = this.computeItems(false); const nextOption = diff --git a/addons/web/static/src/views/fields/translation_dialog.js b/addons/web/static/src/views/fields/translation_dialog.js index 9a9e0ea2d65..12871219519 100644 --- a/addons/web/static/src/views/fields/translation_dialog.js +++ b/addons/web/static/src/views/fields/translation_dialog.js @@ -2,7 +2,6 @@ import { Dialog } from "@web/core/dialog/dialog"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { loadLanguages } from "@web/core/l10n/translation"; import { Component, onWillStart } from "@odoo/owl"; @@ -10,7 +9,7 @@ import { Component, onWillStart } from "@odoo/owl"; export class TranslationDialog extends Component { setup() { super.setup(); - this.title = sprintf(this.env._t("Translate: %s"), this.props.fieldName); + this.title = this.env._t("Translate: %s", this.props.fieldName); this.orm = useService("orm"); this.user = useService("user"); diff --git a/addons/web/static/src/views/fields/x2many/x2many_field.js b/addons/web/static/src/views/fields/x2many/x2many_field.js index 57c96425d7e..294129ed001 100644 --- a/addons/web/static/src/views/fields/x2many/x2many_field.js +++ b/addons/web/static/src/views/fields/x2many/x2many_field.js @@ -5,7 +5,6 @@ import { evalDomain } from "@web/core/domain"; import { _t } from "@web/core/l10n/translation"; import { Pager } from "@web/core/pager/pager"; import { registry } from "@web/core/registry"; -import { sprintf } from "@web/core/utils/strings"; import { useActiveActions, useAddInlineRecord, @@ -258,7 +257,7 @@ export class X2ManyField extends Component { context = makeContext([this.props.context, context]); if (this.isMany2Many) { const { string } = this.props; - const title = sprintf(this.env._t("Add: %s"), string); + const title = this.env._t("Add: %s", string); return this.selectCreate({ domain, context, title }); } if (editable) { diff --git a/addons/web/static/src/views/graph/graph_model.js b/addons/web/static/src/views/graph/graph_model.js index 9579f554ea4..b3a1f3173e4 100644 --- a/addons/web/static/src/views/graph/graph_model.js +++ b/addons/web/static/src/views/graph/graph_model.js @@ -7,7 +7,6 @@ import { getGroupBy } from "@web/search/utils/group_by"; import { GROUPABLE_TYPES } from "@web/search/utils/misc"; import { Model } from "@web/model/model"; import { computeReportMeasures, processMeasure } from "@web/views/utils"; -import { sprintf } from "@web/core/utils/strings"; import { Domain } from "@web/core/domain"; export const SEP = " / "; @@ -306,7 +305,7 @@ export class GraphModel extends Model { if (!description) { return this.env._t("Sum"); } else { - return sprintf(this.env._t("Sum (%s)"), description); + return this.env._t("Sum (%s)", description); } } diff --git a/addons/web/static/src/views/kanban/kanban_header.js b/addons/web/static/src/views/kanban/kanban_header.js index fcdf84122b5..2b64c646d87 100644 --- a/addons/web/static/src/views/kanban/kanban_header.js +++ b/addons/web/static/src/views/kanban/kanban_header.js @@ -7,7 +7,6 @@ import { DropdownItem } from "@web/core/dropdown/dropdown_item"; import { usePopover } from "@web/core/popover/popover_hook"; import { memoize } from "@web/core/utils/functions"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { useDebounced } from "@web/core/utils/timing"; import { isRelational } from "@web/model/relational_model/utils"; import { isNull } from "@web/views/utils"; @@ -168,7 +167,7 @@ export class KanbanHeader extends Component { context, resId: value, resModel: groupByField.relation, - title: sprintf(this.env._t("Edit: %s"), displayName), + title: this.env._t("Edit: %s", displayName), onRecordSaved: async () => { await this.props.list.load(); this.props.list.model.notify(); diff --git a/addons/web/static/src/views/kanban/kanban_record.js b/addons/web/static/src/views/kanban/kanban_record.js index d47e56684d2..1e2fd7f3913 100644 --- a/addons/web/static/src/views/kanban/kanban_record.js +++ b/addons/web/static/src/views/kanban/kanban_record.js @@ -7,7 +7,6 @@ import { DropdownItem } from "@web/core/dropdown/dropdown_item"; import { registry } from "@web/core/registry"; import { useTooltip } from "@web/core/tooltip/tooltip_hook"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { url } from "@web/core/utils/urls"; import { useRecordObserver } from "@web/model/relational_model/utils"; import { Field } from "@web/views/fields/field"; @@ -334,10 +333,8 @@ export class KanbanRecord extends Component { ) { this.dialog.add(KanbanCoverImageDialog, { autoOpen, fieldName, record }); } else { - const warning = sprintf( - env._t( - `Could not set the cover image: incorrect field ("%s") is provided in the view.` - ), + const warning = env._t( + `Could not set the cover image: incorrect field ("%s") is provided in the view.`, fieldName ); this.notification.add({ title: warning, type: "danger" }); diff --git a/addons/web/static/src/views/kanban/kanban_renderer.js b/addons/web/static/src/views/kanban/kanban_renderer.js index c6c91cfb569..ba3df1a0622 100644 --- a/addons/web/static/src/views/kanban/kanban_renderer.js +++ b/addons/web/static/src/views/kanban/kanban_renderer.js @@ -6,7 +6,6 @@ import { useHotkey } from "@web/core/hotkeys/hotkey_hook"; import { registry } from "@web/core/registry"; import { useBus, useService } from "@web/core/utils/hooks"; import { useSortable } from "@web/core/utils/sortable"; -import { sprintf } from "@web/core/utils/strings"; import { isNull } from "@web/views/utils"; import { ColumnProgress } from "@web/views/view_components/column_progress"; import { useBounceButton } from "@web/views/view_hook"; @@ -329,7 +328,7 @@ export class KanbanRenderer extends Component { if (this.exampleData && this.exampleData.ghostColumns) { colNames = this.exampleData.ghostColumns; } else { - colNames = [1, 2, 3, 4].map((num) => sprintf(this.env._t("Column %s"), num)); + colNames = [1, 2, 3, 4].map((num) => this.env._t("Column %s", num)); } return colNames.map((colName) => ({ name: colName, diff --git a/addons/web/static/src/webclient/actions/action_service.js b/addons/web/static/src/webclient/actions/action_service.js index 86f860c23ca..84c5b522328 100644 --- a/addons/web/static/src/webclient/actions/action_service.js +++ b/addons/web/static/src/webclient/actions/action_service.js @@ -7,7 +7,6 @@ import { evaluateExpr } from "@web/core/py_js/py"; import { registry } from "@web/core/registry"; import { Deferred, KeepLast } from "@web/core/utils/concurrency"; import { useBus, useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { cleanDomFromBootstrap } from "@web/legacy/utils"; import { View, ViewNotFoundError } from "@web/views/view"; import { ActionDialog } from "./action_dialog"; @@ -1306,10 +1305,7 @@ function makeActionManager(env) { const view = _getView(viewType); if (!view) { throw new ViewNotFoundError( - sprintf( - env._t("No view of type '%s' could be found in the current action."), - viewType - ) + env._t("No view of type '%s' could be found in the current action.", viewType) ); } const newController = controller.action.controllers[viewType] || { diff --git a/addons/web/static/src/webclient/actions/client_actions.js b/addons/web/static/src/webclient/actions/client_actions.js index 684b80e288f..b1d1dbf2aac 100644 --- a/addons/web/static/src/webclient/actions/client_actions.js +++ b/addons/web/static/src/webclient/actions/client_actions.js @@ -33,10 +33,7 @@ class InvalidAction extends Component { } onMounted() { - const message = sprintf( - this.env._t("No action with id '%s' could be found"), - this.props.actionId - ); + const message = this.env._t("No action with id '%s' could be found", this.props.actionId); this.notification.add(message, { type: "danger" }); } } diff --git a/addons/web/static/src/webclient/settings_form_view/widgets/res_config_invite_users.js b/addons/web/static/src/webclient/settings_form_view/widgets/res_config_invite_users.js index a9944c05d32..b59a61c33dd 100644 --- a/addons/web/static/src/webclient/settings_form_view/widgets/res_config_invite_users.js +++ b/addons/web/static/src/webclient/settings_form_view/widgets/res_config_invite_users.js @@ -4,7 +4,6 @@ import { registry } from "@web/core/registry"; import { _t } from "@web/core/l10n/translation"; import { unique } from "@web/core/utils/arrays"; import { useService } from "@web/core/utils/hooks"; -import { sprintf } from "@web/core/utils/strings"; import { Component, useState, onWillStart } from "@odoo/owl"; import { standardWidgetProps } from "@web/views/widgets/standard_widget_props"; @@ -70,15 +69,15 @@ class ResConfigInviteUsers extends Component { }); switch (invalidEmails.length) { case 1: - return sprintf(_t("Invalid email address: %(address)s"), { + return _t("Invalid email address: %(address)s", { address: invalidEmails[0], }); case 2: - return sprintf(_t("Invalid email addresses: %(2 addresses)s"), { + return _t("Invalid email addresses: %(2 addresses)s", { "2 addresses": listFormatter.format(invalidEmails), }); default: - return sprintf(_t("Invalid email addresses: %(addresses)s"), { + return _t("Invalid email addresses: %(addresses)s", { addresses: listFormatter.format(invalidEmails), }); } diff --git a/addons/web/static/tests/core/l10n/translation_tests.js b/addons/web/static/tests/core/l10n/translation_tests.js index b3aa96eb4ba..c66c0d969e9 100644 --- a/addons/web/static/tests/core/l10n/translation_tests.js +++ b/addons/web/static/tests/core/l10n/translation_tests.js @@ -171,3 +171,23 @@ QUnit.test("tamil has the correct numbering system", async (assert) => { "௧௦/௧௨/௨௦௨௧ ௧௨:௦௦:௦௦" ); }); + +QUnit.test( + "_t fills the format specifiers in translated terms with its extra arguments", + async (assert) => { + patchWithCleanup(translatedTerms, { "Due in %s days": "Échéance dans %s jours" }); + const translatedStr = _t("Due in %s days", 513); + assert.strictEqual(translatedStr, "Échéance dans 513 jours"); + } +); + +QUnit.test( + "_t fills the format specifiers in lazy translated terms with its extra arguments", + async (assert) => { + translatedTerms[translationLoaded] = false; + const translatedStr = _t("Due in %s days", 513); + patchWithCleanup(translatedTerms, { "Due in %s days": "Échéance dans %s jours" }); + translatedTerms[translationLoaded] = true; + assert.equal(translatedStr, "Échéance dans 513 jours"); + } +); diff --git a/addons/web_editor/static/src/js/common/ace.js b/addons/web_editor/static/src/js/common/ace.js index d1f29f39625..8a8735d3d0f 100644 --- a/addons/web_editor/static/src/js/common/ace.js +++ b/addons/web_editor/static/src/js/common/ace.js @@ -7,7 +7,6 @@ import dom from "@web/legacy/js/core/dom"; import Dialog from "@web/legacy/js/core/dialog"; import Widget from "@web/legacy/js/core/widget"; import localStorage from "@web/legacy/js/core/local_storage"; -import { sprintf } from "@web/core/utils/strings"; import { debounce } from "@web/core/utils/timing"; import { sortBy } from "@web/core/utils/arrays"; import { pick } from "@web/core/utils/objects"; @@ -429,11 +428,11 @@ var ViewEditor = Widget.extend({ this.aceEditor.setSession(editingSession); if (this.currentType === 'xml') { - this.$viewID.text(sprintf(_t("Template ID: %s"), this.views[resID].key)); + this.$viewID.text(_t("Template ID: %s", this.views[resID].key)); } else if (this.currentType === 'scss') { - this.$viewID.text(sprintf(_t("SCSS file: %s"), resID)); + this.$viewID.text(_t("SCSS file: %s", resID)); } else { - this.$viewID.text(sprintf(_t("JS file: %s"), resID)); + this.$viewID.text(_t("JS file: %s", resID)); } const isCustomized = this._isCustomResource(resID); this.$lists[this.currentType].select2('val', resID); diff --git a/addons/web_editor/static/src/js/editor/snippets.editor.js b/addons/web_editor/static/src/js/editor/snippets.editor.js index 4e2fab06740..ba6e5524fe0 100644 --- a/addons/web_editor/static/src/js/editor/snippets.editor.js +++ b/addons/web_editor/static/src/js/editor/snippets.editor.js @@ -10,7 +10,7 @@ import options from "@web_editor/js/editor/snippets.options"; import SmoothScrollOnDrag from "@web_editor/js/editor/smooth_scroll_on_drag"; import weUtils from "@web_editor/js/common/utils"; import * as gridUtils from "@web_editor/js/common/grid_layout_utils"; -import { sprintf, escape } from "@web/core/utils/strings"; +import { escape } from "@web/core/utils/strings"; const QWeb = core.qweb; import { closestElement, isUnremovable } from "@web_editor/js/editor/odoo-editor/src/utils/utils"; import { debounce, throttleForAnimation } from "@web/core/utils/timing"; @@ -2958,12 +2958,12 @@ var SnippetsMenu = Widget.extend({ const btnRenameEl = document.createElement('we-button'); btnRenameEl.dataset.snippetId = $snippet.data('oeSnippetId'); btnRenameEl.classList.add('o_rename_btn', 'fa', 'fa-pencil', 'btn', 'o_we_hover_success'); - btnRenameEl.title = sprintf(_t("Rename %s"), name); + btnRenameEl.title = _t("Rename %s", name); $snippet.append(btnRenameEl); const btnEl = document.createElement('we-button'); btnEl.dataset.snippetId = $snippet.data('oeSnippetId'); btnEl.classList.add('o_delete_btn', 'fa', 'fa-trash', 'btn', 'o_we_hover_danger'); - btnEl.title = sprintf(_t("Delete %s"), name); + btnEl.title = _t("Delete %s", name); $snippet.append(btnEl); } }) @@ -3830,9 +3830,9 @@ var SnippetsMenu = Widget.extend({ var moduleID = $snippet.data('moduleId'); var name = $snippet.attr('name'); new Dialog(this, { - title: sprintf(_t("Install %s"), name), + title: _t("Install %s", name), size: 'medium', - $content: $('
', {text: sprintf(_t("Do you want to install the %s App?"), name)}).append( + $content: $('
', {text: _t("Do you want to install the %s App?", name)}).append( $('', { target: '_blank', href: '/web#id=' + encodeURIComponent(moduleID) + '&view_type=form&model=ir.module.module&action=base.open_module_tree', @@ -3858,7 +3858,7 @@ var SnippetsMenu = Widget.extend({ }).guardedCatch(reason => { reason.event.preventDefault(); this.close(); - const message = sprintf(Markup(_t("Could not install module %s")), name); + const message = Markup(_t("Could not install module %s", escape(name))); self.displayNotification({ message: message, type: 'danger', @@ -3919,7 +3919,7 @@ var SnippetsMenu = Widget.extend({ new Dialog(this, { size: 'medium', title: _t('Confirmation'), - $content: $('

' + sprintf(_t("Are you sure you want to delete the snippet: %s?"), $snippet.attr('name')) + '

'), + $content: $('

' + _t("Are you sure you want to delete the snippet: %s?", $snippet.attr('name')) + '

'), buttons: [{ text: _t("Yes"), close: true, diff --git a/addons/web_editor/static/src/js/editor/snippets.options.js b/addons/web_editor/static/src/js/editor/snippets.options.js index aabf96497ef..29ca207278e 100644 --- a/addons/web_editor/static/src/js/editor/snippets.options.js +++ b/addons/web_editor/static/src/js/editor/snippets.options.js @@ -35,7 +35,6 @@ import { } from "@web_editor/js/editor/image_processing"; import * as OdooEditorLib from "@web_editor/js/editor/odoo-editor/src/OdooEditor"; import {SIZES, MEDIAS_BREAKPOINTS} from "@web/core/ui/ui_service"; -import { sprintf } from "@web/core/utils/strings"; import { uniqueId } from "@web/core/utils/functions"; import { pick } from "@web/core/utils/objects"; import { _t } from "@web/core/l10n/translation"; @@ -5846,14 +5845,14 @@ const ImageHandlerOption = SnippetOptionWidget.extend({ 1024: ['1024px', 'image/webp'], 1920: ['1920px', 'image/webp'], }; - widths[img.naturalWidth] = [sprintf(_t("%spx"), img.naturalWidth), 'image/webp']; - widths[optimizedWidth] = [sprintf(_t("%spx (Suggested)"), optimizedWidth), 'image/webp']; + widths[img.naturalWidth] = [_t("%spx", img.naturalWidth), 'image/webp']; + widths[optimizedWidth] = [_t("%spx (Suggested)", optimizedWidth), 'image/webp']; const imgMimetype = this._getImageMimetype(img); - widths[maxWidth] = [sprintf(_t("%spx (Original)"), maxWidth), imgMimetype]; + widths[maxWidth] = [_t("%spx (Original)", maxWidth), imgMimetype]; if (imgMimetype !== 'image/webp') { // Avoid a key collision by subtracting 0.1 - putting the webp // above the original format one of the same size. - widths[maxWidth - 0.1] = [sprintf(_t("%spx"), maxWidth), 'image/webp']; + widths[maxWidth - 0.1] = [_t("%spx", maxWidth), 'image/webp']; } return Object.entries(widths) .filter(([width]) => width <= maxWidth) @@ -8047,7 +8046,7 @@ registry.SnippetSave = SnippetOptionWidget.extend({ reloadEditor: true, invalidateSnippetCache: true, onSuccess: async () => { - const defaultSnippetName = sprintf(_t("Custom %s"), this.data.snippetName); + const defaultSnippetName = _t("Custom %s", this.data.snippetName); const targetCopyEl = this.$target[0].cloneNode(true); delete targetCopyEl.dataset.name; // By the time onSuccess is called after request_save, the diff --git a/addons/web_unsplash/static/src/components/media_dialog/image_selector.js b/addons/web_unsplash/static/src/components/media_dialog/image_selector.js index 65e8baacb5a..c57c40346c6 100644 --- a/addons/web_unsplash/static/src/components/media_dialog/image_selector.js +++ b/addons/web_unsplash/static/src/components/media_dialog/image_selector.js @@ -8,7 +8,6 @@ import { useService } from '@web/core/utils/hooks'; import { uploadService, AUTOCLOSE_DELAY } from '@web_editor/components/upload_progress_toast/upload_service'; import { useState, Component } from "@odoo/owl"; -import { sprintf } from '@web/core/utils/strings'; class UnsplashCredentials extends Component { setup() { @@ -244,8 +243,8 @@ patch(uploadService, { const file = service.addFile({ id: service.fileId, name: records.length > 1 ? - sprintf(env._t("Uploading %s '%s' images."), records.length, records[0].query) : - sprintf(env._t("Uploading '%s' image."), records[0].query), + env._t("Uploading %s '%s' images.", records.length, records[0].query) : + env._t("Uploading '%s' image.", records[0].query), size: null, progress: 0, }); diff --git a/addons/website/static/src/client_actions/website_preview/website_preview.js b/addons/website/static/src/client_actions/website_preview/website_preview.js index a7072f40f59..66e85a2df86 100644 --- a/addons/website/static/src/client_actions/website_preview/website_preview.js +++ b/addons/website/static/src/client_actions/website_preview/website_preview.js @@ -12,7 +12,6 @@ import {OptimizeSEODialog} from '@website/components/dialog/seo'; import { WebsiteDialog } from "@website/components/dialog/dialog"; import { routeToUrl } from "@web/core/browser/router_service"; import { getActiveHotkey } from "@web/core/hotkeys/hotkey_service"; -import { sprintf } from "@web/core/utils/strings"; import wUtils from '@website/js/utils'; import config from "@web/legacy/js/services/config"; @@ -70,10 +69,7 @@ export class WebsitePreview extends Component { // same session and CORS errors won't be a thing in such a case) this.dialogService.add(WebsiteDialog, { title: this.env._t("Redirecting..."), - body: sprintf(this.env._t( - "You are about to be redirected to the domain configured for your website ( %s ). " + - "This is necessary to edit or view your website from the Website app. You might need to log back in." - ), this.websiteDomain), + body: this.env._t("You are about to be redirected to the domain configured for your website ( %s ). This is necessary to edit or view your website from the Website app. You might need to log back in.", this.websiteDomain), showSecondaryButton: false, }, { onClose: () => { diff --git a/addons/website/static/src/components/views/page_list.js b/addons/website/static/src/components/views/page_list.js index facc6b55461..46cb261734a 100644 --- a/addons/website/static/src/components/views/page_list.js +++ b/addons/website/static/src/components/views/page_list.js @@ -5,7 +5,6 @@ import {registry} from '@web/core/registry'; import {listView} from '@web/views/list/list_view'; import {ConfirmationDialog} from "@web/core/confirmation_dialog/confirmation_dialog"; import {useService} from "@web/core/utils/hooks"; -import {sprintf} from "@web/core/utils/strings"; import {DeletePageDialog} from '@website/components/dialog/page_properties'; import {SearchDropdownItem} from "@web/search/search_dropdown_item/search_dropdown_item"; @@ -44,12 +43,7 @@ export class PageListController extends PageControllerMixin(listView.Controller) callback: async () => { this.dialogService.add(ConfirmationDialog, { title: this.env._t("Publish Website Content"), - body: sprintf( - this.env._t( - "%s record(s) selected, are you sure you want to publish them all?" - ), - this.model.root.selection.length - ), + body: this.env._t("%s record(s) selected, are you sure you want to publish them all?", this.model.root.selection.length), confirm: () => this.togglePublished(true), }); }, diff --git a/addons/website/static/src/js/editor/snippets.options.js b/addons/website/static/src/js/editor/snippets.options.js index aa33d2731ed..188f4999271 100644 --- a/addons/website/static/src/js/editor/snippets.options.js +++ b/addons/website/static/src/js/editor/snippets.options.js @@ -3,7 +3,7 @@ import core from "@web/legacy/js/services/core"; import { loadBundle, loadCSS } from "@web/core/assets"; import Dialog from "@web/legacy/js/core/dialog"; -import { Markup, sprintf } from "@web/legacy/js/core/utils"; +import { Markup } from "@web/legacy/js/core/utils"; import weUtils from "@web_editor/js/common/utils"; import options from "@web_editor/js/editor/snippets.options"; import { NavbarLinkPopoverWidget } from "@website/js/widgets/link_popover_widget"; @@ -2807,7 +2807,7 @@ options.registry.anchor = options.Class.extend({ _buildClipboard(buttonEl) { const clipboard = new ClipboardJS(buttonEl, {text: () => this._getAnchorLink()}); clipboard.on("success", () => { - const message = sprintf(Markup(_t("Anchor copied to clipboard
Link: %s")), this._getAnchorLink()); + const message = Markup(_t("Anchor copied to clipboard
Link: %s", this._getAnchorLink())); this.displayNotification({ type: "success", message: message, diff --git a/addons/website/static/src/js/tours/tour_utils.js b/addons/website/static/src/js/tours/tour_utils.js index 8b6daa45156..6860977af8b 100644 --- a/addons/website/static/src/js/tours/tour_utils.js +++ b/addons/website/static/src/js/tours/tour_utils.js @@ -4,7 +4,6 @@ import { _t } from "@web/core/l10n/translation"; import { Markup } from "@web/legacy/js/core/utils"; import { registry } from "@web/core/registry"; -import { sprintf } from "@web/core/utils/strings"; function addMedia(position = "right") { return { @@ -106,7 +105,7 @@ function changeOption(optionName, weName = '', optionTooltipLabel = '', position const option_block = `${noPalette} we-customizeblock-option[class='snippet-option-${optionName}']`; return { trigger: `${option_block} ${weName}, ${option_block} [title='${weName}']`, - content: Markup(sprintf(_t("Click on this option to change the %s of the block."), optionTooltipLabel)), + content: Markup(_t("Click on this option to change the %s of the block.", optionTooltipLabel)), position: position, in_modal: false, run: "click", @@ -118,7 +117,7 @@ function selectNested(trigger, optionName, alt_trigger = null, optionTooltipLabe const option_block = `${noPalette} we-customizeblock-option[class='snippet-option-${optionName}']`; return { trigger: trigger, - content: Markup(sprintf(_t("Select a %s."), optionTooltipLabel)), + content: Markup(_t("Select a %s.", optionTooltipLabel)), alt_trigger: alt_trigger == null ? undefined : `${option_block} ${alt_trigger}`, position: position, run: 'click', @@ -135,7 +134,7 @@ function changePaddingSize(direction) { } return { trigger: `iframe .oe_overlay.ui-draggable.o_we_overlay_sticky.oe_active .o_handle.${paddingDirection}`, - content: Markup(sprintf(_t("Slide this button to change the %s padding"), direction)), + content: Markup(_t("Slide this button to change the %s padding", direction)), consumeEvent: 'mousedown', position: position, }; @@ -235,7 +234,7 @@ function dragNDrop(snippet, position = "bottom") { return { trigger: `#oe_snippets .oe_snippet[name="${snippet.name}"] .oe_snippet_thumbnail:not(.o_we_already_dragging)`, extra_trigger: ".o_website_preview.editor_enable.editor_has_snippets", - content: Markup(sprintf(_t("Drag the %s building block and drop it at the bottom of the page."), snippet.name)), + content: Markup(_t("Drag the %s building block and drop it at the bottom of the page.", snippet.name)), position: position, // Normally no main snippet can be dropped in the default footer but // targeting it allows to force "dropping at the end of the page". diff --git a/addons/website/static/src/services/website_custom_menus.js b/addons/website/static/src/services/website_custom_menus.js index 533f3743b98..e649b432822 100644 --- a/addons/website/static/src/services/website_custom_menus.js +++ b/addons/website/static/src/services/website_custom_menus.js @@ -4,7 +4,6 @@ import { registry } from "@web/core/registry"; import { EditMenuDialog } from '@website/components/dialog/edit_menu'; import { OptimizeSEODialog } from '@website/components/dialog/seo'; import {PagePropertiesDialog} from '@website/components/dialog/page_properties'; -import {sprintf} from '@web/core/utils/strings'; /** * This service displays contextual menus, depending of the state of the @@ -53,7 +52,7 @@ export const websiteCustomMenus = { // 'navbar menus' display system. filteredSections.push(...website.currentWebsite.metadata.contentMenus.map((menu, index) => ({ ...section, - name: sprintf(env._t("Edit %s"), menu[0]), + name: env._t("Edit %s", menu[0]), dynamicProps: {rootID: parseInt(menu[1], 10)}, // Prevent a 't-foreach' duplicate key on menus template. id: `${section.id}-${index}`, diff --git a/addons/website/static/src/snippets/s_website_form/000.js b/addons/website/static/src/snippets/s_website_form/000.js index 4d4ed3dd659..9411761857d 100644 --- a/addons/website/static/src/snippets/s_website_form/000.js +++ b/addons/website/static/src/snippets/s_website_form/000.js @@ -8,7 +8,6 @@ import publicWidget from "@web/legacy/js/public/public_widget"; import dom from "@web/legacy/js/core/dom"; import concurrency from "@web/legacy/js/core/concurrency"; - import { sprintf } from "@web/core/utils/strings"; import { debounce } from "@web/core/utils/timing"; import { _t } from "@web/core/l10n/translation"; @@ -284,12 +283,15 @@ if (!self.check_error_fields({})) { if (this.fileInputError) { const errorMessage = this.fileInputError.type === "number" - ? sprintf(_t( - "Please fill in the form correctly. You uploaded too many files. (Maximum %s files)" - ), this.fileInputError.limit) - : sprintf(_t( - "Please fill in the form correctly. The file \"%s\" is too big. (Maximum %s MB)" - ), this.fileInputError.fileName, this.fileInputError.limit); + ? _t( + "Please fill in the form correctly. You uploaded too many files. (Maximum %s files)", + this.fileInputError.limit + ) + : _t( + "Please fill in the form correctly. The file \"%s\" is too big. (Maximum %s MB)", + this.fileInputError.fileName, + this.fileInputError.limit + ); this.update_status("error", errorMessage); delete this.fileInputError; } else { @@ -580,13 +582,13 @@ if (datetime.isValid()) { return time.datetime_to_str(datetime.toDate()); } - throw new Error(sprintf(_t("'%s' is not a correct datetime"), value)); + throw new Error(_t("'%s' is not a correct datetime", value)); case 'date': var date = moment(value, [date_pattern, date_pattern_wo_zero], true); if (date.isValid()) { return time.date_to_str(date.toDate()); } - throw new Error(sprintf(_t("'%s' is not a correct date"), value)); + throw new Error(_t("'%s' is not a correct date", value)); } return value; }, diff --git a/addons/website/static/src/snippets/s_website_form/options.js b/addons/website/static/src/snippets/s_website_form/options.js index 75d12c0a6c9..35ca145b242 100644 --- a/addons/website/static/src/snippets/s_website_form/options.js +++ b/addons/website/static/src/snippets/s_website_form/options.js @@ -7,7 +7,6 @@ import Dialog from "@web/legacy/js/core/dialog"; import dom from "@web/legacy/js/core/dom"; import weUtils from "@web_editor/js/common/utils"; import "@website/js/editor/snippets.options"; -import { sprintf } from "@web/core/utils/strings"; import { unique } from "@web/core/utils/arrays"; import { _t } from "@web/core/l10n/translation"; @@ -1370,7 +1369,7 @@ options.registry.WebsiteFieldEditor = FieldEditor.extend({ const list = document.createElement('we-list'); const optionText = select ? 'Option' : type === 'selection' ? 'Radio' : 'Checkbox'; list.setAttribute('string', `${optionText} List`); - list.dataset.addItemTitle = sprintf(_t("Add new %s"), optionText); + list.dataset.addItemTitle = _t("Add new %s", optionText); list.dataset.renderListItems = ''; list.dataset.hasDefault = ['one2many', 'many2many'].includes(type) ? 'multiple' : 'unique'; @@ -1586,8 +1585,7 @@ options.registry.WebsiteFormFieldRequired = DisableOverlayButtonOption.extend({ const fieldName = this.$target[0] .querySelector("input.s_website_form_input").getAttribute("name"); const spanEl = document.createElement("span"); - spanEl.innerText = sprintf(_t( - "The field '%s' is mandatory for the action '%s'."), fieldName, currentActionName); + spanEl.innerText = _t("The field '%s' is mandatory for the action '%s'.", fieldName, currentActionName); uiFragment.querySelector("we-alert").appendChild(spanEl); }, }); diff --git a/addons/website/static/src/systray_items/new_content.js b/addons/website/static/src/systray_items/new_content.js index 2d0cf411b1b..6a413e2e934 100644 --- a/addons/website/static/src/systray_items/new_content.js +++ b/addons/website/static/src/systray_items/new_content.js @@ -216,9 +216,7 @@ export class NewContentModal extends Component { } return el; }); - this.website.showLoader({ - title: sprintf(this.env._t("Building your %s"), name), - }); + this.website.showLoader({ title: this.env._t("Building your %s", name) }); try { await this.installModule(id, element.redirectUrl); } catch (error) { diff --git a/addons/website_blog/static/src/js/website_blog.js b/addons/website_blog/static/src/js/website_blog.js index 14664fa314e..b4ce5baa941 100644 --- a/addons/website_blog/static/src/js/website_blog.js +++ b/addons/website_blog/static/src/js/website_blog.js @@ -1,6 +1,5 @@ /** @odoo-module **/ -import { sprintf } from "@web/core/utils/strings"; import { _t } from "@web/core/l10n/translation"; import dom from "@web/legacy/js/core/dom"; import publicWidget from "@web/legacy/js/public/public_widget"; @@ -74,8 +73,11 @@ publicWidget.registry.websiteBlog = publicWidget.Widget.extend({ var blogPostTitle = $('#o_wblog_post_name').html() || ''; var articleURL = window.location.href; if ($element.hasClass('o_twitter')) { - var twitterText = _t("Amazing blog article: %s! Check it live: %s"); - var tweetText = sprintf(twitterText, blogPostTitle, articleURL); + var tweetText = _t( + "Amazing blog article: %s! Check it live: %s", + blogPostTitle, + articleURL + ); url = 'https://twitter.com/intent/tweet?tw_p=tweetbutton&text=' + encodeURIComponent(tweetText); } else if ($element.hasClass('o_facebook')) { url = 'https://www.facebook.com/sharer/sharer.php?u=' + encodeURIComponent(articleURL); diff --git a/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js b/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js index d69cd8d1f00..0ffa0ab84cf 100644 --- a/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js +++ b/addons/website_crm_partner_assign/static/src/js/crm_partner_assign.js @@ -1,6 +1,5 @@ /** @odoo-module **/ -import { sprintf } from "@web/core/utils/strings"; import { _t } from "@web/core/l10n/translation"; import publicWidget from "@web/legacy/js/public/public_widget"; import time from "@web/legacy/js/core/time"; @@ -241,7 +240,7 @@ publicWidget.registry.crmPartnerAssign = publicWidget.Widget.extend({ const contactName = ev.currentTarget.value.trim(); let titleEl = this.el.querySelector('.title'); if (!titleEl.value.trim()) { - titleEl.value = contactName ? sprintf(_t("%s's Opportunity"), contactName) : ''; + titleEl.value = contactName ? _t("%s's Opportunity", contactName) : ''; } }, /** diff --git a/addons/website_event_track/static/src/js/event_track_reminder.js b/addons/website_event_track/static/src/js/event_track_reminder.js index fad4b0837fd..bf2b4447eb3 100644 --- a/addons/website_event_track/static/src/js/event_track_reminder.js +++ b/addons/website_event_track/static/src/js/event_track_reminder.js @@ -1,6 +1,5 @@ /** @odoo-module **/ -import { sprintf } from "@web/core/utils/strings"; import { debounce } from "@web/core/utils/timing"; import core from "@web/legacy/js/services/core"; import publicWidget from "@web/legacy/js/public/public_widget"; @@ -51,7 +50,7 @@ publicWidget.registry.websiteEventTrackReminder = publicWidget.Widget.extend({ self.displayNotification({ type: 'info', title: _t('Error'), - message: sprintf(_t('Talk already in your Favorites')), + message: _t('Talk already in your Favorites'), }); } else { self.reminderOn = reminderOnValue; diff --git a/addons/website_forum/static/src/js/website_forum.js b/addons/website_forum/static/src/js/website_forum.js index 61d16896747..94b1a19a3e6 100644 --- a/addons/website_forum/static/src/js/website_forum.js +++ b/addons/website_forum/static/src/js/website_forum.js @@ -8,7 +8,7 @@ import { loadWysiwygFromTextarea } from "@web_editor/js/frontend/loadWysiwygFrom import publicWidget from "@web/legacy/js/public/public_widget"; import { Markup } from '@web/legacy/js/core/utils'; import session from "web.session"; -import { escape, sprintf } from "@web/core/utils/strings"; +import { escape } from "@web/core/utils/strings"; import { _t } from "@web/core/l10n/translation"; var qweb = core.qweb; @@ -264,7 +264,7 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({ ev.preventDefault(); if (session.is_website_user) { this._displayAccessDeniedNotification( - Markup`

${sprintf(_t('Oh no! Please sign in to vote'), "/web/login")}

` + Markup`

${_t('Oh no! Please sign in to vote', "/web/login")}

` ); return; } @@ -373,7 +373,7 @@ publicWidget.registry.websiteForum = publicWidget.Widget.extend({ const message = data.error === 'own_post' ? _t('Sorry, you cannot vote for your own posts') : data.error === 'anonymous_user' - ? Markup`

${sprintf(_t('Oh no! Please sign in to vote'), "/web/login")}

` + ? Markup`

${_t('Oh no! Please sign in to vote', "/web/login")}

` : data.error; this._displayAccessDeniedNotification(message); } else { diff --git a/addons/website_livechat/static/src/core/persona_model_patch.js b/addons/website_livechat/static/src/core/persona_model_patch.js index 591c810a950..d3abdee2b8f 100644 --- a/addons/website_livechat/static/src/core/persona_model_patch.js +++ b/addons/website_livechat/static/src/core/persona_model_patch.js @@ -3,7 +3,6 @@ import { Persona } from "@mail/core/common/persona_model"; import { _t } from "@web/core/l10n/translation"; import { patch } from "@web/core/utils/patch"; -import { sprintf } from "@web/core/utils/strings"; patch(Persona.prototype, { get countryFlagUrl() { @@ -14,7 +13,7 @@ patch(Persona.prototype, { }, get nameOrDisplayName() { if (this.type === "visitor" && !this.name) { - return sprintf(_t("Visitor #%s"), this.id); + return _t("Visitor #%s", this.id); } return super.nameOrDisplayName; }, diff --git a/addons/website_payment/static/src/js/website_payment_form.js b/addons/website_payment/static/src/js/website_payment_form.js index 459c1e48b3c..3c1601248fa 100644 --- a/addons/website_payment/static/src/js/website_payment_form.js +++ b/addons/website_payment/static/src/js/website_payment_form.js @@ -2,7 +2,6 @@ import core, { _t } from "@web/legacy/js/services/core"; import checkoutForm from '@payment/js/checkout_form'; -import { sprintf } from '@web/core/utils/strings'; checkoutForm.include({ events: Object.assign({}, checkoutForm.prototype.events || {}, { @@ -46,7 +45,7 @@ checkoutForm.include({ const $field = this.$('input[name="' + id + '"],select[name="' + id + '"]'); $field.removeClass('is-invalid').popover('dispose'); if (!$field.val().trim()) { - errorFields[id] = sprintf(_t("Field '%s' is mandatory"), mandatoryFields[id]); + errorFields[id] = _t("Field '%s' is mandatory", mandatoryFields[id]); } } if (Object.keys(errorFields).length) { diff --git a/addons/website_payment/static/src/snippets/s_donation/000.js b/addons/website_payment/static/src/snippets/s_donation/000.js index 79627500409..98728d3cd58 100644 --- a/addons/website_payment/static/src/snippets/s_donation/000.js +++ b/addons/website_payment/static/src/snippets/s_donation/000.js @@ -1,6 +1,5 @@ /** @odoo-module **/ -import { sprintf } from '@web/core/utils/strings'; import { _t } from "@web/core/l10n/translation"; import publicWidget from '@web/legacy/js/public/public_widget'; @@ -141,7 +140,7 @@ publicWidget.registry.DonationSnippet = publicWidget.Widget.extend({ } else if (amount < parseFloat(minAmount)) { const before = this.currency.position === "before" ? this.currency.symbol : ""; const after = this.currency.position === "after" ? this.currency.symbol : ""; - errorMessage = sprintf(_t("The minimum donation amount is %s%s%s"), before, minAmount, after); + errorMessage = _t("The minimum donation amount is %s%s%s", before, minAmount, after); } if (errorMessage) { $(ev.currentTarget).before($('

', { diff --git a/addons/website_sale/static/src/js/sale_variant_mixin.js b/addons/website_sale/static/src/js/sale_variant_mixin.js index 4bb28364101..18838fc8ef5 100644 --- a/addons/website_sale/static/src/js/sale_variant_mixin.js +++ b/addons/website_sale/static/src/js/sale_variant_mixin.js @@ -4,7 +4,6 @@ import concurrency from "@web/legacy/js/core/concurrency"; import core from "@web/legacy/js/services/core"; import utils from "@web/legacy/js/core/utils"; import ajax from "@web/legacy/js/core/ajax"; -import { sprintf } from "@web/core/utils/strings"; import { memoize, uniqueId } from "@web/core/utils/functions"; import { throttleForAnimation } from "@web/core/utils/timing"; @@ -517,7 +516,7 @@ var VariantMixin = { } excludedByData.push(excludedByName); - $target.attr('title', sprintf(_t('Not available with %s'), excludedByData.join(', '))); + $target.attr('title', _t('Not available with %s', excludedByData.join(', '))); $target.data('excluded-by', JSON.stringify(excludedByData)); } }, diff --git a/addons/website_sale_stock/static/src/js/website_sale_reorder.js b/addons/website_sale_stock/static/src/js/website_sale_reorder.js index 2b50d146c98..1eca1459b6d 100644 --- a/addons/website_sale_stock/static/src/js/website_sale_reorder.js +++ b/addons/website_sale_stock/static/src/js/website_sale_reorder.js @@ -2,7 +2,6 @@ import { ReorderDialog } from "@website_sale/js/website_sale_reorder"; import { patch } from "@web/core/utils/patch"; -import { sprintf } from "@web/core/utils/strings"; patch(ReorderDialog.prototype, { /** @@ -33,16 +32,16 @@ patch(ReorderDialog.prototype, { product.add_to_cart_allowed = false; } if (product.max_quantity_available < product.qty) { - product.qty_warning = sprintf( - this.env._t("You ask for %s Units but only %s are available."), + product.qty_warning = this.env._t( + "You ask for %s Units but only %s are available.", product.qty.toFixed(1), product.max_quantity_available.toFixed(1) ); product.qty = product.max_quantity_available; product.stock_warning = true; } else if (product.combinationInfo.cart_qty) { - product.qty_warning = sprintf( - this.env._t("You already have %s Units in your cart."), + product.qty_warning = this.env._t( + "You already have %s Units in your cart.", product.combinationInfo.cart_qty.toFixed(1) ); } @@ -63,8 +62,8 @@ patch(ReorderDialog.prototype, { */ changeProductQty(product, newQty) { if (product.max_quantity_available && newQty > product.max_quantity_available) { - product.qty_warning = sprintf( - this.env._t("You ask for %s Units but only %s are available."), + product.qty_warning = this.env._t( + "You ask for %s Units but only %s are available.", newQty.toFixed(1), product.max_quantity_available.toFixed(1) ); diff --git a/addons/website_slides/static/src/js/portal_chatter.js b/addons/website_slides/static/src/js/portal_chatter.js index d3f7c42bbe4..d4c0fcf2c16 100644 --- a/addons/website_slides/static/src/js/portal_chatter.js +++ b/addons/website_slides/static/src/js/portal_chatter.js @@ -2,7 +2,6 @@ import { _t } from "@web/core/l10n/translation"; import PortalChatter from '@portal/js/portal_chatter'; -import { sprintf } from '@web/core/utils/strings'; /** * PortalChatter @@ -19,7 +18,7 @@ PortalChatter.include({ _reloadChatterContent: async function (data) { await this._super(...arguments); if (this.options.res_model === "slide.channel") { - $('#review-tab').text(sprintf(_t('Reviews (%s)'), data.rating_count)); + $('#review-tab').text(_t('Reviews (%s)', data.rating_count)); } }, }); diff --git a/addons/website_slides/static/src/js/slides_course_tag_add.js b/addons/website_slides/static/src/js/slides_course_tag_add.js index 9f287689de4..a4d19ffb292 100644 --- a/addons/website_slides/static/src/js/slides_course_tag_add.js +++ b/addons/website_slides/static/src/js/slides_course_tag_add.js @@ -1,7 +1,6 @@ /** @odoo-module **/ import { uniqueId } from '@web/core/utils/functions'; -import { sprintf } from '@web/core/utils/strings'; import { _t } from "@web/core/l10n/translation"; import Dialog from '@web/legacy/js/core/dialog'; import publicWidget from '@web/legacy/js/public/public_widget'; @@ -121,7 +120,7 @@ var TagCourseDialog = Dialog.extend({ id: uniqueId("tag_"), create: true, tag: term, - text: sprintf(_t("Create new %s '%s'"), tag, term), + text: _t("Create new %s '%s'", tag, term), }; } else { return undefined; diff --git a/addons/website_slides/static/src/js/slides_upload.js b/addons/website_slides/static/src/js/slides_upload.js index 93f421110ef..70e3b2ff67e 100644 --- a/addons/website_slides/static/src/js/slides_upload.js +++ b/addons/website_slides/static/src/js/slides_upload.js @@ -1,7 +1,6 @@ /** @odoo-module **/ import { uniqueId } from '@web/core/utils/functions'; -import { sprintf } from '@web/core/utils/strings'; import {_t, qweb as QWeb} from "@web/legacy/js/services/core"; import Dialog from '@web/legacy/js/core/dialog'; import publicWidget from '@web/legacy/js/public/public_widget'; @@ -312,7 +311,7 @@ var SlideUploadDialog = Dialog.extend({ id: uniqueId("tag_"), create: true, tag: term, - text: sprintf(_t("Create new %s '%s'"), tag, term), + text: _t("Create new %s '%s'", tag, term), }; } else { return undefined; @@ -661,23 +660,23 @@ var SlideUploadDialog = Dialog.extend({ this.set('state', '_import'); if (this.modulesToInstallStatus.installing) { this.$('#o_wslides_install_module_text') - .text(sprintf(_t('Already installing "%s".'), this.modulesToInstallStatus.name)); + .text(_t('Already installing "%s".', this.modulesToInstallStatus.name)); } else if (this.modulesToInstallStatus.failed) { this.$('#o_wslides_install_module_text') - .text(sprintf(_t('Failed to install "%s".'), this.modulesToInstallStatus.name)); + .text(_t('Failed to install "%s".', this.modulesToInstallStatus.name)); } } else { this.modulesToInstallStatus = Object.assign({}, this.modulesToInstall.find( function (item) { return item.id === moduleId; })); this.set('state', '_import'); this.$('#o_wslides_install_module_text') - .text(sprintf(_t('Do you want to install the "%s" app?'), this.modulesToInstallStatus.name)); + .text(_t('Do you want to install the "%s" app?', this.modulesToInstallStatus.name)); } }, _onClickInstallModuleConfirm: function () { var self = this; var $el = this.$('#o_wslides_install_module_text'); - $el.text(sprintf(_t('Installing "%s".'), this.modulesToInstallStatus.name)); + $el.text(_t('Installing "%s".', this.modulesToInstallStatus.name)); this.modulesToInstallStatus.installing = true; this._resetModalButton(); this._rpc({ @@ -692,7 +691,7 @@ var SlideUploadDialog = Dialog.extend({ } window.location.href = redirectUrl; }, function () { - $el.text(sprintf(_t('Failed to install "%s".'), self.modulesToInstallStatus.name)); + $el.text(_t('Failed to install "%s".', self.modulesToInstallStatus.name)); self.modulesToInstallStatus.installing = false; self.modulesToInstallStatus.failed = true; self._resetModalButton();