Skip to content

Conversation

@aj-fuentes
Copy link
Contributor

@aj-fuentes aj-fuentes commented Jan 19, 2024

When we update an inline translated element (like <span>) for the base
language en_US we should update the modifiers attributes for all
languages.

For example when updating from (16.0)
https://github.com/odoo/odoo/blob/7ecd9413/odoo/addons/base/views/ir_ui_view_views.xml#L127-L129
https://github.com/odoo/odoo/blob/7ecd9413/odoo/addons/base/i18n/fr.po#L7233-L7235
to (17.0)
https://github.com/odoo/odoo/blob/b1461d28/odoo/addons/base/views/ir_ui_view_views.xml#L126-L128
https://github.com/odoo/odoo/blob/b1461d28/odoo/addons/base/i18n/fr.po#L12087-L12089

The base term, en_US, is updated since the text matches but the
attributes of the translated terms, fr_FR for example, are not
updated. Later when the PO file is loaded in non-overwrite mode the
translated terms for fr_FR is not updated. This causes all sort of
issues during an upgrade for inline-translated terms -- like <span>.
More so since the recent change that converts domain-based attributes
into inline Python expressions.

In this patch we propagate modifiers attributes from inline-translated
items in the new base term into all translated terms when the base term
is updated. In that way we ensure the attributes are correct in all
languages even if later the loading of their corresponding PO file
doesn't update the term.

For a detailed example, let's see what happens when loading the view
above during an upgrade 16->17, right at the first load of the XML file
at https://github.com/odoo/odoo/blob/b1461d28/odoo/fields.py#L1864

(Pdb) p old_term
'<span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'soft\')]}">This view has no previous version.</span>\n                        <span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'hard\')]}">This view is not coming from a file.</span>\n                        <span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'other_view\')]}">You need two views to compare.</span>'
(Pdb) p closest_term
'<span invisible="reset_mode != \'soft\'">This view has no previous version.</span>\n                        <span invisible="reset_mode != \'hard\'">This view is not coming from a file.</span>\n                        <span invisible="reset_mode != \'other_view\'">You need two views to compare.</span>'
(Pdb) p translation_dictionary[old_term]
defaultdict(<class 'dict'>, {'fr_FR': '<span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'soft\')]}">Cette vue n\'a pas de version antérieure.</span>\n                        <span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'hard\')]}">Cette vue ne provient pas d\'un fichier.</span>\n                        <span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'other_view\')]}">Vous avez besoin de deux vues pour comparer.</span>'})

As we can see the new term will get an updated value for its invisible
attribute, while also removing attrs. The translated terms will be
still keep the old modifier though.
Now later when the fr_FR.po file is loaded we reach this point
https://github.com/odoo/odoo/blob/b1461d28/odoo/tools/translate.py#L1442

(Pdb) p term_en
'<span invisible="reset_mode != \'soft\'">This view has no previous version.</span>\n                        <span invisible="reset_mode != \'hard\'">This view is not coming from a file.</span>\n                        <span invisible="reset_mode != \'other_view\'">You need two views to compare.</span>'
(Pdb) p translation_dictionary[term_en]
defaultdict(<function DeepDefaultDict at 0x7fc9640cdfc0>, {'fr_FR': '<span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'soft\')]}">Cette vue n\'a pas de version antérieure.</span>\n                        <span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'hard\')]}">Cette vue ne provient pas d\'un fichier.</span>\n                        <span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'other_view\')]}">Vous avez besoin de deux vues pour comparer.</span>'})

Thus the translated values are NOT updated, keeping the wrong
modifiers. This is later fixed during the upgrade in a clumsy way.
C.f. the warnings like this one in runbot:

Incomplete conversion for view(id=77, lang=fr_FR) at
<span attrs="{'invisible': [('reset_mode', '!=', 'soft')]}">Cette vue n'a pas de version ant&https://github.com/odoo-dev/odoo/pull/233;rieure.</span>

Note that such warnings are gone in current PR CI.
The root issue here is that when the fr_FR translation is loaded the
terms are not updated due to a combination of factors:

  1. The text content of the term didn't change
  2. There is no override flag set for translations

