[IMP] website, *: allow to add a language from theme tab + options
*: web_editor, portal This adds a button to add language in theme tab. When clicked, opens the "add a language" wizard. If the website has only one language, the options to enable language in header & footer are hidden. When an user add a language, the default behavior is Header: dropdown & Footer: inline. Also provides two more rendering options : Language Code and Flag + Language Code. task-3457554 closes odoo/odoo#119650 Related: odoo/design-themes#651 Related: odoo/upgrade#5214 Signed-off-by: Quentin Smetz (qsm) <qsm@odoo.com> Co-authored-by: qsm-odoo <qsm@odoo.com>
This commit is contained in:
committed by
qsm-odoo
co-authored by
qsm-odoo
parent
fb63a6abbb
commit
dc81acfb2d
@@ -71,6 +71,7 @@
|
||||
<template id="language_selector" name="Language Selector">
|
||||
<t t-nocache="The query strings can change for the same page and the same rendering."
|
||||
t-nocache-no_text="no_text"
|
||||
t-nocache-codes="codes"
|
||||
t-nocache-_div_classes="_div_classes"
|
||||
t-nocache-_btn_class="_btn_class"
|
||||
t-nocache-_txt_class="_txt_class"
|
||||
@@ -82,13 +83,15 @@
|
||||
<span t-if="not no_text"
|
||||
t-attf-class="align-middle #{_txt_class}"
|
||||
t-esc="active_lang[2].split('/').pop()"/>
|
||||
<span t-elif="codes" class="align-middle" t-esc="active_lang[1].split('_').pop(0).upper()"/>
|
||||
</button>
|
||||
<div t-attf-class="dropdown-menu #{_dropdown_menu_class}" role="menu">
|
||||
<t t-foreach="languages" t-as="lg">
|
||||
<a class="dropdown-item" t-att-href="url_for(request.httprequest.path + '?' + keep_query(), lang_code=lg[0])"
|
||||
t-attf-class="dropdown-item js_change_lang #{active_lang == lg and 'active'}"
|
||||
t-att-data-url_code="lg[1]" t-att-title="lg[2].split('/').pop()">
|
||||
<span t-if="not no_text" t-out="lg[2].split('/').pop()" t-attf-class="#{_txt_class}"/>
|
||||
<span t-if="not no_text" t-esc="lg[2].split('/').pop()" t-attf-class="#{_txt_class}"/>
|
||||
<span t-elif="codes" t-esc="lg[1].split('_').pop(0).upper()" t-attf-class="align-middle #{_txt_class}"/>
|
||||
</a>
|
||||
</t>
|
||||
</div>
|
||||
|
||||
@@ -2109,7 +2109,7 @@ var SnippetsMenu = Widget.extend({
|
||||
this.el.ownerDocument.body.classList.remove('editor_has_snippets');
|
||||
// Dispose BS tooltips.
|
||||
this.tooltips.dispose();
|
||||
options.clearM2oRpcCache();
|
||||
options.clearServiceCache();
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
@@ -45,7 +45,38 @@ import { jsonrpc } from "@web/core/network/rpc_service";
|
||||
const preserveCursor = OdooEditorLib.preserveCursor;
|
||||
const { DateTime } = luxon;
|
||||
const resetOuids = OdooEditorLib.resetOuids;
|
||||
|
||||
let _serviceCache = {
|
||||
orm: {},
|
||||
rpc: {},
|
||||
};
|
||||
const clearServiceCache = () => {
|
||||
_serviceCache = {
|
||||
orm: {},
|
||||
rpc: {},
|
||||
};
|
||||
};
|
||||
/**
|
||||
* Caches rpc/orm service
|
||||
* @param {Function} service
|
||||
* @param {...any} args
|
||||
* @returns
|
||||
*/
|
||||
function serviceCached(service) {
|
||||
const cache = _serviceCache;
|
||||
return Object.assign(Object.create(service), {
|
||||
call() {
|
||||
const serviceName = Object.prototype.hasOwnProperty.call(service, "call")
|
||||
? "orm"
|
||||
: "rpc";
|
||||
const cacheId = JSON.stringify(arguments);
|
||||
if (!cache[serviceName][cacheId]) {
|
||||
cache[serviceName][cacheId] =
|
||||
serviceName == "rpc" ? service(...arguments) : service.call(...arguments);
|
||||
}
|
||||
return cache[serviceName][cacheId];
|
||||
},
|
||||
});
|
||||
}
|
||||
/**
|
||||
* @param {HTMLElement} el
|
||||
* @param {string} [title]
|
||||
@@ -2596,10 +2627,6 @@ const SelectPagerUserValueWidget = SelectUserValueWidget.extend({
|
||||
}
|
||||
});
|
||||
|
||||
let m2oRpcCache = {};
|
||||
const clearM2oRpcCache = () => {
|
||||
m2oRpcCache = {};
|
||||
};
|
||||
const Many2oneUserValueWidget = SelectUserValueWidget.extend({
|
||||
className: (SelectUserValueWidget.prototype.className || '') + ' o_we_many2one',
|
||||
events: Object.assign({}, SelectUserValueWidget.prototype.events, {
|
||||
@@ -2621,7 +2648,6 @@ const Many2oneUserValueWidget = SelectUserValueWidget.extend({
|
||||
init(parent, title, options, $target) {
|
||||
this.afterSearch = [];
|
||||
this.displayNameCache = {};
|
||||
this._rpcCache = m2oRpcCache;
|
||||
const {dataAttributes} = options;
|
||||
Object.assign(options, {
|
||||
limit: '5',
|
||||
@@ -2645,7 +2671,7 @@ const Many2oneUserValueWidget = SelectUserValueWidget.extend({
|
||||
options.nullText = $target[0].dataset.nullText ||
|
||||
JSON.parse($target[0].dataset.oeContactOptions || '{}')['null_text'];
|
||||
|
||||
this.orm = this._withCache(this.bindService("orm"));
|
||||
this.orm = serviceCached(this.bindService("orm"));
|
||||
|
||||
return this._super(...arguments);
|
||||
},
|
||||
@@ -2773,23 +2799,6 @@ const Many2oneUserValueWidget = SelectUserValueWidget.extend({
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Caches the rpc.
|
||||
*
|
||||
* @override
|
||||
*/
|
||||
_withCache(orm) {
|
||||
const cache = this._rpcCache;
|
||||
return Object.assign(Object.create(orm), {
|
||||
call() {
|
||||
const cacheId = JSON.stringify(arguments);
|
||||
if (!cache[cacheId]) {
|
||||
cache[cacheId] = orm.call(...arguments);
|
||||
}
|
||||
return cache[cacheId];
|
||||
},
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Searches the database for corresponding records and updates the dropdown
|
||||
*
|
||||
@@ -9209,6 +9218,6 @@ export default {
|
||||
// Other names for convenience
|
||||
Class: SnippetOptionWidget,
|
||||
registry: registry,
|
||||
|
||||
clearM2oRpcCache,
|
||||
serviceCached,
|
||||
clearServiceCache,
|
||||
};
|
||||
|
||||
@@ -988,7 +988,7 @@ export class WysiwygAdapterComponent extends Wysiwyg {
|
||||
} else if (event.data.action) {
|
||||
callback = () => {
|
||||
this.leaveEditMode({
|
||||
onLeave: () => this.action.doAction(event.data.action),
|
||||
onLeave: () => this.action.doAction(event.data.action, event.data.options || {}),
|
||||
forceLeave: true,
|
||||
reloadIframe: false,
|
||||
});
|
||||
|
||||
@@ -1406,6 +1406,39 @@ options.registry.OptionsTab = options.Class.extend({
|
||||
action: 'website.theme_install_kanban_action',
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
async addLanguage(previewMode, widgetValue, params) {
|
||||
// Retrieve the website id to check by default the website checkbox in
|
||||
// the dialog box 'action_view_base_language_install'
|
||||
const websiteId = this.options.context.website_id;
|
||||
const save = await new Promise((resolve) => {
|
||||
Dialog.confirm(
|
||||
this,
|
||||
_t("Adding a language requires to leave the editor. This will save all your changes, are you sure you want to proceed?"),
|
||||
{
|
||||
confirm_callback: () => resolve(true),
|
||||
cancel_callback: () => resolve(false),
|
||||
}
|
||||
);
|
||||
});
|
||||
if (!save) {
|
||||
return;
|
||||
}
|
||||
this.trigger_up("request_save", {
|
||||
reload: false,
|
||||
action: "base.action_view_base_language_install",
|
||||
options: {
|
||||
additionalContext: {
|
||||
params: {
|
||||
website_id: websiteId,
|
||||
url_return: "[lang]",
|
||||
}
|
||||
},
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
@@ -2191,7 +2224,13 @@ options.registry.collapse = options.Class.extend({
|
||||
options.registry.WebsiteLevelColor = options.Class.extend({
|
||||
specialCheckAndReloadMethodsNames: options.Class.prototype.specialCheckAndReloadMethodsNames
|
||||
.concat(['customizeWebsiteLayer2Color']),
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this._rpc = options.serviceCached(this.bindService("rpc"));
|
||||
},
|
||||
/**
|
||||
* @see this.selectClass for parameters
|
||||
*/
|
||||
@@ -2234,6 +2273,24 @@ options.registry.WebsiteLevelColor = options.Class.extend({
|
||||
}
|
||||
return this._super(...arguments);
|
||||
},
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
async _computeWidgetVisibility(widgetName, params) {
|
||||
const _super = this._super.bind(this);
|
||||
if (
|
||||
[
|
||||
"footer_language_selector_label_opt",
|
||||
"footer_language_selector_opt",
|
||||
].includes(widgetName)
|
||||
) {
|
||||
this._languages = await this._rpc.call("/website/get_languages");
|
||||
if (this._languages.length === 1) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return _super(...arguments);
|
||||
},
|
||||
});
|
||||
|
||||
options.registry.HeaderLayout = options.registry.WebsiteLevelColor.extend({
|
||||
@@ -2259,6 +2316,36 @@ options.registry.HeaderLayout = options.registry.WebsiteLevelColor.extend({
|
||||
}
|
||||
});
|
||||
|
||||
options.registry.HeaderElements = options.Class.extend({
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
init() {
|
||||
this._super(...arguments);
|
||||
this._rpc = options.serviceCached(this.bindService("rpc"));
|
||||
},
|
||||
|
||||
//--------------------------------------------------------------------------
|
||||
// Private
|
||||
//--------------------------------------------------------------------------
|
||||
|
||||
/**
|
||||
* @override
|
||||
*/
|
||||
async _computeWidgetVisibility(widgetName, params) {
|
||||
const _super = this._super.bind(this);
|
||||
switch (widgetName) {
|
||||
case "header_language_selector_opt":
|
||||
this._languages = await this._rpc.call("/website/get_languages");
|
||||
if (this._languages.length === 1) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return _super(...arguments);
|
||||
},
|
||||
});
|
||||
|
||||
options.registry.HeaderNavbar = options.Class.extend({
|
||||
/**
|
||||
* Particular case: we want the option to be associated on the header navbar
|
||||
|
||||
@@ -1452,6 +1452,7 @@ header {
|
||||
margin-right: 0.2em;
|
||||
border-radius: $border-radius-pill;
|
||||
object-fit: cover;
|
||||
box-shadow: rgba(0, 0, 0, 0.429) 1px 1px 1px;
|
||||
}
|
||||
span.list-inline-item.o_add_language:last-child {
|
||||
display: none !important; // Hide the separator if it is the last list item
|
||||
|
||||
@@ -27,7 +27,7 @@ export class WebsiteSwitcherSystray extends Component {
|
||||
const path = pathname + search + hash;
|
||||
window.location.href = `${encodeURI(website.domain)}/web#action=website.website_preview&path=${encodeURIComponent(path)}&website_id=${encodeURIComponent(website.id)}`;
|
||||
} else {
|
||||
this.websiteService.goToWebsite({ websiteId: website.id });
|
||||
this.websiteService.goToWebsite({ websiteId: website.id, path: "", lang: "default" });
|
||||
if (!website.domain) {
|
||||
const closeFn = this.notificationService.add(
|
||||
_t(
|
||||
|
||||
@@ -4,16 +4,18 @@ import wTourUtils from "@website/js/tours/tour_utils";
|
||||
|
||||
wTourUtils.registerWebsitePreviewTour('automatic_editor_on_new_website', {
|
||||
test: true,
|
||||
edition: true,
|
||||
url: '/',
|
||||
},
|
||||
() => [
|
||||
{
|
||||
content: "Select the language dropdown",
|
||||
trigger: 'iframe .js_language_selector .dropdown-toggle'
|
||||
},
|
||||
wTourUtils.goToTheme(),
|
||||
{
|
||||
content: "click on Add a language",
|
||||
trigger: 'iframe a.o_add_language',
|
||||
trigger: "we-button[data-add-language]"
|
||||
},
|
||||
{
|
||||
content: "confirm leave editor",
|
||||
trigger: ".modal-dialog button.btn-primary"
|
||||
},
|
||||
{
|
||||
content: "type Parseltongue",
|
||||
@@ -37,7 +39,6 @@ wTourUtils.registerWebsitePreviewTour('automatic_editor_on_new_website', {
|
||||
{
|
||||
content: "Select parseltongue",
|
||||
trigger: 'iframe a.js_change_lang[data-url_code=pa_GB]',
|
||||
extra_trigger: 'iframe a.js_change_lang .o_lang_flag',
|
||||
},
|
||||
{
|
||||
content: "Check that we're on parseltongue and then go to settings",
|
||||
|
||||
@@ -6,13 +6,16 @@ import { whenReady } from "@odoo/owl";
|
||||
wTourUtils.registerWebsitePreviewTour('rte_translator', {
|
||||
test: true,
|
||||
url: '/',
|
||||
edition: true,
|
||||
wait_for: whenReady(),
|
||||
}, () => [{
|
||||
content: "click language dropdown",
|
||||
trigger: 'iframe .js_language_selector .dropdown-toggle',
|
||||
}, {
|
||||
}, () => [
|
||||
wTourUtils.goToTheme(),
|
||||
{
|
||||
content: "click on Add a language",
|
||||
trigger: 'iframe a.o_add_language',
|
||||
trigger: "we-button[data-add-language]"
|
||||
}, {
|
||||
content: "confirm leave editor",
|
||||
trigger: ".modal-dialog button.btn-primary"
|
||||
}, {
|
||||
content: "type Parseltongue",
|
||||
trigger: 'div[name="lang_ids"] .o_input_dropdown input',
|
||||
|
||||
@@ -987,7 +987,8 @@
|
||||
data-page-option-name="header_text_color"/>
|
||||
</div>
|
||||
|
||||
<div data-selector="#wrapwrap > header"
|
||||
<div data-js="HeaderElements"
|
||||
data-selector="#wrapwrap > header"
|
||||
data-no-check="true"
|
||||
groups="website.group_website_designer">
|
||||
<we-row string="Elements" class="o_we_full_row align-items-start">
|
||||
@@ -997,6 +998,7 @@
|
||||
data-reset-view-arch="true"
|
||||
data-reload="/"/>
|
||||
<we-button title="Show/hide language selector" class="fa fa-flag d-flex justify-content-center flex-grow-1"
|
||||
data-name="header_language_selector_opt"
|
||||
data-customize-website-views="website.header_language_selector"
|
||||
data-reset-view-arch="true"
|
||||
data-reload="/"/>
|
||||
@@ -1107,6 +1109,8 @@
|
||||
<we-button data-customize-website-views="">Text</we-button>
|
||||
<we-button data-customize-website-views="website.header_language_selector_flag, website.header_language_selector_no_text">Flag</we-button>
|
||||
<we-button data-customize-website-views="website.header_language_selector_flag">Flag and Text</we-button>
|
||||
<we-button data-customize-website-views="website.header_language_selector_code, website.header_language_selector_no_text">Code</we-button>
|
||||
<we-button data-customize-website-views="website.header_language_selector_flag, website.header_language_selector_code, website.header_language_selector_no_text">Flag and Code</we-button>
|
||||
</we-select>
|
||||
</div>
|
||||
|
||||
@@ -1259,16 +1263,18 @@
|
||||
data-null-value="'NULL'"
|
||||
data-with-combinations="customizeWebsiteColor"
|
||||
data-with-gradients="true"/>
|
||||
<we-select string="Language Selector" data-reload="/">
|
||||
<we-select data-name="footer_language_selector_opt" string="Language Selector" data-reload="/">
|
||||
<we-button data-name="language_selector_none_opt"
|
||||
data-customize-website-views="">None</we-button>
|
||||
<we-button data-customize-website-views="portal.footer_language_selector">Dropdown</we-button>
|
||||
<we-button data-customize-website-views="portal.footer_language_selector, website.footer_language_selector_inline">Inline</we-button>
|
||||
</we-select>
|
||||
<we-select string="Label" class="o_we_sublevel_1" data-dependencies="!language_selector_none_opt" data-reload="/">
|
||||
<we-select data-name="footer_language_selector_label_opt" string="Label" class="o_we_sublevel_1" data-dependencies="!language_selector_none_opt" data-reload="/">
|
||||
<we-button data-customize-website-views="">Text</we-button>
|
||||
<we-button data-customize-website-views="website.footer_language_selector_flag, website.footer_language_selector_no_text">Flag</we-button>
|
||||
<we-button data-customize-website-views="website.footer_language_selector_flag">Flag and Text</we-button>
|
||||
<we-button data-customize-website-views="website.footer_language_selector_code, website.footer_language_selector_no_text">Code</we-button>
|
||||
<we-button data-customize-website-views="website.footer_language_selector_flag, website.footer_language_selector_code, website.footer_language_selector_no_text">Flag and Code</we-button>
|
||||
</we-select>
|
||||
</div>
|
||||
|
||||
@@ -1690,6 +1696,9 @@
|
||||
<we-row string="Theme">
|
||||
<we-button data-switch-theme="" data-no-preview="true" class="o_we_bg_brand_primary">Switch Theme</we-button>
|
||||
</we-row>
|
||||
<we-row string="Language">
|
||||
<we-button data-add-language="" data-no-preview="true" class="o_we_bg_brand_primary">Add a Language</we-button>
|
||||
</we-row>
|
||||
<we-select string="Page Layout" data-variable="layout">
|
||||
<we-button data-customize-website-variable="'full'" data-name="layout_full_opt">Full</we-button>
|
||||
<we-button data-customize-website-variable="'boxed'">Boxed</we-button>
|
||||
|
||||
@@ -2078,17 +2078,6 @@
|
||||
<img t-attf-class="o_lang_flag #{_flag_class}" t-attf-src="#{flag_image_src}?height=25"/>
|
||||
</template>
|
||||
|
||||
<template id="language_selector_add_language">
|
||||
<!-- When clicked from the iframe preview in backend, the click will be
|
||||
prevented to directly call the action in the top window -->
|
||||
<a t-attf-class="o_add_language d-none #{dropdown and 'd-sm-block dropdown-item' or 'd-sm-inline-block list-inline-item'} small"
|
||||
groups="website.group_website_restricted_editor"
|
||||
t-attf-href="/web#action=base.action_view_base_language_install&website_id=#{website.id if website else ''}&url_return=#{quote_plus(url_for('', '[lang]') + '?' + keep_query())}">
|
||||
<i class="fa fa-plus-circle"/>
|
||||
<span>Add a language...</span>
|
||||
</a>
|
||||
</template>
|
||||
|
||||
<template id="language_selector" inherit_id="portal.language_selector">
|
||||
<xpath expr="//t[@t-nocache]" position="attributes">
|
||||
<attribute name="t-nocache-flags">flags</attribute>
|
||||
@@ -2101,18 +2090,6 @@
|
||||
</t>
|
||||
</xpath>
|
||||
|
||||
<!-- Always show the language selector on editable websites -->
|
||||
<xpath expr="//t[@t-set='language_selector_visible']" position="after">
|
||||
<t t-set="language_selector_visible" t-value="language_selector_visible or (website and (editable or translatable))"/>
|
||||
</xpath>
|
||||
|
||||
<!-- Add the button to add a language -->
|
||||
<xpath expr="//t[@t-foreach='languages']" position="after">
|
||||
<t t-call="website.language_selector_add_language">
|
||||
<t t-set="dropdown" t-value="True"/>
|
||||
</t>
|
||||
</xpath>
|
||||
|
||||
<!-- Add the 'flags' possibility -->
|
||||
<xpath expr="//button[contains(@t-attf-class, 'dropdown-toggle')]/span" position="before">
|
||||
<t t-if="flags" t-call="website.lang_flag">
|
||||
@@ -2144,18 +2121,11 @@
|
||||
<span class="list-inline-item">|</span>
|
||||
</t>
|
||||
</xpath>
|
||||
<xpath expr="//t[@t-call='website.language_selector_add_language']" position="replace">
|
||||
<span class="o_add_language list-inline-item"
|
||||
groups="website.group_website_restricted_editor">|</span>
|
||||
<t t-call="website.language_selector_add_language">
|
||||
<t t-set="dropdown" t-value="False"/>
|
||||
</t>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="placeholder_header_language_selector" name="Placeholder Header Language Selector"/>
|
||||
|
||||
<template id="header_language_selector" inherit_id="website.placeholder_header_language_selector" name="Header Language Selector" active="False">
|
||||
<template id="header_language_selector" inherit_id="website.placeholder_header_language_selector" name="Header Language Selector" active="True">
|
||||
<xpath expr="." position="inside">
|
||||
<li data-name="Language Selector" t-attf-class="o_header_language_selector #{_item_class}">
|
||||
<t id="header_language_selector_call" t-call="portal.language_selector">
|
||||
@@ -2171,30 +2141,42 @@
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="header_language_selector_flag" name="Header Language Selector Flag" inherit_id="website.header_language_selector" active="True">
|
||||
<template id="header_language_selector_flag" name="Header Language Selector Flag" inherit_id="website.header_language_selector" active="False">
|
||||
<xpath expr="//t[@id='header_language_selector_call']" position="before">
|
||||
<t t-set="flags" t-value="True"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="header_language_selector_code" name="Header Language Selector Code" inherit_id="website.header_language_selector" active="False">
|
||||
<xpath expr="//t[@id='header_language_selector_call']" position="before">
|
||||
<t t-set="codes" t-value="True"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="header_language_selector_no_text" name="Header Language Selector No Text" inherit_id="website.header_language_selector" active="False">
|
||||
<xpath expr="//t[@id='header_language_selector_call']" position="before">
|
||||
<t t-set="no_text" t-value="True"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="footer_language_selector_inline" name="Footer Language Selector Inline" inherit_id="portal.footer_language_selector" active="False">
|
||||
<template id="footer_language_selector_inline" name="Footer Language Selector Inline" inherit_id="portal.footer_language_selector" active="True">
|
||||
<xpath expr="//t[@id='language_selector_call']" position="replace">
|
||||
<t id="language_selector_call" t-call="website.language_selector_inline"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="footer_language_selector_flag" name="Footer Language Selector Flag" inherit_id="portal.footer_language_selector" active="True">
|
||||
<template id="footer_language_selector_flag" name="Footer Language Selector Flag" inherit_id="portal.footer_language_selector" active="False">
|
||||
<xpath expr="//t[@id='language_selector_call']" position="before">
|
||||
<t t-set="flags" t-value="True"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="footer_language_selector_code" name="Footer Language Selector Code" inherit_id="portal.footer_language_selector" active="False">
|
||||
<xpath expr="//t[@id='language_selector_call']" position="before">
|
||||
<t t-set="codes" t-value="True"/>
|
||||
</xpath>
|
||||
</template>
|
||||
|
||||
<template id="footer_language_selector_no_text" name="Footer Language Selector No Text" inherit_id="portal.footer_language_selector" active="False">
|
||||
<xpath expr="//t[@id='language_selector_call']" position="before">
|
||||
<t t-set="no_text" t-value="True"/>
|
||||
|
||||
Reference in New Issue
Block a user