details: https://code.tryton.org/tryton/commit/80db193bd3ac
branch: default
user: Cédric Krier <[email protected]>
date: Tue Feb 24 11:35:00 2026 +0100
description:
Add model and field Many2One on translations
diffstat:
trytond/trytond/ir/translation.py | 98 ++++++++++++++++++++++++----
trytond/trytond/ir/view/translation_list.xml | 3 +-
2 files changed, 86 insertions(+), 15 deletions(-)
diffs (149 lines):
diff -r 6af5c937a3ce -r 80db193bd3ac trytond/trytond/ir/translation.py
--- a/trytond/trytond/ir/translation.py Tue Feb 24 11:34:42 2026 +0100
+++ b/trytond/trytond/ir/translation.py Tue Feb 24 11:35:00 2026 +0100
@@ -14,6 +14,7 @@
from sql.aggregate import Max
from sql.conditionals import Case
from sql.functions import CharLength, Position, Substring
+from sql.operators import Concat
import trytond.config as config
from trytond.cache import Cache
@@ -67,6 +68,11 @@
fields.fmany2one(
'overriding_module_ref', 'overriding_module', 'ir.module,name',
"Overriding Module", readonly=True),
+ fields.fmany2one(
+ 'model_ref', 'model', 'ir.model,name', "Model", readonly=True),
+ fields.fmany2one(
+ 'field_ref', 'field,model', 'ir.model.field,name,model', "Field",
+ readonly=True),
ModelSQL, ModelView):
__name__ = "ir.translation"
@@ -96,8 +102,10 @@
module = fields.Char('Module', readonly=True)
fuzzy = fields.Boolean('Fuzzy')
- model = fields.Function(fields.Char('Model'), 'get_model',
- searcher='search_model')
+ model = fields.Function(
+ fields.Char("Model"), 'get_model', searcher='search_model')
+ field = fields.Function(
+ fields.Char("Field"), 'get_field', searcher='search_field')
overriding_module = fields.Char('Overriding Module', readonly=True)
_translation_cache = Cache('ir.translation', context=False)
_translation_report_cache = Cache(
@@ -285,6 +293,80 @@
return self.name.split(',')[0]
@classmethod
+ def search_model(cls, name, clause):
+ table = cls.__table__()
+ _, operator, value = clause
+ Operator = fields.SQL_OPERATORS[operator]
+ return [('id', 'in', table.select(table.id,
+ where=Operator(Substring(table.name, 1,
+ Case((
+ Position(',', table.name) > 0,
+ Position(',', table.name) - 1),
+ else_=CharLength(table.name))), value)))]
+
+ @classmethod
+ def domain_model_ref(cls, clause, tables):
+ pool = Pool()
+ IrModel = pool.get('ir.model')
+ table, _ = tables[None]
+ if 'model_ref' not in tables:
+ ir_model = IrModel.__table__()
+ tables['model_ref'] = {
+ None: (ir_model, ir_model.name == Substring(table.name, 1,
+ Case((
+ Position(',', table.name) > 0,
+ Position(',', table.name) - 1),
+ else_=CharLength(table.name)))),
+ }
+ nested = clause[0][len('model_ref') + 1:]
+ if not nested:
+ if isinstance(clause[2], str):
+ nested = 'rec_name'
+ else:
+ nested = 'id'
+ domain = [(nested, *clause[1:])]
+ tables, clause = IrModel.search_domain(
+ domain, tables=tables['model_ref'])
+ return clause
+
+ def get_field(self, name):
+ return self.name.split(',', 1)[1] if ',' in self.name else None
+
+ @classmethod
+ def search_field(cls, name, clause):
+ table = cls.__table__()
+ _, operator, value = clause
+ Operator = fields.SQL_OPERATORS[operator]
+ return [('id', 'in', table.select(table.id,
+ where=Operator(Substring(table.name,
+ Case((
+ Position(',', table.name) > 0,
+ Position(',', table.name) - 1),
+ else_=CharLength(table.name))), value)))]
+
+ @classmethod
+ def domain_field_ref(cls, clause, tables):
+ pool = Pool()
+ Field = pool.get('ir.model.field')
+ table, _ = tables[None]
+ if 'field_ref' not in tables:
+ field = Field.__table__()
+ tables['field_ref'] = {
+ None: (field, (table.name == Concat(Concat(
+ field.model, ','), field.name))),
+ }
+ nested = clause[0][len('model_ref') + 1:]
+ if not nested:
+ if isinstance(clause[2], str):
+ nested = 'rec_name'
+ else:
+ nested = 'id'
+ domain = [(nested, *clause[1:])]
+ tables, clause = Field.search_domain(
+ domain, tables=tables['field_ref'])
+ return clause
+
+ @classmethod
def search_rec_name(cls, name, clause):
clause = tuple(clause)
if clause[1].startswith('!') or clause[1].startswith('not '):
@@ -298,18 +380,6 @@
]
@classmethod
- def search_model(cls, name, clause):
- table = cls.__table__()
- _, operator, value = clause
- Operator = fields.SQL_OPERATORS[operator]
- return [('id', 'in', table.select(table.id,
- where=Operator(Substring(table.name, 1,
- Case((
- Position(',', table.name) > 0,
- Position(',', table.name) - 1),
- else_=CharLength(table.name))), value)))]
-
- @classmethod
def get_language(cls):
language = Transaction().language
result = cls._get_language_cache.get(language)
diff -r 6af5c937a3ce -r 80db193bd3ac
trytond/trytond/ir/view/translation_list.xml
--- a/trytond/trytond/ir/view/translation_list.xml Tue Feb 24 11:34:42
2026 +0100
+++ b/trytond/trytond/ir/view/translation_list.xml Tue Feb 24 11:35:00
2026 +0100
@@ -6,7 +6,8 @@
<field name="src_plural" widget="char" expand="2" optional="1"/>
<field name="value" widget="char" expand="2"/>
<field name="fuzzy"/>
- <field name="name" expand="1" optional="1"/>
+ <field name="model_ref" expand="1" optional="1"/>
+ <field name="field_ref" expand="1" optional="1"/>
<field name="lang"/>
<field name="type" optional="1"/>
<field name="res_id" optional="1"/>