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
Translation contribution modules — a design for future consideration
Deferred by choice for now (the near-term approach is: modules carry their own translations, and the platform language set is a tenant-overridable configuration defaulting to en). Stored here so the design survives until the need is real — e.g. shipping FR/DE/… as separate deliverables, including for the platform's own Inbox/Documents surfaces.
The idea
A separately deployable module contributes translations to an EXISTING module (application or platform surface), declared by a .translations artefact — a runtime artifact with a synchronizer, exactly like .roles declares roles and .numbers declares number series:
the artefact carries metadata only (target project + language); the module carries the actual catalogs in a conventional folder mirroring the target's i18n layout (the .csvim + sibling .csv pattern);
the synchronizer merges the catalogs into the target project's registry path, so the consuming machinery (i18next, locales.js, the fetch URLs) is unchanged;
content is global, exposure is per tenant: the registry is shared, so merged catalogs exist once; a per-tenant DIRIGIBLE_LANGUAGES table (code, enabled) controls what each tenant's picker offers. Ship 100 languages, enable per tenant. DIRIGIBLE_APPLICATION_LANGUAGES remains only the bootstrap seed.
The three structural rules for the merge (the hard part)
A ledger, not faith — every merged file is recorded (DIRIGIBLE_TRANSLATION_FILES); DELETE removes exactly the ledger entries, never guesses. One writer per piece of state, applied to files.
Conflict policy mirroring .numbers — a file the target itself ships is never overwritten (the base module wins); two translation modules contributing the same file for the same target+language: identical content is a skip, differing content fails that artefact loudly naming both.
Verify-and-repair every sync cycle — republishing the TARGET project wipes merged files without touching the artefact's checksum, so the synchronizer must cheaply verify the ledger against the registry and re-copy. Healing window = one sync cycle; the interim fallback is the default language.
Coverage becomes DISCOVERED, never declared
With add-on modules, the base module's languages: declaration (intent → config.js) only says what the base ships. Coverage reporting must come from the ledger + platform-catalog discovery, exposed as GET /services/core/languages/coverage. The Settings page then inverts: first level = languages (with the per-tenant enable toggle, ADMINISTRATOR/OPERATOR), expanding a language lists the modules missing it — replacing the per-app config.js-grep warning wall (which also misclassified the platform's Inbox/Documents as untranslated apps).
Data translations are the second kind from day one: a translation module can already ship .csvim into the base module's _LANG tables; the coverage model counts ui and data separately.
Artefact name/shape (.translations, one per target+language vs a list).
Do availability rows auto-enable only for languages in the bootstrap set?
Is the coverage universe "projects contributing application-perspectives", or broader?
Merge-into-target vs a serve-time overlay (the merge was preferred to keep consumers unchanged; the overlay avoids the ownership rules at the cost of one machinery change).
reacted with thumbs up emoji reacted with thumbs down emoji reacted with laugh emoji reacted with hooray emoji reacted with confused emoji reacted with heart emoji reacted with rocket emoji reacted with eyes emoji
Uh oh!
There was an error while loading. Please reload this page.
Translation contribution modules — a design for future consideration
Deferred by choice for now (the near-term approach is: modules carry their own translations, and the platform language set is a tenant-overridable configuration defaulting to
en). Stored here so the design survives until the need is real — e.g. shipping FR/DE/… as separate deliverables, including for the platform's own Inbox/Documents surfaces.The idea
A separately deployable module contributes translations to an EXISTING module (application or platform surface), declared by a
.translationsartefact — a runtime artifact with a synchronizer, exactly like.rolesdeclares roles and.numbersdeclares number series:.csvim+ sibling.csvpattern);locales.js, the fetch URLs) is unchanged;DIRIGIBLE_LANGUAGEStable (code, enabled) controls what each tenant's picker offers. Ship 100 languages, enable per tenant.DIRIGIBLE_APPLICATION_LANGUAGESremains only the bootstrap seed.The three structural rules for the merge (the hard part)
DIRIGIBLE_TRANSLATION_FILES); DELETE removes exactly the ledger entries, never guesses. One writer per piece of state, applied to files..numbers— a file the target itself ships is never overwritten (the base module wins); two translation modules contributing the same file for the same target+language: identical content is a skip, differing content fails that artefact loudly naming both.Coverage becomes DISCOVERED, never declared
With add-on modules, the base module's
languages:declaration (intent →config.js) only says what the base ships. Coverage reporting must come from the ledger + platform-catalog discovery, exposed asGET /services/core/languages/coverage. The Settings page then inverts: first level = languages (with the per-tenant enable toggle, ADMINISTRATOR/OPERATOR), expanding a language lists the modules missing it — replacing the per-appconfig.js-grep warning wall (which also misclassified the platform's Inbox/Documents as untranslated apps).Data translations are the second kind from day one: a translation module can already ship
.csviminto the base module's_LANGtables; the coverage model countsuianddataseparately.Increments (each PR-sized)
GET/PUT /services/core/languages+ picker reads it, env fallback) — useful alone..translationsartefact + synchronizer (ledger, conflict rules, verify-and-repair, per-tenant availability rows insert-if-absent).bgcatalogs into atranslations-bgmodule contributing toapplication-core(proves the core-surfaces story).languages:in docs; the.csvim→_LANGadd-on recipe counted as data coverage).Open questions to settle first
.translations, one per target+language vs a list).application-perspectives", or broader?All reactions