Skip to content

feat(intent): multi-language data — Java Translator port, language/file seeds, Region & Language flag#6125

Merged
delchev merged 4 commits into
masterfrom
feat/intent-multilingual
Jul 3, 2026
Merged

feat(intent): multi-language data — Java Translator port, language/file seeds, Region & Language flag#6125
delchev merged 4 commits into
masterfrom
feat/intent-multilingual

Conversation

@delchev

@delchev delchev commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

What

Ports the TS templates' multilingual entity feature (multilingual="true" + <TABLE>_LANG overlay, cf. db/translator.ts and codbex-uoms-data) to the Java stack, exposes it through the intent DSL, and adds a single per-user language flag — a Region & Language entry in the generated Settings page — driving both frontend and backend.

languages: [en, bg]                 # top level: the data languages the app offers
entities:
  - name: UoM
    kind: setting
    multilingual: true              # translatable properties may carry per-language values
seeds:
  - name: uoms-bg
    entity: UoM
    language: bg                    # translations -> UOMS_UOM_LANG
    rows:
      - { id: 8, name: "Килограм" }
  - name: countries
    entity: Country
    file: data/countries.csv        # large data sets reference an authored CSV (only .csvim generated)

How

  • SDK: new org.eclipse.dirigible.sdk.db.Translator — name-based <TABLE>_LANG merge (GUID, Id, <PascalCase property columns>, Language), case-insensitive column↔field matching (in-memory filter, independent of identifier casing/quoting), locale-tag normalization, missing-table tolerance.
  • DAO template: a multilingual repository overrides all read paths — findById, findOne (the generated controller reads single records through it), findAll ×3, query — overlaying for the thread-bound Accept-Language (User.getLanguage()); non-request callers (listeners, jobs) read base values.
  • Schema template: generates <TABLE>_LANG per multilingual model.
  • Intent DSL: multilingual: true, seed language: (translation rows, auto-numbered GUIDs), seed file: (authored CSV in a subfolder — root .csv is scrub-owned; keeps countries/currencies-sized data OUT of the intent), top-level languages:; parser validation for all of it.
  • Harmonia: shared locale Alpine store (localStorage) + Region & Language picker in the generated Settings page (mirroring the IDE's settings-locale view); the shared fetch client sends the value as Accept-Language on every call; Print prefers the configured language. Harmonia itself has no i18n API (verified against 1.24.2) — UI-label i18n remains a follow-up on top of the store; data translation is complete.
  • Docs: the aspirational "repository carries multi-language support" CLAUDE.md claim now describes the real mechanism.

Tests / verification

  • IntentEngineIT#multilingual_entity_generates_the_translation_stack (schema _LANG, repository overlays incl. findOne, _LANG csvim, config languages) + parser/EDM/csvim unit-test additions — green.
  • Verified live on the adapted sample-intent-multi-model: GET UoMController with Accept-Language: bg returns Килограм/Литър (list AND by-id), base English without the header, untranslated rows fall back; 249 countries load from the authored data/countries.csv file seed; served shell assets carry the picker/store/header wiring.

Sample-repo update (multilingual uoms + CSV-file countries) follows after this merges — its file:/language: seeds fail validation on a pre-feature platform.

🤖 Generated with Claude Code

…ation, language/file seeds, Harmonia Region & Language

Ports the TS-era `multilingual` entity feature to the Java stack and exposes
it through the intent DSL, with one per-user language flag driving frontend
and backend.

- SDK: new org.eclipse.dirigible.sdk.db.Translator (api-modules-java) — the
  db/translator.ts port with a name-based merge: reads <TABLE>_LANG
  (GUID, Id, <PascalCase property columns>, Language — the codbex-uoms-data
  convention), matches columns to entity fields case-insensitively (reads the
  whole table and filters in memory, so it is independent of identifier
  casing/quoting), normalizes locale tags to the primary subtag, tolerates a
  missing table and no-ops without a language.
- DAO template: a multilingual="true" entity overrides ALL read paths —
  findById, findOne (the generated controller reads single records through
  findOne; findById alone missed GET-by-id, caught live), findAll x3, query —
  overlaying translations for the thread-bound Accept-Language
  (User.getLanguage(); listeners/jobs read base values). TS-parity caveat
  documented: editing under a non-base language saves displayed values.
- Schema template: emits <TABLE>_LANG per multilingual model (string-typed
  non-PK/FK/calculated/audit properties, columns named after the PROPERTY,
  Language VARCHAR(2)); own loop before the base tables so the existing
  comma logic stays untouched.
- Intent DSL: entity `multilingual: true` -> EDM attribute (same one the EDM
  editor writes); seeds gain `language: bg` (translation rows -> _LANG csvim
  with auto-numbered GUIDs) and `file: data/x.csv` (large data sets reference
  an authored CSV — only the .csvim is generated; the path must be in a
  subfolder since root-level .csv files are intent-owned and scrubbed);
  top-level `languages: [en, bg]` -> .model root -> Harmonia config.js.
- Harmonia: shared locale Alpine store (localStorage codbex.harmonia.language)
  + Region & Language picker in the generated Settings page (mirrors the IDE
  settings-locale view); the shared fetch client sends the value as
  Accept-Language on every call; the document Print flow prefers the
  configured language. Harmonia itself has no i18n API (verified 1.24.2) —
  UI-label i18n stays a follow-up on top of the store.
- Docs: intent-assistant-guide, engine-intent/root/engine-java CLAUDE.md
  (the aspirational "repository carries multi-language support" claim now
  describes the real mechanism), Harmonia README.

Tests: Translator-stack IntentEngineIT (multilingual_entity_generates_the_
translation_stack) + IntentParserTest/EdmIntentGeneratorTest/
CsvimIntentGeneratorTest additions. Verified live on the adapted
sample-intent-multi-model uoms: list + by-id reads translate under
Accept-Language: bg, base values without it, untranslated rows fall back;
countries load from an authored data/countries.csv file seed.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* feat(reports): cross-model dimensions + typed per-column filters (server-side)

Two report-stack upgrades driven by the multi-model sample:

- ReportIntentGenerator joins a CROSS-MODEL relation dimension
  (dimensions: [Customer] where Customer is model: customers) against the
  owning model's real table / primary-key column / label field, resolved
  via CrossModelSupport exactly like the EDM FK (leaf-first, loud failure).
  Previously the join used this model's intent-prefixed naming and pointed
  at a non-existent local table; relation.field and filter (buildWhere)
  paths are covered too.
- The generated report stack gains typed per-column filters applied
  SERVER-side, so pagination, count and CSV export all reflect them:
  - dao template (reportFileEntity): accepts conditions
    [{column, operator, value}] validated against the report's own column
    aliases + types (FILTER_COLUMNS from $columns) and wraps the query -
    SELECT * FROM (QUERY) AS "REPORT_DATA" WHERE "alias" <op> :reportFilter<i>
    with typed named parameters; operators EQ/NE/GT/GTE/LT/LTE/LIKE;
    exportCsv(filter) honors the same conditions; the count subquery gains
    the PostgreSQL-required alias.
  - rest template: /search, /count and /export map an unknown
    column/operator (IllegalArgumentException) to 400; /export accepts the
    conditions body.
  - Harmonia report page: generation-time typed column metadata
    (reportColumns [{key, kind: date|number|boolean|text}] from the report
    columns) renders a filter panel - date ranges, number ranges, boolean
    select, text contains - with Apply/Clear, an active-count badge on the
    toolbar toggle, and a POST /search + /count switch when filters are on.

Tests: IntentEngineIT.report_file_stack_generates_typed_column_filters.
Verified live on the multi-model sample's four new invoice reports:
cross-model joins return customer/product NAMES ("Acme Ltd", not FK ids);
GTE/LTE ranges, LIKE contains, combined ranges, filtered count and export
all correct over HTTP; unknown column -> 400.

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

* fix(test): report UI files live under the RAW genFolderName (case-sensitive FS)

The generation service derives genFolderName from the report file name in
its ORIGINAL case ("OrdersByCustomer"), while the Java files use the
sanitized javaGenFolderName ("ordersbycustomer") - two distinct folders on
a case-sensitive filesystem. The IT asserted both under the sanitized name,
which only passed on macOS where the two paths collapse into one folder.

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

* feat(reports): month()/year() date-bucket dimensions

`dimensions: ["month(date)"]` groups a date for aggregation as a sortable
YYYYMM integer ((EXTRACT(YEAR) * 100 + EXTRACT(MONTH)) - e.g. 202607);
`year(field)` emits the plain year. Enables monthly income/VAT reports:

    - name: MonthlyRevenue
      source: SalesInvoice
      dimensions: ["month(date)"]
      measures: ["count(*)", "sum(net)", "sum(vat)", "sum(total)"]

The YYYYMM integer pairs naturally with the number-range column filters.
Standard-SQL EXTRACT (H2/PostgreSQL; SQL Server lacks EXTRACT - documented
limitation). Covered by the OrdersByMonth assertions in IntentEngineIT;
verified live on the multi-model sample (June/July buckets with correct
net/VAT/total sums, month >= 202607 range filter).

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

* feat(reports): right-align numeric columns and format by pattern, from the .report model

The .report model now carries per-column rendering metadata (single source
for every report UI): numeric columns (INTEGER/BIGINT/DECIMAL) get
`align: "right"`, decimals additionally the platform money pattern
(`### ### ### ##0.00`). ReportIntentGenerator emits them; the Harmonia
report-file template derives the same defaults from the column types for
.report files that predate the metadata.

Consumers:
- report-file page: headers + cells right-align via the metadata; decimal
  cells format by the DecimalFormat-style pattern (grouped thousands, fixed
  decimals - the document totals formatter); pattern-less numeric columns
  (counts, year/month buckets) render as clean integers instead of the
  DB's 1.0 / 202607.0.
- in-SPA EDM report table: same alignment + formatter, resolved at
  generation time from the property types / formatPattern.

Covered by the align/pattern assertions in IntentEngineIT; verified live on
MonthlyRevenue (five right-aligned columns, patterned sums, clean buckets).

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

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
@delchev delchev merged commit 227fcee into master Jul 3, 2026
10 of 11 checks passed
@delchev delchev deleted the feat/intent-multilingual branch July 3, 2026 06:45
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