Option 2 is not a valid option during upgrades because we want to keep
custom translations. We could instead of the current patch tweak how
option 1 works and perhaps make the closest term more restricted. This
would lead to the update of the whole translation though while the
actual issue here is just the modifiers. Moreover if the translations
are out of sync the translated terms will still keep the wrong values
that could still be essential for the correct functioning of the record
they belong too (view archs -- for example).

Finally this is a more extreme case (16.0):
https://github.com/odoo/enterprise/blob/1e63b4a8/sale_subscription/views/sale_order_views.xml#L142
In this case during the upgrade we fix the modifier value (refer to
runbot warning above -- it's the same script that fixes it) and set

invisible="(subscription_management == 'upsell') or (recurrence_id == False)"

for translations, which is wrong. The correct value is (17.0):

invisible="not plan_id or subscription_state == '7_upsell'"

https://github.com/odoo/enterprise/blob/530ba3ad/sale_subscription/views/sale_order_views.xml#L136

@robodoo
Copy link
Contributor

robodoo commented Jan 19, 2024

Pull request status dashboard.

@aj-fuentes aj-fuentes changed the title [FIX] core: propagate update of untranslatable attributes from base terms [FIX] core: propagate update of modifiers attributes from base terms Jan 19, 2024
@aj-fuentes aj-fuentes force-pushed the 17.0-fix_update_unstranslated_attribs-afu branch from 3b3ccdd to 73bf5fb Compare January 19, 2024 14:04
@C3POdoo C3POdoo added the RD research & development, internal work label Jan 19, 2024
@KangOl KangOl requested a review from rco-odoo January 19, 2024 15:43
Copy link
Contributor

@HydrionBurst HydrionBurst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems a very interesting feature. But

  1. The transform cannot handle multi layer xml/html
    e.g. <small><span ...
  2. I don't know if it would be too complicated to explain the expected result after translation sync. For your problem, can we just diasble the close matching feature when env.lang is None / a special context / upgrade?

@aj-fuentes
Copy link
Contributor Author

It seems a very interesting feature. But

  1. The transform cannot handle multi layer xml/html
    e.g. <span ...
  2. I don't know if it would be too complicated to explain the expected result after translation sync. For your problem, can we just diasble the close matching feature when env.lang is None / a special context / upgrade?
  1. iter will do a DFS, still to be 100% correct the transformer should check the full structure like parents matching etc. (as you suggested the full length is part of it). This was a first draft to see if the idea is workable ;) The premise in the current implementation is that if the nodes match in DFS fashion then the structure is OK (it misses the case where one of the iterators stops earlier -- can be fixed with strict=True for python3.10 -- but to ensure a perfectly matching structure needs a dedicated algorithm)
>>> from lxml import etree as et
>>> x=et.fromstring("<a><b/><c><d/></c></a>")
>>> [y.tag for y in x.iter()]
['a', 'b', 'c', 'd']
  1. I can add details about the expected result in the commit message. The gist of it is that if we update the attribute in the base term it doesn't matter what translations have, they will be updated. Regarding the idea of using a context flag for upgrades, I think that's also OK for us. In theory we could patch this part
    https://github.com/odoo/odoo/blob/73bf5fbfc4b9e07667727cafcb9a314187c3e3c0/odoo/tools/translate.py#L1482-L1485
    to take the attributes from the base (en_US) term. I experimented with a similar patch there. I opted to move it to the current place because it seemed more "logical" to propagate the change when the base term was changed instead of checking each term at translations load time. With the current way if the attributes are instrumental for the correct functioning of the DB they will be propagated already when the base term is updated, even if the translations are out of sync.

@aj-fuentes aj-fuentes force-pushed the 17.0-fix_update_unstranslated_attribs-afu branch 5 times, most recently from 1c9a589 to 04ee594 Compare January 23, 2024 12:46
@aj-fuentes
Copy link
Contributor Author

Hello @HydrionBurst

I pushed a more detailed description with a full example. I also made the structural matching more robust and put the use of the new transformation behind install_mode=True context flag.

@aj-fuentes aj-fuentes force-pushed the 17.0-fix_update_unstranslated_attribs-afu branch from 04ee594 to 2e24f40 Compare January 23, 2024 12:57
Copy link
Member

@rco-odoo rco-odoo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That patch feels a bit invasive for a module upgrade issue. But it looks kind of okay to me. Did you benchmark it? Is the result worth the added overhead?

@aj-fuentes
Copy link
Contributor Author

For info I'm working in running this in runbot DB with all modules installed + some improvements. I'll take into account the suggestions ;)

@aj-fuentes aj-fuentes force-pushed the 17.0-fix_update_unstranslated_attribs-afu branch from 2e24f40 to a8bef90 Compare January 25, 2024 15:34
@aj-fuentes
Copy link
Contributor Author

aj-fuentes commented Jan 25, 2024

OK, so I pushed all your suggestions with some small details.

  • Better name for the adapter
  • Check if install mode and en_US
  • No try/except for terms xml parser

With the previous version the run I did on a runbot db with all modules installed in an upgrade 16->17 with these languages installed en_US,ar_001,zh_CN,zh_HK,zh_TW,nl_BE,nl_NL,en_AU,en_CA,en_GB,en_IN,fr_BE,fr_CA,fr_CH,fr_FR,de_DE,de_CH,ja_JP,es_AR,es_CL,es_MX,es_VE,es_ES

  • total calls to the adapter: 8471
  • of which 1871 were via the ident adapter (i.e. do nothing -> fast)

I'm re-running the upgrade with the latest version of the code. I'll post the total time spent in the adapters. Total time of the upgrade is around 40min.

EDIT: on my last run, 31min total upgrade time of which just 0.3 seconds were spent in the adapters for the roughly 7.5K calls to the non-identity adapter.

@aj-fuentes aj-fuentes force-pushed the 17.0-fix_update_unstranslated_attribs-afu branch from a8bef90 to cae6592 Compare January 26, 2024 07:34
@aj-fuentes aj-fuentes force-pushed the 17.0-fix_update_unstranslated_attribs-afu branch from cae6592 to 863fe79 Compare January 26, 2024 15:35
Copy link
Contributor

@HydrionBurst HydrionBurst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Except the small question, others LGTM
Thanks for your great work!

@aj-fuentes aj-fuentes force-pushed the 17.0-fix_update_unstranslated_attribs-afu branch 2 times, most recently from 6f7ae59 to 9eb8a29 Compare January 29, 2024 14:21
@aj-fuentes aj-fuentes marked this pull request as ready for review January 29, 2024 14:30
@C3POdoo C3POdoo requested review from a team and xmo-odoo and removed request for a team January 29, 2024 14:35
@aj-fuentes
Copy link
Contributor Author

@odoo/rd-security could you please approve this? The getattr calls are used in a standard way to get the bound methods from the current translate method.

@xmo-odoo
Copy link
Collaborator

could you please approve this? The getattr calls are used in a standard way to get the bound methods from the current translate method.

They're both completely unnecessary, you can use hasattr and a regular attribute lookup.

@rco-odoo
Copy link
Member

@robodoo override=ci/security

Both calls to getattr are fine, since the attribute is static. They are used to access attributes field.translate.is_text and field.translate.term_adapter, which are optional on field.translate.

`get_text_content` will transform contiguous space chars into single
spaces, plus translate special HTML elements
```
>>> " ".join(html.fromstring(f"a\n    b &amp; c").text_content().split())
'a b & c'
```

In order the correctly verify if a term is text-only we need to use the
HTML parser. Note that to be resilient against bad XML, but valid HTML,
we cannot use the default XML parser.
When we update an inline translated element (like `<span>`) for the base
language `en_US` we should update the modifiers attributes for all
languages.

For example when updating from (16.0)
https://github.com/odoo/odoo/blob/7ecd9413/odoo/addons/base/views/ir_ui_view_views.xml#L127-L129
https://github.com/odoo/odoo/blob/7ecd9413/odoo/addons/base/i18n/fr.po#L7233-L7235
to (17.0)
https://github.com/odoo/odoo/blob/b1461d28/odoo/addons/base/views/ir_ui_view_views.xml#L126-L128
https://github.com/odoo/odoo/blob/b1461d28/odoo/addons/base/i18n/fr.po#L12087-L12089

The base term, `en_US`, is updated since the text matches but the
attributes of the translated terms, `fr_FR` for example, are not
updated. Later when the PO file is loaded in non-overwrite mode the
translated terms for `fr_FR` is not updated. This causes all sort of
issues during an upgrade for inline-translated terms -- like `<span>`.
More so since the recent change that converts domain-based attributes
into inline Python expressions.

