[FIX] tools: no pollution during export translation
Before this commit, the export of translations was incorrect for code and model translations: For code translations, the field 'name' was used for the matching while the _() method explicitly use None in the _get_source call to only use the field 'src' for the search. For model translations, the field 'res_id' was not used when searching for a translation. For instance, if a ir.model.fields did not have translated label, exporting the translations was using the translations of the first field having the same source. While this could be convient during the import (to be discussed), doing so in an export of translation is clearly an unexpected side-effect. closes odoo/odoo#40909 X-original-commit: 4534181f106cf5aa50c65c942c260eeec122d3d2 Signed-off-by: Martin Trigaux (mat) <mat@odoo.com>
This commit is contained in:
@@ -7,7 +7,7 @@ import io
|
||||
import odoo
|
||||
from odoo.tests import common, tagged
|
||||
from odoo.tools.misc import file_open, mute_logger
|
||||
from odoo.tools.translate import _, _lt
|
||||
from odoo.tools.translate import _, _lt, TranslationFileReader
|
||||
|
||||
|
||||
TRANSLATED_TERM = _lt("Klingon")
|
||||
@@ -228,6 +228,48 @@ class TestTermCount(common.TransactionCase):
|
||||
self.env.context = dict(self.env.context, lang="dot")
|
||||
self.assertEqual(_("Accounting"), "samva", "The code translation was not applied")
|
||||
|
||||
def test_export_pollution(self):
|
||||
""" Test that exporting the translation only exports the translations of the module """
|
||||
with file_open('test_translation_import/i18n/dot.csv', 'rb') as f:
|
||||
csv_file = base64.b64encode(f.read())
|
||||
|
||||
# dot.csv only contains one term
|
||||
import_tlh = self.env["base.language.import"].create({
|
||||
'name': 'Dothraki',
|
||||
'code': 'dot',
|
||||
'data': csv_file,
|
||||
'filename': 'dot.csv',
|
||||
})
|
||||
with mute_logger('odoo.addons.base.models.res_lang'):
|
||||
import_tlh.import_lang()
|
||||
|
||||
# create a translation that has the same src as an existing field but no module
|
||||
# information and a different res_id that the real field
|
||||
# this translation should not be included in the export
|
||||
self.env['ir.translation'].create({
|
||||
'src': '1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB',
|
||||
'value': '1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB in Dothraki',
|
||||
'type': 'model',
|
||||
'name': 'ir.model.fields,field_description',
|
||||
'res_id': -1,
|
||||
'lang': 'dot',
|
||||
})
|
||||
module = self.env.ref('base.module_test_translation_import')
|
||||
export = self.env["base.language.export"].create({
|
||||
'lang': 'dot',
|
||||
'format': 'po',
|
||||
'modules': [(6, 0, [module.id])]
|
||||
})
|
||||
export.act_getfile()
|
||||
po_file = export.data
|
||||
reader = TranslationFileReader(base64.b64decode(po_file).decode(), fileformat='po')
|
||||
for row in reader:
|
||||
if row['value']:
|
||||
# should contains only one row from the csv, not the manual one
|
||||
self.assertEqual(row['src'], "Accounting")
|
||||
self.assertEqual(row['value'], "samva")
|
||||
|
||||
|
||||
@tagged('post_install', '-at_install')
|
||||
class TestTranslationFlow(common.TransactionCase):
|
||||
|
||||
|
||||
+16
-6
@@ -903,11 +903,22 @@ class TranslationModuleReader:
|
||||
""" Export ir.translation values for all retrieved records """
|
||||
|
||||
IrTranslation = self.env['ir.translation']
|
||||
for module, source, name, res_id, ttype, comments in self._to_translate:
|
||||
trans = IrTranslation._get_source(name, ttype, self._lang, source) if self._lang else ""
|
||||
for module, source, name, res_id, ttype, comments, record_id in self._to_translate:
|
||||
trans = (
|
||||
IrTranslation._get_source(name if type != "code" else None, ttype, self._lang, source, res_id=record_id)
|
||||
if self._lang
|
||||
else ""
|
||||
)
|
||||
yield (module, ttype, name, res_id, source, encode(trans) or '', comments)
|
||||
|
||||
def _push_translation(self, module, ttype, name, res_id, source, comments=None):
|
||||
def _push_translation(self, module, ttype, name, res_id, source, comments=None, record_id=None):
|
||||
""" Insert a translation that will be used in the file generation
|
||||
In po file will create an entry
|
||||
#: <ttype>:<name>:<res_id>
|
||||
#, <comment>
|
||||
msgid "<source>"
|
||||
record_id is the database id of the record being translated
|
||||
"""
|
||||
# empty and one-letter terms are ignored, they probably are not meant to be
|
||||
# translated, and would be very hard to translate anyway.
|
||||
sanitized_term = (source or '').strip()
|
||||
@@ -915,8 +926,7 @@ class TranslationModuleReader:
|
||||
sanitized_term = re.sub(r'\W+', '', sanitized_term)
|
||||
if not sanitized_term or len(sanitized_term) <= 1:
|
||||
return
|
||||
|
||||
self._to_translate.append((module, source, name, res_id, ttype, tuple(comments or ())))
|
||||
self._to_translate.append((module, source, name, res_id, ttype, tuple(comments or ()), record_id))
|
||||
|
||||
def _get_translatable_records(self, records):
|
||||
""" Filter the records that are translatable
|
||||
@@ -1004,7 +1014,7 @@ class TranslationModuleReader:
|
||||
continue
|
||||
for term in set(field.get_trans_terms(value)):
|
||||
trans_type = 'model_terms' if callable(field.translate) else 'model'
|
||||
self._push_translation(module, trans_type, name, xml_name, term)
|
||||
self._push_translation(module, trans_type, name, xml_name, term, record_id=record.id)
|
||||
|
||||
def _get_module_from_path(self, path):
|
||||
for (mp, rec) in self._path_list:
|
||||
|
||||
Reference in New Issue
Block a user