From b43d117e0ecd849d6bfab0c90153567edd97e845 Mon Sep 17 00:00:00 2001 From: "Chong Wang (cwg)" Date: Wed, 7 Sep 2022 21:13:44 +0200 Subject: [PATCH] [IMP] web, web_editor: translation dialog for new translate api --- .../legacy/js/views/basic/basic_controller.js | 16 +-- .../legacy/js/widgets/translation_dialog.js | 71 ++++++----- .../src/legacy/xml/translation_dialog.xml | 8 +- .../src/views/fields/translation_button.js | 15 +-- .../src/views/fields/translation_dialog.js | 50 +++++--- .../tests/legacy/fields/basic_fields_tests.js | 82 ++++-------- .../static/tests/legacy/views/form_tests.js | 59 ++------- .../static/tests/legacy/views/list_tests.js | 35 +----- .../tests/views/fields/char_field_tests.js | 112 ++++++----------- .../tests/views/fields/text_field_tests.js | 42 +++---- .../tests/views/form/form_view_tests.js | 117 +++++++----------- .../web/static/tests/views/list_view_tests.js | 43 ++----- .../static/tests/field_html_tests.js | 30 ++--- 13 files changed, 237 insertions(+), 443 deletions(-) diff --git a/addons/web/static/src/legacy/js/views/basic/basic_controller.js b/addons/web/static/src/legacy/js/views/basic/basic_controller.js index bd48e2ab3bf..8fc49d99031 100644 --- a/addons/web/static/src/legacy/js/views/basic/basic_controller.js +++ b/addons/web/static/src/legacy/js/views/basic/basic_controller.js @@ -944,25 +944,15 @@ var BasicController = AbstractController.extend(FieldManagerMixin, { } var record = this.model.get(ev.data.id, { raw: true }); var res_id = record.res_id || record.res_ids[0]; - var result = await this._rpc({ - route: '/web/dataset/call_button', - params: { - model: 'ir.translation', - method: 'translate_fields', - args: [record.model, res_id, ev.data.fieldName], - kwargs: { context: record.getContext() }, - } - }); this.translationDialog = new TranslationDialog(this, { - domain: result.domain, - searchName: result.context.search_default_name, fieldName: ev.data.fieldName, + resId: res_id, userLanguageValue: ev.target.value || '', + dataPointModel: record.model, dataPointID: record.id, isComingFromTranslationAlert: ev.data.isComingFromTranslationAlert, - isText: result.context.translation_type === 'text', - showSrc: result.context.translation_show_src, + context: record.getContext(), }); return this.translationDialog.open(); }, diff --git a/addons/web/static/src/legacy/js/widgets/translation_dialog.js b/addons/web/static/src/legacy/js/widgets/translation_dialog.js index 4e145bff33e..2655304a3d0 100644 --- a/addons/web/static/src/legacy/js/widgets/translation_dialog.js +++ b/addons/web/static/src/legacy/js/widgets/translation_dialog.js @@ -14,29 +14,24 @@ odoo.define('web.TranslationDialog', function (require) { * @constructor * @param {Widget} parent * @param {Object} [options] - * @param {string} [options.domain] the domain needed to get the translation terms * @param {string} [options.fieldName] the name of the field currently translated (from the model of the form view) - * @param {string} [options.searchName] the name of the actual field that is the reference for translation (in the form of model,field) + * @param {integer} [options.resId] the ID of record currently translated * @param {string} [options.userLanguageValue] the value of the translation in the language of the user, as seen in the from view (might be empty) * @param {string} [options.dataPointID] the data point id of the record for which we do the translations * @param {boolean} [options.isComingFromTranslationAlert] the initiator of the dialog, might be a link on a field or the translation alert on top of the form - * @param {boolean} [options.isText] is the field a text field (multiline) or char (single line) - * @param {boolean} [options.showSrc] is the source of the translation should be rendered (for partial translations, i.e. XML content) * */ init: function (parent, options) { options = options || {}; this.fieldName = options.fieldName; - this.domain = options.domain; - this.searchName = options.searchName; + this.resId = options.resId; this.userLanguageValue = options.userLanguageValue; - this.domain.push(['name', "=", `${this.searchName}`]); + this.dataPointModel = options.dataPointModel; this.dataPointID = options.dataPointID; this.isComingFromTranslationAlert = options.isComingFromTranslationAlert; this.currentInterfaceLanguage = session.user_context.lang; - this.isText = options.isText; - this.showSrc = options.showSrc; + this.context = options.context; this._super(parent, _.extend({ size: 'large', @@ -56,31 +51,35 @@ odoo.define('web.TranslationDialog', function (require) { this._loadLanguages().then((l) => { this.languages = l; return this._loadTranslations().then((t) => { - this.translations = t; + [this.translations, this.context] = t; + let id = 1; + this.translations.forEach((t) => t['id'] = id++); + this.isText = this.context.translation_type === 'text'; + this.showSource = this.context.translation_show_source; }); }), ]).then(() => { this.data = this.translations.map((term) => { let relatedLanguage = this.languages.find((language) => language[0] === term.lang); - if (!term.value && !this.showSrc) { - term.value = term.src; + if (!term.value && !this.showSource) { + term.value = term.source; } return { id: term.id, lang: term.lang, langName: relatedLanguage[1], - source: term.src, + source: term.source, // we set the translation value coming from the database, except for the language // the user is currently utilizing. Then we set the translation value coming // from the value of the field in the form value: (term.lang === this.currentInterfaceLanguage && - !this.showSrc && + !this.showSource && !this.isComingFromTranslationAlert) ? this.userLanguageValue : term.value || '' }; }); this.data.sort((left, right) => - (left.langName < right.langName || (left.langName === right.langName && left.source < right.source)) ? -1 : 1); + left.langName.localeCompare(right.langName)); }); }, @@ -92,12 +91,11 @@ odoo.define('web.TranslationDialog', function (require) { * @private */ _loadTranslations: function () { - const domain = [...this.domain, ['lang', 'in', this.languages.map(l => l[0])]]; return this._rpc({ - model: 'ir.translation', - method: 'search_read', - fields: ['lang', 'src', 'value'], - domain: domain, + model: this.dataPointModel, + method: 'get_field_translations', + args: [[this.resId], this.fieldName], + context: this.context, }); }, /** @@ -137,16 +135,16 @@ odoo.define('web.TranslationDialog', function (require) { this.el.querySelectorAll('input[type=text],textarea').forEach((t) => { var initialValue = this.data.find((d) => d.id == t.dataset.id); if (initialValue.value !== t.value) { - updatedTerm[t.dataset.id] = t.value; + updatedTerm[t.dataset.id] = {lang: initialValue.lang, source: initialValue.source, value: t.value}; - if (initialValue.lang === this.currentInterfaceLanguage && !this.showSrc) { + if (initialValue.lang === this.currentInterfaceLanguage && !this.showSource) { // when the user has changed the term for the language he is // using in the interface, this change should be reflected // in the form view // partial translations being handled server side are // also ignored var changes = {}; - changes[this.fieldName] = updatedTerm[initialValue.id]; + changes[this.fieldName] = t.value; updateFormViewField = { dataPointID: this.dataPointID, changes: changes, @@ -158,16 +156,23 @@ odoo.define('web.TranslationDialog', function (require) { // updatedTerm only contains the id and values of the terms that // have been updated by the user - var saveUpdatedTermsProms = Object.keys(updatedTerm).map((id) => { - var writeTranslation = { - model: 'ir.translation', - method: 'write', - context: this.context, - args: [[parseInt(id, 10)], { value: updatedTerm[id] }] - }; - return this._rpc(writeTranslation); - }); - return Promise.all(saveUpdatedTermsProms).then(() => { + const translations = {}; + if (this.showSource) { // model terms translation + Object.entries(updatedTerm).forEach(([id, term]) => { + if (!translations[term.lang]) { + translations[term.lang] = {}; + } + translations[term.lang][term.source] = term.value; + }); + } + else { // model translation + Object.entries(updatedTerm).forEach(([id, term]) => translations[term.lang] = term.value); + } + return this._rpc({ + model: this.dataPointModel, + method: 'update_field_translations', + args: [[this.resId], this.fieldName, translations], + }).then(() => { // we might have to update the value of the field on the form // view that opened the translation dialog if (updateFormViewField) { diff --git a/addons/web/static/src/legacy/xml/translation_dialog.xml b/addons/web/static/src/legacy/xml/translation_dialog.xml index 2ccfc981ec1..37bc49a1a61 100644 --- a/addons/web/static/src/legacy/xml/translation_dialog.xml +++ b/addons/web/static/src/legacy/xml/translation_dialog.xml @@ -2,13 +2,13 @@
-
+
-
+
-
+
diff --git a/addons/web/static/src/views/fields/translation_button.js b/addons/web/static/src/views/fields/translation_button.js index 769ecffaea0..a92a79ce66e 100644 --- a/addons/web/static/src/views/fields/translation_button.js +++ b/addons/web/static/src/views/fields/translation_button.js @@ -17,7 +17,6 @@ const { Component, useEnv } = owl; */ export function useTranslationDialog() { const dialog = useService("dialog"); - const rpc = useService("rpc"); const env = useEnv(); async function openTranslationDialog({ record, fieldName, updateField }) { @@ -43,22 +42,14 @@ export function useTranslationDialog() { return; } } - const { resModel, resId, context } = record; - const result = await rpc("/web/dataset/call_button", { - model: "ir.translation", - method: "translate_fields", - args: [resModel, resId, fieldName], - kwargs: { context }, - }); + const { resModel, resId } = record; dialog.add(TranslationDialog, { - domain: result.domain, - searchName: result.context.search_default_name, fieldName: fieldName, + resId: resId, + resModel: resModel, userLanguageValue: record.data[fieldName] || "", isComingFromTranslationAlert: false, - isText: result.context.translation_type === "text", - showSource: result.context.translation_show_src, updateField, }); } diff --git a/addons/web/static/src/views/fields/translation_dialog.js b/addons/web/static/src/views/fields/translation_dialog.js index 0cf8348e013..5e227d8d517 100644 --- a/addons/web/static/src/views/fields/translation_dialog.js +++ b/addons/web/static/src/views/fields/translation_dialog.js @@ -20,18 +20,22 @@ export class TranslationDialog extends Component { onWillStart(async () => { const languages = await loadLanguages(this.orm); - const translations = await this.loadTranslations(languages); + const [translations, context] = await this.loadTranslations(languages); + let id = 1; + translations.forEach((t) => (t.id = id++)); + this.props.isText = context.translation_type === "text"; + this.props.showSource = context.translation_show_source; this.terms = translations.map((term) => { const relatedLanguage = languages.find((l) => l[0] === term.lang); if (!term.value && !this.props.showSource) { - term.value = term.src; + term.value = term.source; } return { id: term.id, lang: term.lang, langName: relatedLanguage[1], - source: term.src, + source: term.source, // we set the translation value coming from the database, except for the language // the user is currently utilizing. Then we set the translation value coming // from the value of the field in the form @@ -43,11 +47,7 @@ export class TranslationDialog extends Component { : term.value || "", }; }); - this.terms.sort((a, b) => - a.langName < b.langName || (a.langName === b.langName && a.source < b.source) - ? -1 - : 1 - ); + this.terms.sort((a, b) => a.langName.localeCompare(b.langName)); }); } @@ -63,23 +63,37 @@ export class TranslationDialog extends Component { * Load the translation terms for the installed language, for the current model and res_id */ async loadTranslations(languages) { - const domain = [...this.domain, ["lang", "in", languages.map((l) => l[0])]]; - return this.orm.searchRead("ir.translation", domain, ["lang", "src", "value"]); + return this.orm.call(this.props.resModel, "get_field_translations", [ + [this.props.resId], + this.props.fieldName, + ]); } /** * Save all the terms that have been updated */ async onSave() { - await Promise.all( - this.terms.map(async (term) => { - if (term.id in this.updatedTerms && term.value !== this.updatedTerms[term.id]) { - await this.orm.write("ir.translation", [term.id], { - value: this.updatedTerms[term.id], - }); + const translations = {}; + + this.terms.map((term) => { + const updatedTermValue = this.updatedTerms[term.id]; + if (term.id in this.updatedTerms && term.value !== updatedTermValue) { + if (this.props.showSource) { + if (!translations[term.lang]) { + translations[term.lang] = {}; + } + translations[term.lang][term.source] = updatedTermValue; + } else { + translations[term.lang] = updatedTermValue; } - }) - ); + } + }); + + await this.orm.call(this.props.resModel, "update_field_translations", [ + [this.props.resId], + this.props.fieldName, + translations, + ]); // we might have to update the value of the field on the form // view that opened the translation dialog diff --git a/addons/web/static/tests/legacy/fields/basic_fields_tests.js b/addons/web/static/tests/legacy/fields/basic_fields_tests.js index 17b41385726..4f2152ea32c 100644 --- a/addons/web/static/tests/legacy/fields/basic_fields_tests.js +++ b/addons/web/static/tests/legacy/fields/basic_fields_tests.js @@ -160,19 +160,6 @@ QUnit.module('Legacy basic_fields', { position: "after", }] }, - "ir.translation": { - fields: { - lang: {type: "char"}, - value: {type: "char"}, - res_id: {type: "integer"} - }, - records: [{ - id: 99, - res_id: 37, - value: '', - lang: 'en_US' - }] - }, }; target = getFixture(); @@ -1653,25 +1640,18 @@ QUnit.module('Legacy basic_fields', { user_context: {lang: 'en_US'}, }, mockRPC: function (route, args) { - if (route === "/web/dataset/call_button" && args.method === 'translate_fields') { - assert.deepEqual(args.args, ["partner",1,"foo"], 'should call "call_button" route'); - return Promise.resolve({ - domain: [], - context: {search_default_name: 'partnes,foo'}, - }); + if (route === "/web/dataset/call_kw/partner/get_field_translations") { + assert.deepEqual(args.args, [[1],"foo"], "should translate the foo field of the record"); + return Promise.resolve([ + [{lang: "en_US", source: "yop", value: "yop"}, {lang: "fr_BE", source: "yop", value: "valeur français"}], + {translation_type: "char", translation_show_source: false}, + ]); } if (route === "/web/dataset/call_kw/res.lang/get_installed") { return Promise.resolve([["en_US", "English"], ["fr_BE", "French (Belgium)"]]); } - if (args.method === "search_read" && args.model == "ir.translation") { - return Promise.resolve([ - {lang: 'en_US', src: 'yop', value: 'yop', id: 42}, - {lang: 'fr_BE', src: 'yop', value: 'valeur français', id: 43} - ]); - } - if (args.method === "write" && args.model == "ir.translation") { - assert.deepEqual(args.args[1], {value: "english value"}, - "the new translation value should be written"); + if (route === "/web/dataset/call_kw/partner/update_field_translations") { + assert.deepEqual(args.args, [[1], "foo", {"en_US": "english value"}], "the new translation value should be written"); return Promise.resolve(); } return this._super.apply(this, arguments); @@ -1741,31 +1721,21 @@ QUnit.module('Legacy basic_fields', { user_context: {lang: 'en_US'}, }, mockRPC: function (route, args) { - if (route === "/web/dataset/call_button" && args.method === 'translate_fields') { - assert.deepEqual(args.args, ["partner",1,"foo"], 'should call "call_button" route'); - return Promise.resolve({ - domain: [], - context: { - search_default_name: 'partner,foo', - translation_type: 'char', - translation_show_src: true, - }, - }); + if (route === "/web/dataset/call_kw/partner/get_field_translations") { + assert.deepEqual(args.args, [[1],"foo"], "should translate the foo field of the record"); + return Promise.resolve([ + [{lang: "en_US", source: "first paragraph", value: "first paragraph"}, + {lang: "en_US", source: "second paragraph", value: "second paragraph"}, + {lang: "fr_BE", source: "first paragraph", value: "premier paragraphe"}, + {lang: "fr_BE", source: "second paragraph", value: "deuxième paragraphe"}], + {translation_type: "char", translation_show_source: true}, + ]); } if (route === "/web/dataset/call_kw/res.lang/get_installed") { return Promise.resolve([["en_US", "English"], ["fr_BE", "French (Belgium)"]]); } - if (args.method === "search_read" && args.model == "ir.translation") { - return Promise.resolve([ - {lang: 'en_US', src: 'first paragraph', value: 'first paragraph', id: 42}, - {lang: 'en_US', src: 'second paragraph', value: 'second paragraph', id: 43}, - {lang: 'fr_BE', src: 'first paragraph', value: 'premier paragraphe', id: 44}, - {lang: 'fr_BE', src: 'second paragraph', value: 'deuxième paragraphe', id: 45}, - ]); - } - if (args.method === "write" && args.model == "ir.translation") { - assert.deepEqual(args.args[1], {value: "first paragraph modified"}, - "Wrong update on translation"); + if (route === "/web/dataset/call_kw/partner/update_field_translations") { + assert.deepEqual(args.args, [[1], "foo", {"en_US": {"first paragraph": "first paragraph modified"}}], "the new translation value should be written"); return Promise.resolve(); } return this._super.apply(this, arguments); @@ -2694,15 +2664,15 @@ QUnit.module('Legacy basic_fields', { '', res_id: 1, mockRPC: function (route, args) { - if (route === "/web/dataset/call_button" && args.method === 'translate_fields') { - assert.deepEqual(args.args, ["partner",1,"txt"], 'should call "call_button" route'); - return Promise.resolve({ - domain: [], - context: {search_default_name: 'partnes,foo'}, - }); + if (route === "/web/dataset/call_kw/partner/get_field_translations") { + assert.deepEqual(args.args, [[1],"txt"], "should translate the txt field of the record"); + return Promise.resolve([ + [{lang: "en_US", source: "yop", value: "yop"}, {lang: "fr_BE", source: "yop", value: "valeur français"}], + {translation_type: "text", translation_show_source: false}, + ]); } if (route === "/web/dataset/call_kw/res.lang/get_installed") { - return Promise.resolve([["en_US"], ["fr_BE"]]); + return Promise.resolve([["en_US", "English"], ["fr_BE", "French (Belgium)"]]); } return this._super.apply(this, arguments); }, diff --git a/addons/web/static/tests/legacy/views/form_tests.js b/addons/web/static/tests/legacy/views/form_tests.js index 3d075d30439..96e348b6b7b 100644 --- a/addons/web/static/tests/legacy/views/form_tests.js +++ b/addons/web/static/tests/legacy/views/form_tests.js @@ -140,19 +140,6 @@ QUnit.module('LegacyViews', { {id: 14, display_name: "silver", color: 5}, ] }, - "ir.translation": { - fields: { - lang_code: {type: "char"}, - value: {type: "char"}, - res_id: {type: "integer"} - }, - records: [{ - id: 99, - res_id: 12, - value: '', - lang_code: 'en_US' - }] - }, user: { fields: { name: {string: "Name", type: "char"}, @@ -7382,16 +7369,7 @@ QUnit.module('LegacyViews', { }); QUnit.test('translation alerts preserved on reverse breadcrumb', async function (assert) { - assert.expect(2); - - serverData.models['ir.translation'] = { - fields: { - name: { string: "name", type: "char" }, - source: {string: "Source", type: "char"}, - value: {string: "Value", type: "char"}, - }, - records: [], - }; + assert.expect(1); serverData.models.partner.fields.foo.translate = true; @@ -7402,12 +7380,6 @@ QUnit.module('LegacyViews', { '' + '', 'partner,false,search': '', - 'ir.translation,false,list': '' + - '' + - '' + - '' + - '', - 'ir.translation,false,search': '', }; serverData.actions = { @@ -7418,14 +7390,6 @@ QUnit.module('LegacyViews', { type: 'ir.actions.act_window', views: [[false, 'form']], }, - 2: { - id: 2, - name: 'Translate', - res_model: 'ir.translation', - type: 'ir.actions.act_window', - views: [[false, 'list']], - target: 'current', - } }; const webClient = await createWebClient({ serverData }); @@ -7440,13 +7404,6 @@ QUnit.module('LegacyViews', { assert.containsOnce(target, '.o_legacy_form_view .alert > div', "should have a translation alert"); - - await doAction(webClient, 2); - - await testUtils.dom.click($('.o_control_panel .breadcrumb a:first')); - await legacyExtraNextTick(); - assert.containsOnce(target, '.o_legacy_form_view .alert > div', - "should have a translation alert"); }); QUnit.test('translate event correctly handled with multiple controllers', async function (assert) { @@ -7485,16 +7442,16 @@ QUnit.module('LegacyViews', { if (route === '/web/dataset/call_kw/product/get_formview_id') { return Promise.resolve(false); } - if (route === "/web/dataset/call_button" && args.method === 'translate_fields') { - assert.deepEqual(args.args, ["product",37,"name"], 'should call "call_button" route'); + if (route === "/web/dataset/call_kw/product/get_field_translations") { + assert.deepEqual(args.args, [[37],"name"], "should translate the name field of the record"); nbTranslateCalls++; - return Promise.resolve({ - domain: [], - context: {search_default_name: 'partnes,foo'}, - }); + return Promise.resolve([ + [{lang: "en_US", source: "yop", value: "yop"}, {lang: "fr_BE", source: "yop", value: "valeur français"}], + {translation_type: "char", translation_show_source: false}, + ]); } if (route === "/web/dataset/call_kw/res.lang/get_installed") { - return Promise.resolve([["en_US"], ["fr_BE"]]); + return Promise.resolve([["en_US", "English"], ["fr_BE", "French (Belgium)"]]); } return this._super.apply(this, arguments); }, diff --git a/addons/web/static/tests/legacy/views/list_tests.js b/addons/web/static/tests/legacy/views/list_tests.js index 40bf500d8e5..3e6190f3334 100644 --- a/addons/web/static/tests/legacy/views/list_tests.js +++ b/addons/web/static/tests/legacy/views/list_tests.js @@ -123,31 +123,6 @@ QUnit.module('LegacyViews', { {id: "2-20170808020000", name: "virtual"}, ] }, - "ir.translation": { - fields: { - lang_code: {type: "char"}, - src: {type: "char"}, - value: {type: "char"}, - res_id: {type: "integer"}, - name: {type: "char"}, - lang: {type: "char"}, - }, - records: [{ - id: 99, - res_id: 1, - value: '', - lang_code: 'en_US', - lang: 'en_US', - name: 'foo,foo' - },{ - id: 100, - res_id: 1, - value: '', - lang_code: 'fr_BE', - lang: 'fr_BE', - name: 'foo,foo' - }] - }, }; serverData = { models: this.data }; @@ -3620,11 +3595,11 @@ QUnit.module('LegacyViews', { model: 'foo', data: this.data, mockRPC: function (route, args) { - if (route === "/web/dataset/call_button" && args.method === 'translate_fields') { - return Promise.resolve({ - domain: [], - context: {search_default_name: 'foo,foo'}, - }); + if (route === "/web/dataset/call_kw/foo/get_field_translations") { + return Promise.resolve([ + [{lang: "en_US", source: "yop", value: "yop"}, {lang: "fr_BE", source: "yop", value: "valeur français"}], + {translation_type: "char", translation_show_source: false}, + ]); } if (route === "/web/dataset/call_kw/res.lang/get_installed") { return Promise.resolve([["en_US","English"], ["fr_BE", "Frenglish"]]); diff --git a/addons/web/static/tests/views/fields/char_field_tests.js b/addons/web/static/tests/views/fields/char_field_tests.js index 213c0473996..05bd6634daf 100644 --- a/addons/web/static/tests/views/fields/char_field_tests.js +++ b/addons/web/static/tests/views/fields/char_field_tests.js @@ -123,21 +123,6 @@ QUnit.module("Fields", (hooks) => { }, ], }, - "ir.translation": { - fields: { - lang: { type: "char" }, - value: { type: "char" }, - res_id: { type: "integer" }, - }, - records: [ - { - id: 99, - res_id: 37, - value: "", - lang: "en_US", - }, - ], - }, }, }; @@ -301,7 +286,7 @@ QUnit.module("Fields", (hooks) => { }); QUnit.test("char field translatable", async function (assert) { - assert.expect(13); + assert.expect(11); serverData.models.partner.fields.foo.translate = true; serviceRegistry.add("localization", makeFakeLocalizationService({ multiLang: true }), { @@ -325,33 +310,25 @@ QUnit.module("Fields", (hooks) => { `, mockRPC(route, { args, method, model }) { - if (route === "/web/dataset/call_button" && method === "translate_fields") { - assert.deepEqual( - args, - ["partner", 1, "foo"], - 'should call "call_button" route' - ); - return Promise.resolve({ - domain: [], - context: { search_default_name: "partnes,foo" }, - }); - } if (route === "/web/dataset/call_kw/res.lang/get_installed") { return Promise.resolve([ ["en_US", "English"], ["fr_BE", "French (Belgium)"], ]); } - if (method === "search_read" && model === "ir.translation") { + if (route === "/web/dataset/call_kw/partner/get_field_translations") { return Promise.resolve([ - { lang: "en_US", src: "yop", value: "yop", id: 42 }, - { lang: "fr_BE", src: "yop", value: "valeur français", id: 43 }, + [ + { lang: "en_US", source: "yop", value: "yop" }, + { lang: "fr_BE", source: "yop", value: "valeur français" }, + ], + { translation_type: "char", translation_show_source: false }, ]); } - if (method === "write" && model === "ir.translation") { + if (route === "/web/dataset/call_kw/partner/update_field_translations") { assert.deepEqual( - args[1], - { value: "english value" }, + args[2], + { en_US: "english value" }, "the new translation value should be written" ); return Promise.resolve(null); @@ -417,7 +394,7 @@ QUnit.module("Fields", (hooks) => { }); QUnit.test("html field translatable", async function (assert) { - assert.expect(6); + assert.expect(5); serverData.models.partner.fields.foo.translate = true; serviceRegistry.add("localization", makeFakeLocalizationService({ multiLang: true }), { @@ -441,55 +418,48 @@ QUnit.module("Fields", (hooks) => { `, mockRPC(route, { args, method, model }) { - if (route === "/web/dataset/call_button" && method === "translate_fields") { - assert.deepEqual( - args, - ["partner", 1, "foo"], - `should call "call_button" route` - ); - return Promise.resolve({ - domain: [], - context: { - search_default_name: "partner,foo", - translation_type: "char", - translation_show_src: true, - }, - }); - } if (route === "/web/dataset/call_kw/res.lang/get_installed") { return Promise.resolve([ ["en_US", "English"], ["fr_BE", "French (Belgium)"], ]); } - if (method === "search_read" && model === "ir.translation") { + if (route === "/web/dataset/call_kw/partner/get_field_translations") { return Promise.resolve([ - { lang: "en_US", src: "first paragraph", value: "first paragraph", id: 42 }, + [ + { + lang: "en_US", + source: "first paragraph", + value: "first paragraph", + }, + { + lang: "en_US", + source: "second paragraph", + value: "second paragraph", + }, + { + lang: "fr_BE", + source: "first paragraph", + value: "premier paragraphe", + }, + { + lang: "fr_BE", + source: "second paragraph", + value: "deuxième paragraphe", + }, + ], { - lang: "en_US", - src: "second paragraph", - value: "second paragraph", - id: 43, - }, - { - lang: "fr_BE", - src: "first paragraph", - value: "premier paragraphe", - id: 44, - }, - { - lang: "fr_BE", - src: "second paragraph", - value: "deuxième paragraphe", - id: 45, + translation_type: "char", + translation_show_source: true, }, ]); } - if (method === "write" && model === "ir.translation") { + + if (route === "/web/dataset/call_kw/partner/update_field_translations") { assert.deepEqual( - args[1], - { value: "first paragraph modified" }, - "Wrong update on translation" + args[2], + { en_US: { "first paragraph": "first paragraph modified" } }, + "the new translation value should be written" ); return Promise.resolve(null); } diff --git a/addons/web/static/tests/views/fields/text_field_tests.js b/addons/web/static/tests/views/fields/text_field_tests.js index 3c878d98426..e216d15286c 100644 --- a/addons/web/static/tests/views/fields/text_field_tests.js +++ b/addons/web/static/tests/views/fields/text_field_tests.js @@ -49,21 +49,6 @@ QUnit.module("Fields", (hooks) => { }, ], }, - "ir.translation": { - fields: { - lang: { type: "char" }, - value: { type: "char" }, - res_id: { type: "integer" }, - }, - records: [ - { - id: 99, - res_id: 37, - value: "", - lang: "en_US", - }, - ], - }, }, }; @@ -370,7 +355,7 @@ QUnit.module("Fields", (hooks) => { }); QUnit.test("text field translatable", async function (assert) { - assert.expect(4); + assert.expect(3); serverData.models.partner.fields.txt.translate = true; serviceRegistry.add("localization", makeFakeLocalizationService({ multiLang: true }), { @@ -391,19 +376,20 @@ QUnit.module("Fields", (hooks) => { `, mockRPC(route, { args, method }) { - if (route === "/web/dataset/call_button" && method === "translate_fields") { - assert.deepEqual( - args, - ["partner", 1, "txt"], - `should call "call_button" route` - ); - return Promise.resolve({ - domain: [], - context: { search_default_name: "partnes,foo" }, - }); - } if (route === "/web/dataset/call_kw/res.lang/get_installed") { - return Promise.resolve([["en_US"], ["fr_BE"]]); + return Promise.resolve([ + ["en_US", "English"], + ["fr_BE", "French (Belgium)"], + ]); + } + if (route === "/web/dataset/call_kw/partner/get_field_translations") { + return Promise.resolve([ + [ + { lang: "en_US", source: "yop", value: "yop" }, + { lang: "fr_BE", source: "yop", value: "valeur français" }, + ], + { translation_type: "text", translation_show_source: false }, + ]); } }, }); diff --git a/addons/web/static/tests/views/form/form_view_tests.js b/addons/web/static/tests/views/form/form_view_tests.js index 8cf882e1ae7..270d27a7209 100644 --- a/addons/web/static/tests/views/form/form_view_tests.js +++ b/addons/web/static/tests/views/form/form_view_tests.js @@ -180,21 +180,6 @@ QUnit.module("Views", (hooks) => { { id: 14, display_name: "silver", color: 5 }, ], }, - "ir.translation": { - fields: { - lang_code: { type: "char" }, - value: { type: "char" }, - res_id: { type: "integer" }, - }, - records: [ - { - id: 99, - res_id: 12, - value: "", - lang_code: "en_US", - }, - ], - }, user: { fields: { name: { string: "Name", type: "char" }, @@ -8466,20 +8451,16 @@ QUnit.module("Views", (hooks) => { ["CUST2", "second custom"], ]; } - if (args.method === "translate_fields") { + if (route === "/web/dataset/call_kw/partner/get_field_translations") { assert.step(`translate args ${JSON.stringify(args.args)}`); assert.step(`translate context ${JSON.stringify(args.kwargs.context)}`); - return { - domain: [["res_id", "=", args.args[1]]], - context: { search_default_name: "partner_type,foo" }, - }; - } - if (args.method === "search_read" && args.model === "ir.translation") { - assert.step( - `search_read translations args: ${JSON.stringify( - args.args - )} ; kwargs: ${JSON.stringify(args.kwargs)}` - ); + return Promise.resolve([ + [ + { lang: "CUST", source: "yop", value: "yop" }, + { lang: "CUST2", source: "yop", value: "valeur français" }, + ], + { translation_type: "char", translation_show_source: false }, + ]); } }, }); @@ -8487,9 +8468,8 @@ QUnit.module("Views", (hooks) => { await click(target, ".o_field_translate.btn-link"); assert.verifySteps([ - `translate args ["partner",1,"foo"]`, + `translate args [[1],"foo"]`, `translate context {"lang":"en","uid":7,"tz":"taht"}`, - `search_read translations args: [] ; kwargs: {"domain":[["res_id","=",1],["name","=","partner_type,foo"],["lang","in",["CUST","CUST2"]]],"fields":["lang","src","value"],"context":{"lang":"en","uid":7,"tz":"taht"}}`, ]); assert.containsOnce(target, ".modal"); @@ -8522,6 +8502,15 @@ QUnit.module("Views", (hooks) => { ["CUST2", "second custom"], ]; } + if (route === "/web/dataset/call_kw/partner/get_field_translations") { + return Promise.resolve([ + [ + { lang: "CUST", source: "yop", value: "yop" }, + { lang: "CUST2", source: "yop", value: "valeur français" }, + ], + { translation_type: "char", translation_show_source: false }, + ]); + } }, }); @@ -8536,7 +8525,7 @@ QUnit.module("Views", (hooks) => { await click(target, ".o_field_translate.btn-link"); await click(target.querySelectorAll(".modal-footer button")[0]); // save - assert.verifySteps(["create", "read", "translate_fields", "get_installed", "search_read"]); + assert.verifySteps(["create", "read", "get_installed", "get_field_translations"]); assert.containsOnce(target, ".modal"); assert.strictEqual(target.querySelector(".modal-title").textContent, "Translate: foo"); }); @@ -8583,12 +8572,21 @@ QUnit.module("Views", (hooks) => { ["CUST2", "second custom"], ]; } + if (route === "/web/dataset/call_kw/partner/get_field_translations") { + return Promise.resolve([ + [ + { lang: "CUST", source: "yop", value: "yop" }, + { lang: "CUST2", source: "yop", value: "valeur français" }, + ], + { translation_type: "char", translation_show_source: false }, + ]); + } }, }); assert.verifySteps(["get_views", "read"]); await clickEdit(target); await click(target, ".o_field_legacy_char .o_field_translate.btn-link"); - assert.verifySteps(["translate_fields", "get_installed", "search_read"]); + assert.verifySteps(["get_installed", "get_field_translations"]); assert.containsOnce(target, ".modal"); assert.strictEqual(target.querySelector(".modal-title").textContent, "Translate: foo"); }); @@ -8644,22 +8642,11 @@ QUnit.module("Views", (hooks) => { }); QUnit.test("translation alerts preserved on reverse breadcrumb", async function (assert) { - serverData.models["ir.translation"] = { - fields: { - name: { string: "name", type: "char" }, - source: { string: "Source", type: "char" }, - value: { string: "Value", type: "char" }, - }, - records: [], - }; - serverData.models.partner.fields.foo.translate = true; serverData.views = { "partner,false,form": `
`, "partner,false,search": "", - "ir.translation,false,list": ``, - "ir.translation,false,search": "", }; serverData.actions = { @@ -8670,15 +8657,6 @@ QUnit.module("Views", (hooks) => { type: "ir.actions.act_window", views: [[false, "form"]], }, - 2: { - id: 2, - name: "Translate", - res_model: "ir.translation", - type: "ir.actions.act_window", - views: [[false, "list"]], - target: "current", - flags: { search_view: true, action_buttons: true }, - }, }; const webClient = await createWebClient({ serverData }); @@ -8692,17 +8670,12 @@ QUnit.module("Views", (hooks) => { await clickSave(target); assert.containsOnce(target, ".alert .o_field_translate", "should have a translation alert"); - - await doAction(webClient, 2); - - await click(target.querySelector(".o_control_panel .breadcrumb a:nth-child(1)")); - assert.containsOnce(target, ".alert .o_field_translate", "should have a translation alert"); }); QUnit.test( "translate event correctly handled with multiple controllers", async function (assert) { - assert.expect(3); + assert.expect(2); serverData.models.product.fields.name.translate = true; serverData.models.partner.records[0].product_id = 37; @@ -8742,23 +8715,21 @@ QUnit.module("Views", (hooks) => { if (route === "/web/dataset/call_kw/product/get_formview_id") { return false; } - if ( - route === "/web/dataset/call_button" && - args.method === "translate_fields" - ) { - assert.deepEqual( - args.args, - ["product", 37, "name"], - 'should call "call_button" route' - ); - nbTranslateCalls++; - return { - domain: [], - context: { search_default_name: "partnes,foo" }, - }; - } if (route === "/web/dataset/call_kw/res.lang/get_installed") { - return [["en_US"], ["fr_BE"]]; + return [ + ["en_US", "English"], + ["fr_BE", "French (Belgium)"], + ]; + } + if (route === "/web/dataset/call_kw/product/get_field_translations") { + nbTranslateCalls++; + return Promise.resolve([ + [ + { lang: "en_US", source: "yop", value: "yop" }, + { lang: "fr_BE", source: "yop", value: "valeur français" }, + ], + { translation_type: "char", translation_show_source: false }, + ]); } }, }); diff --git a/addons/web/static/tests/views/list_view_tests.js b/addons/web/static/tests/views/list_view_tests.js index 8af79664a08..89193d519b9 100644 --- a/addons/web/static/tests/views/list_view_tests.js +++ b/addons/web/static/tests/views/list_view_tests.js @@ -197,34 +197,6 @@ QUnit.module("Views", (hooks) => { }, records: [{ id: "2-20170808020000", name: "virtual" }], }, - "ir.translation": { - fields: { - lang_code: { type: "char" }, - src: { type: "char" }, - value: { type: "char" }, - res_id: { type: "integer" }, - name: { type: "char" }, - lang: { type: "char" }, - }, - records: [ - { - id: 99, - res_id: 1, - value: "", - lang_code: "en_US", - lang: "en_US", - name: "foo,foo", - }, - { - id: 100, - res_id: 1, - value: "", - lang_code: "fr_BE", - lang: "fr_BE", - name: "foo,foo", - }, - ], - }, }, }; setupViewRegistries(); @@ -4096,18 +4068,21 @@ QUnit.module("Views", (hooks) => { resModel: "foo", serverData, mockRPC(route, args) { - if (route === "/web/dataset/call_button" && args.method === "translate_fields") { - return Promise.resolve({ - domain: [], - context: { search_default_name: "foo,foo" }, - }); - } if (route === "/web/dataset/call_kw/res.lang/get_installed") { return Promise.resolve([ ["en_US", "English"], ["fr_BE", "Frenglish"], ]); } + if (route === "/web/dataset/call_kw/foo/get_field_translations") { + return Promise.resolve([ + [ + { lang: "en_US", source: "yop", value: "yop" }, + { lang: "fr_BE", source: "yop", value: "valeur français" }, + ], + { translation_type: "char", translation_show_source: false }, + ]); + } }, arch: '' + '' + "", }); diff --git a/addons/web_editor/static/tests/field_html_tests.js b/addons/web_editor/static/tests/field_html_tests.js index b41db58e906..4afbc222408 100644 --- a/addons/web_editor/static/tests/field_html_tests.js +++ b/addons/web_editor/static/tests/field_html_tests.js @@ -130,19 +130,6 @@ QUnit.module('web_editor', {}, function () { body_arch: "
yep
", }], }, - "ir.translation": { - fields: { - lang_code: {type: "char"}, - value: {type: "char"}, - res_id: {type: "integer"} - }, - records: [{ - id: 99, - res_id: 12, - value: '', - lang_code: 'en_US' - }] - }, }); testUtils.mock.patch(ajax, { @@ -880,15 +867,18 @@ QUnit.module('web_editor', {}, function () { '', res_id: 1, mockRPC: function (route, args) { - if (route === '/web/dataset/call_button' && args.method === 'translate_fields') { - assert.deepEqual(args.args, ['note.note', 1, 'body'], "should call 'call_button' route"); - return Promise.resolve({ - domain: [], - context: {search_default_name: 'partnes,foo'}, - }); + if (route === "/web/dataset/call_kw/note.note/get_field_translations") { + assert.deepEqual(args.args, [[1],"body"], "should translate the body field of the record"); + return Promise.resolve([ + [{lang: "en_US", source: "first paragraph", value: "first paragraph"}, + {lang: "en_US", source: "second paragraph", value: "second paragraph"}, + {lang: "fr_BE", source: "first paragraph", value: "premier paragraphe"}, + {lang: "fr_BE", source: "second paragraph", value: "deuxième paragraphe"}], + {translation_type: "text", translation_show_source: true}, + ]); } if (route === "/web/dataset/call_kw/res.lang/get_installed") { - return Promise.resolve([["en_US"], ["fr_BE"]]); + return Promise.resolve([["en_US", "English"], ["fr_BE", "French (Belgium)"]]); } return this._super.apply(this, arguments); },