[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) <sad@odoo.com>
This commit is contained in:
Louis Wicket (wil)
2023-08-10 18:14:04 +02:00
parent 62fba62067
commit f1722c7334
123 changed files with 353 additions and 495 deletions
@@ -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),
},
];
@@ -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")
);
}
@@ -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(", ");
}
@@ -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 () {});
},
/**
@@ -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();
}
}
@@ -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 {
@@ -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.
@@ -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",
}
);
@@ -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
@@ -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],
});
@@ -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);
},
});
@@ -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(() => {
+1 -2
View File
@@ -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;
}
@@ -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;
}
@@ -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),
});
+6 -15
View File
@@ -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("<samp>%(send_keybind)s</samp><i> to send</i>"), {
send_keybind: this.sendKeybind,
})
_t("<samp>%(send_keybind)s</samp><i> to send</i>", { 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(
"<samp>%(cancel_keybind)s</samp><i> to <a href='#' data-type='%(cancel_type)s'>cancel</a></i>, <samp>%(save_keybind)s</samp><i> to <a href='#' data-type='%(save_type)s'>save</a></i>"
),
_t(
"<samp>%(cancel_keybind)s</samp><i> to <a href='#' data-type='%(cancel_type)s'>cancel</a></i>, <samp>%(save_keybind)s</samp><i> to <a href='#' data-type='%(save_type)s'>save</a></i>",
{
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" }
);
}
@@ -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,
@@ -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" }
);
}
@@ -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),
});
}
@@ -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,
});
}
+1 -2
View File
@@ -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;
}
@@ -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)));
}
}
@@ -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 });
}
}
@@ -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,
@@ -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) {
@@ -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],
});
@@ -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" }
@@ -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" }
);
}
@@ -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" });
@@ -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),
@@ -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]);
}
}
@@ -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" }
@@ -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) => {
@@ -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),
})
);
}
@@ -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 <strong>successfully unsubscribed from %s</strong>."),
escape(unsubscribed_list)
));
$("#subscription_info").html(
_t(
"You have been <strong>successfully unsubscribed from %s</strong>.",
escape(unsubscribed_list)
)
)
}
else{
$('#subscription_info').html(_t('You have been <strong>successfully unsubscribed</strong>.'));
@@ -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: `<p>${escape(description) || ''}</p>`,
buttons: [{text: _t("Ok"), close: true}]
@@ -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
);
}
@@ -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"),
@@ -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
);
}
@@ -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
),
});
@@ -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
);
}
@@ -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());
}
}
@@ -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
),
@@ -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;
@@ -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
),
});
@@ -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 <strong>%s</strong>"),
escape(file.name)),
message: _t("Could not save file <strong>%s</strong>", escape(file.name)),
type: 'warning',
sticky: true,
});
@@ -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);
});
@@ -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");
}
@@ -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(", ")
@@ -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
);
}
@@ -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);
},
});
@@ -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"),
@@ -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)
);
@@ -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
),
});
@@ -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;
}
@@ -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;
}
@@ -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
),
});
@@ -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
@@ -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
)
);
@@ -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 {
@@ -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",
@@ -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,
() => {
@@ -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]],
@@ -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;
@@ -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 };
@@ -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") {
@@ -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();
},
@@ -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),
};
}
}
@@ -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;
+1 -2
View File
@@ -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
+19 -5
View File
@@ -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`);
}
@@ -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));
}
+2 -3
View File
@@ -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));
}
}
@@ -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;
@@ -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
);
@@ -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",
+3 -4
View File
@@ -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,
})
@@ -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(),
},
@@ -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,
@@ -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;
@@ -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);
}
}
@@ -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() {
@@ -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" });
}
}
@@ -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
);
}
@@ -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",
},
];
@@ -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({
@@ -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() {
@@ -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);
},
@@ -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 =
@@ -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");
@@ -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) {
@@ -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);
}
}
@@ -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();
@@ -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" });
@@ -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,
@@ -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] || {
@@ -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" });
}
}
@@ -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),
});
}
@@ -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");
}
);
@@ -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);

Some files were not shown because too many files have changed in this diff Show More