diff --git a/addons/l10n_fr_fec/wizard/account_fr_fec.py b/addons/l10n_fr_fec/wizard/account_fr_fec.py index bdc7e3979b7..50bd4700272 100644 --- a/addons/l10n_fr_fec/wizard/account_fr_fec.py +++ b/addons/l10n_fr_fec/wizard/account_fr_fec.py @@ -153,14 +153,19 @@ class AccountFrFec(models.TransientModel): unaffected_earnings_results = self._do_query_unaffected_earnings() unaffected_earnings_line = False - sql_query = ''' + if self.pool['account.account'].name.translate: + lang = self.env.user.lang or get_lang(self.env).code + aa_name = f"COALESCE(aa.name->>'{lang}', aa.name->>'en_US')" + else: + aa_name = "aa.name" + sql_query = f''' SELECT 'OUV' AS JournalCode, 'Balance initiale' AS JournalLib, 'OUVERTURE/' || %s AS EcritureNum, %s AS EcritureDate, MIN(aa.code) AS CompteNum, - replace(replace(MIN(aa.name), '|', '/'), '\t', '') AS CompteLib, + replace(replace(MIN({aa_name}), '|', '/'), '\t', '') AS CompteLib, '' AS CompAuxNum, '' AS CompAuxLib, '-' AS PieceRef, @@ -236,14 +241,14 @@ class AccountFrFec(models.TransientModel): rows_to_write.append(unaffected_earnings_results) # INITIAL BALANCE - receivable/payable - sql_query = ''' + sql_query = f''' SELECT 'OUV' AS JournalCode, 'Balance initiale' AS JournalLib, 'OUVERTURE/' || %s AS EcritureNum, %s AS EcritureDate, MIN(aa.code) AS CompteNum, - replace(MIN(aa.name), '|', '/') AS CompteLib, + replace(MIN({aa_name}), '|', '/') AS CompteLib, CASE WHEN MIN(aa.account_type) IN ('asset_receivable', 'liability_payable') THEN CASE WHEN rp.ref IS null OR rp.ref = '' @@ -298,14 +303,19 @@ class AccountFrFec(models.TransientModel): rows_to_write.append(listrow) # LINES - sql_query = ''' + if self.pool['account.journal'].name.translate: + lang = self.env.user.lang or get_lang(self.env).code + aj_name = f"COALESCE(aj.name->>'{lang}', aj.name->>'en_US')" + else: + aj_name = "aj.name" + sql_query = f''' SELECT REGEXP_REPLACE(replace(aj.code, '|', '/'), '[\\t\\r\\n]', ' ', 'g') AS JournalCode, - REGEXP_REPLACE(replace(COALESCE(aj__name.value, aj.name), '|', '/'), '[\\t\\r\\n]', ' ', 'g') AS JournalLib, + REGEXP_REPLACE(replace({aj_name}, '|', '/'), '[\\t\\r\\n]', ' ', 'g') AS JournalLib, REGEXP_REPLACE(replace(am.name, '|', '/'), '[\\t\\r\\n]', ' ', 'g') AS EcritureNum, TO_CHAR(am.date, 'YYYYMMDD') AS EcritureDate, aa.code AS CompteNum, - REGEXP_REPLACE(replace(aa.name, '|', '/'), '[\\t\\r\\n]', ' ', 'g') AS CompteLib, + REGEXP_REPLACE(replace({aa_name}, '|', '/'), '[\\t\\r\\n]', ' ', 'g') AS CompteLib, CASE WHEN aa.account_type IN ('asset_receivable', 'liability_payable') THEN CASE WHEN rp.ref IS null OR rp.ref = '' @@ -343,11 +353,6 @@ class AccountFrFec(models.TransientModel): LEFT JOIN account_move am ON am.id=aml.move_id LEFT JOIN res_partner rp ON rp.id=aml.partner_id JOIN account_journal aj ON aj.id = am.journal_id - LEFT JOIN ir_translation aj__name ON aj__name.res_id = aj.id - AND aj__name.type = 'model' - AND aj__name.name = 'account.journal,name' - AND aj__name.lang = %s - AND aj__name.value != '' JOIN account_account aa ON aa.id = aml.account_id LEFT JOIN res_currency rc ON rc.id = aml.currency_id LEFT JOIN account_full_reconcile rec ON rec.id = aml.full_reconcile_id @@ -369,9 +374,8 @@ class AccountFrFec(models.TransientModel): am.name, aml.id ''' - lang = self.env.user.lang or get_lang(self.env).code self._cr.execute( - sql_query, (lang, self.date_from, self.date_to, company.id)) + sql_query, (self.date_from, self.date_to, company.id)) for row in self._cr.fetchall(): rows_to_write.append(list(row)) diff --git a/addons/mail/models/__init__.py b/addons/mail/models/__init__.py index a7eaa787c3b..03bcd6eb3c1 100644 --- a/addons/mail/models/__init__.py +++ b/addons/mail/models/__init__.py @@ -52,7 +52,6 @@ from . import ir_http from . import ir_mail_server from . import ir_model from . import ir_model_fields -from . import ir_translation from . import ir_ui_view from . import ir_qweb from . import ir_websocket diff --git a/addons/mail/models/ir_translation.py b/addons/mail/models/ir_translation.py deleted file mode 100644 index a572447c11a..00000000000 --- a/addons/mail/models/ir_translation.py +++ /dev/null @@ -1,59 +0,0 @@ -# -*- coding: utf-8 -*- -# Part of Odoo. See LICENSE file for full copyright and licensing details. - -from odoo import models, _ -from odoo.exceptions import AccessError - - -class IrTranslation(models.Model): - _inherit = 'ir.translation' - - def create(self, vals_list): - translations = super().create(vals_list) - translations._check_is_dynamic() - return translations - - def write(self, vals): - res = super().write(vals) - self._check_is_dynamic() - return res - - def _check_is_dynamic(self): - # if we don't modify translation of at least a model that inherits from mail.render.mixin, we ignore it - # translation.name can be a path, and so not in the pool, so type(None) will exclude these translations. - translations_for_mail_render_mixin = self.filtered( - lambda translation: issubclass(type(self.env.get(translation.name.split(',')[0])), self.pool['mail.render.mixin']) - ) - if not translations_for_mail_render_mixin: - return - - # if we are admin, or that we can update mail.template we ignore - if self.env.is_admin() or self.env.user.has_group('mail.group_mail_template_editor'): - return - - # Check that we don't add qweb code in translation when you don't have the rights - - # prefill cache - ids_by_model_by_lang = {} - tuple_lang_model_id = translations_for_mail_render_mixin.mapped( - lambda translation: (translation.lang, translation.name.split(',')[0], translation.res_id) - ) - for lang, model, _id in tuple_lang_model_id: - ids_by_model_by_lang.setdefault(lang, {}).setdefault(model, set()).add(_id) - for lang in ids_by_model_by_lang: - for res_model, res_ids in ids_by_model_by_lang[lang].items(): - self.env[res_model].with_context(lang=lang).browse(res_ids) - - for trans in translations_for_mail_render_mixin: - res_model, res_id = trans.name.split(',')[0], trans.res_id - rec = self.env[res_model].with_context(lang=trans.lang).browse(res_id) - - if rec._is_dynamic(): - group = self.env.ref('mail.group_mail_template_editor') - more_info = len(self) > 1 and ' [%s]' % rec or '' - raise AccessError( - _('Only users belonging to the "%(group)s" group can modify translation related to dynamic templates.%(xtra)s', - group=group.name, - xtra=more_info - ) - ) diff --git a/addons/mail/models/mail_render_mixin.py b/addons/mail/models/mail_render_mixin.py index 9e8d2cff87d..39874b7b25c 100644 --- a/addons/mail/models/mail_render_mixin.py +++ b/addons/mail/models/mail_render_mixin.py @@ -104,6 +104,16 @@ class MailRenderMixin(models.AbstractModel): self._check_access_right_dynamic_template() return True + def _update_field_translations(self, fname, translations, digest=None): + res = super()._update_field_translations(fname, translations, digest) + # TBD the below check is only for model_term translations. + # Because for model translations, super().update_field_translations will call write to check + if self._unrestricted_rendering: + # If the rendering is unrestricted (e.g. mail.template), + # check the user is part of the mail editor group to modify a template if the template is dynamic + self._check_access_right_dynamic_template() + return res + # ------------------------------------------------------------ # TOOLS # ------------------------------------------------------------ diff --git a/addons/mail/models/template_reset_mixin.py b/addons/mail/models/template_reset_mixin.py index b03aa5362d5..9b7b59fce3d 100644 --- a/addons/mail/models/template_reset_mixin.py +++ b/addons/mail/models/template_reset_mixin.py @@ -11,7 +11,7 @@ from odoo.modules import get_module_resource from odoo.modules.module import get_resource_from_path, get_resource_path from odoo.tools.convert import xml_import from odoo.tools.misc import file_open -from odoo.tools.translate import TranslationFileReader +from odoo.tools.translate import TranslationFileReader, _trans_load_data class TemplateResetMixin(models.AbstractModel): @@ -62,10 +62,8 @@ class TemplateResetMixin(models.AbstractModel): def _override_translation_term(self, module_name, xml_ids): processed_base_langs = [] - trans_to_remove = [] for code, _ in self.env['res.lang'].get_installed(): lang_code = tools.get_iso_codes(code) - translation_processed = False # In case of sub languages (e.g fr_BE), load the base language first, (e.g fr.po) and # then load the main translation file (e.g fr_BE.po) @@ -76,28 +74,17 @@ class TemplateResetMixin(models.AbstractModel): if base_trans_file: if base_lang_code not in processed_base_langs: processed_base_langs.append(base_lang_code) - translation_processed = self._process_translation_data(base_trans_file, code, xml_ids) + self._process_translation_data(base_trans_file, code, xml_ids) # Step 2: reset translation file with main language file (can possibly override the # terms coming from the base language) trans_file = get_module_resource(module_name, 'i18n', lang_code + '.po') if trans_file: - translation_processed = self._process_translation_data(trans_file, code, xml_ids) + self._process_translation_data(trans_file, code, xml_ids) - # If no translation data available to update, unlink custom translated terms linked to template - if not translation_processed: - trans_to_remove.append(code) - - if trans_to_remove: - self.env['ir.translation'].search([ - ('name', 'like', f'{self._name},%'), - ('res_id', '=', self.id), - ('lang', 'in', trans_to_remove), - ('module', 'in', [module_name, False]), - ]).unlink() def _process_translation_data(self, trans_file, lang, xml_ids): - """Populates the ir_translation table. + """Load translations. :param trans_file: path to a translation file to open, e.g. {addon_path}/mail/i18n/es.po :param lang: language code of the translations contained in `trans_file` @@ -106,17 +93,9 @@ class TemplateResetMixin(models.AbstractModel): :return Boolean: True if translation data is available and processed, False otherwise """ with file_open(trans_file, mode='rb', filter_ext=(".po",)) as fileobj: - translation_processed = False reader = TranslationFileReader(fileobj) # Process a single PO entry - for row in reader: - if row.get('imd_name') in xml_ids: - translation_processed = True - # copy Translation from Source to Destination object - self.env['ir.translation']._set_ids( - row['name'], 'model', lang, self._ids, row.get('value', ''), row['src'], - ) - return translation_processed + _trans_load_data(self.env.cr, reader, lang, overwrite=True, force_overwrite=True, xml_ids=xml_ids) def reset_template(self): """Resets the Template with values given in source file. We ignore the case of diff --git a/addons/mail/tests/common.py b/addons/mail/tests/common.py index 26fbd22c4d3..d8061f99946 100644 --- a/addons/mail/tests/common.py +++ b/addons/mail/tests/common.py @@ -1117,84 +1117,11 @@ class MailCommon(common.TransactionCase, MailCase): # Make sure Spanish translations have not been altered if test_record: - description_translations = cls.env['ir.translation'].search([ - ('module', '=', 'test_mail'), - ('src', '=', test_record._description), - ('lang', '=', lang_code) - ]) - if description_translations: - description_translations.update({'value': 'Spanish description'}) - else: - description_translations.create({ - 'lang': lang_code, - 'module': 'test_mail', - 'name': 'ir.model,name', - 'res_id': cls.env['ir.model']._get_id(test_record._name), - 'src': test_record._description, - 'state': 'translated', - 'type': 'model', - 'value': 'Spanish description', - }) - - translations_tocreate = [] - # Have a TestStuff always available - test_stuff_translations = cls.env['ir.translation'].search([ - ('module', '=', 'test_mail'), - ('src', '=', 'TestStuff'), - ('lang', '=', lang_code) - ]) - if test_stuff_translations: - test_stuff_translations.update({'value': 'TestSpanishStuff'}) - else: - translations_tocreate.append({ - 'lang': lang_code, - 'name': 'idontknow', - 'module': 'test_mail', - 'res_id': False, - 'src': 'TestStuff', - 'state': 'translated', - 'type': 'code', - 'value': 'TestSpanishStuff', - }) - - view_translations = cls.env['ir.translation'].search([ - ('module', '=', 'mail'), - ('src', '=', 'View %s'), - ('lang', '=', lang_code) - ]) - if view_translations: - view_translations.update({'value': 'SpanishView'}) - else: - translations_tocreate.append({ - 'lang': lang_code, - 'name': 'idontknow', - 'module': 'mail', - 'res_id': False, - 'src': 'View %s', - 'state': 'translated', - 'type': 'code', - 'value': 'SpanishView %s', - }) + cls.env['ir.model']._get(test_record._name).with_context(lang=lang_code).name = 'Spanish description' # Prepare some translated value for template if given - if test_template: - translations_tocreate += [{ - 'lang': lang_code, - 'module': 'mail', - 'name': 'mail.template,subject', - 'res_id': test_template.id, - 'state': 'translated', - 'type': 'model', - 'value': 'SpanishSubject for {{ object.name }}', - }, { - 'lang': lang_code, - 'module': 'mail', - 'name': 'mail.template,body_html', - 'res_id': test_template.id, - 'state': 'translated', - 'type': 'model', - 'value': '

SpanishBody for

', - }] + test_template.with_context(lang=lang_code).subject = 'SpanishSubject for {{ object.name }}' + test_template.with_context(lang=lang_code).body_html = '

SpanishBody for

' # create a custom layout for email notification if not layout_arch_db: @@ -1233,17 +1160,11 @@ class MailCommon(common.TransactionCase, MailCase): 'name': 'test_layout', 'res_id': view.id }) - translations_tocreate.append({ - 'lang': lang_code, - 'module': 'mail', - 'name': 'ir.ui.view,arch_db', - 'res_id': view.id, - 'src': 'English Layout for', - 'state': 'translated', - 'type': 'model_terms', - 'value': 'Spanish Layout para', + view.update_field_translations('arch_db', { + lang_code: { + 'English Layout for': 'Spanish Layout para' + } }) - cls.env['ir.translation'].create(translations_tocreate) def _generate_attachments_data(self, count, res_model=None, res_id=None, attach_values=None): # attachment visibility depends on what they are attached to diff --git a/addons/mail/tests/test_mail_render.py b/addons/mail/tests/test_mail_render.py index 1c3c8202653..7a601f87a35 100644 --- a/addons/mail/tests/test_mail_render.py +++ b/addons/mail/tests/test_mail_render.py @@ -112,22 +112,9 @@ class TestMailRender(common.MailCommon): }) # some translations - cls.env['ir.translation'].create({ - 'type': 'model', - 'name': 'mail.template,subject', - 'lang': 'fr_FR', - 'res_id': cls.test_template.id, - 'src': cls.test_template.subject, - 'value': cls.base_qweb_bits_fr[0], - }) - cls.env['ir.translation'].create({ - 'type': 'model', - 'name': 'mail.template,body_html', - 'lang': 'fr_FR', - 'res_id': cls.test_template.id, - 'src': cls.test_template.body_html, - 'value': cls.base_qweb_bits_fr[1], - }) + cls.test_template.with_context(lang='fr_FR').subject = cls.base_qweb_bits_fr[0] + cls.test_template.with_context(lang='fr_FR').body_html = cls.base_qweb_bits_fr[1] + cls.env['ir.model.data'].create({ 'name': 'test_template_xmlid', 'module': 'mail', diff --git a/addons/mail/tests/test_mail_template.py b/addons/mail/tests/test_mail_template.py index 6d7d50df4fe..75ab522fad7 100644 --- a/addons/mail/tests/test_mail_template.py +++ b/addons/mail/tests/test_mail_template.py @@ -107,62 +107,29 @@ class TestMailTemplate(MailCommon): 'body_html': '

foo

', }) - Translation = self.env['ir.translation'] - ### check qweb dynamic - Translation.insert_missing(employee_template._fields['body_html'], employee_template) - employee_translations_of_body = Translation.with_user(self.user_employee).search( - [('res_id', '=', employee_template.id), ('name', '=', 'mail.template,body_html'), ('lang', '=', 'fr_FR')], - limit=1 - ) - # keep a copy to create new translation later - body_translation_vals = employee_translations_of_body.read([])[0] - # write on translation for template without dynamic code is allowed - employee_translations_of_body.value = 'non-qweb' + employee_template.with_context(lang='fr_FR').body_html = 'non-qweb' # cannot write dynamic code on mail_template translation for employee without the group mail_template_editor. with self.assertRaises(AccessError): - employee_translations_of_body.value = '' + employee_template.with_context(lang='fr_FR').body_html = '' - employee_translations_of_body.unlink() # delete old translation, to test the creation now - body_translation_vals['value'] = '

' - - # admin can create - new = Translation.create(body_translation_vals) - new.unlink() - - # Employee without mail_template_editor group cannot create dynamic translation for mail.render.mixin - with self.assertRaises(AccessError): - Translation.with_user(self.user_employee).create(body_translation_vals) + employee_template.with_context(lang='fr_FR').sudo().body_html = '' + # reset the body_html to static + employee_template.body_html = False + employee_template.body_html = '

foo

' ### check qweb inline dynamic - Translation.insert_missing(employee_template._fields['subject'], employee_template) - employee_translations_of_subject = Translation.with_user(self.user_employee).search( - [('res_id', '=', employee_template.id), ('name', '=', 'mail.template,subject'), ('lang', '=', 'fr_FR')], - limit=1 - ) - # keep a copy to create new translation later - subject_translation_vals = employee_translations_of_subject.read([])[0] - # write on translation for template without dynamic code is allowed - employee_translations_of_subject.value = 'non-qweb' + employee_template.with_context(lang='fr_FR').subject = 'non-qweb' # cannot write dynamic code on mail_template translation for employee without the group mail_template_editor. with self.assertRaises(AccessError): - employee_translations_of_subject.value = '{{ object.foo }}' + employee_template.with_context(lang='fr_FR').subject = '{{ object.foo }}' - employee_translations_of_subject.unlink() # delete old translation, to test the creation now - subject_translation_vals['value'] = '{{ object.foo }}' - - # admin can create - new = Translation.create(subject_translation_vals) - new.unlink() - - # Employee without mail_template_editor group cannot create dynamic translation for mail.render.mixin - with self.assertRaises(AccessError): - Translation.with_user(self.user_employee).create(subject_translation_vals) + employee_template.with_context(lang='fr_FR').sudo().subject = '{{ object.foo }}' def test_server_archived_usage_protection(self): """ Test the protection against using archived server (servers used cannot be archived) """ diff --git a/addons/privacy_lookup/wizard/privacy_lookup_wizard.py b/addons/privacy_lookup/wizard/privacy_lookup_wizard.py index 1be671a4b25..4d362285ee5 100644 --- a/addons/privacy_lookup/wizard/privacy_lookup_wizard.py +++ b/addons/privacy_lookup/wizard/privacy_lookup_wizard.py @@ -131,6 +131,8 @@ class PrivacyLookupWizard(models.TransientModel): rec_name = model._rec_name or 'name' is_normalized = field_name == 'email_normalized' or (model_name == 'mailing.trace' and field_name == 'email') if rec_name in model and model._fields[model._rec_name].type == 'char': + if model._fields[model._rec_name].translate: + rec_name = f"jsonb_path_query_array({rec_name}, '$.*')::text" additional_query += """ {field_name} {search_type} %s OR {rec_name} ilike %s """.format( diff --git a/addons/purchase_stock/tests/test_reordering_rule.py b/addons/purchase_stock/tests/test_reordering_rule.py index 9e934fb1c3b..028a0ca1fec 100644 --- a/addons/purchase_stock/tests/test_reordering_rule.py +++ b/addons/purchase_stock/tests/test_reordering_rule.py @@ -576,8 +576,8 @@ class TestReorderingRule(TransactionCase): ])], }) self.env['res.lang']._activate_lang('fr_FR') - self.env['ir.translation']._set_ids('product.template,name', 'model', 'fr_FR', product.product_tmpl_id.ids, 'produit en français') - self.env['ir.translation']._set_ids('product.product,name', 'model', 'fr_FR', product.ids, 'produit en français') + product.product_tmpl_id.with_context(lang='fr_FR').name = 'produit en français' + product.with_context(lang='fr_FR').name = 'produit en français' default_vendor = self.env["res.partner"].create({ "name": "Supplier A", }) diff --git a/addons/sale_management/models/sale_order_template.py b/addons/sale_management/models/sale_order_template.py index 34369d1271a..648880d8d16 100644 --- a/addons/sale_management/models/sale_order_template.py +++ b/addons/sale_management/models/sale_order_template.py @@ -94,33 +94,7 @@ class SaleOrderTemplate(models.Model): for lang in languages: for line in self.sale_order_template_line_ids: if line.name == line.product_id.get_product_multiline_description_sale(): - self.create_or_update_translations( - model_name='sale.order.template.line,name', lang_code=lang.code, - res_id=line.id, src=line.name, - value=line.product_id.with_context(lang=lang.code).get_product_multiline_description_sale()) + line.with_context(lang=lang.code).name = line.product_id.with_context(lang=lang.code).get_product_multiline_description_sale() for option in self.sale_order_template_option_ids: if option.name == option.product_id.get_product_multiline_description_sale(): - self.create_or_update_translations( - model_name='sale.order.template.option,name', lang_code=lang.code, - res_id=option.id, src=option.name, - value=option.product_id.with_context(lang=lang.code).get_product_multiline_description_sale()) - - def create_or_update_translations(self, model_name, lang_code, res_id, src, value): - data = { - 'type': 'model', - 'name': model_name, - 'lang': lang_code, - 'res_id': res_id, - 'src': src, - 'value': value, - 'state': 'inprogress', - } - existing_trans = self.env['ir.translation'].search([ - ('name', '=', model_name), - ('res_id', '=', res_id), - ('lang', '=', lang_code) - ]) - if not existing_trans: - self.env['ir.translation'].create(data) - else: - existing_trans.write(data) + option.with_context(lang=lang.code).name = option.product_id.with_context(lang=lang.code).get_product_multiline_description_sale() diff --git a/addons/test_mail/tests/test_message_post.py b/addons/test_mail/tests/test_message_post.py index 7341c9fd44a..56ddcbf4a2b 100644 --- a/addons/test_mail/tests/test_message_post.py +++ b/addons/test_mail/tests/test_message_post.py @@ -19,6 +19,7 @@ from odoo.exceptions import AccessError from odoo.tests import tagged from odoo.tools import mute_logger, formataddr from odoo.tests.common import users +from odoo.tools.translate import code_translations class TestMessagePostCommon(TestMailCommon, TestRecipients): @@ -1240,6 +1241,12 @@ class TestMessagePostLang(TestMailCommon, TestRecipients): @users('employee') @mute_logger('odoo.addons.mail.models.mail_mail') def test_layout_email_lang_context(self): + # hack into code translations: + code_translations.python_translations[('test_mail', 'es_ES')] = {'TestStuff': 'TestSpanishStuff'} + self.addCleanup(code_translations.python_translations.pop, ('test_mail', 'es_ES')) + code_translations.python_translations[('mail', 'es_ES')] = {'View %s': 'SpanishView %s'} + self.addCleanup(code_translations.python_translations.pop, ('mail', 'es_ES')) + test_records = self.test_records.with_user(self.env.user).with_context(lang='es_ES') test_records[1].message_subscribe(self.partner_2.ids) diff --git a/addons/test_mail_sms/tests/test_sms_composer.py b/addons/test_mail_sms/tests/test_sms_composer.py index 3c54a009c32..03ed57fccaf 100644 --- a/addons/test_mail_sms/tests/test_sms_composer.py +++ b/addons/test_mail_sms/tests/test_sms_composer.py @@ -415,14 +415,7 @@ class TestSMSComposerMass(TestSMSCommon): def test_composer_mass_active_ids_w_template_and_lang(self): self.env['res.lang']._activate_lang('fr_FR') - self.env['ir.translation'].create({ - 'type': 'model', - 'name': 'sms.template,body', - 'lang': 'fr_FR', - 'res_id': self.sms_template.id, - 'src': self.sms_template.body, - 'value': 'Cher·e· {{ object.display_name }} ceci est un SMS.', - }) + self.sms_template.with_context(lang='fr_FR').body = 'Cher·e· {{ object.display_name }} ceci est un SMS.' # set template to try to use customer lang self.sms_template.write({ 'lang': '{{ object.customer_id.lang }}', @@ -481,14 +474,7 @@ class TestSMSComposerMass(TestSMSCommon): and the usage with the sms composer """ # Create the lang info self.env['res.lang']._activate_lang('fr_FR') - self.env['ir.translation'].create({ - 'type': 'model', - 'name': 'sms.template,body', - 'lang': 'fr_FR', - 'res_id': self.sms_template.id, - 'src': self.sms_template.body, - 'value': "Hello {{ object.display_name }} ceci est en français.", - }) + self.sms_template.with_context(lang='fr_FR').body = 'Hello {{ object.display_name }} ceci est en français.' # set template to try to use customer lang self.sms_template.write({ 'lang': '{{ object.customer_id.lang }}', diff --git a/addons/test_mail_sms/tests/test_sms_template.py b/addons/test_mail_sms/tests/test_sms_template.py index beebc78124a..225a95fdda3 100644 --- a/addons/test_mail_sms/tests/test_sms_template.py +++ b/addons/test_mail_sms/tests/test_sms_template.py @@ -29,13 +29,8 @@ class TestSmsTemplate(TestSMSCommon, TestSMSRecipients): def test_sms_template_lang(self): self.env['res.lang']._activate_lang('fr_FR') self.user_admin.write({'lang': 'en_US'}) - self.env['ir.translation'].create({ - 'type': 'model', - 'name': 'sms.template,body', - 'lang': 'fr_FR', - 'res_id': self.sms_template.id, - 'src': self.sms_template.body, - 'value': self.body_fr, + self.sms_template.update_field_translations('body', { + 'fr_FR': self.body_fr }) # set template to try to use customer lang self.sms_template.write({ diff --git a/addons/test_website/tests/test_is_multilang.py b/addons/test_website/tests/test_is_multilang.py index 167256c15bf..22167234d01 100644 --- a/addons/test_website/tests/test_is_multilang.py +++ b/addons/test_website/tests/test_is_multilang.py @@ -38,21 +38,10 @@ class TestIsMultiLang(odoo.tests.HttpCase): website.domain = self.base_url() # for _is_canonical_url website.default_lang_id = en website.language_ids = en + it + be - params = { - 'src': country1.name, - 'value': country1.name + ' Italia', - 'type': 'model', - 'name': 'res.country,name', - 'res_id': country1.id, - 'lang': it.code, - 'state': 'translated', - } - self.env['ir.translation'].create(params) - params.update({ - 'value': country1.name + ' Belgium', - 'lang': be.code, + country1.update_field_translations('name', { + it.code: country1.name + ' Italia', + be.code: country1.name + ' Belgium' }) - self.env['ir.translation'].create(params) r = self.url_open(f'/test_lang_url/{country1.id}') self.assertEqual(r.status_code, 200) diff --git a/addons/website/models/ir_module_module.py b/addons/website/models/ir_module_module.py index 24ed8667643..69988752211 100644 --- a/addons/website/models/ir_module_module.py +++ b/addons/website/models/ir_module_module.py @@ -3,7 +3,7 @@ import logging import os -from collections import OrderedDict +from collections import defaultdict, OrderedDict from odoo import api, fields, models from odoo.addons.base.models.ir_model import MODULE_UNINSTALL_FLAG @@ -188,14 +188,31 @@ class IrModuleModule(models.Model): def _post_copy(self, old_rec, new_rec): self.ensure_one() translated_fields = self._theme_translated_fields.get(old_rec._name, []) + cur_lang = self.env.lang or 'en_US' + old_rec.flush_recordset() for (src_field, dst_field) in translated_fields: - self._cr.execute("""INSERT INTO ir_translation (lang, src, name, res_id, state, value, type, module) - SELECT t.lang, t.src, %s, %s, t.state, t.value, t.type, t.module - FROM ir_translation t - WHERE name = %s - AND res_id = %s - ON CONFLICT DO NOTHING""", - (dst_field, new_rec.id, src_field, old_rec.id)) + __, src_fname = src_field.split(',') + dst_mname, dst_fname = dst_field.split(',') + if dst_mname != new_rec._name: + continue + old_field = old_rec._fields[src_fname] + old_translations = old_field._get_stored_translations(old_rec) + if not old_translations: + continue + if not callable(old_field.translate): + if old_rec[src_fname] == new_rec[dst_fname]: + new_rec.update_field_translations(dst_fname, old_translations) + else: + old_translation_lang = old_translations.get(cur_lang) or old_translations.get('en_US') + # {from_lang_term: {lang: to_lang_term} + translation_dictionary = old_field.get_translation_dictionary(old_translation_lang, { + lang: value for lang, value in old_translations.items() if lang != cur_lang}) + # {lang: {old_term: new_term} + translations = defaultdict(dict) + for from_lang_term, to_lang_terms in translation_dictionary.items(): + for lang, to_lang_term in to_lang_terms.items(): + translations[lang][from_lang_term] = to_lang_term + new_rec.with_context(install_filename='dummy').update_field_translations(dst_fname, translations) def _theme_load(self, website): """ diff --git a/addons/website/models/website.py b/addons/website/models/website.py index 3114f59fe12..61ef236875a 100644 --- a/addons/website/models/website.py +++ b/addons/website/models/website.py @@ -1379,12 +1379,13 @@ class Website(models.Model): def _get_cached(self, field): return self._get_cached_values()[field] - def _get_html_fields(self): - html_fields = [('ir_ui_view', 'arch_db')] + def _get_html_fields_attributes(self): + html_fields = [('ir_ui_view', 'arch_db', True)] cr = self.env.cr cr.execute(r""" SELECT f.model, - f.name + f.name, + f.translate FROM ir_model_fields f JOIN ir_model m ON m.id = f.model_id @@ -1394,10 +1395,10 @@ class Website(models.Model): AND f.model NOT LIKE 'ir.actions%' AND f.model != 'mail.message' """) - for model, name in cr.fetchall(): + for model, name, translate in cr.fetchall(): table = self.env[model]._table if tools.table_exists(cr, table) and tools.column_exists(cr, table, name): - html_fields.append((table, name)) + html_fields.append((table, name, translate)) return html_fields def _get_snippets_assets(self): @@ -1428,7 +1429,7 @@ class Website(models.Model): """) return self.env.cr.fetchall() - def _is_snippet_used(self, snippet_module, snippet_id, asset_version, asset_type, html_fields): + def _is_snippet_used(self, snippet_module, snippet_id, asset_version, asset_type, html_fields_attributes): snippet_occurences = [] # Check snippet template definition to avoid disabling its related assets. # This special case is needed because snippet template definitions do not @@ -1440,11 +1441,12 @@ class Website(models.Model): # As well as every snippet dropped in html fields self.env.cr.execute(sql.SQL(" UNION ").join( - sql.SQL("SELECT regexp_matches({}, {}, 'g') FROM {}").format( + sql.SQL("SELECT regexp_matches({}{}, {}, 'g') FROM {}").format( sql.Identifier(column), + sql.SQL("->>'en_US'" if translate else ''), sql.Placeholder('snippet_regex'), sql.Identifier(table) - ) for table, column in html_fields + ) for table, column, translate in html_fields_attributes ), {'snippet_regex': f'<([^>]*data-snippet="{snippet_id}"[^>]*)>'}) results = self.env.cr.fetchall() for r in results: @@ -1461,10 +1463,10 @@ class Website(models.Model): def _disable_unused_snippets_assets(self): snippets_assets = self._get_snippets_assets() - html_fields = self._get_html_fields() + html_fields_attributes = self._get_html_fields_attributes() for snippet_module, snippet_id, asset_version, asset_type, _asset_id in snippets_assets: - is_snippet_used = self._is_snippet_used(snippet_module, snippet_id, asset_version, asset_type, html_fields) + is_snippet_used = self._is_snippet_used(snippet_module, snippet_id, asset_version, asset_type, html_fields_attributes) # The regex catches XXX.scss, XXX.js and XXX_variables.scss assets_regex = f'{snippet_id}/{asset_version}.+{asset_type}' @@ -1652,6 +1654,7 @@ class Website(models.Model): """ match_pattern = r'[\w-]{%s,}' % min(4, len(search) - 3) similarity_threshold = 0.3 + lang = self.env.lang or 'en_US' for search_detail in search_details: model_name, fields = search_detail['model'], search_detail['search_fields'] model = self.env[model_name] @@ -1668,6 +1671,11 @@ class Website(models.Model): field=unaccent(sql.SQL("{table}.{field}").format( table=sql.Identifier((self.env['ir.ui.view'] if field == 'arch_db' or (field == 'name' and 'arch_db' in fields) else model)._table), field=sql.Identifier(field) + )) if not (self.env['ir.ui.view'] if field == 'arch_db' or (field == 'name' and 'arch_db' in fields) else model)._fields[field].translate else + unaccent(sql.SQL("COALESCE({table}.{field}->>{lang}, {table}.{field}->>'en_US')").format( + table=sql.Identifier((self.env['ir.ui.view'] if field == 'arch_db' or (field == 'name' and 'arch_db' in fields) else model)._table), + field=sql.Identifier(field), + lang=sql.Literal(lang) )) ) for field in fields] best_similarity = sql.SQL('GREATEST({similarities})').format( @@ -1698,55 +1706,6 @@ class Website(models.Model): ) self.env.cr.execute(query, {'search': search}) ids = {row[0] for row in self.env.cr.fetchall() if row[1] and row[1] >= similarity_threshold} - if self.env.lang: - # Specific handling for website.page that inherits its arch_db and name fields - # TODO make more generic - if 'arch_db' in fields: - # Look for partial translations - similarity = sql.SQL("word_similarity({search}, {field})").format( - search=unaccent(sql.Placeholder('search')), - field=unaccent(sql.SQL('t.value')) - ) - names = ['%s,%s' % (self.env['ir.ui.view']._name, field) for field in fields] - query = sql.SQL(""" - SELECT {table}.id, {similarity} AS _similarity - FROM {table} - LEFT JOIN ir_ui_view v ON {table}.view_id = v.id - LEFT JOIN ir_translation t ON v.id = t.res_id - WHERE t.lang = {lang} - AND t.name = ANY({names}) - AND t.type = 'model_terms' - AND t.value IS NOT NULL - ORDER BY _similarity desc - LIMIT 1000 - """).format( - table=sql.Identifier(model._table), - similarity=similarity, - lang=sql.Placeholder('lang'), - names=sql.Placeholder('names'), - ) - else: - similarity = sql.SQL("word_similarity({search}, {field})").format( - search=unaccent(sql.Placeholder('search')), - field=unaccent(sql.SQL('value')) - ) - names = ['%s,%s' % (model._name, field) for field in fields] - query = sql.SQL(""" - SELECT res_id, {similarity} AS _similarity - FROM ir_translation - WHERE lang = {lang} - AND name = ANY({names}) - AND type = 'model' - AND value IS NOT NULL - ORDER BY _similarity desc - LIMIT 1000 - """).format( - similarity=similarity, - lang=sql.Placeholder('lang'), - names=sql.Placeholder('names'), - ) - self.env.cr.execute(query, {'lang': self.env.lang, 'names': names, 'search': search}) - ids.update(row[0] for row in self.env.cr.fetchall() if row[1] and row[1] >= similarity_threshold) domain.append([('id', 'in', list(ids))]) domain = AND(domain) records = model.search_read(domain, fields, limit=limit) diff --git a/addons/website/models/website_page.py b/addons/website/models/website_page.py index 2a0945456d4..eeb9139dc9d 100644 --- a/addons/website/models/website_page.py +++ b/addons/website/models/website_page.py @@ -231,28 +231,18 @@ class Page(models.Model): # Perform search in translations # TODO Remove when domains will support xml_translate fields query = sql.SQL(""" - SELECT {table}.{id} + SELECT {table}.id FROM {table} - LEFT JOIN ir_ui_view v ON {table}.{view_id} = v.{id} - LEFT JOIN ir_translation t ON v.{id} = t.{res_id} - WHERE t.lang = {lang} - AND t.name = ANY({names}) - AND t.type = 'model_terms' - AND t.value ilike {search} + LEFT JOIN ir_ui_view v ON {table}.view_id = v.id + WHERE v.name ILIKE {search} + OR jsonb_path_query_array(v.arch_db,'$.*')::text ILIKE {search} LIMIT {limit} """).format( table=sql.Identifier(self._table), - id=sql.Identifier('id'), - view_id=sql.Identifier('view_id'), - res_id=sql.Identifier('res_id'), - lang=sql.Placeholder('lang'), - names=sql.Placeholder('names'), search=sql.Placeholder('search'), limit=sql.Placeholder('limit'), ) self.env.cr.execute(query, { - 'lang': self.env.lang, - 'names': ['ir.ui.view,arch_db', 'ir.ui.view,name'], 'search': '%%%s%%' % escape_psql(search), 'limit': limit, }) diff --git a/addons/website/tests/test_configurator.py b/addons/website/tests/test_configurator.py index 641c7406b0d..5cf817e609a 100644 --- a/addons/website/tests/test_configurator.py +++ b/addons/website/tests/test_configurator.py @@ -56,11 +56,10 @@ class TestConfiguratorCommon(odoo.tests.HttpCase): class TestConfiguratorTranslation(TestConfiguratorCommon): def test_01_configurator_translation(self): - with mute_logger('odoo.addons.base.models.ir_translation'): - self.env["base.language.install"].create({ - 'overwrite': True, - 'lang_ids': [(6, 0, [self.env.ref('base.lang_fr').id])], - }).lang_install() + self.env["base.language.install"].create({ + 'overwrite': True, + 'lang_ids': [(6, 0, [self.env.ref('base.lang_fr').id])], + }).lang_install() feature = self.env['website.configurator.feature'].search([('name', '=', 'Privacy Policy')]) feature.with_context(lang='fr_FR').write({'name': 'Politique de confidentialité'}) self.env.ref('base.user_admin').write({'lang': self.env.ref('base.lang_fr').code}) diff --git a/addons/website/tests/test_controllers.py b/addons/website/tests/test_controllers.py index 2b9acdf929a..ae7a843c291 100644 --- a/addons/website/tests/test_controllers.py +++ b/addons/website/tests/test_controllers.py @@ -20,6 +20,7 @@ class TestControllers(tests.HttpCase): base_url = self.env['ir.config_parameter'].sudo().get_param('web.base.url') suggested_links_url = base_url + '/website/get_suggested_links' + old_pages = Page for i in range(0, 10): new_page = Page.create({ 'name': 'Generic', @@ -32,11 +33,14 @@ class TestControllers(tests.HttpCase): 'is_published': True, }) if i % 2 == 0: - # mark as old - new_page._write({'write_date': '2020-01-01'}) + old_pages += new_page else: last_5_url_edited.append(new_page.url) + self.opener.post(url=suggested_links_url, json={'params': {'needle': '/'}}) + # mark as old + old_pages._write({'write_date': '2020-01-01'}) + res = self.opener.post(url=suggested_links_url, json={'params': {'needle': '/'}}) resp = json.loads(res.content) assert 'result' in resp diff --git a/addons/website/tests/test_disable_unused_snippets_assets.py b/addons/website/tests/test_disable_unused_snippets_assets.py index 4be813cb03d..87a9d0f2f1f 100644 --- a/addons/website/tests/test_disable_unused_snippets_assets.py +++ b/addons/website/tests/test_disable_unused_snippets_assets.py @@ -63,9 +63,11 @@ class TestDisableSnippetsAssets(TransactionCase): self.homepage.write({ 'arch_db': HOMEPAGE_UP_TO_DATE, }) + self.homepage.flush_recordset() self.mega_menu.write({ 'mega_menu_content': MEGA_MENU_OUTDATED, }) + self.mega_menu.flush_recordset() self.Website._disable_unused_snippets_assets() s_website_form_000_scss = self._get_snippet_asset('s_website_form', '000', 'scss') diff --git a/addons/website/tests/test_menu.py b/addons/website/tests/test_menu.py index eb0e721292b..b391304be45 100644 --- a/addons/website/tests/test_menu.py +++ b/addons/website/tests/test_menu.py @@ -69,7 +69,7 @@ class TestMenu(common.TransactionCase): self.assertEqual(total_menus + 4, Menu.search_count([]), "New website's bootstraping should have duplicate default menu tree (Top/Home/Contactus/Sub Default Menu)") def test_04_specific_menu_translation(self): - Translation = self.env['ir.translation'] + IrModuleModule = self.env['ir.module.module'] Menu = self.env['website.menu'] existing_menus = Menu.search([]) @@ -85,9 +85,6 @@ class TestMenu(common.TransactionCase): # create fr_FR translation for template menu self.env.ref('base.lang_fr').active = True template_menu.with_context(lang='fr_FR').name = 'Menu en français' - Translation.search([ - ('name', '=', 'website.menu,name'), ('res_id', '=', template_menu.id), - ]).module = 'website' self.assertEqual(specific1.name, 'Menu in english', 'Translating template menu does not translate specific menu') @@ -95,7 +92,7 @@ class TestMenu(common.TransactionCase): specific1.name = 'Menu in french' # loading translation add missing specific translation - Translation._load_module_terms(['website'], ['fr_FR']) + IrModuleModule._load_module_terms(['website'], ['fr_FR']) Menu.invalidate_model(['name']) self.assertEqual(specific1.name, 'Menu in french', 'Load translation without overwriting keep existing translation') @@ -103,7 +100,7 @@ class TestMenu(common.TransactionCase): 'Load translation add missing translation from template menu') # loading translation with overwrite sync all translations from menu template - Translation._load_module_terms(['website'], ['fr_FR'], overwrite=True) + IrModuleModule._load_module_terms(['website'], ['fr_FR'], overwrite=True) Menu.invalidate_model(['name']) self.assertEqual(specific1.name, 'Menu en français', 'Load translation with overwriting update existing menu from template') diff --git a/addons/website/tests/test_views.py b/addons/website/tests/test_views.py index 1dcdeb807f2..a039f42b219 100644 --- a/addons/website/tests/test_views.py +++ b/addons/website/tests/test_views.py @@ -995,29 +995,25 @@ class TestCowViewSaving(TestViewSavingCommon): View.pool._init = original_pool_init def test_specific_view_translation(self): - Translation = self.env['ir.translation'] - - Translation.insert_missing(self.base_view._fields['arch_db'], self.base_view) - translation = Translation.search([ - ('res_id', '=', self.base_view.id), ('name', '=', 'ir.ui.view,arch_db') - ]) - translation.value = 'hello' - translation.module = 'website' - + self.env['res.lang']._activate_lang('fr_BE') + self.base_view.with_context(lang='en_US').arch_db = '
hello
' + self.base_view.update_field_translations('arch_db', {'fr_BE': {'hello': 'bonjour'}}) + self.assertEqual(self.base_view.with_context(lang='fr_BE').arch, '
bonjour
') self.base_view.with_context(website_id=1).write({'active': True}) specific_view = self.base_view._get_specific_views() - self.base_view - self.assertEqual(specific_view.with_context(lang='en_US').arch, '
hello
', + self.assertEqual(specific_view.with_context(lang='fr_BE').arch, '
bonjour
', "copy on write (COW) also copy existing translations") - translation.value = 'hi' - self.assertEqual(specific_view.with_context(lang='en_US').arch, '
hello
', + self.base_view.update_field_translations('arch_db', {'fr_BE': {'bonjour': 'salut'}}) + self.assertEqual(self.base_view.with_context(lang='fr_BE').arch, '
salut
') + self.assertEqual(specific_view.with_context(lang='fr_BE').arch, '
bonjour
', "updating translation of base view doesn't update specific view") - Translation._load_module_terms(['website'], ['en_US'], overwrite=True) + self.env['ir.module.module']._load_module_terms(['website'], ['en_US', 'fr_BE'], overwrite=True) specific_view.invalidate_model(['arch_db', 'arch']) - self.assertEqual(specific_view.with_context(lang='en_US').arch, '
hi
', + self.assertEqual(specific_view.with_context(lang='fr_BE').arch, '
salut
', "loading module translation copy translation from base to specific view") def test_soc_complete_flow(self): diff --git a/addons/website_blog/tests/test_performance.py b/addons/website_blog/tests/test_performance.py index 164656e44c2..b05c8b8a4f1 100644 --- a/addons/website_blog/tests/test_performance.py +++ b/addons/website_blog/tests/test_performance.py @@ -14,7 +14,7 @@ class TestBlogPerformance(UtilPerf): def test_10_perf_sql_blog_standard_data(self): self.assertEqual(self._get_url_hot_query('/blog'), 11) - self.assertEqual(self._get_url_hot_query('/blog', cache=False), 27) + self.assertEqual(self._get_url_hot_query('/blog', cache=False), 24) def test_20_perf_sql_blog_bigger_data_scaling(self): BlogPost = self.env['blog.post'] @@ -27,9 +27,9 @@ class TestBlogPerformance(UtilPerf): blog_post.tag_ids += blog_tags blog_tags = blog_tags[:-1] self.assertEqual(self._get_url_hot_query('/blog'), 11) - self.assertEqual(self._get_url_hot_query('/blog', cache=False), 34) - self.assertEqual(self._get_url_hot_query(blog_post[0].website_url), 19) - self.assertEqual(self._get_url_hot_query(blog_post[0].website_url, cache=False), 22) + self.assertEqual(self._get_url_hot_query('/blog', cache=False), 31) + self.assertEqual(self._get_url_hot_query(blog_post[0].website_url), 16) + self.assertEqual(self._get_url_hot_query(blog_post[0].website_url, cache=False), 18) def test_30_perf_sql_blog_bigger_data_scaling(self): BlogPost = self.env['blog.post'] diff --git a/odoo/addons/test_translation_import/__manifest__.py b/odoo/addons/test_translation_import/__manifest__.py index 5a80ba01f7f..85aeefdbb7c 100644 --- a/odoo/addons/test_translation_import/__manifest__.py +++ b/odoo/addons/test_translation_import/__manifest__.py @@ -7,7 +7,8 @@ 'depends': ['base'], 'data': [ 'security/ir.model.access.csv', - 'view.xml' + 'view.xml', + 'data/test_translation_import_data.xml' ], 'installable': True, 'assets': { diff --git a/odoo/addons/test_translation_import/data/test_translation_import_data.xml b/odoo/addons/test_translation_import/data/test_translation_import_data.xml new file mode 100644 index 00000000000..4e8678b376f --- /dev/null +++ b/odoo/addons/test_translation_import/data/test_translation_import_data.xml @@ -0,0 +1,7 @@ + + + + Tableware +
Knife
Spoon
+
+
diff --git a/odoo/addons/test_translation_import/i18n/dot.csv b/odoo/addons/test_translation_import/i18n/dot.csv index 5c4fc6ae34d..f39f2e152e8 100644 --- a/odoo/addons/test_translation_import/i18n/dot.csv +++ b/odoo/addons/test_translation_import/i18n/dot.csv @@ -1,2 +1,5 @@ module,type,name,res_id,src,value,comments -test_translation_import,code,addons/test_translation_import/models.py,20,Accounting,samva, +test_translation_import,model,"test.translation.import.model1,name",test_translation_import.test_translation_import_model1_record1,Tableware,"Tableware, Dot" +test_translation_import,model_term,"test.translation.import.model1,html",test_translation_import.test_translation_import_model1_record1,Fork,"Fork, Dot" +test_translation_import,model_term,"test.translation.import.model1,html",test_translation_import.test_translation_import_model1_record1,Spoon,"Spoon, Dot" +test_translation_import,model_term,"test.translation.import.model1,html",test_translation_import.test_translation_import_model1_record1,Knife,"Kinfe, Dot" diff --git a/odoo/addons/test_translation_import/i18n/fr.po b/odoo/addons/test_translation_import/i18n/fr.po index 7d888109b76..9b099908c6d 100644 --- a/odoo/addons/test_translation_import/i18n/fr.po +++ b/odoo/addons/test_translation_import/i18n/fr.po @@ -1,80 +1,169 @@ -# This is a test PO file, not a true french translation. -# See the POT file for further information. +# Translation of Odoo Server. +# This file contains the translation of the following modules: +# * test_translation_import +# msgid "" msgstr "" -"Project-Id-Version: OpenERP Server 6.1\n" +"Project-Id-Version: Odoo Server 15.5alpha1+e\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" +"POT-Creation-Date: 2022-07-01 12:46+0000\n" +"PO-Revision-Date: 2022-07-01 12:46+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" -# Note: there is normally an additional line: -# #: code:addons/test_translation_import/models.py:15 -# This line is present in the POT and removed here to test the translation -# import behavior. #. module: test_translation_import -#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import__name +#: code:addons/test_translation_import/models/models.py:0 #, python-format -msgid "1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB" -msgstr "1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB in french" +msgid "Code Lazy, English" +msgstr "" #. module: test_translation_import -#: model:ir.model.fields.selection,name:test_translation_import.selection__test_translation_import__import_type__bar -msgid "Bar Import Type" -msgstr "Bar in french" - -# Note: the line in pot is 21 and is 14 here -#. module: test_translation_import -#: code:addons/test_translation_import/models.py:14 +#: code:addons/test_translation_import/models/models.py:0 #, python-format -msgid "Ijkl" -msgstr "Ijkl in french" +msgid "Code, %(num)s, %(symbol)s, English" +msgstr "" #. module: test_translation_import -#: model:ir.model.fields.selection,name:test_translation_import.selection__test_translation_import__import_type__foo -msgid "Foo Import Type" -msgstr "Foo in french" +#: code:addons/test_translation_import/models/models.py:0 +#, python-format +msgid "Code, %s, English" +msgstr "" #. module: test_translation_import -#: model:ir.model.fields,help:test_translation_import.field_test_translation_import__name -msgid "Efgh" -msgstr "Efgh in french" +#: code:addons/test_translation_import/models/models.py:0 +#, python-format +msgid "Code, English" +msgstr "Code, Français" #. module: test_translation_import -#: model:ir.model,name:test_translation_import.model_test_translation_import -msgid "Test: Translation Import" -msgstr "Test: Import de traductions" +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__create_uid +msgid "Created by" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__create_date +msgid "Created on" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__display_name +msgid "Display Name" +msgstr "" + +#. module: test_translation_import +#: model_terms:test.translation.import.model1,xml:test_translation_import.test_translation_import_model1_record1 +msgid "Fork" +msgstr "Fourchette" + +#. module: test_translation_import +#: model:ir.model.fields,help:test_translation_import.field_test_translation_import_model1__name +msgid "Help, English" +msgstr "Aider, Français" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__id +msgid "ID" +msgstr "" + +#. module: test_translation_import +#: model_terms:test.translation.import.model1,xml:test_translation_import.test_translation_import_model1_record1 +msgid "Knife" +msgstr "Couteau" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1____last_update +msgid "Last Modified on" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__write_date +msgid "Last Updated on" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__name +msgid "Name" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__selection +msgid "Selection" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields.selection,name:test_translation_import.selection__test_translation_import_model1__selection__bar +msgid "Selection Bar" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields.selection,name:test_translation_import.selection__test_translation_import_model1__selection__foo +msgid "Selection Foo" +msgstr "" + +#. module: test_translation_import +#: model_terms:test.translation.import.model1,xml:test_translation_import.test_translation_import_model1_record1 +msgid "Spoon" +msgstr "Cuillère" + +#. module: test_translation_import +#: model:test.translation.import.model1,name:test_translation_import.test_translation_import_model1_record1 +msgid "Tableware" +msgstr "Vaisselle" + +#. module: test_translation_import +#: model:ir.actions.act_window,name:test_translation_import.action_test_translation_import_model1 +#: model:ir.ui.menu,name:test_translation_import.menu_test_translation_import_model1 +msgid "Test translation import model1" +msgstr "" #. module: test_translation_import -#: model:ir.actions.act_window,name:test_translation_import.action_test_translation_import #: model:ir.ui.menu,name:test_translation_import.menu_test_translation_import -msgid "Test translation import" +msgid "Test translation model1" msgstr "Test translation import in french" #. module: test_translation_import -#: model:ir.ui.menu,name:test_translation_import.menu_test_translation -msgid "Test translation" -msgstr "Test translation in french" +#: model:ir.model,name:test_translation_import.model_test_translation_import_model1 +msgid "Translation Test 1" +msgstr "" #. module: test_translation_import -#: code:addons/base/models/arthur.py:42 -#: code:addons/base/models/arthur.py:43 -msgid "Test translation with two code lines" -msgstr "Test traduction avec deux lignes de code" +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__xml +msgid "XML" +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 "Test traduction avec un type code mais différent numéro de ligne dans le pot" +#. openerp-web +#: code:addons/test_translation_import/static/src/xml/js_templates.xml:0 +#, python-format +msgid "do export" +msgstr "" #. module: test_translation_import -#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import__other_name -#: code:addons/base/models/marvin.py:42 -#: code:addons/base/models/trillian.py:42 -msgid "Test translation with two code type and model" -msgstr "Test traduction avec deux type code et un model" +#. openerp-web +#: code:addons/test_translation_import/static/src/xml/js_templates.xml:0 +#, python-format +msgid "slot" +msgstr "" + +#. module: test_translation_import +#. openerp-web +#: code:addons/test_translation_import/static/src/xml/js_templates.xml:0 +#, python-format +msgid "slot 2" +msgstr "" + +#. module: test_translation_import +#. openerp-web +#: code:addons/test_translation_import/static/src/xml/js_templates.xml:0 +#, python-format +msgid "text node" +msgstr "" diff --git a/odoo/addons/test_translation_import/i18n/fr_BE.po b/odoo/addons/test_translation_import/i18n/fr_BE.po index 371d1f00606..20b4021ae52 100644 --- a/odoo/addons/test_translation_import/i18n/fr_BE.po +++ b/odoo/addons/test_translation_import/i18n/fr_BE.po @@ -1,12 +1,13 @@ -# This is a test PO file, not a true french translation. -# See the POT file for further information. +# This is a test PO file, not a complete one. It is manually maintained +# to test the import translation behavior of Odoo. + msgid "" msgstr "" -"Project-Id-Version: OpenERP Server 6.1\n" +"Project-Id-Version: Odoo Server 15.5alpha1+e\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" +"POT-Creation-Date: 2022-07-01 12:46+0000\n" +"PO-Revision-Date: 2022-07-01 12:46+0000\n" +"Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -14,22 +15,154 @@ msgstr "" "Plural-Forms: \n" #. module: test_translation_import -#: code:addons/test_translation_import/models.py:14 +#: code:addons/test_translation_import/models/models.py:0 #, 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: Import de traductions in belgian french" - -#. module: test_translation_import -#: model:ir.model.fields,help:test_translation_import.field_test_translation_import__name -msgid "Efgh" +msgid "Code Lazy, English" 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" +#: code:addons/test_translation_import/models/models.py:0 +#, python-format +msgid "Code, %(num)s, %(symbol)s, English" +msgstr "Code, %(num)s, %(symbol)s, Français, Belgium" + +#. module: test_translation_import +#: code:addons/test_translation_import/models/models.py:0 +#, python-format +msgid "Code, %s, English" +msgstr "Code, %s, Français, Belgium" + +#. module: test_translation_import +#: code:addons/test_translation_import/models/models.py:0 +#, python-format +msgid "Code, English" +msgstr "Code, Français, Belgium" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__create_uid +msgid "Created by" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__create_date +msgid "Created on" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__display_name +msgid "Display Name" +msgstr "" + +#. module: test_translation_import +#: model_terms:test.translation.import.model1,xml:test_translation_import.test_translation_import_model1_record1 +msgid "Fork" +msgstr "Fourchette, Belgium" + +#. module: test_translation_import +#: model:ir.model.fields,help:test_translation_import.field_test_translation_import_model1__name +msgid "Help, English" +msgstr "Aider, Français, Belgium" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__id +msgid "ID" +msgstr "" + +#. module: test_translation_import +#: model_terms:test.translation.import.model1,xml:test_translation_import.test_translation_import_model1_record1 +msgid "Knife" +msgstr "Couteau, Belgium" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1____last_update +msgid "Last Modified on" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__write_uid +msgid "Last Updated by" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__write_date +msgid "Last Updated on" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__name +msgid "Name" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__selection +msgid "Selection" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields.selection,name:test_translation_import.selection__test_translation_import_model1__selection__bar +msgid "Selection Bar" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields.selection,name:test_translation_import.selection__test_translation_import_model1__selection__foo +msgid "Selection Foo" +msgstr "" + +#. module: test_translation_import +#: model_terms:test.translation.import.model1,xml:test_translation_import.test_translation_import_model1_record1 +msgid "Spoon" +msgstr "Cuillère, Belgium" + +#. module: test_translation_import +#: model:test.translation.import.model1,name:test_translation_import.test_translation_import_model1_record1 +msgid "Tableware" +msgstr "Vaisselle, Belgium" + +#. module: test_translation_import +#: model:ir.actions.act_window,name:test_translation_import.action_test_translation_import_model1 +#: model:ir.ui.menu,name:test_translation_import.menu_test_translation_import_model1 +msgid "Test translation import model1" +msgstr "" + +#. module: test_translation_import +#: model:ir.ui.menu,name:test_translation_import.menu_test_translation_import +msgid "Test translation model1" +msgstr "" + +#. module: test_translation_import +#: model:ir.model,name:test_translation_import.model_test_translation_import_model1 +msgid "Translation Test 1" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__xml +msgid "XML" +msgstr "" + +#. module: test_translation_import +#. openerp-web +#: code:addons/test_translation_import/static/src/xml/js_templates.xml:0 +#, python-format +msgid "do export" +msgstr "" + +#. module: test_translation_import +#. openerp-web +#: code:addons/test_translation_import/static/src/xml/js_templates.xml:0 +#, python-format +msgid "slot" +msgstr "" + +#. module: test_translation_import +#. openerp-web +#: code:addons/test_translation_import/static/src/xml/js_templates.xml:0 +#, python-format +msgid "slot 2" +msgstr "" + +#. module: test_translation_import +#. openerp-web +#: code:addons/test_translation_import/static/src/xml/js_templates.xml:0 +#, python-format +msgid "text node" msgstr "" diff --git a/odoo/addons/test_translation_import/i18n/fr_CA.po b/odoo/addons/test_translation_import/i18n/fr_CA.po new file mode 100644 index 00000000000..04ac1beab61 --- /dev/null +++ b/odoo/addons/test_translation_import/i18n/fr_CA.po @@ -0,0 +1,50 @@ +# This is a test PO file, not a complete one. It is manually maintained +# to test the import translation behavior of Odoo. +msgid "" +msgstr "" +"Project-Id-Version: Odoo Server 15.5alpha1+e\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2022-07-01 12:46+0000\n" +"PO-Revision-Date: 2022-07-01 12:46+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 +#: model:test.translation.import.model1,name:test_translation_import.test_translation_import_model1_record1 +msgid "Tableware" +msgstr "" + +#. module: test_translation_import +#: model_term:test.translation.import.model1,xml:test_translation_import.test_translation_import_model1_record1 +msgid "Fork" +msgstr "" + +#. module: test_translation_import +#: model_term:test.translation.import.model1,xml:test_translation_import.test_translation_import_model1_record1 +msgid "Knife" +msgstr "Couteau, Canada" + +#. module: test_translation_import +#: model_term:test.translation.import.model1,xml:test_translation_import.test_translation_import_model1_record1 +msgid "Spoon" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,help:test_translation_import.field_test_translation_import_model1__name +msgid "Help, English" +msgstr "" + +#. module: test_translation_import +#: code:addons/test_translation_import/models/model.py:0 +msgid "Code, English" +msgstr "" + +#. module: test_translation_import +#: code:addons/test_translation_import/models/model.py:0 +msgid "Code Lazy, English" +msgstr "" diff --git a/odoo/addons/test_translation_import/i18n/test_translation_import.pot b/odoo/addons/test_translation_import/i18n/test_translation_import.pot index 2685bd6f2b5..ec34ab91d10 100644 --- a/odoo/addons/test_translation_import/i18n/test_translation_import.pot +++ b/odoo/addons/test_translation_import/i18n/test_translation_import.pot @@ -1,21 +1,13 @@ # This is a test POT file, not a true template. It is manually maintained # to test the import translation behavior of OpenERP. # -# In particular, the -# `1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB` source is -# given with two targets (the #: comments): `code` and `field`. The code one is -# removed in the fr.po file. Still, the import should generate a database entry -# for the `code` one. I.e. the targets defined in the POT must be added to the -# targets defined in the PO file. This was done to fix a bug, as reported by -# lp:933496. -# msgid "" msgstr "" -"Project-Id-Version: OpenERP Server 6.1\n" +"Project-Id-Version: Odoo Server 15.5alpha1+e\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" +"POT-Creation-Date: 2022-07-01 12:46+0000\n" +"PO-Revision-Date: 2022-07-01 12:46+0000\n" +"Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -23,113 +15,154 @@ msgstr "" "Plural-Forms: \n" #. module: test_translation_import -#: code:addons/test_translation_import/models.py:15 -#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import__name +#: code:addons/test_translation_import/models/models.py:0 #, python-format -msgid "1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB" +msgid "Code Lazy, English" msgstr "" #. module: test_translation_import -#: code:addons/test_translation_import/models.py:21 +#: code:addons/test_translation_import/models/models.py:0 #, python-format -msgid "Accounting" +msgid "Code, %(num)s, %(symbol)s, English" msgstr "" #. module: test_translation_import -#: model:ir.model.fields.selection,name:test_translation_import.selection__test_translation_import__import_type__bar -msgid "Bar Import Type" +#: code:addons/test_translation_import/models/models.py:0 +#, python-format +msgid "Code, %s, English" msgstr "" #. module: test_translation_import -#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import__create_uid +#: code:addons/test_translation_import/models/models.py:0 +#, python-format +msgid "Code, English" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__create_uid msgid "Created by" msgstr "" #. module: test_translation_import -#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import__create_date +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__create_date msgid "Created on" msgstr "" #. module: test_translation_import -#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import__display_name +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__display_name msgid "Display Name" msgstr "" #. module: test_translation_import -#: model:ir.model.fields,help:test_translation_import.field_test_translation_import__name -msgid "Efgh" +#: model_terms:test.translation.import.model1,xml:test_translation_import.test_translation_import_model1_record1 +msgid "Fork" msgstr "" #. module: test_translation_import -#: model:ir.model.fields.selection,name:test_translation_import.selection__test_translation_import__import_type__foo -msgid "Foo Import Type" +#: model:ir.model.fields,help:test_translation_import.field_test_translation_import_model1__name +msgid "Help, English" msgstr "" #. module: test_translation_import -#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import__id +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__id msgid "ID" msgstr "" #. module: test_translation_import -#: code:addons/test_translation_import/models.py:21 -#, python-format -msgid "Ijkl" +#: model_terms:test.translation.import.model1,xml:test_translation_import.test_translation_import_model1_record1 +msgid "Knife" msgstr "" #. module: test_translation_import -#: code:addons/test_translation_import/models.py:19 -#, python-format -msgid "Klingon" -msgstr "" - -#. module: test_translation_import -#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import____last_update +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1____last_update msgid "Last Modified on" msgstr "" #. module: test_translation_import -#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import__write_uid +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__write_uid msgid "Last Updated by" msgstr "" #. module: test_translation_import -#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import__write_date +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__write_date msgid "Last Updated on" msgstr "" #. module: test_translation_import -#: model:ir.ui.menu,name:test_translation_import.menu_test_translation -msgid "Test translation" +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__name +msgid "Name" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__selection +msgid "Selection" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields.selection,name:test_translation_import.selection__test_translation_import_model1__selection__bar +msgid "Selection Bar" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields.selection,name:test_translation_import.selection__test_translation_import_model1__selection__foo +msgid "Selection Foo" +msgstr "" + +#. module: test_translation_import +#: model_terms:test.translation.import.model1,xml:test_translation_import.test_translation_import_model1_record1 +msgid "Spoon" +msgstr "" + +#. module: test_translation_import +#: model:test.translation.import.model1,name:test_translation_import.test_translation_import_model1_record1 +msgid "Tableware" +msgstr "" + +#. module: test_translation_import +#: model:ir.actions.act_window,name:test_translation_import.action_test_translation_import_model1 +#: model:ir.ui.menu,name:test_translation_import.menu_test_translation_import_model1 +msgid "Test translation import model1" msgstr "" #. module: test_translation_import -#: model:ir.actions.act_window,name:test_translation_import.action_test_translation_import #: model:ir.ui.menu,name:test_translation_import.menu_test_translation_import -msgid "Test translation import" +msgid "Test translation model1" msgstr "" #. module: test_translation_import -#: code:addons/base/models/arthur.py:42 -#: code:addons/base/models/arthur.py:43 +#: model:ir.model,name:test_translation_import.model_test_translation_import_model1 +msgid "Translation Test 1" +msgstr "" + +#. module: test_translation_import +#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import_model1__xml +msgid "XML" +msgstr "" + +#. module: test_translation_import +#. openerp-web +#: code:addons/test_translation_import/static/src/xml/js_templates.xml:0 #, python-format -msgid "Test translation with two code lines" +msgid "do export" msgstr "" #. module: test_translation_import -#: code:addons/base/models/ford.py:42 +#. openerp-web +#: code:addons/test_translation_import/static/src/xml/js_templates.xml:0 #, python-format -msgid "Test translation with a code type but different line number in pot" +msgid "slot" msgstr "" #. module: test_translation_import -#: model:ir.model.fields,field_description:test_translation_import.field_test_translation_import__other_name -#: code:addons/base/models/marvin.py:42 -#: code:addons/base/models/trillian.py:42 +#. openerp-web +#: code:addons/test_translation_import/static/src/xml/js_templates.xml:0 #, python-format -msgid "Test translation with two code type and model" +msgid "slot 2" msgstr "" #. module: test_translation_import -#: model:ir.model,name:test_translation_import.model_test_translation_import -msgid "Test: Translation Import" +#. openerp-web +#: code:addons/test_translation_import/static/src/xml/js_templates.xml:0 +#, python-format +msgid "text node" msgstr "" diff --git a/odoo/addons/test_translation_import/i18n/tlh.po b/odoo/addons/test_translation_import/i18n/tlh.po index 758a27ffa08..0906bc6ad7b 100644 --- a/odoo/addons/test_translation_import/i18n/tlh.po +++ b/odoo/addons/test_translation_import/i18n/tlh.po @@ -2,11 +2,11 @@ # to test the import translation behavior of Odoo. msgid "" msgstr "" -"Project-Id-Version: Odoo Server saas~11.4\n" +"Project-Id-Version: Odoo Server 15.5alpha1+e\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2018-09-18 13:20+0000\n" -"PO-Revision-Date: 2018-09-18 13:20+0000\n" -"Last-Translator: <>\n" +"POT-Creation-Date: 2022-07-01 12:46+0000\n" +"PO-Revision-Date: 2022-07-01 12:46+0000\n" +"Last-Translator: \n" "Language-Team: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=UTF-8\n" @@ -14,7 +14,16 @@ msgstr "" "Plural-Forms: \n" #. module: test_translation_import -#: code:addons/test_translation_import/models.py:18 -#, python-format -msgid "Klingon" -msgstr "tlhIngan" +#: model:test.translation.import.model1,name:test_translation_import.test_translation_import_model1_record1 +msgid "Tableware" +msgstr "Tableware, Klingon" + +#. module: test_translation_import +#: code:addons/test_translation_import/models/model.py:0 +msgid "Code, English" +msgstr "Code, Klingon" + +#. module: test_translation_import +#: code:addons/test_translation_import/models/model.py:0 +msgid "Code Lazy, English" +msgstr "Code Lazy, Klingon" diff --git a/odoo/addons/test_translation_import/models.py b/odoo/addons/test_translation_import/models.py deleted file mode 100644 index 9f5bd31b547..00000000000 --- a/odoo/addons/test_translation_import/models.py +++ /dev/null @@ -1,25 +0,0 @@ -# -*- coding: utf-8 -*- - -from odoo import models, fields, _ - -class m(models.TransientModel): - """ A model to provide source strings. """ - _name = 'test.translation.import' - _description = 'Test: Translation Import' - - name = fields.Char('1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB', - size=32, help='Efgh') - other_name = fields.Char('Test translation with two code type and model') - import_type = fields.Selection([ - ('foo', 'Foo Import Type'), - ('bar', 'Bar Import Type'), - ]) - - _('Ijkl') - - # With the name label above, this source string should be generated twice. - _('1XBUO5PUYH2RYZSA1FTLRYS8SPCNU1UYXMEYMM25ASV7JC2KTJZQESZYRV9L8CGB') - - _('Klingon') - - _('Accounting') diff --git a/odoo/addons/test_translation_import/models/__init__.py b/odoo/addons/test_translation_import/models/__init__.py new file mode 100644 index 00000000000..0650744f6bc --- /dev/null +++ b/odoo/addons/test_translation_import/models/__init__.py @@ -0,0 +1 @@ +from . import models diff --git a/odoo/addons/test_translation_import/models/models.py b/odoo/addons/test_translation_import/models/models.py new file mode 100644 index 00000000000..87f41253965 --- /dev/null +++ b/odoo/addons/test_translation_import/models/models.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +from odoo import fields, models, _, _lt +from odoo.tools.translate import xml_translate + +class TestTranslationImportModel1(models.Model): + _name = 'test.translation.import.model1' + _description = 'Translation Test 1' + + name = fields.Char('Name', translate=True, help='Help, English') + selection = fields.Selection([ + ('foo', 'Selection Foo'), + ('bar', 'Selection Bar'), + ]) + xml = fields.Text('XML', translate=xml_translate) + + def get_code_translation(self): + return _('Code, English') + + def get_code_lazy_translation(self): + return _lt('Code Lazy, English') + + def get_code_placeholder_translation(self, *args, **kwargs): + return _('Code, %s, English', *args, **kwargs) + + def get_code_named_placeholder_translation(self, *args, **kwargs): + return _('Code, %(num)s, %(symbol)s, English', *args, **kwargs) diff --git a/odoo/addons/test_translation_import/security/ir.model.access.csv b/odoo/addons/test_translation_import/security/ir.model.access.csv index ba076d4035f..228691f2d88 100644 --- a/odoo/addons/test_translation_import/security/ir.model.access.csv +++ b/odoo/addons/test_translation_import/security/ir.model.access.csv @@ -1,2 +1,2 @@ "id","name","model_id:id","group_id:id","perm_read","perm_write","perm_create","perm_unlink" -"access_test_translation_import","access.test.translation.import","model_test_translation_import",,0,0,0,0 +"access_test_translation_import_model1","access.test.translation.import.model1","model_test_translation_import_model1",,0,0,0,0 \ No newline at end of file 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 1ae05ac3228..a0f0b9e7a21 100644 --- a/odoo/addons/test_translation_import/tests/test_term_count.py +++ b/odoo/addons/test_translation_import/tests/test_term_count.py @@ -9,153 +9,96 @@ from odoo.tests import common, tagged from odoo.tools.misc import file_open, mute_logger from odoo.tools.translate import _, _lt, TranslationFileReader, TranslationModuleReader from odoo import Command +from odoo.addons.base.models.ir_fields import BOOLEAN_TRANSLATIONS -TRANSLATED_TERM = _lt("Klingon") +class TestImport(common.TransactionCase): -class TestTermCount(common.TransactionCase): + def test_import_code_translation(self): + self.env['res.lang']._activate_lang('fr_FR') - def test_count_term(self): - """ - Just make sure we have as many translation entries as we wanted. - """ + # Tip: code translations don't need to be imported explicitly + # odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr.po', 'fr_FR', verbose=False) + + model = self.env['test.translation.import.model1'] + self.assertEqual( + model.with_context(lang='fr_FR').get_code_translation(), + 'Code, Français' + ) + + def test_import_model_translation(self): 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'), - ], order='type') - self.assertEqual(len(translations), 2) - self.assertEqual(translations[0].type, 'code') - self.assertEqual(translations[0].module, 'test_translation_import') - self.assertEqual(translations[0].name, 'addons/test_translation_import/models.py') - self.assertEqual(translations[0].comments, '') - self.assertEqual(translations[0].res_id, 15) - self.assertEqual(translations[1].type, 'model') - self.assertEqual(translations[1].module, 'test_translation_import') - self.assertEqual(translations[1].name, 'ir.model.fields,field_description') - self.assertEqual(translations[1].comments, '') - field = self.env['ir.model.fields'].search([('model', '=', 'test.translation.import'), ('name', '=', 'name')]) - self.assertEqual(translations[1].res_id, field.id) - def test_count_term_module(self): - """ - Just make sure we have as many translation entries as we wanted and module deducted from file content - """ + record = self.env.ref('test_translation_import.test_translation_import_model1_record1') + self.assertEqual( + record.with_context(lang='fr_FR').name, + 'Vaisselle' + ) + + def test_import_model_term_translation(self): 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', '=', 'Ijkl'), - ('module', '=', 'test_translation_import'), - ]) - self.assertEqual(len(translations), 1) - self.assertEqual(translations.res_id, 21) + + record = self.env.ref('test_translation_import.test_translation_import_model1_record1') + self.assertEqual( + record.with_context(lang='fr_FR').xml, + '
Couteau
Cuillère
' + ) def test_noupdate(self): """ Make sure no update do not overwrite translations """ menu = self.env.ref('test_translation_import.menu_test_translation_import') - menu.name = "New Name" + self.assertEqual(menu.name, 'Test translation model1') # install french and change translation 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) + self.assertEqual(menu.with_context(lang='fr_FR').name, "Test translation import in french") 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', verbose=False, overwrite=True) - # trans_load invalidates ormcache but not record cache - self.env.invalidate_all() - self.assertEqual(menu.name, "New Name") + self.assertEqual(menu.name, "Test translation model1") self.assertEqual(menu.with_context(lang='fr_FR').name, "Nouveau nom") def test_lang_with_base(self): self.env['res.lang']._activate_lang('fr_BE') + self.env['res.lang']._activate_lang('fr_CA') 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) + odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr.po', 'fr_CA', verbose=False) + odoo.tools.trans_load(self.cr, 'test_translation_import/i18n/fr_CA.po', 'fr_CA', verbose=False, overwrite=True) # language override base language - translations = self.env['ir.translation'].search([ - ('lang', '=', 'fr_BE'), - ('value', '=like', '% belgian french'), - ]) - self.assertEqual(len(translations), 2) + record = self.env.ref('test_translation_import.test_translation_import_model1_record1') + self.assertEqual( + record.with_context(lang='fr_BE').get_code_translation(), + 'Code, Français, Belgium' + ) + self.assertEqual( + record.with_context(lang='fr_BE').name, + 'Vaisselle, Belgium' + ) + self.assertEqual( + record.with_context(lang='fr_BE').xml, + '
Couteau, Belgium
Cuillère, Belgium
' + ) # 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 - """ - 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'), - ]) - self.assertEqual(len(ids), 1) - - 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([ - ('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) - - def test_export_empty_string(self): - """When the string and the translation is equal the translation is empty""" - # Export the translations - 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, - create_empty_translation=create_empty_translation, - overwrite=True) - - # Check that the not translated key is not created - update_translations() - translation = self.env['ir.translation'].search_count([('src', '=', 'Efgh'), ('value', '=', '')]) - 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) - translation = self.env['ir.translation'].search_count([('src', '=', 'Efgh'), ('value', '=', '')]) - self.assertTrue(translation, 'The translation of "Efgh" should be empty') - - # Modify the value translated for the equal value of the key - menu = self.env.ref('test_translation_import.menu_test_translation_import') - menu.name = "New Name" - menu.with_context(lang='fr_FR').name = "New Name" - update_translations() - self.assertEqual(menu.with_context(lang='fr_FR').name, "New Name", 'The translation of "New Name" should be "New Name"') - - # Modify the value translated for another different value - menu.name = "New Name" - menu.with_context(lang='fr_FR').name = "Nouveau nom" - update_translations() - self.assertEqual(menu.with_context(lang='fr_FR').name, "Nouveau nom", 'The translation of "New Name" should be "Nouveau nom"') + self.assertEqual( + record.with_context(lang='fr_CA').get_code_translation(), + 'Code, Français' + ) + self.assertEqual( + record.with_context(lang='fr_CA').name, + 'Vaisselle' + ) + self.assertEqual( + record.with_context(lang='fr_CA').xml, + '
Couteau, Canada
Cuillère
' + ) def test_import_from_po_file(self): """Test the import from a single po file works""" @@ -174,11 +117,15 @@ class TestTermCount(common.TransactionCase): tlh_lang = self.env['res.lang']._lang_get('tlh') self.assertTrue(tlh_lang, "The imported language was not creates") - trans_count = self.env['ir.translation'].search_count([('lang', '=', 'tlh')]) - self.assertEqual(trans_count, 1, "The imported translations were not created") - - self.env = self.env(context=dict(self.env.context, lang="tlh")) - self.assertEqual(_("Klingon"), "tlhIngan", "The code translation was not applied") + record = self.env.ref('test_translation_import.test_translation_import_model1_record1') + self.assertEqual( + record.with_context(lang='tlh').get_code_translation(), + 'Code, Klingon' + ) + self.assertEqual( + record.with_context(lang='tlh').name, + 'Tableware, Klingon' + ) def test_lazy_translation(self): """Test the import from a single po file works""" @@ -194,21 +141,34 @@ class TestTermCount(common.TransactionCase): with mute_logger('odoo.addons.base.models.res_lang'): import_tlh.import_lang() - context = {'lang': "tlh"} - self.assertEqual(_("Klingon"), "tlhIngan", "The direct code translation was not applied") + model = self.env['test.translation.import.model1'] + TRANSLATED_TERM = model.get_code_lazy_translation() + + self.assertEqual( + model.with_context(lang='tlh').get_code_translation(), + "Code, Klingon", + "The direct code translation was not applied" + ) context = None # Comparison of lazy strings must be explicitely casted to string with self.assertRaises(NotImplementedError): - TRANSLATED_TERM == "Klingon" - self.assertEqual(str(TRANSLATED_TERM), "Klingon", "The translation should not be applied yet") + TRANSLATED_TERM == "Code, English" + self.assertEqual(str(TRANSLATED_TERM), "Code Lazy, English", "The translation should not be applied yet") context = {'lang': "tlh"} - self.assertEqual(str(TRANSLATED_TERM), "tlhIngan", "The lazy code translation was not applied") + self.assertEqual(str(TRANSLATED_TERM), "Code Lazy, Klingon", "The lazy code translation was not applied") - self.assertEqual("Do you speak " + TRANSLATED_TERM, "Do you speak tlhIngan", "str + _lt concatenation failed") - self.assertEqual(TRANSLATED_TERM + ", I speak it", "tlhIngan, I speak it", "_lt + str concatenation failed") - self.assertEqual(TRANSLATED_TERM + TRANSLATED_TERM, "tlhIngantlhIngan", "_lt + _lt concatenation failed") + self.assertEqual("Do you speak " + TRANSLATED_TERM, "Do you speak Code Lazy, Klingon", "str + _lt concatenation failed") + self.assertEqual(TRANSLATED_TERM + ", I speak it", "Code Lazy, Klingon, I speak it", "_lt + str concatenation failed") + self.assertEqual(TRANSLATED_TERM + TRANSLATED_TERM, "Code Lazy, KlingonCode Lazy, Klingon", "_lt + _lt concatenation failed") + + # test lazy translation in another module + self.env['res.lang']._activate_lang('fr_FR') + context = {'lang': 'en_US'} + self.assertEqual(str(BOOLEAN_TRANSLATIONS[0]), 'yes') + context = {'lang': 'fr_FR'} + self.assertEqual(str(BOOLEAN_TRANSLATIONS[0]), 'oui') def test_import_from_csv_file(self): """Test the import from a single CSV file works""" @@ -227,118 +187,44 @@ class TestTermCount(common.TransactionCase): dot_lang = self.env['res.lang']._lang_get('dot') self.assertTrue(dot_lang, "The imported language was not creates") - trans_count = self.env['ir.translation'].search_count([('lang', '=', 'dot')]) - self.assertEqual(trans_count, 1, "The imported translations were not created") - - self.env = 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': [Command.set([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") + # code translation cannot be changed or imported, it only depends on the po file in the module directory + record = self.env.ref('test_translation_import.test_translation_import_model1_record1') + self.assertEqual( + record.with_context(lang='dot').get_code_translation(), + 'Code, English' + ) + self.assertEqual( + record.with_context(lang='dot').name, + 'Tableware, Dot' + ) def test_translation_placeholder(self): """Verify placeholder use in _()""" - context = {'lang': "fr_BE"} - self.env.ref("base.lang_fr_BE").active = True + self.env['res.lang']._activate_lang('fr_BE') - # translation with positional placeholders - translation = self.env['ir.translation'].create({ - 'src': 'Text with %s placeholder', - 'value': 'Text avec %s marqueur', - 'type': 'code', - 'name': 'addons/test_translation_import/tests/test_count_term.py', - 'res_id': 0, - 'lang': 'fr_BE', - }) + model_fr_BE = self.env['test.translation.import.model1'].with_context(lang='fr_BE') # correctly translate self.assertEqual( - _("Text with %s placeholder", 1), - "Text avec 1 marqueur", + model_fr_BE.get_code_placeholder_translation(1), + "Code, 1, Français, Belgium", "Translation placeholders were not applied" ) # source error: wrong arguments - with self.assertRaises(TypeError), self.cr.savepoint(): - _("Text with %s placeholder", 1, "🧀") - - # translation error: log error and fallback on source - translation.value = "Text avec s% marqueur" - with self.assertLogs('odoo.tools.translate', 'ERROR'): - self.assertEqual( - _("Text with %s placeholder", 1), - "Text with 1 placeholder", - "Fallback to source was not used for bad translation" - ) - - - # translation with named placeholders - translation = self.env['ir.translation'].create({ - 'src': 'Text with %(num)s placeholders %(symbol)s', - 'value': 'Text avec %(num)s marqueurs %(symbol)s', - 'type': 'code', - 'name': 'addons/test_translation_import/tests/test_count_term.py', - 'res_id': 0, - 'lang': 'fr_BE', - }) + with self.assertRaises(TypeError): + model_fr_BE.get_code_placeholder_translation(1, "🧀") # correctly translate self.assertEqual( - _("Text with %(num)s placeholders %(symbol)s", num=2, symbol="🧀"), - "Text avec 2 marqueurs 🧀", + model_fr_BE.get_code_named_placeholder_translation(num=2, symbol="🧀"), + "Code, 2, 🧀, Français, Belgium", "Translation placeholders were not applied" ) # source error: wrong arguments - with self.assertRaises(KeyError), self.cr.savepoint(): - _("Text with %(num)s placeholders %(symbol)s", symbol="🧀") - - # translation error: log error and fallback on source - translation.value = "Text avec %(num)s marqueurs %(symbole)s" - with self.assertLogs('odoo.tools.translate', 'ERROR'): - self.assertEqual( - _("Text with %(num)s placeholders %(symbol)s", num=2, symbol="🧀"), - "Text with 2 placeholders 🧀", - "Fallback to source was not used for bad translation" - ) + with self.assertRaises(KeyError): + model_fr_BE.get_code_named_placeholder_translation(symbol="🧀"), @tagged('post_install', '-at_install') @@ -347,23 +233,10 @@ class TestTranslationFlow(common.TransactionCase): def test_export_import(self): """ Ensure export+import gives the same result as loading a language """ # load language and generate missing terms to create missing empty terms - with mute_logger('odoo.addons.base.models.ir_translation'): - self.env["base.language.install"].create({ - 'overwrite': True, - 'lang_ids': [(6, 0, [self.env.ref('base.lang_fr').id])], - }).lang_install() - - self.env["base.update.translations"].create({'lang': 'fr_FR'}).act_update() - - translations = self.env["ir.translation"].search([ - ('lang', '=', 'fr_FR'), - ('module', '=', 'test_translation_import'), - ('value', '!=', ''), - ]) - - # minus 3 as the original fr.po contains 3 fake code translations (cf - # test_no_duplicate test) which are not found by babel_extract_terms - init_translation_count = len(translations) - 3 + self.env["base.language.install"].create({ + 'overwrite': True, + 'lang_ids': [(6, 0, [self.env.ref('base.lang_fr').id])], + }).lang_install() module = self.env.ref('base.module_test_translation_import') export = self.env["base.language.export"].create({ @@ -375,7 +248,30 @@ class TestTranslationFlow(common.TransactionCase): po_file = export.data self.assertIsNotNone(po_file) - translations.unlink() + record = self.env.ref('test_translation_import.test_translation_import_model1_record1') + record.invalidate_recordset() + self.assertEqual( + record.with_context(lang='fr_FR').name, + 'Vaisselle' + ) + self.assertEqual( + record.with_context(lang='fr_FR').xml, + '
Couteau
Cuillère
' + ) + + # remove All translations + record.name = False + record.name = 'Tableware' + record.xml = False + record.xml = '
Knife
Spoon
' + self.assertEqual( + record.with_context(lang='fr_FR').name, + 'Tableware' + ) + self.assertEqual( + record.with_context(lang='fr_FR').xml, + '
Knife
Spoon
' + ) import_fr = self.env["base.language.import"].create({ 'name': 'French', @@ -387,13 +283,14 @@ class TestTranslationFlow(common.TransactionCase): with mute_logger('odoo.addons.base.models.res_lang'): import_fr.import_lang() - import_translation = self.env["ir.translation"].search([ - ('lang', '=', 'fr_FR'), - ('module', '=', 'test_translation_import'), - ('value', '!=', ''), - ]) - - self.assertEqual(init_translation_count, len(import_translation)) + self.assertEqual( + record.with_context(lang='fr_FR').name, + 'Vaisselle' + ) + self.assertEqual( + record.with_context(lang='fr_FR').xml, + '
Couteau
Cuillère
' + ) def test_export_import_csv(self): """ Ensure can reimport exported csv """ @@ -409,11 +306,6 @@ class TestTranslationFlow(common.TransactionCase): po_file = export.data self.assertIsNotNone(po_file) - self.env["ir.translation"].search([ - ('lang', '=', 'fr_FR'), - ('module', '=', 'test_translation_import') - ]).unlink() - import_fr = self.env["base.language.import"].create({ 'name': 'French', 'code': 'fr_FR', @@ -422,7 +314,7 @@ 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.with_context().import_lang() def test_export_static_templates(self): trans_static = [] diff --git a/odoo/addons/test_translation_import/view.xml b/odoo/addons/test_translation_import/view.xml index b77f4ef9210..1c60768166d 100644 --- a/odoo/addons/test_translation_import/view.xml +++ b/odoo/addons/test_translation_import/view.xml @@ -2,19 +2,19 @@ - - Test translation import + + Test translation import model1 ir.actions.act_window - test.translation.import + test.translation.import.model1 tree,form current - + - +