Skip to content
Snippets Groups Projects
Commit 57d7e75b authored by xO-Tx's avatar xO-Tx
Browse files

[FIX] website, tools: make select options translatable


Steps to reproduce:

- Go to a website page > Add a 'Form' block > Add a new 'Selection'
field.
- Go to the page (in 'edit_translations' mode) > The selection field
options are not translatable.

The goal of this commit is to make the select options translatable
by adding an intermediate `.o_translation_select` element.

This element will handle option's text translations from the linked
`<select/>`. The final values are copied to the original element
right before save.

opw-3233360

closes odoo/odoo#117519

Signed-off-by: default avatarQuentin Smetz (qsm) <qsm@odoo.com>
parent a816c632
No related branches found
No related tags found
No related merge requests found
......@@ -7453,6 +7453,13 @@ msgstr ""
msgid "Translate Attribute"
msgstr ""
#. module: website
#. openerp-web
#: code:addons/website/static/src/js/editor/wysiwyg_multizone_translate.js:0
#, python-format
msgid "Translate Selection Option"
msgstr ""
#. module: website
#. openerp-web
#: code:addons/website/static/src/xml/translator.xml:0
......
......@@ -69,6 +69,41 @@ var AttributeTranslateDialog = Dialog.extend({
}
});
// Used to translate the text of `<select/>` options since it should not be
// possible to interact with the content of `.o_translation_select` elements.
const SelectTranslateDialog = Dialog.extend({
/**
* @constructor
*/
init: function (parent, options) {
this._super(parent, {
...options,
title: _t("Translate Selection Option"),
buttons: [
{text: _t("Close"), click: this.save}
],
});
this.optionEl = this.options.targetEl;
this.translationObject = this.optionEl.closest('[data-oe-translation-id]');
},
/**
* @override
*/
start: function () {
const inputEl = document.createElement('input');
inputEl.className = 'form-control my-3';
inputEl.value = this.optionEl.textContent;
inputEl.addEventListener('keyup', () => {
this.optionEl.textContent = inputEl.value;
const translationUpdated = inputEl.value !== this.optionEl.dataset.initialTranslationValue;
this.translationObject.classList.toggle('o_dirty', translationUpdated);
this.optionEl.classList.add('o_option_translated');
});
this.el.appendChild(inputEl);
return this._super(...arguments);
},
});
var WysiwygTranslate = WysiwygMultizone.extend({
custom_events: _.extend({}, WysiwygMultizone.prototype.custom_events || {}, {
ready_to_save: '_onSave',
......@@ -101,8 +136,9 @@ var WysiwygTranslate = WysiwygMultizone.extend({
return promise.then(function () {
self._relocateEditorBar();
var attrs = ['placeholder', 'title', 'alt'];
const $editable = self._getEditableArea();
_.each(attrs, function (attr) {
self._getEditableArea().filter('[' + attr + '*="data-oe-translation-id="]').filter(':empty, input, select, textarea, img').each(function () {
$editable.filter('[' + attr + '*="data-oe-translation-id="]').filter(':empty, input, select, textarea, img').each(function () {
var $node = $(this);
var translation = $node.data('translation') || {};
var trans = $node.attr(attr);
......@@ -125,6 +161,25 @@ var WysiwygTranslate = WysiwygMultizone.extend({
});
});
// Hack: we add a temporary element to handle option's text
// translations from the linked <select/>. The final values are
// copied to the original element right before save.
$editable.filter('[data-oe-translation-id] > select').each((index, select) => {
// Keep the default width of the translation `<span/>`.
select.parentElement.classList.remove('o_is_inline_editable');
const selectTranslationEl = document.createElement('div');
selectTranslationEl.className = 'o_translation_select';
const optionNames = [...select.options].map(option => option.text);
optionNames.forEach(option => {
const optionEl = document.createElement('div');
optionEl.textContent = option;
optionEl.dataset.initialTranslationValue = option;
optionEl.className = 'o_translation_select_option';
selectTranslationEl.appendChild(optionEl);
});
select.before(selectTranslationEl);
});
self.translations = [];
self.$editables_attr = self._getEditableArea().filter('.o_translatable_attribute');
self.$editables_attribute = $('.o_editable_translatable_attribute');
......@@ -274,18 +329,25 @@ var WysiwygTranslate = WysiwygMultizone.extend({
});
});
this.$editables_attr.prependEvent('mousedown.translator click.translator mouseup.translator', function (ev) {
if (ev.ctrlKey) {
return;
}
ev.preventDefault();
ev.stopPropagation();
if (ev.type !== 'mousedown') {
return;
}
this.$editables_attr
.add(this._getEditableArea().filter('.o_translation_select_option'))
.prependEvent('mousedown.translator click.translator mouseup.translator', function (ev) {
if (ev.ctrlKey) {
return;
}
ev.preventDefault();
ev.stopPropagation();
if (ev.type !== 'mousedown') {
return;
}
new AttributeTranslateDialog(self, {}, ev.target).open();
});
const targetEl = ev.target;
if (targetEl.closest('.o_translation_select')) {
new SelectTranslateDialog(self, {size: 'medium', targetEl}).open();
} else {
new AttributeTranslateDialog(self, {}, targetEl).open();
}
});
},
//--------------------------------------------------------------------------
......@@ -294,6 +356,19 @@ var WysiwygTranslate = WysiwygMultizone.extend({
_onSave: function (ev) {
ev.stopPropagation();
// Adapt translation values for `select` > `options`s and remove all
// temporary `.o_translation_select` elements.
for (const optionsEl of this.el.querySelectorAll('.o_translation_select')) {
const selectEl = optionsEl.nextElementSibling;
const translatedOptions = optionsEl.children;
const selectOptions = selectEl.tagName === 'SELECT' ? [...selectEl.options] : [];
if (selectOptions.length === translatedOptions.length) {
selectOptions.map((option, i) => {
option.text = translatedOptions[i].textContent;
});
}
optionsEl.remove();
}
},
});
......
......@@ -1551,3 +1551,18 @@ $ribbon-padding: 100px;
opacity: 0;
}
}
[data-oe-translation-id] {
> .o_translation_select {
border: $input-border-width solid $input-border-color;
@include border-radius($input-border-radius, 0);
// Hide translatable `<select/>`s since we use `.o_translation_select`
// elements to handle translations.
& + select {
display: none !important;
}
> div:not(:last-child) {
border-bottom: inherit;
}
}
}
......@@ -81,13 +81,17 @@
}
html[lang] > body.editor_enable [data-oe-translation-state] {
background: rgba($o-we-content-to-translate-color, 0.5) !important;
&, & .o_translation_select_option {
background: rgba($o-we-content-to-translate-color, 0.5) !important;
}
&[data-oe-translation-state="translated"] {
background: rgba($o-we-translated-content-color, 0.5) !important;
&, & .o_translation_select_option {
background: rgba($o-we-translated-content-color, 0.5) !important;
}
}
&.o_dirty {
&.o_dirty, & .o_option_translated {
background: rgba($o-we-translated-content-color, 0.25) !important;
}
}
......
......@@ -140,7 +140,7 @@ TRANSLATED_ELEMENTS = {
'abbr', 'b', 'bdi', 'bdo', 'br', 'cite', 'code', 'data', 'del', 'dfn', 'em',
'font', 'i', 'ins', 'kbd', 'keygen', 'mark', 'math', 'meter', 'output',
'progress', 'q', 'ruby', 's', 'samp', 'small', 'span', 'strong', 'sub',
'sup', 'time', 'u', 'var', 'wbr', 'text',
'sup', 'time', 'u', 'var', 'wbr', 'text', 'select', 'option',
}
# which attributes must be translated
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment