From 233bae7de957d9458a927f3812bd1c5bf03d87cc Mon Sep 17 00:00:00 2001 From: Nicolas Lempereur Date: Tue, 30 Oct 2018 15:09:50 +0000 Subject: [PATCH] [FIX] translate.py: fallback on base lang if noterm When there is a language and its base language translation files in a module, in 12.0 an empty translation in the language translation file will not fallback on the base language. eg. fr.po : {'one': 'un', 'seventy': 'soixante-dix'} fr_BE.po: {'one': '', 'seventy': 'septante'} => the translation in fr_BE will be {'one': '', 'seventy': 'septante'} but before 7288b477 it would have been {'one': 'un', 'seventy': 'septante'} With this commit, an empty translation is no longer created so does not override base language. Also the intention of d84b795b0a6 that was also lost is restored by upserting only noupdate==false translation, and inserting the other ones. opw-1904638 closes #28297 --- odoo/addons/base/models/ir_translation.py | 23 ++++---- .../test_translation_import/i18n/fr_BE.po | 35 ++++++++++++ .../tests/test_term_count.py | 53 +++++++++++++++---- odoo/tools/translate.py | 4 ++ 4 files changed, 96 insertions(+), 19 deletions(-) create mode 100644 odoo/addons/test_translation_import/i18n/fr_BE.po diff --git a/odoo/addons/base/models/ir_translation.py b/odoo/addons/base/models/ir_translation.py index 2639d258347..60616552967 100644 --- a/odoo/addons/base/models/ir_translation.py +++ b/odoo/addons/base/models/ir_translation.py @@ -132,20 +132,23 @@ class IrTranslationImport(object): if hasattr(field, 'translate') and callable(field.translate): src_relevant_fields.append("%s,%s" % (model, field_name)) - # Step 2: insert new or upsert translations + count = 0 + # Step 2: insert new or upsert non-noupdate translations if self._overwrite: cr.execute(""" INSERT INTO %s(name, lang, res_id, src, type, value, module, state, comments) SELECT name, lang, res_id, src, type, value, module, state, comments FROM %s WHERE type = 'code' + AND noupdate IS NOT TRUE ON CONFLICT (type, lang, md5(src)) WHERE type = 'code' DO UPDATE SET (name, lang, res_id, src, type, value, module, state, comments) = (EXCLUDED.name, EXCLUDED.lang, EXCLUDED.res_id, EXCLUDED.src, EXCLUDED.type, EXCLUDED.value, EXCLUDED.module, EXCLUDED.state, EXCLUDED.comments); """ % (self._model_table, self._table)) - count = cr.rowcount + count += cr.rowcount cr.execute(""" INSERT INTO %s(name, lang, res_id, src, type, value, module, state, comments) SELECT name, lang, res_id, src, type, value, module, state, comments FROM %s WHERE type = 'model' + AND noupdate IS NOT TRUE ON CONFLICT (type, lang, name, res_id) WHERE type = 'model' DO UPDATE SET (name, lang, res_id, src, type, value, module, state, comments) = (EXCLUDED.name, EXCLUDED.lang, EXCLUDED.res_id, EXCLUDED.src, EXCLUDED.type, EXCLUDED.value, EXCLUDED.module, EXCLUDED.state, EXCLUDED.comments); """ % (self._model_table, self._table)) @@ -154,6 +157,7 @@ class IrTranslationImport(object): SELECT name, lang, res_id, src, type, value, module, state, comments FROM %s WHERE type IN ('selection', 'constraint', 'sql_constraint') + AND noupdate IS NOT TRUE ON CONFLICT (type, lang, name, md5(src)) WHERE type IN ('selection', 'constraint', 'sql_constraint') DO UPDATE SET (name, lang, res_id, src, type, value, module, state, comments) = (EXCLUDED.name, EXCLUDED.lang, EXCLUDED.res_id, EXCLUDED.src, EXCLUDED.type, EXCLUDED.value, EXCLUDED.module, EXCLUDED.state, EXCLUDED.comments); """ % (self._model_table, self._table)) @@ -162,17 +166,18 @@ class IrTranslationImport(object): SELECT name, lang, res_id, src, type, value, module, state, comments FROM %s WHERE type = 'model_terms' + AND noupdate IS NOT TRUE ON CONFLICT (type, name, lang, res_id, md5(src)) DO UPDATE SET (name, lang, res_id, src, type, value, module, state, comments) = (EXCLUDED.name, EXCLUDED.lang, EXCLUDED.res_id, EXCLUDED.src, EXCLUDED.type, EXCLUDED.value, EXCLUDED.module, EXCLUDED.state, EXCLUDED.comments); """ % (self._model_table, self._table)) count += cr.rowcount - else: - cr.execute(""" INSERT INTO %s(name, lang, res_id, src, type, value, module, state, comments) - SELECT name, lang, res_id, src, type, value, module, state, comments - FROM %s - ON CONFLICT DO NOTHING; - """ % (self._model_table, self._table)) - count = cr.rowcount + cr.execute(""" INSERT INTO %s(name, lang, res_id, src, type, value, module, state, comments) + SELECT name, lang, res_id, src, type, value, module, state, comments + FROM %s + WHERE %s + ON CONFLICT DO NOTHING; + """ % (self._model_table, self._table, 'noupdate IS TRUE' if self._overwrite else 'TRUE')) + count += cr.rowcount if self._debug: cr.execute("SELECT COUNT(*) FROM ONLY %s" % self._model_table) diff --git a/odoo/addons/test_translation_import/i18n/fr_BE.po b/odoo/addons/test_translation_import/i18n/fr_BE.po new file mode 100644 index 00000000000..1d41cc94dcb --- /dev/null +++ b/odoo/addons/test_translation_import/i18n/fr_BE.po @@ -0,0 +1,35 @@ +# This is a test PO file, not a true french translation. +# See the POT file for further information. +msgid "" +msgstr "" +"Project-Id-Version: OpenERP Server 6.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2012-10-17 12:36+0000\n" +"PO-Revision-Date: 2012-10-17 12:36+0000\n" +"Last-Translator: <>\n" +"Language-Team: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: \n" +"Plural-Forms: \n" + +#. module: test_translation_import +#: code:addons/test_translation_import/models.py:14 +#, python-format +msgid "Ijkl" +msgstr "Ijkl in belgian french" + +#. module: test_translation_import +#: model:ir.model,name:test_translation_import.model_test_translation_import +msgid "test.translation.import" +msgstr "test.translation.import in belgian french" + +#. module: test_translation_import +#: model:ir.model.fields,help:test_translation_import.field_test_translation_import__name +msgid "Efgh" +msgstr "" + +#. module: test_translation_import +#: code:addons/base/models/ford.py:43 +msgid "Test translation with a code type but different line number in pot" +msgstr "" diff --git a/odoo/addons/test_translation_import/tests/test_term_count.py b/odoo/addons/test_translation_import/tests/test_term_count.py index 7e224bfdd7b..8a4976e1771 100644 --- a/odoo/addons/test_translation_import/tests/test_term_count.py +++ b/odoo/addons/test_translation_import/tests/test_term_count.py @@ -17,8 +17,10 @@ class TestTermCount(common.TransactionCase): Just make sure we have as many translation entries as we wanted. """ odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr.po', 'fr_FR', module_name='test_translation_import', verbose=False) - ids = self.env['ir.translation'].search( - [('src', '=', '1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB')]) + ids = self.env['ir.translation'].search([ + ('lang', '=', 'fr_FR'), + ('src', '=', '1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB'), + ]) self.assertEqual(len(ids), 2) def test_noupdate(self): @@ -34,25 +36,56 @@ class TestTermCount(common.TransactionCase): odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr.po', 'fr_FR', module_name='test_translation_import', verbose=False, context={'overwrite': True}) # trans_load invalidates ormcache but not record cache - menu.clear_caches() + menu.env.cache.invalidate() self.assertEqual(menu.name, "New Name") self.assertEqual(menu.with_context(lang='fr_FR').name, "Nouveau nom") + def test_lang_with_base(self): + odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr.po', 'fr_BE', module_name='test_translation_import', verbose=False) + odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr_BE.po', 'fr_BE', module_name='test_translation_import', verbose=False, context={'overwrite': True}) + + # language override base language + translations = self.env['ir.translation'].search([ + ('lang', '=', 'fr_BE'), + ('value', '=like', '% belgian french'), + ]) + self.assertEqual(len(translations), 2) + + # not specified localized language fallback on base language + translations = self.env['ir.translation'].search([ + ('lang', '=', 'fr_BE'), + ('src', '=', 'Efgh'), + ('value', '=', 'Efgh in french'), + ]) + self.assertEqual(len(translations), 1) + translations = self.env['ir.translation'].search([ + ('lang', '=', 'fr_BE'), + ('src', '=', 'Test translation with a code type but different line number in pot'), + ('value', '=', 'Test traduction avec un type code mais différent numéro de ligne dans le pot'), + ]) + self.assertEqual(len(translations), 1) + def test_no_duplicate(self): """ Just make sure we do not create duplicated translation with 'code' type """ odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr.po', 'fr_FR', module_name='test_translation_import', verbose=False) - ids = self.env['ir.translation'].search( - [('src', '=', 'Test translation with two code lines')]) + ids = self.env['ir.translation'].search([ + ('lang', '=', 'fr_FR'), + ('src', '=', 'Test translation with two code lines'), + ]) self.assertEqual(len(ids), 1) - ids = self.env['ir.translation'].search( - [('src', '=', 'Test translation with a code type but different line number in pot')]) + ids = self.env['ir.translation'].search([ + ('lang', '=', 'fr_FR'), + ('src', '=', 'Test translation with a code type but different line number in pot'), + ]) self.assertEqual(len(ids), 1) - ids = self.env['ir.translation'].search( - [('src', '=', 'Test translation with two code type and model')]) + ids = self.env['ir.translation'].search([ + ('lang', '=', 'fr_FR'), + ('src', '=', 'Test translation with two code type and model'), + ]) self.assertEqual(len(ids), 2) self.assertEqual(len(ids.filtered(lambda t: t.type == 'code')), 1) @@ -68,7 +101,7 @@ class TestTermCount(common.TransactionCase): # Check if the not translated key is empty string update_translations() translation = self.env['ir.translation'].search_count([('src', '=', 'Efgh'), ('value', '=', '')]) - self.assertTrue(translation, 'The translation of "Efgh" should be empty') + self.assertFalse(translation, 'An empty translation is not imported') # Modify the value translated for the equal value of the key menu = self.env.ref('test_translation_import.menu_test_translation_import') diff --git a/odoo/tools/translate.py b/odoo/tools/translate.py index cb672b9d3b4..3854e9436d4 100644 --- a/odoo/tools/translate.py +++ b/odoo/tools/translate.py @@ -1102,6 +1102,10 @@ def trans_load_data(cr, fileobj, fileformat, lang, lang_name=None, verbose=True, dic['lang'] = lang dic.update(pycompat.izip(fields, row)) + # ignore empty value that may be set from base language + if not dic['value']: + return + if use_pot_reference: # discard the target from the POT targets. src = dic['src']