From 66c06f1393940b57eeecfa5b8f78b57ab2a7d2e2 Mon Sep 17 00:00:00 2001 From: Nikol Georgieva Date: Tue, 25 Aug 2026 20:01:44 +0300 Subject: [PATCH] fix(harmonia): print data in the chosen print language, not the UI locale (#6945) The Harmonia document/manage Print flow picked the template folder by the chosen print language (labels came out correctly) but fetched the print data from the generated PrintFeeder with the UI locale as Accept-Language. Since the generated repositories overlay multilingual values from Accept-Language, the nomenclature VALUES (Payment Method, Sent Method, Status, ...) rendered in the UI locale while the template rendered in the print language - the two halves of the document disagreed. Pin the feeder GET's Accept-Language to the chosen print language so the multilingual overlay resolves data in the same language as the template: - api.js: request() honors an opt-in { language } per-call override; absent it, the Region & Language store applies as before. - document-page / form-page print feeder fetch passes { language: lang }. Also refresh the engine-document guide, which still described the pre-feeder "client supplies the data" design. Co-Authored-By: Claude Opus 4.8 (1M context) --- components/engine/engine-document/CLAUDE.md | 15 +++++++++++---- .../application-core/shell/js/services/api.js | 5 ++++- .../document/document-page.js.template | 5 ++++- .../ui/perspective/manage/form-page.js.template | 5 ++++- 4 files changed, 23 insertions(+), 7 deletions(-) diff --git a/components/engine/engine-document/CLAUDE.md b/components/engine/engine-document/CLAUDE.md index d85c4d2b188..4e3910f9651 100644 --- a/components/engine/engine-document/CLAUDE.md +++ b/components/engine/engine-document/CLAUDE.md @@ -47,10 +47,17 @@ uploaded). The Print button asks which to use when several exist. (404 with a clear message when absent), then `DocumentParser → DataBinder → XslFoRenderer → PDFFacade.generate(fo, "")`. -**The client supplies the data.** The Harmonia document page POSTs the record + items it already -loaded, with dropdown FKs resolved to display labels (`printPayload()` in -`document-page.js.template`). Deliberate: no server-side entity fetching, no auth forwarding, and -the printout matches exactly what the user sees. The JSON body is parsed with a **plain Gson** +**The client feeds this endpoint from a server-side feeder, not from its own screen state.** The +Harmonia document/manage page first GETs the generated `…PrintFeeder/{id}` (client-Java), which +loads the record + its related graph through the generated repositories and returns the nested +`{ document, items }` payload — so `{{document..}}` resolves and validations, events +and the **multilingual translation overlay** all apply. The page then POSTs that payload here. The +feeder is called as the logged-in user (auth + tenant are the caller's). **Language:** the print +dialog's chosen language drives BOTH halves — it is the `?lang=` that selects the CMS template folder +(labels) AND is sent as the feeder GET's `Accept-Language` (via `api.get(url, { language: lang })`), +because the repositories' multilingual overlay reads `User.getLanguage()`/`Accept-Language`; without +that pin the nomenclature VALUES would translate to the UI locale while the template is in the print +language (dirigible #6945). The JSON body is parsed with a **plain Gson** (`ToNumberPolicy.LONG_OR_DOUBLE`) — never `JsonHelper`/`GsonHelper` (the `@Expose` trap; and LONG_OR_DOUBLE keeps integers integral while decimals arrive as `Double`, which `DataBinder` formats in the form money pattern `### ### ### ##0.00`). diff --git a/components/resources/application-core/src/main/resources/META-INF/dirigible/application-core/shell/js/services/api.js b/components/resources/application-core/src/main/resources/META-INF/dirigible/application-core/shell/js/services/api.js index 5a17d2107b8..5fa0dda581a 100644 --- a/components/resources/application-core/src/main/resources/META-INF/dirigible/application-core/shell/js/services/api.js +++ b/components/resources/application-core/src/main/resources/META-INF/dirigible/application-core/shell/js/services/api.js @@ -98,7 +98,10 @@ App.services.api = { // browser's native login dialog never pops over a background poll. const headers = { 'Accept': 'application/json', 'X-Requested-With': 'XMLHttpRequest' }; if (!isForm) headers['Content-Type'] = 'application/json'; - const language = this.language(); + // A caller may pin the request language for THIS call ({ language: 'bg' }) — e.g. Print, where the + // chosen print language must drive the multilingual data overlay, not the UI locale. Absent the + // override, the app's single language flag (the Region & Language store) applies as before. + const language = opts.language !== undefined ? opts.language : this.language(); if (language) headers['Accept-Language'] = language; let r; diff --git a/components/template/template-application-ui-harmonia-java/src/main/resources/META-INF/dirigible/template-application-ui-harmonia-java/ui/perspective/document/document-page.js.template b/components/template/template-application-ui-harmonia-java/src/main/resources/META-INF/dirigible/template-application-ui-harmonia-java/ui/perspective/document/document-page.js.template index 68a14e49783..1633382e69d 100644 --- a/components/template/template-application-ui-harmonia-java/src/main/resources/META-INF/dirigible/template-application-ui-harmonia-java/ui/perspective/document/document-page.js.template +++ b/components/template/template-application-ui-harmonia-java/src/main/resources/META-INF/dirigible/template-application-ui-harmonia-java/ui/perspective/document/document-page.js.template @@ -281,8 +281,11 @@ document.addEventListener('alpine:init', () => { // graph through the repositories and returns the { document, items } payload the template binds // - so {{document..}} resolves. It is the auditable source of what a print // receives; the browser calls it as the logged-in user (auth + tenant + i18n are the caller's). + // Pin the feeder's language to the CHOSEN print language, not the UI locale: the repositories' + // multilingual overlay reads Accept-Language, so without this the nomenclature values (Payment + // Method, Status, ...) would render in the UI locale while the template is in the print language. const payload = await App.services.api.get( - '/services/java/${projectName}/gen/events/${javaGenFolderName}/${name}PrintFeeder/' + encodeURIComponent(this.id), { baseUrl: '' }); + '/services/java/${projectName}/gen/events/${javaGenFolderName}/${name}PrintFeeder/' + encodeURIComponent(this.id), { baseUrl: '', language: lang }); const response = await fetch('/services/print/${name}?lang=' + encodeURIComponent(lang), { method: 'POST', headers: { 'Content-Type': 'application/json' }, diff --git a/components/template/template-application-ui-harmonia-java/src/main/resources/META-INF/dirigible/template-application-ui-harmonia-java/ui/perspective/manage/form-page.js.template b/components/template/template-application-ui-harmonia-java/src/main/resources/META-INF/dirigible/template-application-ui-harmonia-java/ui/perspective/manage/form-page.js.template index 2e73fd833f6..dd11adfb8d8 100644 --- a/components/template/template-application-ui-harmonia-java/src/main/resources/META-INF/dirigible/template-application-ui-harmonia-java/ui/perspective/manage/form-page.js.template +++ b/components/template/template-application-ui-harmonia-java/src/main/resources/META-INF/dirigible/template-application-ui-harmonia-java/ui/perspective/manage/form-page.js.template @@ -717,8 +717,11 @@ document.addEventListener('alpine:init', () => { // graph through the repositories and returns the { document, items } payload the template binds // - so {{document..}} resolves. It is called as the logged-in user (auth + // tenant + i18n are the caller's). + // Pin the feeder's language to the CHOSEN print language, not the UI locale: the repositories' + // multilingual overlay reads Accept-Language, so without this the nomenclature values (Payment + // Method, Status, ...) would render in the UI locale while the template is in the print language. const payload = await App.services.api.get( - '/services/java/${projectName}/gen/events/${javaGenFolderName}/${name}PrintFeeder/' + encodeURIComponent(this.id), { baseUrl: '' }); + '/services/java/${projectName}/gen/events/${javaGenFolderName}/${name}PrintFeeder/' + encodeURIComponent(this.id), { baseUrl: '', language: lang }); const response = await fetch('/services/print/${name}?lang=' + encodeURIComponent(lang), { method: 'POST', headers: { 'Content-Type': 'application/json' },