fix(templates): a BPM task form renders its labels through its module i18n catalog (#6692) - #6706
Merged
Conversation
… i18n catalog (#6692) Every generated BPM task form rendered in English - the title, the status stepper, the field labels and the button captions - while the application shell pages around it were fully translated. The catalogs were never the problem: the same generation pass that renders the page already emits `i18n/en-US/<Form>.form.json` with a key per label, and modules already ship their translated copies next to it. The page simply never consumed them: it baked every label in as a literal and did not load the translator at all. It does now, following the standalone report page's precedent: - the page loads the shared `application-core/shell/js/services/i18n.js` and, having no SPA shell to read it from, bootstraps `App.config.projectName` in a one-line inline script first - without that, i18n.js fetches only the platform `application-core` chrome catalog and every module-authored key silently stays English; - the title, paragraph, link, field labels, read-only detail labels and button captions bind through `T('<project>:<tprefix>.t.<id>', '<English literal>')`, emitted by one `#tlabel` macro so every control site is identical, and the English literal stays as the element's own text so a control the catalog never keyed (a hand-authored form without a label) is unchanged; - a status step keeps its untranslated `label` - that is what the record's status value is MATCHED against - and gains a translated `title`, which is what shows; - the two submit outcome messages resolve through the catalog's `dialogs` section. Everything is resolved at generation time: the translation id was assigned to the model by the catalog-emitting pass, so nothing is derived in the browser, and an untranslated key degrades to the baked English literal exactly as elsewhere in the stack. An authored label may contain an apostrophe, which would close the JS string literal the `T()` call sits in and break the whole Alpine expression, so both the interpolated literal and the KEY derived from it are escaped - `translationId` strips only spaces and `_ . :`, so `Customer's Note` keys as `Customer'sNote`. Covered by `IntentEngineIT.task_form_renders_its_labels_through_the_module_catalog`, which asserts both halves - the labels land in the emitted catalog, and the generated page resolves them through it. `BPMStarterTemplateIT` (a real browser filling and submitting a generated form) stays green, which is what proves the new Alpine bindings actually evaluate. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #6692.
Every generated BPM task form rendered in English — the title, the status stepper, the field labels and the button captions — while the application shell pages around it were fully translated.
The catalogs were never the problem. The same generation pass that renders the page already emits
i18n/en-US/<Form>.form.jsonwith a key per label (translationId(label)), and modules already ship their translated copies next to it. The page simply never consumed them: it baked every label in as a literal and did not load the translator at all.What changed (
template-form-builder-harmonia)application-core/shell/js/services/i18n.js) and, having no SPA shell to read it from, bootstrapsApp.config.projectNamein a one-line inline script first — the same thing the standalone report page already does. Without it, i18n.js fetches only the platformapplication-corechrome catalog, so every module-authored key silently stays English.T('<project>:<tprefix>.t.<id>', '<English literal>')— title, paragraph, link, field labels, read-only detail labels, button captions — emitted by a single#tlabelmacro so every control site is identical. The English literal stays as the element's own text, so a control the catalog never keyed (a hand-authored form without a label) renders exactly as before.label— that is what the record's status value is matched against — and gains a translatedtitle, which is what shows. Translatinglabelwould leave every step inactive.dialogssection (wheresuccessMsgcarries the form's authored success text).Everything is resolved at generation time — the translation id was assigned to the model by the catalog-emitting pass, so nothing is derived in the browser — and an untranslated key degrades to the baked English literal, exactly as elsewhere in the stack. The default language still renders entirely from the literals (i18n.js skips catalog loading there).
One trap worth naming: an authored label may contain an apostrophe, which would close the JS string literal the
T()call sits in and break the whole Alpine expression. Both the interpolated literal and the key derived from it are escaped —translationIdstrips only spaces and_ . :, soCustomer's Notekeys asCustomer'sNote. (Found by rendering the templates offline against a Velocity harness before building.)Verification
IntentEngineIT.task_form_renders_its_labels_through_the_module_catalog— asserts both halves, that the labels land in the emitted catalog and that the generated page resolves them through it (plus the namespace bootstrap and the untranslated-label/ translated-titlesplit). HTTP-only, in the PR smoke set.BPMStarterTemplateIT— a real browser that opens a generated form, fills it by field id and clicks the nowx-text-bound button. That is what proves the new Alpine bindings actually evaluate; a broken expression here has no server-side symptom.GenerationParityITis unaffected: the templates are shared by both generation pipelines, so their output stays byte-identical to each other.Out of scope (still English, filed as known gaps)
The task-form dialog title and the shell Inbox / notification-bell task names — those are cross-project (the raw BPMN task name reaches the shell with no key), and need a key on
TaskDTOrather than a template change. A status step's optionaldescriptionalso stays as authored: the catalog pass keyslabelanderrorMessageonly, and adding a third would clash with thetranslationid it writes onto the same node.🤖 Generated with Claude Code