[FIX] iap_mail, web: fix untranslatable text

Some translations could not be exported, as reported in odoo/odoo#114711.

This is due to Babel—the library we use to extract `_t` occurrences from
the sources—ignoring javascript template literals, resulting in any
gettext inside template literals not being parsed and exported as
translatable resources.

From version 2.12.0 of the library, support for javascript template
literals can be enabled by providing the `parse_template_string` option
to the Babel `extract` method.

Unfortunately, the library cannot be updated for now, as our policy is
to stick to the version of packages from the official Debian repository.

This commit refactors some code so that no gettext is inlined within
template literals.

closes odoo/odoo#119996

Related: odoo/enterprise#40457
Signed-off-by: Louis Wicket (wil) <wil@odoo.com>
This commit is contained in:
Louis Wicket (wil)
2023-05-09 14:28:58 +02:00
parent ceba69f3ed
commit 7792d45604
9 changed files with 67 additions and 20 deletions
@@ -1,6 +1,7 @@
/** @odoo-module **/
import { Markup } from 'web.utils';
import { _t } from "@web/core/l10n/translation";
import { registry } from "@web/core/registry";
export const iapNotificationService = {
@@ -33,7 +34,10 @@ export const iapNotificationService = {
* Displays the IAP failure notification on user's screen
*/
function displayFailureIapNotification(notif) {
const message = Markup`<a class='btn btn-link' href='${notif.url}' target='_blank' ><i class='fa fa-arrow-right'></i> ${env._t("Buy more credits")}</a>`;
// ℹ️ `_t` can only be inlined directly inside JS template literals
// after Babel has been updated to version 2.12.
const translatedText = _t("Buy more credits");
const message = Markup`<a class='btn btn-link' href='${notif.url}' target='_blank' ><i class='fa fa-arrow-right'></i> ${translatedText}</a>`;
notification.add(message, {
type: notif.error_type,
title: notif.title
@@ -1,5 +1,7 @@
/** @odoo-module alias=web/static/src/js/control_panel/control_panel_model_extension.js **/
import { _t } from "@web/core/l10n/translation";
import { sprintf } from "@web/core/utils/strings";
import ActionModel from "web.ActionModel";
import Domain from "web.Domain";
import pyUtils from "web.py_utils";
@@ -436,7 +438,10 @@
);
} catch (err) {
throw new Error(
`${this.env._t("Control panel model extension failed to evaluate domain")}:/n${JSON.stringify(err)}`
sprintf(
_t("Control panel model extension failed to evaluate domain:\n%(error)s"),
{ error: JSON.stringify(err) }
)
);
}
}
@@ -663,9 +663,14 @@ var FieldMany2One = AbstractField.extend({
const [id, fullName] = result;
const displayName = this._getDisplayName(fullName).trim();
result[1] = displayName;
// ℹ️ `_t` can only be inlined directly inside JS template literals
// after Babel has been updated to version 2.12.
const translatedText = _t("Unnamed");
return {
id,
label: escape(displayName) || `<em class="text-warning">${escape(_t("Unnamed"))}</em>`,
label:
escape(displayName) ||
`<em class="text-warning">${escape(translatedText)}</em>`,
value: displayName,
name: displayName,
};
+13 -7
View File
@@ -5,6 +5,7 @@ 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,
@@ -1590,7 +1591,10 @@ export class SearchModel extends EventBus {
return context;
} catch (error) {
throw new Error(
`${this.env._t("Failed to evaluate the context")} ${context}.\n${error.message}`
sprintf(_t("Failed to evaluate the context: %(context)s.\n%(error)s"), {
context,
error: error.message,
})
);
}
}
@@ -1675,9 +1679,10 @@ export class SearchModel extends EventBus {
return params.raw ? domain : domain.toList(this.domainEvalContext);
} catch (error) {
throw new Error(
`${this.env._t("Failed to evaluate the domain")} ${domain.toString()}.\n${
error.message
}`
sprintf(_t("Failed to evaluate the domain: %(domain)s.\n%(error)s"), {
domain: domain.toString(),
error: error.message,
})
);
}
}
@@ -2075,9 +2080,10 @@ export class SearchModel extends EventBus {
}
} catch (error) {
throw new Error(
`${this.env._t("Failed to evaluate the context")} "${
searchItem.context
}".\n${error.message}`
sprintf(_t("Failed to evaluate the context: %(context)s.\n%(error)s"), {
context: searchItem.context,
error: error.message,
})
);
}
}
@@ -3,6 +3,7 @@
import { ConfirmationDialog } from "@web/core/confirmation_dialog/confirmation_dialog";
import { _lt, _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 { useModel } from "@web/views/model";
import { ViewScaleSelector } from "@web/views/view_components/view_scale_selector";
@@ -163,7 +164,7 @@ export class CalendarController extends Component {
resModel: this.model.resModel,
resId: record.id || false,
context,
title: record.id ? `${_t("Open")}: ${record.title}` : _t("New Event"),
title: record.id ? sprintf(_t("Open: %s"), record.title) : _t("New Event"),
viewId: this.model.formViewId,
onRecordSaved: () => this.model.load(),
},
@@ -29,7 +29,7 @@ export class CalendarFilterPanel extends Component {
return {
autoSelect: true,
resetOnSelect: true,
placeholder: `+ ${_t("Add")} ${section.label}`,
placeholder: sprintf(_t("+ Add %s"), section.label),
sources: [
{
placeholder: _t("Loading..."),
@@ -1,6 +1,7 @@
/** @odoo-module **/
import { Dialog } from "@web/core/dialog/dialog";
import { _t } from "@web/core/l10n/translation";
import { useService } from "@web/core/utils/hooks";
import { View } from "@web/views/view";
import { escape } from "@web/core/utils/strings";
@@ -14,11 +15,14 @@ export class SelectCreateDialog extends Component {
this.viewService = useService("view");
this.dialogService = useService("dialog");
this.state = useState({ resIds: [] });
// ℹ️ `_t` can only be inlined directly inside JS template literals
// after Babel has been updated to version 2.12.
const translatedText = _t("No records found!");
this.baseViewProps = {
display: { searchPanel: false },
editable: false, // readonly
noBreadcrumbs: true,
noContentHelp: markup(`<p>${escape(this.env._t("No records found!"))}</p>`),
noContentHelp: markup(`<p>${escape(translatedText)}</p>`),
showButtons: false,
selectRecord: (resId) => this.select([resId]),
onSelectionChanged: (resIds) => {
@@ -4,6 +4,7 @@ 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";
@@ -19,6 +20,7 @@ class ResConfigInviteUsers extends Component {
this.invite = useService("user_invite");
this.action = useService("action");
this.notification = useService("notification");
this.user = useService("user");
this.state = useState({
status: "idle", // idle, inviting
@@ -61,11 +63,27 @@ class ResConfigInviteUsers extends Component {
}
}
if (invalidEmails.length) {
throw new Error(
`${_t("Invalid email address")}${
invalidEmails.length > 1 ? "es" : ""
}: ${invalidEmails.join(", ")}`
);
const errorMessage = (() => {
const listFormatter = new Intl.ListFormat(this.user.lang.replace("_", "-"), {
type: "conjunction",
style: "long",
});
switch (invalidEmails.length) {
case 1:
return sprintf(_t("Invalid email address: %(address)s"), {
address: invalidEmails[0],
});
case 2:
return sprintf(_t("Invalid email addresses: %(2 addresses)s"), {
"2 addresses": listFormatter.format(invalidEmails),
});
default:
return sprintf(_t("Invalid email addresses: %(addresses)s"), {
addresses: listFormatter.format(invalidEmails),
});
}
})();
throw new Error(errorMessage);
}
}
@@ -2,6 +2,7 @@
import { Component, markup } from "@odoo/owl";
import { isMacOS } from "@web/core/browser/feature_detection";
import { _t } from "@web/core/l10n/translation";
import { escape } from "@web/core/utils/strings";
import { session } from "@web/session";
import { browser } from "../../core/browser/browser";
@@ -43,13 +44,16 @@ class ShortcutsFooterComponent extends Component {
ShortcutsFooterComponent.template = "web.UserMenu.ShortcutsFooterComponent";
function shortCutsItem(env) {
// ℹ️ `_t` can only be inlined directly inside JS template literals after
// Babel has been updated to version 2.12.
const translatedText = _t("Shortcuts");
return {
type: "item",
id: "shortcuts",
hide: env.isSmall,
description: markup(
`<div class="d-flex align-items-center justify-content-between">
<span>${escape(env._t("Shortcuts"))}</span>
<span>${escape(translatedText)}</span>
<span class="fw-bold">${isMacOS() ? "CMD" : "CTRL"}+K</span>
</div>`
),