diff --git a/odoo/addons/base/models/ir_module.py b/odoo/addons/base/models/ir_module.py index 330bd41ffea..b32b9b1d496 100644 --- a/odoo/addons/base/models/ir_module.py +++ b/odoo/addons/base/models/ir_module.py @@ -873,7 +873,7 @@ class Module(models.Model): cat_id = modules.db.create_categories(self._cr, categs) self.write({'category_id': cat_id}) - def _update_translations(self, filter_lang=None): + def _update_translations(self, filter_lang=None, overwrite=False): if not filter_lang: langs = self.env['res.lang'].get_installed() filter_lang = [code for code, _ in langs] @@ -886,7 +886,7 @@ class Module(models.Model): for mod in update_mods } mod_names = topological_sort(mod_dict) - self.env['ir.translation']._load_module_terms(mod_names, filter_lang) + self.env['ir.translation']._load_module_terms(mod_names, filter_lang, overwrite) def _check(self): for module in self: diff --git a/odoo/addons/base/models/ir_translation.py b/odoo/addons/base/models/ir_translation.py index cc8ef945cde..e554ec32201 100644 --- a/odoo/addons/base/models/ir_translation.py +++ b/odoo/addons/base/models/ir_translation.py @@ -27,15 +27,15 @@ class IrTranslationImport(object): """ _table = 'tmp_ir_translation_import' - def __init__(self, model): + def __init__(self, cr, overwrite=False): """ Store some values, and also create a temporary SQL table to accept the data. :param model: the model to insert the data into (as a recordset) """ - self._cr = model._cr - self._model_table = model._table - self._overwrite = model._context.get('overwrite', False) + self._cr = cr + self._model_table = "ir_translation" + self._overwrite = overwrite self._debug = False self._rows = [] @@ -774,12 +774,11 @@ class IrTranslation(models.Model): return action - @api.model - def _get_import_cursor(self): + def _get_import_cursor(self, overwrite): """ Return a cursor-like object for fast inserting translations """ - return IrTranslationImport(self) + return IrTranslationImport(self._cr, overwrite) - def _load_module_terms(self, modules, langs): + def _load_module_terms(self, modules, langs, overwrite=False): """ Load PO files of the given modules for the given languages. """ # load i18n files for module_name in modules: @@ -787,7 +786,6 @@ class IrTranslation(models.Model): if not modpath: continue for lang in langs: - context = dict(self._context) lang_code = tools.get_iso_codes(lang) base_lang_code = None if '_' in lang_code: @@ -798,28 +796,28 @@ class IrTranslation(models.Model): base_trans_file = get_module_resource(module_name, 'i18n', base_lang_code + '.po') if base_trans_file: _logger.info('module %s: loading base translation file %s for language %s', module_name, base_lang_code, lang) - tools.trans_load(self._cr, base_trans_file, lang, verbose=False, module_name=module_name, context=context) - context['overwrite'] = True # make sure the requested translation will override the base terms later + tools.trans_load(self._cr, base_trans_file, lang, verbose=False, module_name=module_name, overwrite=overwrite) + overwrite = True # make sure the requested translation will override the base terms later # i18n_extra folder is for additional translations handle manually (eg: for l10n_be) base_trans_extra_file = get_module_resource(module_name, 'i18n_extra', base_lang_code + '.po') if base_trans_extra_file: _logger.info('module %s: loading extra base translation file %s for language %s', module_name, base_lang_code, lang) - tools.trans_load(self._cr, base_trans_extra_file, lang, verbose=False, module_name=module_name, context=context) - context['overwrite'] = True # make sure the requested translation will override the base terms later + tools.trans_load(self._cr, base_trans_extra_file, lang, verbose=False, module_name=module_name, overwrite=overwrite) + overwrite = True # make sure the requested translation will override the base terms later # Step 2: then load the main translation file, possibly overriding the terms coming from the base language trans_file = get_module_resource(module_name, 'i18n', lang_code + '.po') if trans_file: _logger.info('module %s: loading translation file (%s) for language %s', module_name, lang_code, lang) - tools.trans_load(self._cr, trans_file, lang, verbose=False, module_name=module_name, context=context) + tools.trans_load(self._cr, trans_file, lang, verbose=False, module_name=module_name, overwrite=overwrite) elif lang_code != 'en_US': _logger.info('module %s: no translation for language %s', module_name, lang_code) trans_extra_file = get_module_resource(module_name, 'i18n_extra', lang_code + '.po') if trans_extra_file: _logger.info('module %s: loading extra translation file (%s) for language %s', module_name, lang_code, lang) - tools.trans_load(self._cr, trans_extra_file, lang, verbose=False, module_name=module_name, context=context) + tools.trans_load(self._cr, trans_extra_file, lang, verbose=False, module_name=module_name, overwrite=overwrite) return True @api.model diff --git a/odoo/addons/base/wizard/base_import_language.py b/odoo/addons/base/wizard/base_import_language.py index 8a0992f2380..d85fad07610 100644 --- a/odoo/addons/base/wizard/base_import_language.py +++ b/odoo/addons/base/wizard/base_import_language.py @@ -28,7 +28,6 @@ class BaseLanguageImport(models.TransientModel): def import_lang(self): this = self[0] - this = this.with_context(overwrite=this.overwrite) with TemporaryFile('wb+') as buf: try: buf.write(base64.decodebytes(this.data)) @@ -42,7 +41,9 @@ class BaseLanguageImport(models.TransientModel): self.code, lang_name=self.name ) - tools.trans_load_data(this._cr, buf, fileformat, this.code, context=this._context) + tools.trans_load_data( + this._cr, buf, fileformat, this.code, overwrite=self.overwrite + ) except Exception as e: _logger.exception('File unsuccessfully imported, due to format mismatch.') raise UserError( diff --git a/odoo/addons/base/wizard/base_language_install.py b/odoo/addons/base/wizard/base_language_install.py index 07d9a6dd110..40dd162fbeb 100644 --- a/odoo/addons/base/wizard/base_language_install.py +++ b/odoo/addons/base/wizard/base_language_install.py @@ -34,7 +34,7 @@ class BaseLanguageInstall(models.TransientModel): self.ensure_one() mods = self.env['ir.module.module'].search([('state', '=', 'installed')]) self.env['res.lang']._activate_lang(self.lang) - mods.with_context(overwrite=self.overwrite)._update_translations(self.lang) + mods._update_translations(self.lang, self.overwrite) self.state = 'done' self.env.cr.execute('ANALYZE ir_translation') diff --git a/odoo/addons/base/wizard/base_update_translations.py b/odoo/addons/base/wizard/base_update_translations.py index 7cdc05d3c95..c05d85b7517 100644 --- a/odoo/addons/base/wizard/base_update_translations.py +++ b/odoo/addons/base/wizard/base_update_translations.py @@ -26,9 +26,7 @@ class BaseUpdateTranslations(models.TransientModel): return lang.name def act_update(self): - this = self[0] with tempfile.NamedTemporaryFile() as buf: - tools.trans_export(this.lang, ['all'], buf, 'po', self._cr) - context = {'create_empty_translation': True} - tools.trans_load_data(self._cr, buf, 'po', this.lang, context=context) + tools.trans_export(self.lang, ['all'], buf, 'po', self._cr) + tools.trans_load_data(self._cr, buf, 'po', self.lang, create_empty_translation=True) return {'type': 'ir.actions.act_window_close'} 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 5eb4908eb65..44868208800 100644 --- a/odoo/addons/test_translation_import/tests/test_term_count.py +++ b/odoo/addons/test_translation_import/tests/test_term_count.py @@ -62,7 +62,7 @@ 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) menu.with_context(lang='fr_FR').name = "Nouveau nom" # reload with overwrite - odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr.po', 'fr_FR', module_name='test_translation_import', verbose=False, context={'overwrite': True}) + odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr.po', 'fr_FR', module_name='test_translation_import', verbose=False, overwrite=True) # trans_load invalidates ormcache but not record cache menu.env.cache.invalidate() @@ -72,7 +72,7 @@ class TestTermCount(common.TransactionCase): def test_lang_with_base(self): self.env['res.lang']._activate_lang('fr_BE') 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}) + odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr_BE.po', 'fr_BE', module_name='test_translation_import', verbose=False, overwrite=True) # language override base language translations = self.env['ir.translation'].search([ @@ -123,13 +123,15 @@ class TestTermCount(common.TransactionCase): def test_export_empty_string(self): """When the string and the translation is equal the translation is empty""" # Export the translations - def update_translations(context=None): - context = dict(context or {}, overwrite=True) + def update_translations(create_empty_translation=False): self.env['res.lang']._activate_lang('fr_FR') with closing(io.BytesIO()) as bufferobj: odoo.tools.trans_export('fr_FR', ['test_translation_import'], bufferobj, 'po', self.cr) bufferobj.name = 'test_translation_import/i18n/fr.po' - odoo.tools.trans_load_data(self.cr, bufferobj, 'po', 'fr_FR', verbose=False, context=context) + odoo.tools.trans_load_data(self.cr, bufferobj, 'po', 'fr_FR', + verbose=False, + create_empty_translation=create_empty_translation, + overwrite=True) # Check that the not translated key is not created update_translations() @@ -137,7 +139,7 @@ class TestTermCount(common.TransactionCase): self.assertFalse(translation, 'An empty translation is not imported') # Check that "Generate Missing Terms" create empty string for not translated key - update_translations({'create_empty_translation': True}) + update_translations(create_empty_translation=True) translation = self.env['ir.translation'].search_count([('src', '=', 'Efgh'), ('value', '=', '')]) self.assertTrue(translation, 'The translation of "Efgh" should be empty') @@ -238,7 +240,8 @@ class TestTranslationFlow(common.TransactionCase): translations = self.env["ir.translation"].search([ ('lang', '=', 'fr_FR'), - ('module', '=', 'test_translation_import') + ('module', '=', 'test_translation_import'), + ('value', '!=', ''), ]) # minus 3 as the original fr.po contains 3 fake code translations (cf @@ -265,10 +268,12 @@ class TestTranslationFlow(common.TransactionCase): 'overwrite': False, }) with mute_logger('odoo.addons.base.models.res_lang'): - import_fr.with_context(create_empty_translation=True).import_lang() + import_fr.import_lang() import_translation = self.env["ir.translation"].search([ ('lang', '=', 'fr_FR'), - ('module', '=', 'test_translation_import') + ('module', '=', 'test_translation_import'), + ('value', '!=', ''), ]) + self.assertEqual(init_translation_count, len(import_translation)) diff --git a/odoo/cli/server.py b/odoo/cli/server.py index f6214aaa6e7..0c2fb5ca74f 100644 --- a/odoo/cli/server.py +++ b/odoo/cli/server.py @@ -111,14 +111,14 @@ def export_translation(): def import_translation(): config = odoo.tools.config - context = {'overwrite': config["overwrite_existing_translations"]} + overwrite = config["overwrite_existing_translations"] dbname = config['db_name'] registry = odoo.modules.registry.Registry.new(dbname) with odoo.api.Environment.manage(): with registry.cursor() as cr: odoo.tools.trans_load( - cr, config["translate_in"], config["language"], context=context, + cr, config["translate_in"], config["language"], overwrite=overwrite, ) def main(args): diff --git a/odoo/tools/translate.py b/odoo/tools/translate.py index cf40a326179..1b7e5e0080e 100644 --- a/odoo/tools/translate.py +++ b/odoo/tools/translate.py @@ -1033,30 +1033,44 @@ def trans_generate(lang, modules, cr): return out -def trans_load(cr, filename, lang, verbose=True, module_name=None, context=None): +def trans_load(cr, filename, lang, verbose=True, module_name=None, create_empty_translation=False, overwrite=False): try: with file_open(filename, mode='rb') as fileobj: _logger.info("loading %s", filename) fileformat = os.path.splitext(filename)[-1][1:].lower() - result = trans_load_data(cr, fileobj, fileformat, lang, verbose=verbose, module_name=module_name, context=context) - return result + return trans_load_data(cr, fileobj, fileformat, lang, + verbose=verbose, + module_name=module_name, + create_empty_translation=create_empty_translation, + overwrite=overwrite) except IOError: if verbose: _logger.error("couldn't read translation file %s", filename) return None -def trans_load_data(cr, fileobj, fileformat, lang, verbose=True, module_name=None, context=None): - """Populates the ir_translation table.""" +def trans_load_data(cr, fileobj, fileformat, lang, + verbose=True, module_name=None, create_empty_translation=False, overwrite=False): + """Populates the ir_translation table. + + :param fileobj: buffer open to a translation file + :param fileformat: format of the `fielobj` file, one of 'po' or 'csv' + :param lang: language code of the translations contained in `fileobj` + language must be present and activated in the database + :param verbose: increase log output + :param module_name: name of the module to use for created translations # TODO move to TranslationFileReader? + :param create_empty_translation: create an ir.translation record, even if no value + is provided in the translation entry + :param overwrite: if an ir.translation already exists for a term, replace it with + the one in `fileobj` + """ if verbose: _logger.info('loading translation file for language %s', lang) - env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, context or {}) - Lang = env['res.lang'] - Translation = env['ir.translation'] + env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {}) try: - if not Lang._lang_get(lang): + if not env['res.lang']._lang_get(lang): _logger.error("Couldn't read translation for lang '%s', language not found", lang) return None @@ -1064,8 +1078,9 @@ def trans_load_data(cr, fileobj, fileformat, lang, verbose=True, module_name=Non fileobj.seek(0) reader = TranslationFileReader(fileobj, fileformat=fileformat) - # read the rest of the file - irt_cursor = Translation._get_import_cursor() + # read the rest of the file with a cursor-like object for fast inserting translations" + Translation = env['ir.translation'] + irt_cursor = Translation._get_import_cursor(overwrite) def process_row(row): """Process a single PO (or POT) entry.""" @@ -1078,7 +1093,7 @@ def trans_load_data(cr, fileobj, fileformat, lang, verbose=True, module_name=Non dic.update(row) # do not import empty values - if not env.context.get('create_empty_translation', False) and not dic['value']: + if not create_empty_translation and not dic['value']: return if dic['type'] == 'code' and module_name: