diff --git a/odoo/addons/base/tests/test_translate.py b/odoo/addons/base/tests/test_translate.py index 6a652d17d70..05a4723bb90 100644 --- a/odoo/addons/base/tests/test_translate.py +++ b/odoo/addons/base/tests/test_translate.py @@ -874,6 +874,57 @@ class TestXMLTranslation(TransactionCase): self.assertEqual(view.with_env(env_fr).arch_db, archf % (terms_en[0], terms_fr[1])) self.assertEqual(view.with_env(env_nl).arch_db, archf % (terms_en[0], terms_nl[1])) + def test_sync_xml_collision(self): + """ Check translations of 'arch' after xml tags changes in source terms + when the same term appears in different elements with different + styles. + """ + archf = '''
+ %s +
+ +
+
+
+

%s

+
+
+
''' + terms_en = ('Bread and cheese', 'Knive and Fork', 'Knive and Fork') + terms_fr = ('Pain et fromage', 'Couteau et Fourchette', 'Couteau et Fourchette') + terms_nl = ('Brood and kaas', 'Mes en Vork', 'Mes en Vork') + view = self.create_view(archf, terms_en, en_US=terms_en, fr_FR=terms_fr, nl_NL=terms_nl) + + env_nolang = self.env(context={}) + env_en = self.env(context={'lang': 'en_US'}) + env_fr = self.env(context={'lang': 'fr_FR'}) + env_nl = self.env(context={'lang': 'nl_NL'}) + + self.assertEqual(view.with_env(env_nolang).arch_db, archf % terms_en) + self.assertEqual(view.with_env(env_en).arch_db, archf % terms_en) + self.assertEqual(view.with_env(env_fr).arch_db, archf % terms_fr) + self.assertEqual(view.with_env(env_nl).arch_db, archf % terms_nl) + + # modify source term in view (small change) + terms_en = ('Bread and cheese', 'Knife and Fork', 'Knife and Fork') + view.with_env(env_en).write({'arch_db': archf % terms_en}) + + # check whether translations have been kept + self.assertEqual(view.with_env(env_nolang).arch_db, archf % terms_en) + self.assertEqual(view.with_env(env_en).arch_db, archf % terms_en) + self.assertEqual(view.with_env(env_fr).arch_db, archf % terms_fr) + self.assertEqual(view.with_env(env_nl).arch_db, archf % terms_nl) + + # modify source term in view (actual text change) + terms_en = ('Bread and cheese', 'Fork and Knife', 'Fork and Knife') + view.with_env(env_en).write({'arch_db': archf % terms_en}) + + # check whether translations have been reset + self.assertEqual(view.with_env(env_nolang).arch_db, archf % terms_en) + self.assertEqual(view.with_env(env_en).arch_db, archf % terms_en) + self.assertEqual(view.with_env(env_fr).arch_db, archf % (terms_fr[0], terms_en[1], terms_en[2])) + self.assertEqual(view.with_env(env_nl).arch_db, archf % (terms_nl[0], terms_en[1], terms_en[2])) + def test_cache_consistency(self): view = self.env["ir.ui.view"].create({ "name": "test_translate_xml_cache_invalidation", diff --git a/odoo/fields.py b/odoo/fields.py index cf522bdae31..2e03ac87312 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -1801,14 +1801,17 @@ class _String(Field): continue from_lang_value = old_translations.get(lang, old_translations.get('en_US')) translation_dictionary = self.get_translation_dictionary(from_lang_value, old_translations) - text2term = {self.get_text_content(term): term for term in new_terms} + text2terms = defaultdict(list) + for term in new_terms: + text2terms[self.get_text_content(term)].append(term) for old_term in list(translation_dictionary.keys()): if old_term not in new_terms: old_term_text = self.get_text_content(old_term) - matches = get_close_matches(old_term_text, text2term, 1, 0.9) + matches = get_close_matches(old_term_text, text2terms, 1, 0.9) if matches: - translation_dictionary[text2term[matches[0]]] = translation_dictionary.pop(old_term) + closest_term = get_close_matches(old_term, text2terms[matches[0]], 1, 0)[0] + translation_dictionary[closest_term] = translation_dictionary.pop(old_term) # pylint: disable=not-callable new_translations = { l: self.translate(lambda term: translation_dictionary.get(term, {l: None})[l], cache_value) diff --git a/odoo/tools/translate.py b/odoo/tools/translate.py index afcad285d07..2fee48869d8 100644 --- a/odoo/tools/translate.py +++ b/odoo/tools/translate.py @@ -358,7 +358,8 @@ def html_term_converter(value): def get_text_content(term): """ Return the textual content of the given term. """ - return html.fromstring(term).text_content() + content = html.fromstring(term).text_content() + return " ".join(content.split()) xml_translate.get_text_content = get_text_content html_translate.get_text_content = get_text_content