[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:
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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" });
|
||||
}
|
||||
}
|
||||
|
||||
+3
-4
@@ -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");
|
||||
}
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user