diff --git a/addons/test_mail_full/tests/test_sms_composer.py b/addons/test_mail_full/tests/test_sms_composer.py index 060863e4f02..5e0f74fe1b5 100644 --- a/addons/test_mail_full/tests/test_sms_composer.py +++ b/addons/test_mail_full/tests/test_sms_composer.py @@ -368,7 +368,7 @@ class TestSMSComposerMass(test_mail_full_common.BaseFunctionalTest, sms_common.M self.assertSMSOutgoing(record.customer_id, None, 'Dear %s this is an SMS.' % record.display_name) def test_composer_mass_active_ids_w_template_and_lang(self): - self.env.ref('base.lang_fr').write({'active': True}) + self.env['res.lang']._activate_lang('fr_FR') self.env['ir.translation'].create({ 'type': 'model', 'name': 'sms.template,body', @@ -425,7 +425,7 @@ class TestSMSComposerMass(test_mail_full_common.BaseFunctionalTest, sms_common.M """ Test the context action from a SMS template (Add context action button) and the usage with the sms composer """ # Create the lang info - self.env.ref('base.lang_fr').write({'active': True}) + self.env['res.lang']._activate_lang('fr_FR') self.env['ir.translation'].create({ 'type': 'model', 'name': 'sms.template,body', diff --git a/addons/test_mail_full/tests/test_sms_template.py b/addons/test_mail_full/tests/test_sms_template.py index a1c754511ab..ec5b7cf9f39 100644 --- a/addons/test_mail_full/tests/test_sms_template.py +++ b/addons/test_mail_full/tests/test_sms_template.py @@ -25,7 +25,7 @@ class TestSmsTemplate(test_mail_full_common.BaseFunctionalTest, sms_common.MockS self.assertEqual(rendered_body[self.test_record.id], 'Dear %s this is an SMS.' % self.test_record.display_name) def test_sms_template_lang(self): - self.env.ref('base.lang_fr').write({'active': True}) + self.env['res.lang']._activate_lang('fr_FR') self.user_admin.write({'lang': 'en_US'}) self.env['ir.translation'].create({ 'type': 'model', diff --git a/addons/website/tests/test_base_url.py b/addons/website/tests/test_base_url.py index 18d296f38e9..c14a6bc34e9 100644 --- a/addons/website/tests/test_base_url.py +++ b/addons/website/tests/test_base_url.py @@ -15,8 +15,7 @@ class TestUrlCommon(odoo.tests.HttpCase): 'domain': self.domain, }) - lang_fr = self.env.ref('base.lang_fr') - lang_fr.write({'active': True}) + lang_fr = self.env['res.lang']._activate_lang('fr_FR') self.website.language_ids = self.env.ref('base.lang_en') + lang_fr self.website.default_lang_id = self.env.ref('base.lang_en') diff --git a/addons/website/tests/test_lang_url.py b/addons/website/tests/test_lang_url.py index 1c08b866fa4..8e0f5c78da9 100644 --- a/addons/website/tests/test_lang_url.py +++ b/addons/website/tests/test_lang_url.py @@ -13,8 +13,8 @@ class TestLangUrl(HttpCase): # Simulate multi lang without loading translations self.website = self.env.ref('website.default_website') - lang_fr = self.env.ref('base.lang_fr') - lang_fr.write({'active': True, 'url_code': 'fr'}) + lang_fr = self.env['res.lang']._activate_lang('fr_FR') + lang_fr.write({'url_code': 'fr'}) self.website.language_ids = self.env.ref('base.lang_en') + lang_fr self.website.default_lang_id = self.env.ref('base.lang_en') diff --git a/addons/website/tests/test_qweb.py b/addons/website/tests/test_qweb.py index 1c2c1dbe573..99f53078310 100644 --- a/addons/website/tests/test_qweb.py +++ b/addons/website/tests/test_qweb.py @@ -73,7 +73,7 @@ class TestQwebProcessAtt(TransactionCase): def setUp(self): super(TestQwebProcessAtt, self).setUp() self.website = self.env.ref('website.default_website') - self.env.ref('base.lang_fr').active = True + self.env['res.lang']._activate_lang('fr_FR') self.website.language_ids = self.env.ref('base.lang_en') + self.env.ref('base.lang_fr') self.website.default_lang_id = self.env.ref('base.lang_en') self.website.cdn_activated = True 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 caf6be2a4a5..1ba9237344a 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,24 +774,18 @@ 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. """ - # make sure the given languages are active - res_lang = self.env['res.lang'].sudo() - for lang in langs: - res_lang.load_lang(lang) # load i18n files for module_name in modules: modpath = get_module_path(module_name) 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: @@ -802,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, 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, 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, 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, overwrite=overwrite) return True @api.model diff --git a/odoo/addons/base/models/res_currency.py b/odoo/addons/base/models/res_currency.py index 40f1952e219..8bcd9a3d388 100644 --- a/odoo/addons/base/models/res_currency.py +++ b/odoo/addons/base/models/res_currency.py @@ -110,8 +110,7 @@ class Currency(models.Model): integer_value = int(parts[0]) fractional_value = int(parts[2] or 0) - lang_code = self.env.context.get('lang') or self.env.user.lang - lang = self.env['res.lang'].with_context(active_test=False).search([('code', '=', lang_code)]) + lang = tools.get_lang(self.env) amount_words = tools.ustr('{amt_value} {amt_word}').format( amt_value=_num2words(integer_value, lang=lang.iso_code), amt_word=self.currency_unit_label, diff --git a/odoo/addons/base/models/res_lang.py b/odoo/addons/base/models/res_lang.py index eccb6b09c93..44fd28d3d4c 100644 --- a/odoo/addons/base/models/res_lang.py +++ b/odoo/addons/base/models/res_lang.py @@ -88,15 +88,24 @@ class Lang(models.Model): if not self.search_count([]): _logger.error("No language is active.") - @api.model + # TODO remove me after v14 def load_lang(self, lang, lang_name=None): - """ Create the given language if necessary, and make it active. """ - # if the language exists, simply make it active - language = self.with_context(active_test=False).search([('code', '=', lang)], limit=1) - if language: - language.write({'active': True}) - return language.id + _logger.warning("Call to deprecated method load_lang, use _create_lang or _activate_lang instead") + language = self._activate_lang(lang) or self._create_lang(lang, lang_name) + return language.id + def _activate_lang(self, code): + """ Activate languages + :param code: code of the language to activate + :return: the language matching 'code' activated + """ + lang = self.with_context(active_test=False).search([('code', '=', code)]) + if lang and not lang.active: + lang.active = True + return lang + + def _create_lang(self, lang, lang_name=None): + """ Create the given language and make it active. """ # create the language with locale information fail = True iso_lang = tools.get_iso_codes(lang) @@ -149,7 +158,7 @@ class Lang(models.Model): 'grouping' : str(conv.get('grouping', [])), } try: - return self.create(lang_info).id + return self.create(lang_info) finally: tools.resetlocale() @@ -157,18 +166,16 @@ class Lang(models.Model): def install_lang(self): """ - This method is called from odoo/addons/base/base_data.xml to load + This method is called from odoo/addons/data/res_lang_data.xml to load some language and set it as the default for every partners. The - language is set via tools.config by the RPC 'create' method on the + language is set via tools.config by the '_initialize_db' method on the 'db' object. This is a fragile solution and something else should be found. """ # config['load_language'] is a comma-separated list or None lang_code = (tools.config.get('load_language') or 'en_US').split(',')[0] - lang = self._lang_get(lang_code) - if not lang: - self.load_lang(lang_code) + lang = self._activate_lang(lang_code) or self._create_lang(lang_code) IrDefault = self.env['ir.default'] default_value = IrDefault.get('res.partner', 'lang') if default_value is None: @@ -219,7 +226,7 @@ class Lang(models.Model): active_lang = [lang.code for lang in self.filtered(lambda l: l.active)] if active_lang: mods = self.env['ir.module.module'].search([('state', '=', 'installed')]) - mods._update_translations(filter_lang=active_lang) + mods._update_translations(active_lang) @api.model_create_multi def create(self, vals_list): diff --git a/odoo/addons/base/tests/test_expression.py b/odoo/addons/base/tests/test_expression.py index 61d470e094c..e8cf33ef24c 100644 --- a/odoo/addons/base/tests/test_expression.py +++ b/odoo/addons/base/tests/test_expression.py @@ -672,11 +672,13 @@ class TestExpression(SavepointCaseWithUserDemo): def test_lp1071710(self): """ Check that we can exclude translated fields (bug lp:1071710) """ # first install french language - self.env['ir.translation']._load_module_terms(['base'], ['fr_FR']) + self.env['res.lang']._activate_lang('fr_FR') self.env['res.partner'].search([('name', '=', 'Pepper Street')]).country_id = self.env.ref('base.be') # actual test Country = self.env['res.country'].with_context(lang='fr_FR') be = self.env.ref('base.be') + be.with_context(lang='fr_FR').name = "Belgique" + self.assertNotEqual(be.name, "Belgique", "Setting a translation should not impact other languages") not_be = self._search(Country, [('name', '!=', 'Belgique')]) self.assertNotIn(be, not_be) diff --git a/odoo/addons/base/tests/test_misc.py b/odoo/addons/base/tests/test_misc.py index 29d0a021558..5276b2ed6f0 100644 --- a/odoo/addons/base/tests/test_misc.py +++ b/odoo/addons/base/tests/test_misc.py @@ -220,7 +220,8 @@ class TestFormatLangDate(TransactionCase): lang = self.env['res.lang'] # Activate French and Simplified Chinese (test with non-ASCII characters) - lang.search([('active', '=', False), ('code', 'in', ['fr_FR', 'zh_CN'])]).write({'active': True}) + lang._activate_lang('fr_FR') + lang._activate_lang('zh_CN') # -- test `date` # Change a single parameter diff --git a/odoo/addons/base/tests/test_qweb.py b/odoo/addons/base/tests/test_qweb.py index 13b95926f47..780dc6a0cc5 100644 --- a/odoo/addons/base/tests/test_qweb.py +++ b/odoo/addons/base/tests/test_qweb.py @@ -522,10 +522,8 @@ class TestQWebNS(TransactionCase): current_lang = 'en_US' other_lang = 'fr_FR' - self.env['res.lang'].load_lang(lang=other_lang) - - self.env['res.lang']._lang_get(other_lang).write({ - 'active': True, + lang = self.env['res.lang']._activate_lang(other_lang) + lang.write({ 'decimal_point': '*', 'thousands_sep': '/' }) diff --git a/odoo/addons/base/tests/test_translate.py b/odoo/addons/base/tests/test_translate.py index 12c3f8b2d96..0d421842e0d 100644 --- a/odoo/addons/base/tests/test_translate.py +++ b/odoo/addons/base/tests/test_translate.py @@ -247,7 +247,8 @@ class TestTranslation(TransactionCase): def setUp(self): super(TestTranslation, self).setUp() - self.env['ir.translation']._load_module_terms(['base'], ['fr_FR']) + lang = self.env['res.lang']._activate_lang('fr_FR') + self.env.ref('base.module_base')._update_translations(['fr_FR']) self.customers = self.env['res.partner.category'].create({'name': 'Customers'}) self.env['ir.translation'].create({ 'type': 'model', @@ -436,7 +437,7 @@ class TestTranslationWrite(TransactionCase): self.assertEqual(source_name[0]['name'], "English Name", "Reference field not updated") def test_03_fr_single(self): - self.env['res.lang'].load_lang('fr_FR') + self.env['res.lang']._activate_lang('fr_FR') self.env['res.users'].with_context(active_test=False).search([]).write({'lang': 'fr_FR'}) self.env.ref('base.lang_en').active = False @@ -454,7 +455,7 @@ class TestTranslationWrite(TransactionCase): self.assertEqual(len(translations), 0, "No French translation should be created when writing in French") def test_04_fr_multi(self): - self.env['res.lang'].load_lang('fr_FR') + self.env['res.lang']._activate_lang('fr_FR') langs = self.env['res.lang'].get_installed() self.assertEqual([('en_US', 'English (US)'), ('fr_FR', 'French / Français')], langs, @@ -481,9 +482,9 @@ class TestTranslationWrite(TransactionCase): ]) def test_04_fr_multi_no_en(self): - self.env['res.lang'].load_lang('fr_FR') + self.env['res.lang']._activate_lang('fr_FR') + self.env['res.lang']._activate_lang('es_ES') self.env['res.users'].with_context(active_test=False).search([]).write({'lang': 'fr_FR'}) - self.env['res.lang'].load_lang('es_ES') self.env.ref('base.lang_en').active = False langs = self.env['res.lang'].get_installed() @@ -504,7 +505,7 @@ class TestTranslationWrite(TransactionCase): ]) def test_05_remove_multi(self): - self.env['res.lang'].load_lang('fr_FR') + self.env['res.lang']._activate_lang('fr_FR') langs = self.env['res.lang'].get_installed() self.assertEqual([('en_US', 'English (US)'), ('fr_FR', 'French / Français')], langs, @@ -576,7 +577,9 @@ class TestTranslationWrite(TransactionCase): class TestXMLTranslation(TransactionCase): def setUp(self): super(TestXMLTranslation, self).setUp() - self.env['ir.translation']._load_module_terms(['base'], ['fr_FR', 'nl_NL']) + self.env['res.lang']._activate_lang('fr_FR') + self.env['res.lang']._activate_lang('nl_NL') + self.env.ref('base.module_base')._update_translations(['fr_FR', 'nl_NL']) def create_view(self, archf, terms, **kwargs): view = self.env['ir.ui.view'].create({ diff --git a/odoo/addons/base/tests/test_views.py b/odoo/addons/base/tests/test_views.py index 4440ac4a1eb..14e5786a937 100644 --- a/odoo/addons/base/tests/test_views.py +++ b/odoo/addons/base/tests/test_views.py @@ -606,7 +606,7 @@ class TestNoModel(ViewCase): """ Test if translations work correctly without a model """ - self.env['res.lang'].load_lang('fr_FR') + self.env['res.lang']._activate_lang('fr_FR') ARCH = '' TEXT_EN = "Copyright copyrighter" TEXT_FR = u"Copyrighter, tous droits réservés" diff --git a/odoo/addons/base/wizard/base_import_language.py b/odoo/addons/base/wizard/base_import_language.py index 461c95958c3..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)) @@ -37,8 +36,14 @@ class BaseLanguageImport(models.TransientModel): buf.seek(0) fileformat = os.path.splitext(this.filename)[-1][1:].lower() - tools.trans_load_data(this._cr, buf, fileformat, this.code, - lang_name=this.name, context=this._context) + Lang = self.env["res.lang"] + lang = Lang._activate_lang(self.code) or Lang._create_lang( + self.code, lang_name=self.name + ) + + 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 e8bd93991ac..40dd162fbeb 100644 --- a/odoo/addons/base/wizard/base_language_install.py +++ b/odoo/addons/base/wizard/base_language_install.py @@ -33,7 +33,8 @@ class BaseLanguageInstall(models.TransientModel): def lang_install(self): self.ensure_one() mods = self.env['ir.module.module'].search([('state', '=', 'installed')]) - mods.with_context(overwrite=self.overwrite)._update_translations(self.lang) + self.env['res.lang']._activate_lang(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 bfe94fcb3a6..c05d85b7517 100644 --- a/odoo/addons/base/wizard/base_update_translations.py +++ b/odoo/addons/base/wizard/base_update_translations.py @@ -26,10 +26,7 @@ class BaseUpdateTranslations(models.TransientModel): return lang.name def act_update(self): - this = self[0] - lang_name = self._get_lang_name(this.lang) 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, lang_name=lang_name, 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_assetsbundle/tests/test_assetsbundle.py b/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py index 98b0e00dd45..c38245eb19e 100644 --- a/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py +++ b/odoo/addons/test_assetsbundle/tests/test_assetsbundle.py @@ -33,7 +33,7 @@ class TestJavascriptAssetsBundle(FileTouchable): super(TestJavascriptAssetsBundle, self).setUp() self.jsbundle_xmlid = 'test_assetsbundle.bundle1' self.cssbundle_xmlid = 'test_assetsbundle.bundle2' - self.env['res.lang'].load_lang('ar_SY') + self.env['res.lang']._activate_lang('ar_SY') def _get_asset(self, xmlid, env=None): diff --git a/odoo/addons/test_converter/tests/test_html.py b/odoo/addons/test_converter/tests/test_html.py index cf3f4c198ec..5bae2a063d7 100644 --- a/odoo/addons/test_converter/tests/test_html.py +++ b/odoo/addons/test_converter/tests/test_html.py @@ -291,7 +291,7 @@ class TestDurationExport(TestBasicExport): def setUp(self): super(TestDurationExport, self).setUp() # needs to have lang installed otherwise falls back on en_US - self.env['res.lang'].load_lang('fr_FR') + self.env['res.lang']._activate_lang('fr_FR') def test_default_unit(self): converter = self.get_converter('float', 'duration') @@ -332,7 +332,7 @@ class TestRelativeDatetime(TestBasicExport): def setUp(self): super(TestRelativeDatetime, self).setUp() # needs to have lang installed otherwise falls back on en_US - self.env['res.lang'].load_lang('fr_FR') + self.env['res.lang']._activate_lang('fr_FR') def test_basic(self): converter = self.get_converter('datetime', 'relative') diff --git a/odoo/addons/test_impex/tests/test_export.py b/odoo/addons/test_impex/tests/test_export.py index 28a1c7472bd..3d76ec925e6 100644 --- a/odoo/addons/test_impex/tests/test_export.py +++ b/odoo/addons/test_impex/tests/test_export.py @@ -285,7 +285,7 @@ class test_selection(CreatorCase): [[u"Bar"]]) def test_localized_export(self): - self.env['res.lang'].load_lang('fr_FR') + self.env['res.lang']._activate_lang('fr_FR') ir_field = self.env['ir.model.fields']._get('export.selection', 'value') selection = ir_field.selection_ids translations = dict(self.translations_fr) diff --git a/odoo/addons/test_impex/tests/test_load.py b/odoo/addons/test_impex/tests/test_load.py index d2bd80f7e56..35a3b55d31b 100644 --- a/odoo/addons/test_impex/tests/test_load.py +++ b/odoo/addons/test_impex/tests/test_load.py @@ -73,7 +73,7 @@ class ImporterCase(common.TransactionCase): return '__test__.' + name def add_translations(self, name, type, code, *tnx): - self.env['res.lang'].load_lang(code) + self.env['res.lang']._activate_lang(code) Translations = self.env['ir.translation'] for source, value in tnx: Translations.create({ diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py index 1bc0bc78f76..b68ae8220fb 100644 --- a/odoo/addons/test_new_api/tests/test_new_fields.py +++ b/odoo/addons/test_new_api/tests/test_new_fields.py @@ -1412,7 +1412,7 @@ class TestFields(TransactionCaseWithUserDemo): email = self.env.ref('test_new_api.emailmessage_0_0') self.assertEqual(email.message, message) - self.env['res.lang'].load_lang('fr_FR') + self.env['res.lang']._activate_lang('fr_FR') def count(msg): # return the number of translations of msg.label diff --git a/odoo/addons/test_new_api/tests/test_ui.py b/odoo/addons/test_new_api/tests/test_ui.py index 979bf8064a9..94938a3a8b4 100644 --- a/odoo/addons/test_new_api/tests/test_ui.py +++ b/odoo/addons/test_new_api/tests/test_ui.py @@ -27,7 +27,8 @@ class TestUiTranslation(odoo.tests.HttpCase): @mute_logger('odoo.sql_db', 'odoo.http') def test_01_sql_constraints(self): # Raise an SQL constraint and test the message - self.env['ir.translation']._load_module_terms(['test_new_api'], ['fr_FR']) + self.env['res.lang']._activate_lang('fr_FR') + self.env.ref('base.module_test_new_api')._update_translations(['fr_FR']) constraint = self.env.ref('test_new_api.constraint_test_new_api_category_positive_color') message = constraint.with_context(lang='fr_FR').message self.assertEqual(message, "La couleur doit être une valeur positive !") 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 fb98e78661f..234bc743fc3 100644 --- a/odoo/addons/test_translation_import/tests/test_term_count.py +++ b/odoo/addons/test_translation_import/tests/test_term_count.py @@ -18,7 +18,8 @@ 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) + self.env['res.lang']._activate_lang('fr_FR') + odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr.po', 'fr_FR', verbose=False) translations = self.env['ir.translation'].search([ ('lang', '=', 'fr_FR'), ('src', '=', '1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB'), @@ -40,6 +41,7 @@ class TestTermCount(common.TransactionCase): """ Just make sure we have as many translation entries as we wanted and module deducted from file content """ + self.env['res.lang']._activate_lang('fr_FR') odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr.po', 'fr_FR', verbose=False) translations = self.env['ir.translation'].search([ ('lang', '=', 'fr_FR'), @@ -56,10 +58,11 @@ class TestTermCount(common.TransactionCase): menu = self.env.ref('test_translation_import.menu_test_translation_import') menu.name = "New Name" # install french and change translation content - odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr.po', 'fr_FR', module_name='test_translation_import', verbose=False) + self.env['res.lang']._activate_lang('fr_FR') + odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr.po', 'fr_FR', 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', verbose=False, overwrite=True) # trans_load invalidates ormcache but not record cache menu.env.cache.invalidate() @@ -67,8 +70,9 @@ class TestTermCount(common.TransactionCase): 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}) + self.env['res.lang']._activate_lang('fr_BE') + odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr.po', 'fr_BE', verbose=False) + odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr_BE.po', 'fr_BE', verbose=False, overwrite=True) # language override base language translations = self.env['ir.translation'].search([ @@ -95,7 +99,8 @@ class TestTermCount(common.TransactionCase): """ 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) + self.env['res.lang']._activate_lang('fr_FR') + odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr.po', 'fr_FR', verbose=False) ids = self.env['ir.translation'].search([ ('lang', '=', 'fr_FR'), ('src', '=', 'Test translation with two code lines'), @@ -118,12 +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() @@ -131,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') @@ -232,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 @@ -259,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 e185f9c809c..120ba52fdb9 100644 --- a/odoo/tools/translate.py +++ b/odoo/tools/translate.py @@ -13,7 +13,7 @@ import re import tarfile import tempfile import threading -from collections import defaultdict +from collections import defaultdict, namedtuple from datetime import datetime from os.path import join @@ -647,13 +647,13 @@ class PoFileReader: continue _logger.error("malformed po file: unknown occurrence: %s", occurrence) -def TranslationFileWriter(target, fileformat='po', lang=None, modules=None): +def TranslationFileWriter(target, fileformat='po', lang=None): """ Iterate over translation file to return Odoo translation entries """ if fileformat == 'csv': return CSVFileWriter(target) if fileformat == 'po': - return PoFileWriter(target, modules=modules, lang=lang) + return PoFileWriter(target, lang=lang) if fileformat == 'tgz': return TarFileWriter(target, lang=lang) @@ -677,13 +677,34 @@ class CSVFileWriter: class PoFileWriter: """ Iterate over po file to return Odoo translation entries """ - def __init__(self, target, modules, lang): - import odoo.release as release + def __init__(self, target, lang): self.buffer = target self.lang = lang self.po = polib.POFile() + def write_rows(self, rows): + # we now group the translations by source. That means one translation per source. + grouped_rows = {} + modules = set([]) + for module, type, name, res_id, src, trad, comments in rows: + row = grouped_rows.setdefault(src, {}) + row.setdefault('modules', set()).add(module) + if not row.get('translation') and trad != src: + row['translation'] = trad + row.setdefault('tnrs', []).append((type, name, res_id)) + row.setdefault('comments', set()).update(comments) + modules.add(module) + + for src, row in sorted(grouped_rows.items()): + if not self.lang: + # translation template, so no translation value + row['translation'] = '' + elif not row.get('translation'): + row['translation'] = '' + self.add_entry(row['modules'], sorted(row['tnrs']), src, row['translation'], row['comments']) + + import odoo.release as release self.po.header = "Translation of %s.\n" \ "This file contains the translation of the following modules:\n" \ "%s" % (release.description, ''.join("\t* %s\n" % m for m in modules)) @@ -701,25 +722,6 @@ class PoFileWriter: 'Plural-Forms': '', } - def write_rows(self, rows): - # we now group the translations by source. That means one translation per source. - grouped_rows = {} - for module, type, name, res_id, src, trad, comments in rows: - row = grouped_rows.setdefault(src, {}) - row.setdefault('modules', set()).add(module) - if not row.get('translation') and trad != src: - row['translation'] = trad - row.setdefault('tnrs', []).append((type, name, res_id)) - row.setdefault('comments', set()).update(comments) - - for src, row in sorted(grouped_rows.items()): - if not self.lang: - # translation template, so no translation value - row['translation'] = '' - elif not row.get('translation'): - row['translation'] = '' - self.add_entry(row['modules'], row['tnrs'], src, row['translation'], row['comments']) - # buffer expects bytes self.buffer.write(str(self.po).encode()) @@ -763,7 +765,7 @@ class TarFileWriter: for mod, modrows in rows_by_module.items(): with io.BytesIO() as buf: - po = PoFileWriter(buf, modules=[mod], lang=self.lang) + po = PoFileWriter(buf, lang=self.lang) po.write_rows(modrows) buf.seek(0) @@ -781,14 +783,10 @@ class TarFileWriter: self.tar.close() # Methods to export the translation file - def trans_export(lang, modules, buffer, format, cr): - - translations = trans_generate(lang, modules, cr) - modules = set(t[0] for t in translations) - writer = TranslationFileWriter(buffer, fileformat=format, lang=lang, modules=modules) - writer.write_rows(translations) - del translations + reader = TranslationModuleReader(cr, modules=modules, lang=lang) + writer = TranslationFileWriter(buffer, fileformat=format, lang=lang) + writer.write_rows(reader) def trans_parse_rml(de): @@ -872,12 +870,44 @@ def babel_extract_qweb(fileobj, keywords, comment_tags, options): _extract_translatable_qweb_terms(tree.getroot(), handle_text) return result +ImdInfo = namedtuple('ExternalId', ['name', 'model', 'res_id', 'module']) -def trans_generate(lang, modules, cr): - env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {}) - to_translate = set() - def push_translation(module, type, name, id, source, comments=None): +class TranslationModuleReader: + """ Retrieve translated records per module + + :param cr: cursor to database to export + :param modules: list of modules to filter the exported terms, can be ['all'] + records with no external id are always ignored + :param lang: language code to retrieve the translations + retrieve source terms only if not set + """ + + def __init__(self, cr, modules=None, lang=None): + self._cr = cr + self._modules = modules or ['all'] + self._lang = lang + self.env = odoo.api.Environment(cr, odoo.SUPERUSER_ID, {}) + self._to_translate = [] + self._path_list = [(path, True) for path in odoo.addons.__path__] + self._installed_modules = [ + m['name'] + for m in self.env['ir.module.module'].search_read([('state', '=', 'installed')], fields=['name']) + ] + + self._export_translatable_records() + self._export_translatable_resources() + + + def __iter__(self): + """ 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 "" + yield (module, ttype, name, res_id, source, encode(trans) or '', comments) + + def _push_translation(self, module, ttype, name, res_id, source, comments=None): # 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() @@ -886,84 +916,98 @@ def trans_generate(lang, modules, cr): if not sanitized_term or len(sanitized_term) <= 1: return - tnx = (module, source, name, id, type, tuple(comments or ())) - to_translate.add(tnx) + self._to_translate.append((module, source, name, res_id, ttype, tuple(comments or ()))) - def translatable_model(record): - if not record._translate: - return False + def _get_translatable_records(self, records): + """ Filter the records that are translatable - if record._name == 'ir.model.fields.selection': - record = record.field_id - if record._name == 'ir.model.fields': - field_name = record.name - field_model = env.get(record.model) - if (field_model is None or not field_model._translate or - field_name not in field_model._fields): - return False + A record is considered as untranslatable if: + - it does not exist + - the model is flagged with _translate=False + - it is a field of a model flagged with _translate=False + - it is a selection of a field of a model flagged with _translate=False - return True + :param records: a list of namedtuple ImdInfo belonging to the same model + """ + model = next(iter(records)).model + if model not in self.env: + _logger.error("Unable to find object %r", model) + return self.browse() - query = 'SELECT min(name), model, res_id, module FROM ir_model_data' + if not self.env[model]._translate: + return self.env[model].browse() - if 'all_installed' in modules: - query += ' WHERE module IN ( SELECT name FROM ir_module_module WHERE state = \'installed\') ' + res_ids = [r.res_id for r in records] + records = self.env[model].browse(res_ids).exists() + if len(records) < len(res_ids): + missing_ids = set(records.ids) - set(res_ids) + _logger.warning("Unable to find objects %r with id %d", model, ', '.join(missing_ids)) + if not records: + return records - if 'all' not in modules: - query += ' WHERE module IN %s' - query_param = (tuple(modules),) - else: - query += ' WHERE module != %s' - query_param = ('__export__',) + if model == 'ir.model.fields.selection': + fields = defaultdict(list) + for selection in records: + fields[selection.field_id] = selection + for field, selection in fields.items(): + field_name = field.name + field_model = self.env.get(field.model) + if (field_model is None or not field_model._translate or + field_name not in field_model._fields): + # the selection is linked to a model with _translate=False, remove it + records -= selection + elif model == 'ir.model.fields': + for field in records: + field_name = field.name + field_model = self.env.get(field.model) + if (field_model is None or not field_model._translate or + field_name not in field_model._fields): + # the field is linked to a model with _translate=False, remove it + records -= field - query += ' GROUP BY model, res_id, module ORDER BY module, model, min(name)' + return records - cr.execute(query, query_param) - for (xml_name, model, res_id, module) in cr.fetchall(): - xml_name = "%s.%s" % (module, xml_name) + def _export_translatable_records(self): + """ Export translations of all translated records having an external id """ - if model not in env: - _logger.error(u"Unable to find object %r", model) - continue + query = """SELECT min(name), model, res_id, module + FROM ir_model_data + WHERE module = ANY(%s) + GROUP BY model, res_id, module + ORDER BY module, model, min(name)""" - record = env[model].browse(res_id) - if not record.exists(): - _logger.warning(u"Unable to find object %r with id %d", model, res_id) - continue + if 'all' not in self._modules: + query_param = list(self._modules) + else: + query_param = self._installed_modules - if not translatable_model(record): - continue + self._cr.execute(query, (query_param,)) - for field_name, field in record._fields.items(): - if field.translate: - name = model + "," + field_name - try: - value = record[field_name] or '' - except Exception: - continue - for term in set(field.get_trans_terms(value)): - trans_type = 'model_terms' if callable(field.translate) else 'model' - push_translation(module, trans_type, name, xml_name, term) + records_per_model = defaultdict(dict) + for (xml_name, model, res_id, module) in self._cr.fetchall(): + records_per_model[model][res_id] = ImdInfo(xml_name, model, res_id, module) - # End of data for ir.model.data query results + for model, imd_per_id in records_per_model.items(): + records = self._get_translatable_records(imd_per_id.values()) + if not records: + continue - installed_modules = [ - m['name'] - for m in env['ir.module.module'].search_read([('state', '=', 'installed')], fields=['name']) - ] + for record in records: + xml_name = "%s.%s" % (imd_per_id[record.id].module, imd_per_id[record.id].name) + for field_name, field in record._fields.items(): + if field.translate: + name = model + "," + field_name + try: + value = record[field_name] or '' + except Exception: + 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) - path_list = [(path, True) for path in odoo.addons.__path__] - # Also scan these non-addon paths - for bin_path in ['osv', 'report', 'modules', 'service', 'tools']: - path_list.append((os.path.join(config['root_path'], bin_path), True)) - # non-recursive scan for individual files in root directory but without - # scanning subdirectories that may contain addons - path_list.append((config['root_path'], False)) - _logger.debug("Scanning modules at paths: %s", path_list) - - def get_module_from_path(path): - for (mp, rec) in path_list: + def _get_module_from_path(self, path): + for (mp, rec) in self._path_list: mp = os.path.join(mp, '') dirname = os.path.join(os.path.dirname(path), '') if rec and path.startswith(mp) and dirname != mp: @@ -971,22 +1015,24 @@ def trans_generate(lang, modules, cr): return path.split(os.path.sep)[0] return 'base' # files that are not in a module are considered as being in 'base' module - def verified_module_filepaths(fname, path, root): + def _verified_module_filepaths(self, fname, path, root): fabsolutepath = join(root, fname) frelativepath = fabsolutepath[len(path):] display_path = "addons%s" % frelativepath - module = get_module_from_path(fabsolutepath) - if ('all' in modules or module in modules) and module in installed_modules: + module = self._get_module_from_path(fabsolutepath) + if ('all' in self._modules or module in self._modules) and module in self._installed_modules: if os.path.sep != '/': display_path = display_path.replace(os.path.sep, '/') return module, fabsolutepath, frelativepath, display_path return None, None, None, None - def babel_extract_terms(fname, path, root, extract_method="python", trans_type='code', + def _babel_extract_terms(self, fname, path, root, extract_method="python", trans_type='code', extra_comments=None, extract_keywords={'_': None}): - module, fabsolutepath, _, display_path = verified_module_filepaths(fname, path, root) + + module, fabsolutepath, _, display_path = self._verified_module_filepaths(fname, path, root) + if not module: + return extra_comments = extra_comments or [] - if not module: return src_file = open(fabsolutepath, 'rb') options = {} if extract_method == 'python': @@ -996,76 +1042,98 @@ def trans_generate(lang, modules, cr): # Babel 0.9.6 yields lineno, message, comments # Babel 1.3 yields lineno, message, comments, context lineno, message, comments = extracted[:3] - push_translation(module, trans_type, display_path, lineno, + self._push_translation(module, trans_type, display_path, lineno, encode(message), comments + extra_comments) except Exception: _logger.exception("Failed to extract terms from %s", fabsolutepath) finally: src_file.close() - for (path, recursive) in path_list: - _logger.debug("Scanning files of modules at %s", path) - for root, dummy, files in os.walk(path, followlinks=True): - for fname in fnmatch.filter(files, '*.py'): - babel_extract_terms(fname, path, root, - extract_keywords={'_': None, '_lt': None}) - # Javascript source files in the static/src/js directory, rest is ignored (libs) - if fnmatch.fnmatch(root, '*/static/src/js*'): - for fname in fnmatch.filter(files, '*.js'): - babel_extract_terms(fname, path, root, 'javascript', - extra_comments=[WEB_TRANSLATION_COMMENT], - extract_keywords={'_t': None, '_lt': None}) - # QWeb template files - if fnmatch.fnmatch(root, '*/static/src/xml*'): - for fname in fnmatch.filter(files, '*.xml'): - babel_extract_terms(fname, path, root, 'odoo.tools.translate:babel_extract_qweb', - extra_comments=[WEB_TRANSLATION_COMMENT]) - if not recursive: - # due to topdown, first iteration is in first level - break + def _export_translatable_resources(self): + """ Export translations for static terms + + This will include: + - the python strings marked with _() or _lt() + - the javascript strings marked with _t() or _lt() inside static/src/js/ + - the strings inside Qweb files inside static/src/xml/ + """ - out = [] - # translate strings marked as to be translated - Translation = env['ir.translation'] - for module, source, name, id, type, comments in sorted(to_translate): - trans = Translation._get_source(name, type, lang, source) if lang else "" - out.append((module, type, name, id, source, encode(trans) or '', comments)) - return out + # Also scan these non-addon paths + for bin_path in ['osv', 'report', 'modules', 'service', 'tools']: + self._path_list.append((os.path.join(config['root_path'], bin_path), True)) + # non-recursive scan for individual files in root directory but without + # scanning subdirectories that may contain addons + self._path_list.append((config['root_path'], False)) + _logger.debug("Scanning modules at paths: %s", self._path_list) + + for (path, recursive) in self._path_list: + _logger.debug("Scanning files of modules at %s", path) + for root, dummy, files in os.walk(path, followlinks=True): + for fname in fnmatch.filter(files, '*.py'): + self._babel_extract_terms(fname, path, root, + extract_keywords={'_': None, '_lt': None}) + # Javascript source files in the static/src/js directory, rest is ignored (libs) + if fnmatch.fnmatch(root, '*/static/src/js*'): + for fname in fnmatch.filter(files, '*.js'): + self._babel_extract_terms(fname, path, root, 'javascript', + extra_comments=[WEB_TRANSLATION_COMMENT], + extract_keywords={'_t': None, '_lt': None}) + # QWeb template files + if fnmatch.fnmatch(root, '*/static/src/xml*'): + for fname in fnmatch.filter(files, '*.xml'): + self._babel_extract_terms(fname, path, root, 'odoo.tools.translate:babel_extract_qweb', + extra_comments=[WEB_TRANSLATION_COMMENT]) + if not recursive: + # due to topdown, first iteration is in first level + break -def trans_load(cr, filename, lang, verbose=True, module_name=None, context=None): +def trans_load(cr, filename, lang, verbose=True, 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, + 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, lang_name=None, verbose=True, module_name=None, context=None): - """Populates the ir_translation table.""" +def trans_load_data(cr, fileobj, fileformat, lang, + verbose=True, 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 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.search_count([('code', '=', lang)]): - # lets create the language with locale information - Lang.load_lang(lang=lang, lang_name=lang_name) + if not env['res.lang']._lang_get(lang): + _logger.error("Couldn't read translation for lang '%s', language not found", lang) + return None # now, the serious things: we read the language file 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,12 +1146,9 @@ def trans_load_data(cr, fileobj, fileformat, lang, lang_name=None, verbose=True, 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: - dic['module'] = module_name - irt_cursor.push(dic) # First process the entries from the PO file (doing so also fills/removes