[FIX] website, *: keep table of contents title and menu entries in sync
*: web_editor The table of contents menu entries are generated automatically which poses a problem in translation mode. The menu translation entries are not be editable separately, but the users might be trying to. This commit shows a notification when the user clicks on the menu entries while in translation mode, explaining that they are generated from the title entries. It was initially intended to use a tooltip - but the amount of code needed to display a tooltip without marking the DOM as modified is needlessly complex. To avoid that styles applied on a plain text during translation were also appearing in the navigation menu, an attempt at adding a span around them to make sure that their translation was distinct from the one inside the main content. But this led to the risk of losing existing translations. Because of this, and because that situation seems unlikely, any remaining style in the navigation menu is instead stripped when the table of content is started to maintain consistency with what is shown during translation. An `o_translation_without_style` class has been introduced to indicate to the synchronization mechanism that only the text must be replicated for those elements. For labels that have a different `data-oe-translation-initial-sha` than their related header, that value is temporarily kept in another variable, the value is replaced by the one from the header, which make the synchronization mechanism properly associate them, then on save the initial value is restored so that the translation is saved for the right slot. task-2752391 closes odoo/odoo#116270 X-original-commit: 5776a358e1b42186d2c26c9bc25010a12811f416 Signed-off-by: Outagant Mehdi (mou) <mou@odoo.com>
This commit is contained in:
committed by
Benoit Socias
parent
afcf0fba8d
commit
41e3411776
@@ -1075,6 +1075,10 @@ const Wysiwyg = Widget.extend({
|
||||
return;
|
||||
}
|
||||
let $node = $(field);
|
||||
// Do not forward "unstyled" copies to other nodes.
|
||||
if ($node.hasClass('o_translation_without_style')) {
|
||||
return;
|
||||
}
|
||||
let $nodes = $odooFields.filter(function () {
|
||||
return this !== field;
|
||||
});
|
||||
@@ -1127,6 +1131,16 @@ const Wysiwyg = Widget.extend({
|
||||
}
|
||||
const html = $node.html();
|
||||
for (const node of $nodes) {
|
||||
if (node.classList.contains('o_translation_without_style')) {
|
||||
// For generated elements such as the navigation
|
||||
// labels of website's table of content, only the
|
||||
// text of the referenced translation must be used.
|
||||
const text = $node.text();
|
||||
if (node.innerText !== text) {
|
||||
node.innerText = text;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (node.innerHTML !== html) {
|
||||
node.innerHTML = html;
|
||||
}
|
||||
|
||||
@@ -9009,6 +9009,13 @@ msgid ""
|
||||
"site."
|
||||
msgstr ""
|
||||
|
||||
#. module: website
|
||||
#. odoo-javascript
|
||||
#: code:addons/website/static/src/components/translator/translator.js:0
|
||||
#, python-format
|
||||
msgid "This translation is not editable."
|
||||
msgstr ""
|
||||
|
||||
#. module: website
|
||||
#. odoo-javascript
|
||||
#: code:addons/website/static/src/components/dialog/seo.js:0
|
||||
@@ -9205,6 +9212,13 @@ msgstr ""
|
||||
msgid "Translate Attribute"
|
||||
msgstr ""
|
||||
|
||||
#. module: website
|
||||
#. odoo-javascript
|
||||
#: code:addons/website/static/src/components/translator/translator.js:0
|
||||
#, python-format
|
||||
msgid "Translate header in the text. Menu is generated automatically."
|
||||
msgstr ""
|
||||
|
||||
#. module: website
|
||||
#. odoo-javascript
|
||||
#: code:addons/website/static/src/components/translator/translator.xml:0
|
||||
|
||||
@@ -205,6 +205,40 @@ export class WebsiteTranslator extends WebsiteEditorComponent {
|
||||
styleEl.sheet.insertRule(`[data-oe-translation-state].o_dirty {background: ${translatedColor} !important;}`);
|
||||
styleEl.sheet.insertRule(`[data-oe-translation-state="translated"] {background: ${translatedColor} !important;}`);
|
||||
styleEl.sheet.insertRule(`[data-oe-translation-state] {background: ${toTranslateColor} !important;}`);
|
||||
|
||||
const showNotification = ev => {
|
||||
let message = this.env._t('This translation is not editable.');
|
||||
if (ev.target.closest('.s_table_of_content_navbar_wrap')) {
|
||||
message = this.env._t('Translate header in the text. Menu is generated automatically.');
|
||||
}
|
||||
this.env.services.notification.add(message, {
|
||||
type: 'info',
|
||||
sticky: false,
|
||||
});
|
||||
};
|
||||
for (const translationEl of $editable) {
|
||||
if (translationEl.closest('.o_not_editable')) {
|
||||
translationEl.addEventListener('click', showNotification);
|
||||
}
|
||||
if (translationEl.closest('.s_table_of_content_navbar_wrap')) {
|
||||
// Make sure the same translation ids are used.
|
||||
const href = translationEl.closest('a').getAttribute('href');
|
||||
const headerEl = translationEl.closest('.s_table_of_content').querySelector(`${href} [data-oe-translation-initial-sha]`);
|
||||
if (headerEl) {
|
||||
if (translationEl.dataset.oeTranslationInitialSha !== headerEl.dataset.oeTranslationInitialSha) {
|
||||
// Use the same identifier for the generated navigation
|
||||
// label and its associated header so that the general
|
||||
// synchronization mechanism kicks in.
|
||||
// The initial value is kept to be restored before save
|
||||
// in order to keep the translation of the unstyled
|
||||
// label distinct from the one of the header.
|
||||
translationEl.dataset.oeTranslationSaveSha = translationEl.dataset.oeTranslationInitialSha;
|
||||
translationEl.dataset.oeTranslationInitialSha = headerEl.dataset.oeTranslationInitialSha;
|
||||
}
|
||||
translationEl.classList.add('o_translation_without_style');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
markTranslatableNodes() {
|
||||
|
||||
@@ -301,6 +301,26 @@ snippetsEditor.SnippetsMenu.include({
|
||||
return this._super(...arguments);
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Public
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
async cleanForSave() {
|
||||
// Clean unstyled translations
|
||||
return this._super(...arguments).then(() => {
|
||||
for (const el of this.options.editable[0].querySelectorAll('.o_translation_without_style')) {
|
||||
el.classList.remove('o_translation_without_style');
|
||||
if (el.dataset.oeTranslationSaveSha) {
|
||||
el.dataset.oeTranslationInitialSha = el.dataset.oeTranslationSaveSha;
|
||||
delete el.dataset.oeTranslationSaveSha;
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
@@ -12,6 +12,7 @@ const TableOfContent = publicWidget.Widget.extend({
|
||||
* @override
|
||||
*/
|
||||
async start() {
|
||||
this._stripNavbarStyles();
|
||||
await this._super(...arguments);
|
||||
this.$scrollingElement = $().getScrollingElement();
|
||||
this.previousPosition = -1;
|
||||
@@ -36,6 +37,21 @@ const TableOfContent = publicWidget.Widget.extend({
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
_stripNavbarStyles() {
|
||||
// This is needed for styles added on translations when the master text
|
||||
// has no style.
|
||||
for (let el of this.el.querySelectorAll('.s_table_of_content_navbar .table_of_content_link')) {
|
||||
const translationEl = el.querySelector('span[data-oe-translation-state]');
|
||||
if (translationEl) {
|
||||
el = translationEl;
|
||||
}
|
||||
const text = el.textContent; // Get text from el.
|
||||
el.textContent = text; // Replace all of el's content with that text.
|
||||
}
|
||||
},
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user