From 4feb7ba3c8d710f83b33bc8d516e321b9444391a Mon Sep 17 00:00:00 2001 From: HydrionBurst Date: Fri, 24 Feb 2023 15:23:48 +0000 Subject: [PATCH] [FIX] translate: upgrade interraction with l10n_multilang When upgrading a database with `l10n_multilang` installed, the translations kept were not the right ones. Here is the process that was executed during the upgrade: * `base` is loaded: registry is loaded with `translate=True`, which calls `convert_column_translatable`. This has the effect of setting the value of the field to `{'en_US': name}`, with only that language installed. * `l10n_multilang` is loaded: the function `lang_install` is extended to copy the translations of the templates on the instanciated records. * `l10n_*` is loaded: the translation values are loaded from the `.po` files. `lang_install` is called after the loading on the templates, copying the value on the records. The value on the records is now something like `{'en_US': name, 'fr_BE': nom, 'nl_BE': naam}`. * `base/end-migrate` is executed: it copies the values coming from `ir_translation`, without overriding the values that are already on the record because of the order of the `||` operator. This means that any value inputed by the user[^1] will be overriden by the value in the `.po` files. opw-3175383 closes odoo/odoo#115254 X-original-commit: 5d46cdee878d752b9a61a74762a8710be0890e9d Signed-off-by: Christophe Simonis Co-authored-by: william-andre Co-authored-by: HydrionBurst --- odoo/tools/translate.py | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/odoo/tools/translate.py b/odoo/tools/translate.py index e22167bf384..a766adeb2a6 100644 --- a/odoo/tools/translate.py +++ b/odoo/tools/translate.py @@ -1562,17 +1562,19 @@ def _get_translation_upgrade_queries(cr, field): if field.translate is True: query = f""" WITH t AS ( - SELECT res_id, jsonb_object_agg(lang, value) AS value - FROM _ir_translation - WHERE type = 'model' AND name = %s AND state = 'translated' - GROUP BY res_id + SELECT it.res_id as res_id, jsonb_object_agg(it.lang, it.value) AS value, bool_or(imd.noupdate) AS noupdate + FROM _ir_translation it + LEFT JOIN ir_model_data imd + ON imd.model = %s AND imd.res_id = it.res_id + WHERE it.type = 'model' AND it.name = %s AND it.state = 'translated' + GROUP BY it.res_id ) UPDATE {Model._table} m - SET "{field.name}" = t.value || m."{field.name}" + SET "{field.name}" = CASE WHEN t.noupdate THEN m."{field.name}" || t.value ELSE t.value || m."{field.name}" END FROM t WHERE t.res_id = m.id """ - migrate_queries.append(cr.mogrify(query, [translation_name]).decode()) + migrate_queries.append(cr.mogrify(query, [Model._name, translation_name]).decode()) query = "DELETE FROM _ir_translation WHERE type = 'model' AND name = %s" cleanup_queries.append(cr.mogrify(query, [translation_name]).decode()) @@ -1589,27 +1591,29 @@ def _get_translation_upgrade_queries(cr, field): ), t AS ( -- aggregate translations by lang -- - SELECT res_id, jsonb_object_agg(lang, value) AS value + SELECT t0.res_id AS res_id, jsonb_object_agg(t0.lang, t0.value) AS value, bool_or(imd.noupdate) AS noupdate FROM t0 - GROUP BY res_id + LEFT JOIN ir_model_data imd + ON imd.model = %s AND imd.res_id = t0.res_id + GROUP BY t0.res_id ) - SELECT t.res_id, m."{field.name}", t.value + SELECT t.res_id, m."{field.name}", t.value, t.noupdate FROM t JOIN "{Model._table}" m ON t.res_id = m.id - """, [translation_name]) - for id_, old_values, translations in cr.fetchall(): - if not old_values: + """, [translation_name, Model._name]) + for id_, new_translations, translations, noupdate in cr.fetchall(): + if not new_translations: continue - # 'old_values' contain terms possibly updated from PO files during - # the upgrade of modules; prefer those terms over the ones from - # the out-of-date 'translations' dict - src_value = old_values.pop('en_US') + # new_translations contains translations updated from the latest po files + src_value = new_translations.pop('en_US') src_terms = field.get_trans_terms(src_value) - for lang, dst_value in old_values.items(): + for lang, dst_value in new_translations.items(): terms_mapping = translations.setdefault(lang, {}) dst_terms = field.get_trans_terms(dst_value) for src_term, dst_term in zip(src_terms, dst_terms): - if src_term != dst_term: + if src_term == dst_term or noupdate: + terms_mapping.setdefault(src_term, dst_term) + else: terms_mapping[src_term] = dst_term new_values = { lang: field.translate(terms_mapping.get, src_value)