In this patch we propagate modifiers attributes from inline-translated
items in the new base term into all translated terms when the base term
is updated. In that way we ensure the attributes are correct in all
languages even if later the loading of their corresponding PO file
doesn't update the term.

For a detailed example, let's see what happens when loading the view
above during an upgrade 16->17, right at the first load of the XML file
at https://github.com/odoo/odoo/blob/b1461d28/odoo/fields.py#L1864
```
(Pdb) p old_term
'<span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'soft\')]}">This view has no previous version.</span>\n                        <span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'hard\')]}">This view is not coming from a file.</span>\n                        <span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'other_view\')]}">You need two views to compare.</span>'
(Pdb) p closest_term
'<span invisible="reset_mode != \'soft\'">This view has no previous version.</span>\n                        <span invisible="reset_mode != \'hard\'">This view is not coming from a file.</span>\n                        <span invisible="reset_mode != \'other_view\'">You need two views to compare.</span>'
(Pdb) p translation_dictionary[old_term]
defaultdict(<class 'dict'>, {'fr_FR': '<span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'soft\')]}">Cette vue n\'a pas de version antérieure.</span>\n                        <span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'hard\')]}">Cette vue ne provient pas d\'un fichier.</span>\n                        <span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'other_view\')]}">Vous avez besoin de deux vues pour comparer.</span>'})
```
As we can see the new term will get an updated value for its `invisible`
attribute, while also removing `attrs`. The translated terms will be
still keep the old modifier though.
Now later when the fr_FR.po file is loaded we reach this point
https://github.com/odoo/odoo/blob/b1461d28/odoo/tools/translate.py#L1442
```
(Pdb) p term_en
'<span invisible="reset_mode != \'soft\'">This view has no previous version.</span>\n                        <span invisible="reset_mode != \'hard\'">This view is not coming from a file.</span>\n                        <span invisible="reset_mode != \'other_view\'">You need two views to compare.</span>'
(Pdb) p translation_dictionary[term_en]
defaultdict(<function DeepDefaultDict at 0x7fc9640cdfc0>, {'fr_FR': '<span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'soft\')]}">Cette vue n\'a pas de version antérieure.</span>\n                        <span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'hard\')]}">Cette vue ne provient pas d\'un fichier.</span>\n                        <span attrs="{\'invisible\': [(\'reset_mode\', \'!=\', \'other_view\')]}">Vous avez besoin de deux vues pour comparer.</span>'})
```
Thus the translated values are NOT updated, keeping the _wrong_
modifiers. This is later fixed during the upgrade in a clumsy way.
C.f. the warnings like this one in runbot:
```
Incomplete conversion for view(id=77, lang=fr_FR) at
<span attrs="{'invisible': [('reset_mode', '!=', 'soft')]}">Cette vue n'a pas de version ant&#233;rieure.</span>
```
Note that such warnings are gone in current PR CI.
The root issue here is that when the fr_FR translation is loaded the
terms are not updated due to a combination of factors:
1. The text content of the term didn't change
2. There is no override flag set for translations

Option 2 is not a valid option during upgrades because we want to keep
custom translations. We could instead of the current patch tweak how
option 1 works and perhaps make the closest term more restricted. This
would lead to the update of the whole translation though while the
actual issue here is _just_ the modifiers. Moreover if the translations
are out of sync the translated terms will still keep the wrong values
that could still be essential for the correct functioning of the record
they belong too (view archs -- for example).

Finally this is a more extreme case (16.0):
https://github.com/odoo/enterprise/blob/1e63b4a8/sale_subscription/views/sale_order_views.xml#L142
In this case during the upgrade we fix the modifier value (refer to
runbot warning above -- it's the same script that fixes it) and set
```
invisible="(subscription_management == 'upsell') or (recurrence_id == False)"
```
for translations, which is wrong. The correct value is (17.0):
```
invisible="not plan_id or subscription_state == '7_upsell'"
```
https://github.com/odoo/enterprise/blob/530ba3ad/sale_subscription/views/sale_order_views.xml#L136
@aj-fuentes aj-fuentes force-pushed the 17.0-fix_update_unstranslated_attribs-afu branch from 9eb8a29 to 522145a Compare January 30, 2024 15:30
@rco-odoo
Copy link
Member

@robodoo rebase-ff r+

@robodoo
Copy link
Contributor

robodoo commented Jan 30, 2024

Merge method set to rebase and fast-forward.

robodoo pushed a commit that referenced this pull request Jan 30, 2024
`get_text_content` will transform contiguous space chars into single
spaces, plus translate special HTML elements
```
>>> " ".join(html.fromstring(f"a\n    b &amp; c").text_content().split())
'a b & c'
```

In order the correctly verify if a term is text-only we need to use the
HTML parser. Note that to be resilient against bad XML, but valid HTML,
we cannot use the default XML parser.

Part-of: #150152
@robodoo robodoo closed this in ce3aac8 Jan 30, 2024
@fw-bot fw-bot deleted the 17.0-fix_update_unstranslated_attribs-afu branch February 13, 2024 22:46
HydrionBurst added a commit to odoo-dev/odoo that referenced this pull request Jan 23, 2025
Before:
The typofix feature treats terms in the old and new values with similar text
content as the same term, migrating the translations of the old term to the new
term.

For example

The old value has the mapping:
'Draft': 'Brouillon'

The new value contains the term:
'<span invisible="name or name_placeholder or quick_edit_mode">Draft</span>'

Since the old term and the new term share the same text content, 'Draft', after
`write`, the new term reuses the old translation of 'Draft'. However, the
translation 'Brouillon' is always visible, unlike its en_US counterpart.

This behavior is acceptable in non-upgrade mode because the user writes the
en_US value and is responsible for verifying translations afterward. However, it
is problematic during upgrades because users cannot easily identify which
records have changed and need to be rechecked.

After:
The translation inheritance behavior can be described as below
Translations can be inherited after `write` from old terms to new terms which
share the very close text contents
1. when `write` in production mode, text contents for translation terms are more
   important than the HTML/XML structures of them, and the old term translations
   should be remained as much as possible. Because
    * the writing user is responsible to recheck all translations after `write`.
    * it is easier for the writing user to copy technical HTML/XML structures
      than translate text contents for a language they may not know.
    * the feature can also be used as typofix when the only small diff is the
      text content
2. when `write` in upgrade time, the HTML/XML structure is more important than
   the text content, and the new term structure should be remained as much as
   possible. Because
    * HTML/XML structures might be changed a lot after upgrade, which may
      contain behavior relevant diff (e.g. `invisible`), even if text contents
      are not changed.
    * users have no idea which records' values are changed during upgrade and
      are hard to recheck their translations.
    * new terms are highly likely to be correctly translated in the latest po
      files which will be imported during upgrade.
    * the typofix feature can still be remained when the only small diff is the
      text content

Based on the above feature analysis, we use the below new strategy
1. translations can be inherited only if the old source term and the new source
   term share the same HTML/XML structure
2. translations can be inherited only if the old translation term and the new
   source term share the same HTML/XML structure
3. when translations are inherited, MODIFIER_ATTRS will be synchronized with
   the new source term, other attributes will be copied from the source term if
   available.

Backward-Port-Of: odoo#194181
Backward-Port-Of: odoo#150152
robodoo pushed a commit that referenced this pull request Jan 24, 2025
Before:
The typofix feature treats terms in the old and new values with similar text
content as the same term, migrating the translations of the old term to the new
term.

For example

The old value has the mapping:
'Draft': 'Brouillon'

The new value contains the term:
'<span invisible="name or name_placeholder or quick_edit_mode">Draft</span>'

Since the old term and the new term share the same text content, 'Draft', after
`write`, the new term reuses the old translation of 'Draft'. However, the
translation 'Brouillon' is always visible, unlike its en_US counterpart.

This behavior is acceptable in non-upgrade mode because the user writes the
en_US value and is responsible for verifying translations afterward. However, it
is problematic during upgrades because users cannot easily identify which
records have changed and need to be rechecked.

After:
The translation inheritance behavior can be described as below
Translations can be inherited after `write` from old terms to new terms which
share the very close text contents
1. when `write` in production mode, text contents for translation terms are more
   important than the HTML/XML structures of them, and the old term translations
   should be remained as much as possible. Because
    * the writing user is responsible to recheck all translations after `write`.
    * it is easier for the writing user to copy technical HTML/XML structures
      than translate text contents for a language they may not know.
    * the feature can also be used as typofix when the only small diff is the
      text content
2. when `write` in upgrade time, the HTML/XML structure is more important than
   the text content, and the new term structure should be remained as much as
   possible. Because
    * HTML/XML structures might be changed a lot after upgrade, which may
      contain behavior relevant diff (e.g. `invisible`), even if text contents
      are not changed.
    * users have no idea which records' values are changed during upgrade and
      are hard to recheck their translations.
    * new terms are highly likely to be correctly translated in the latest po
      files which will be imported during upgrade.
    * the typofix feature can still be remained when the only small diff is the
      text content

Based on the above feature analysis, we use the below new strategy
1. translations can be inherited only if the old source term and the new source
   term share the same HTML/XML structure
2. translations can be inherited only if the old translation term and the new
   source term share the same HTML/XML structure
3. when translations are inherited, MODIFIER_ATTRS will be synchronized with
   the new source term, other attributes will be copied from the source term if
   available.

closes #194186

Backward-port-of: #194181
Backward-port-of: #150152
Signed-off-by: Raphael Collet <rco@odoo.com>
adhoc-cicd-bot pushed a commit to adhoc-cicd/odoo-odoo that referenced this pull request Jan 27, 2025
Before:
The typofix feature treats terms in the old and new values with similar text
content as the same term, migrating the translations of the old term to the new
term.

For example

The old value has the mapping:
'Draft': 'Brouillon'

The new value contains the term:
'<span invisible="name or name_placeholder or quick_edit_mode">Draft</span>'

Since the old term and the new term share the same text content, 'Draft', after
`write`, the new term reuses the old translation of 'Draft'. However, the
translation 'Brouillon' is always visible, unlike its en_US counterpart.

This behavior is acceptable in non-upgrade mode because the user writes the
en_US value and is responsible for verifying translations afterward. However, it
is problematic during upgrades because users cannot easily identify which
records have changed and need to be rechecked.

After:
The translation inheritance behavior can be described as below
Translations can be inherited after `write` from old terms to new terms which
share the very close text contents
1. when `write` in production mode, text contents for translation terms are more
   important than the HTML/XML structures of them, and the old term translations
   should be remained as much as possible. Because
    * the writing user is responsible to recheck all translations after `write`.
    * it is easier for the writing user to copy technical HTML/XML structures
      than translate text contents for a language they may not know.
    * the feature can also be used as typofix when the only small diff is the
      text content
2. when `write` in upgrade time, the HTML/XML structure is more important than
   the text content, and the new term structure should be remained as much as
   possible. Because
    * HTML/XML structures might be changed a lot after upgrade, which may
      contain behavior relevant diff (e.g. `invisible`), even if text contents
      are not changed.
    * users have no idea which records' values are changed during upgrade and
      are hard to recheck their translations.
    * new terms are highly likely to be correctly translated in the latest po
      files which will be imported during upgrade.
    * the typofix feature can still be remained when the only small diff is the
      text content

Based on the above feature analysis, we use the below new strategy
1. translations can be inherited only if the old source term and the new source
   term share the same HTML/XML structure
2. translations can be inherited only if the old translation term and the new
   source term share the same HTML/XML structure
3. when translations are inherited, MODIFIER_ATTRS will be synchronized with
   the new source term, other attributes will be copied from the source term if
   available.

closes odoo#194186

Backward-port-of: odoo#194181
Backward-port-of: odoo#150152
Signed-off-by: Raphael Collet <rco@odoo.com>
adhoc-cicd-bot pushed a commit to adhoc-cicd/odoo-odoo that referenced this pull request Jan 27, 2025
Before:
The typofix feature treats terms in the old and new values with similar text
content as the same term, migrating the translations of the old term to the new
term.

For example

The old value has the mapping:
'Draft': 'Brouillon'

The new value contains the term:
'<span invisible="name or name_placeholder or quick_edit_mode">Draft</span>'

Since the old term and the new term share the same text content, 'Draft', after
`write`, the new term reuses the old translation of 'Draft'. However, the
translation 'Brouillon' is always visible, unlike its en_US counterpart.

This behavior is acceptable in non-upgrade mode because the user writes the
en_US value and is responsible for verifying translations afterward. However, it
is problematic during upgrades because users cannot easily identify which
records have changed and need to be rechecked.

After:
The translation inheritance behavior can be described as below
Translations can be inherited after `write` from old terms to new terms which
share the very close text contents
1. when `write` in production mode, text contents for translation terms are more
   important than the HTML/XML structures of them, and the old term translations
   should be remained as much as possible. Because
    * the writing user is responsible to recheck all translations after `write`.
    * it is easier for the writing user to copy technical HTML/XML structures
      than translate text contents for a language they may not know.
    * the feature can also be used as typofix when the only small diff is the
      text content
2. when `write` in upgrade time, the HTML/XML structure is more important than
   the text content, and the new term structure should be remained as much as
   possible. Because
    * HTML/XML structures might be changed a lot after upgrade, which may
      contain behavior relevant diff (e.g. `invisible`), even if text contents
      are not changed.
    * users have no idea which records' values are changed during upgrade and
      are hard to recheck their translations.
    * new terms are highly likely to be correctly translated in the latest po
      files which will be imported during upgrade.
    * the typofix feature can still be remained when the only small diff is the
      text content

Based on the above feature analysis, we use the below new strategy
1. translations can be inherited only if the old source term and the new source
   term share the same HTML/XML structure
2. translations can be inherited only if the old translation term and the new
   source term share the same HTML/XML structure
3. when translations are inherited, MODIFIER_ATTRS will be synchronized with
   the new source term, other attributes will be copied from the source term if
   available.

closes odoo#194186

Backward-port-of: odoo#194181
Backward-port-of: odoo#150152
Signed-off-by: Raphael Collet <rco@odoo.com>
adhoc-cicd-bot pushed a commit to adhoc-cicd/odoo-odoo that referenced this pull request Jan 28, 2025
Before:
The typofix feature treats terms in the old and new values with similar text
content as the same term, migrating the translations of the old term to the new
term.

For example

The old value has the mapping:
'Draft': 'Brouillon'

The new value contains the term:
'<span invisible="name or name_placeholder or quick_edit_mode">Draft</span>'

Since the old term and the new term share the same text content, 'Draft', after
`write`, the new term reuses the old translation of 'Draft'. However, the
translation 'Brouillon' is always visible, unlike its en_US counterpart.

This behavior is acceptable in non-upgrade mode because the user writes the
en_US value and is responsible for verifying translations afterward. However, it
is problematic during upgrades because users cannot easily identify which
records have changed and need to be rechecked.

After:
The translation inheritance behavior can be described as below
Translations can be inherited after `write` from old terms to new terms which
share the very close text contents
1. when `write` in production mode, text contents for translation terms are more
   important than the HTML/XML structures of them, and the old term translations
   should be remained as much as possible. Because
    * the writing user is responsible to recheck all translations after `write`.
    * it is easier for the writing user to copy technical HTML/XML structures
      than translate text contents for a language they may not know.
    * the feature can also be used as typofix when the only small diff is the
      text content
2. when `write` in upgrade time, the HTML/XML structure is more important than
   the text content, and the new term structure should be remained as much as
   possible. Because
    * HTML/XML structures might be changed a lot after upgrade, which may
      contain behavior relevant diff (e.g. `invisible`), even if text contents
      are not changed.
    * users have no idea which records' values are changed during upgrade and
      are hard to recheck their translations.
    * new terms are highly likely to be correctly translated in the latest po
      files which will be imported during upgrade.
    * the typofix feature can still be remained when the only small diff is the
      text content

Based on the above feature analysis, we use the below new strategy
1. translations can be inherited only if the old source term and the new source
   term share the same HTML/XML structure
2. translations can be inherited only if the old translation term and the new
   source term share the same HTML/XML structure
3. when translations are inherited, MODIFIER_ATTRS will be synchronized with
   the new source term, other attributes will be copied from the source term if
   available.

closes odoo#194186

Backward-port-of: odoo#194181
Backward-port-of: odoo#150152
Signed-off-by: Raphael Collet <rco@odoo.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

RD research & development, internal work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants