Skip to content

fix(harmonia): print language dialog must always show for multiple templates#6129

Merged
delchev merged 8 commits into
masterfrom
fix/print-language-dialog
Jul 3, 2026
Merged

fix(harmonia): print language dialog must always show for multiple templates#6129
delchev merged 8 commits into
masterfrom
fix/print-language-dialog

Conversation

@delchev

@delchev delchev commented Jul 3, 2026

Copy link
Copy Markdown
Contributor

Regression from #6125: openPrint auto-printed the configured Region & Language when a template for it exists — but the locale store always resolves to a value (en by default, even if the Settings picker was never touched), so with 2+ templates the language dialog became unreachable. Uploading a bg template gave no way to select it.

Fix: several templates → always open the dialog; the configured language is only pre-sorted first as the suggested default. Single-template direct print and the zero-template 404 path are unchanged.

🤖 Generated with Claude Code

delchev and others added 8 commits July 3, 2026 12:31
…mplates

The multilingual feature made openPrint auto-print the configured Region &
Language when a template for it exists. The locale store ALWAYS resolves to
a value (en by default, even untouched), so with 2+ templates the language
dialog became unreachable - uploading a bg template gave no way to pick it.

Several templates now ALWAYS open the dialog; the configured language is
only pre-sorted first as the suggested default. Single/zero template
behavior unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The pattern-less numeric fallback used the DecimalFormat literal '##0'
inside the Velocity-rendered report.js.template. Velocity treats two
hashes as a line comment, so it swallowed the rest of the line including
the closing quote - the generated report.js carried an unterminated
string, failed to parse, reportPage never registered, and every report
page rendered blank with Alpine expression errors.

'0' formats identically in displayNumber (0 decimals, no grouping) and
carries no Velocity-significant characters.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… Settings

