You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
- Drop SHOW TRANSLATIONS (overlaps with SHOW LANGUAGES + QUAL005)
- Add concrete ANTLR integration into propertyValueV3
- Detail read-modify-write impact on writer architecture
- Reorder phases: P1 = DESCRIBE WITH TRANSLATIONS (highest value, zero risk)
- Add ALTER PAGE SET + translation map interaction
- Sort translations by language code for deterministic output
- Fix author attribution
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/plans/2026-04-03-mdl-i18n-design.md
+69-45Lines changed: 69 additions & 45 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,8 +1,8 @@
1
1
# MDL Internationalization (i18n) Support
2
2
3
-
**Date:** 2026-04-03
4
-
**Status:** Proposal
5
-
**Author:**@anthropics/claude-code
3
+
**Date:** 2026-04-03 (updated 2026-04-03)
4
+
**Status:** Proposal (revised per review feedback)
5
+
**Author:**@engalar
6
6
7
7
## Problem
8
8
@@ -11,16 +11,15 @@ MDL currently handles all translatable text fields (page titles, widget captions
11
11
This means:
12
12
-`DESCRIBE PAGE` output loses translations — roundtripping a page strips non-default languages
13
13
-`CREATE PAGE` can only set one language — multi-language projects require Studio Pro for translation
14
-
- No way to audit translation coverage from the CLI
14
+
- No way to see all translations in context (note: `SHOW LANGUAGES` and `QUAL005 MissingTranslations` linter rule already provide language inventory and gap detection via the catalog `strings` table)
15
15
16
16
Mendix stores translations as `Texts$Text` objects containing an array of `Texts$Translation` entries (one per language). The mxcli internal model (`model.Text`) already represents translations as `map[string]string`, and the BSON reader/writer already handles multi-language serialization. The gap is purely at the MDL syntax and command layer.
17
17
18
18
## Scope
19
19
20
20
**In scope (syntax-layer extension):**
21
-
- Inline multi-language text literal syntax for CREATE/ALTER
21
+
- Inline multi-language text literal syntax for CREATE/ALTER/ALTER PAGE SET
22
22
- DESCRIBE WITH TRANSLATIONS output mode
23
-
- SHOW TRANSLATIONS query command
24
23
- Writer changes to serialize multi-language BSON correctly
**Disambiguation from widget body `{`**: `translationMap` only appears inside `propertyValueV3`, which follows `COLON` or `EQUALS` in property definitions. Widget bodies (`widgetBodyV3`) follow `)` at statement level, never after `:`. The parser sees `Caption: {` and enters `propertyValueV3 → translationMap` — there is no ambiguity because `widgetBodyV3` is a separate production in `widgetStatementV3` that requires `(...)` before `{`.
Translation maps work in ALTER PAGE SET, enabling in-place translation updates:
124
136
125
-
-- All translations project-wide
126
-
SHOW TRANSLATIONS MISSING;
137
+
```sql
138
+
ALTER PAGE Module.MyPage
139
+
SET WIDGET saveButton Caption: { en_US: 'Save', zh_CN: '保存' };
127
140
```
128
141
129
-
**Output (tabular):**
142
+
This reuses the `translationMap` rule inside `propertyValueV3` — no additional grammar changes needed since ALTER PAGE SET already uses `propertyValueV3` for values.
### 4. Relationship to Existing Translation Features
138
145
139
-
`✗` indicates a missing translation. The `MISSING` filter shows only rows with at least one gap.
146
+
`SHOW LANGUAGES` (commit a060152) already lists project languages with string counts. `QUAL005 MissingTranslations` linter rule already detects missing translations. The catalog `strings` FTS5 table already stores per-language text with `SELECT * FROM CATALOG.strings WHERE Language = 'nl_NL'`.
140
147
141
-
**Implementation:**Reuses the existing catalog `strings` FTS5 table. Pivots rows by language code into a wide-format table. Requires `REFRESH CATALOG FULL` to index strings first.
148
+
This proposal does **not**duplicate those features. It addresses the gap they cannot fill: **writing and round-tripping multi-language text in MDL syntax**.
142
149
143
-
### 4. Writer Layer Changes
150
+
### 5. Writer Layer Changes
144
151
145
152
When executing CREATE/ALTER with multi-language text, the writer serializes all provided translations into the standard Mendix BSON format:
146
153
@@ -156,17 +163,33 @@ for langCode, text := range translatedText.Translations {
156
163
}
157
164
```
158
165
159
-
**Merge semantics for bare strings:**
160
-
When a bare string `'text'` is used, the writer must:
161
-
1. Read the existing `Texts$Text` from the MPR
162
-
2. Update only the `DefaultLanguageCode` entry
163
-
3. Preserve all other language entries unchanged
166
+
**Merge semantics for bare strings (architectural change):**
167
+
168
+
Currently, all writer functions construct `Texts$Text` from scratch — e.g. `writer_pages.go:219-247` builds a new `Items` array every time. Bare-string merge semantics require a **read-modify-write cycle**:
169
+
170
+
1. Read the existing `Texts$Text` BSON from the MPR via `GetRawUnit`
171
+
2. Parse existing `Items` array to find the entry for `DefaultLanguageCode`
172
+
3. Update that entry's `Text` field (or insert if missing)
173
+
4. Preserve all other `Texts$Translation` entries unchanged
174
+
5. Write back the modified `Items` array
175
+
176
+
This is a significant change to writer architecture. A shared helper should be introduced:
**Serialization ordering:** Translations within `Items` array must be sorted by language code for deterministic BSON output and diff-friendly DESCRIBE.
170
193
171
194
## Translatable Fields Inventory
172
195
@@ -184,16 +207,17 @@ The following fields use `Texts$Text` and are affected by this proposal:
0 commit comments