Skip to content

OD102 translation-interpolated

_() receives an already formatted string, so the source text cannot be translated.

Category Severity Series Needs the index
Quality Warning all no

What it reports

A call to _() or _lt() whose first argument is built in the call by %, +, .format() or an f-string from a value that is not a literal.

Why it matters

Translations are written for the text in the source, such as "Transfer %s has no delivery address". At runtime _() looks up the exact text it receives, and a text that already holds a record name, an amount or a date matches none of them: every user sees the message in English, whatever their language. From 14.0 _() takes the values as further arguments and substitutes them after translating; on earlier series, format the result of _() instead.

Values the model's own code produces, such as literals and attributes of self, are left alone, as in OD101. Only the expression passed to _() is read, so a text formatted into a variable first and then translated is not reported, though it cannot be translated either.

Example

models/stock_picking.py
class StockPicking(models.Model):
    _inherit = "stock.picking"

    def action_check_address(self):
        for picking in self:
            if not picking.partner_id.street:
                raise UserError(_("Transfer %s has no delivery address" % picking.name))

The transfer's name is substituted before the lookup, so no translation matches.

models/stock_picking.py
class StockPicking(models.Model):
    _inherit = "stock.picking"

    def action_check_address(self):
        for picking in self:
            if not picking.partner_id.street:
                raise UserError(_("Transfer %s has no delivery address", picking.name))

Suppressing

message = _("Ref. %s" % code)  # odooscan: ignore[OD102]

See suppressing a finding.