The picker previously existed only in each generated app's own Settings
page (and only when that app's intent declares 2+ languages), so the
shared application shell at /services/web/application/ had no visible
place to switch the data language. The shell's Settings master pane now
offers the same Region & Language select, backed by the shared locale
store (localStorage codbex.harmonia.language, sent as Accept-Language
by every app's fetch client). The offered codes are the union of the
languages the embedded apps declare in their generated js/config.js
(read with a targeted match - the config is a JS file); hidden while
only one language is known.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… per module

The Region & Language design was inverted: each module's intent declared
what languages the picker offers, so an app declaring only 'en' hid the
picker and a 5-language module next to an en-only one could not work.
Corrected contract:

- The PLATFORM defines the supported set: DIRIGIBLE_APPLICATION_LANGUAGES
  (DirigibleConfig + Configuration allow-list), default 'en,bg', served by
  the new platform-core/services/application-languages.js.
- The shared locale store fetches that set; every Region & Language picker
  (shared application shell + generated per-app Settings) always offers
  exactly the platform set.
- A module's intent 'languages:' only declares which languages it PROVIDES
  translations for (config.js semantics reworded accordingly). Untranslated
  content falls back to the default language naturally (the Translator
  overlays only existing _LANG rows).
- The shared application shell's Settings lists every module missing a
  platform language as a visible warning - the developers' to-do list for
  adding translation content.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…th a detail page

Instead of an inline picker above the settings list, Region & Language is
now a built-in entry in the Settings master list (globe icon, selectable
like the app-provided setting entities). Selecting it renders a local
detail page in the right pane: description of the platform-wide language
preference, the language select (the platform's supported set), and the
missing-translations warnings per module. Deep-linkable as
#/settings/region-language via the pseudo setting item.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The generated Harmonia views baked English labels as literals, so
switching Region & Language translated only the data, never the UI.
Labels now translate by REUSING the AngularJS stack's i18n backbone
end-to-end - nothing new server-side:

- i18next (the existing webjar), the locales.js extension service that
  aggregates every project's translations/<locale>/*.json from the
  registry, the platform-locales + application-locales locale registry,
  and the same catalogs/keys the 'translate' generation action already
  emits (namespace = project, path = <tprefix>.t.<DATA_NAME> /
  .defaults.* / .messages.*).
- New shared consumer application-core shell/js/services/i18n.js:
  self-loads i18next, fetches the catalogs (all registered locales, own
  project namespace), resolves the 2-letter platform code to a locale id
  (en -> en-US), exposes a reactive Alpine 'i18n' store + global
  T(key, fallback, options). In the default language the baked literal
  always wins (it is the prettier authored label); in other languages a
  missing key degrades per-key to the English literal. locale.set()
  reloads the page so labels and data switch together.
- All generated view templates (manage list/form, document, master,
  list, shell sidebar/dashboard/settings, detail registry metadata via
  tkey) bind labels as T('<project>:<tprefix>...', 'Fallback') - keys
  identical to the AngularJS stack, so one translations/<locale> folder
  serves both stacks. translations.json.template gains the few keys the
  Harmonia views need beyond the AngularJS set (new/retry/all/
  clearFilters/previous/next/details/selectTitle/back/backToList,
  noDataYet/getStarted/inputSelect, error.saving).

Verified by generating the multi-model sales-invoices model through the
updated templates: all 67 files render, every generated JS parses, keys
and fallbacks correct; the locales service returns the bg-BG catalogs
(project translations/bg-BG/*.json) with English (US) fallback.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
integration-tests-postgresql failed DependsOnIT at
PredefinedProjectIT.test:28 - the 'Published all projects in' toast was
not found in any iframe. Same failure mode as the regenerate-toast flake
fixed in EdmView (#6127): on slow CI runners the Selenide cross-frame
text sweep outlasts the transient toast, and the sweep's timeout
fallback reloads the page, destroying the toast for good while the
publish itself succeeded.

Workbench.publishAll now waits for the DURABLE effect - every workspace
project appearing under /registry/public - via IRepository + Awaitility,
then proceeds to the existing synchronization wait.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s, Dashboard, Settings

The generated per-project shell and the shared application shell now
translate their own chrome, completing the label-i18n story: sidebar
entries and section headings (Application/Entities/Reports/Other),
built-in Inbox / Documents / Dashboard / Settings / Reports / not-found
pages, notifications popover, user menu, and breadcrumb Create/Edit.

The platform ships its own catalog pair: application-core/translations/
{en-US,bg-BG}/shell.json (namespace 'application-core', root 'shell') -
the locales.js service aggregates any registry module's translations
folder, so no service change was needed. i18n.js now always requests the
'application-core' namespace alongside the hosting app's project
namespace. Adding a platform language = adding one shell.json file.

Backward safety: apps generated before label i18n load the shared views
(which now bind through T()) without services/i18n.js - the always-loaded
shared app.js defines a fallback-returning T() stub that i18n.js
overwrites, so old apps keep rendering English instead of throwing.

Verified: locales service returns the bg shell catalog (nav.inbox =
'Входящи'); smoke generation over the updated shell templates renders
all chrome keys with English fallbacks and every generated JS parses.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@delchev delchev merged commit a08e927 into master Jul 3, 2026
11 of 12 checks passed
@delchev delchev deleted the fix/print-language-dialog branch July 3, 2026 16:02
delchev added a commit that referenced this pull request Jul 3, 2026
…es a busy cycle

Two regressions from the publish-toast replacement (#6129) turned master
red on the DB matrices:

- StoreAPISampleProjectIT / ExtensionDecoratorSampleProjectIT: the
  durable-effect check only required the project COLLECTION to exist in
  /registry/public, but publish copies file by file - the check passed
  on half-published projects and the tests hit 'JS source could not be
  found, consider publishing it'. Workbench.publishAll now waits until
  every workspace file exists in the registry (dot-folders like a
  cloned project's .git excluded - synchronizers never read them).

- DependsOnIT: SynchronizationUtil's 30s bound is too tight right after
  a full publish, when the synchronizer legitimately chews through the
  freshly copied projects for longer on the loaded PostgreSQL/MSSQL
  runners. Raised to 120s - an at-most bound only affects failing runs.

Both previously failing tests pass locally with the fix
(StoreAPISampleProjectIT 59.9s, DependsOnIT 60.9s, headless H2).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant