Skip to content

fix(l10n): 27 Dutch strings that never reached the browser - #296

Merged
rubenvdlinde merged 1 commit into
developmentfrom
fix/l10n-js-catalogues
Aug 30, 2026
Merged

fix(l10n): 27 Dutch strings that never reached the browser#296
rubenvdlinde merged 1 commit into
developmentfrom
fix/l10n-js-catalogues

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

versioniq shipped l10n/nl.json and no l10n/nl.js. Nextcloud reads the .json server-side for PHP $l->t(); the browser only ever sees the OC.L10N.register() call in the .js. So all 27 Dutch translations were inert in the frontend.

Fourth and last instance of the same gap, measured across all 21 fleet apps on development: zaakafhandelapp (637 keys, #554), nextcloud-app-template (38 catalogues, #186), thematiq (209 keys, #493) and this one. Every other app already ships both halves.

One difference worth noting

The generator is petstore's build-l10n-js adopted unchanged except for its extension. This package.json declares "type": "module", so a CommonJS .js is loaded as ESM and dies on its first require():

ReferenceError: require is not defined in ES module scope

So the file is .cjs here. The other three apps do not set a type and keep .js. Worth knowing before this travels to any other repo.

Verification

  • nl.js written with 27 keys; the register call names versioniq, read from appinfo/info.xml.
  • The file parses under node --check and is 3,710 bytes rather than empty — an empty file passes node --check, so size was checked separately.
  • check:l10n-js exits 0.

🤖 Generated with Claude Code

versioniq shipped l10n/nl.json and no l10n/nl.js. Nextcloud reads the .json
server-side for PHP $l->t(); the browser only ever sees the OC.L10N.register()
call in the .js. All 27 Dutch translations were therefore inert in the frontend.

Fourth and last instance of a gap measured across all 21 fleet apps:
zaakafhandelapp (637 keys), nextcloud-app-template (38 catalogues), thematiq
(209 keys) and this one. Every other app already ships both halves.

The generator is petstore's build-l10n-js adopted unchanged, EXCEPT for its
extension. This package.json declares "type": "module", so a CommonJS .js is
loaded as ESM and dies on its first require(). The file is .cjs here; the other
three apps do not set a type and keep .js.

Verified: nl.js written with 27 keys, the register call names versioniq read
from appinfo/info.xml, the file parses under node --check and is 3710 bytes
rather than empty, and check:l10n-js exits 0.
@rubenvdlinde

Copy link
Copy Markdown
Contributor Author

Refining one line in the description, because the mechanism is worth stating precisely.

I wrote that the .js is "the only thing the browser ever sees". That is the generator script's wording and it is slightly too strong. What the code in these repos actually says:

  • Apps do call loadTranslations('<app>', …), which fetches l10n/<locale>.json at runtime.
  • But on many installs, including this fleet's standard dev container, Apache only lets the JS/CSS allowlist through and rewrites everything else to index.php. There is no route for /custom_apps/<app>/l10n/<locale>.json, so the fetch fails and loadTranslations rejects. src/main.js documents this and deliberately mounts anyway: "Strings just fall back to their English source on miss."
  • l10n/<locale>.js is an OC.L10N.register() call loaded as a <script>, so it rides the allowlist and works where the JSON does not.

So the accurate claim is: the JSON path is real but unreliable, the .js path is the one that survives, and shipping only .json is what produces a Dutch account reading English. The fix in this PR is unchanged.

@github-actions

Copy link
Copy Markdown
Contributor

Quality Report — ConductionNL/versioniq @ 6369e51

Check PHP Vue Security License Tests
lint
phpcs
phpmd
psalm
phpstan
phpmetrics
eslint
stylelint
build
composer ✅ 29/29
npm ✅ 310/310
app:check-code ⏭️
info.xml
REUSE
PHPUnit
Newman ⏭️
Playwright 🚨 NO VERDICT — enabled but never ran
Hydra gates

Quality workflow — 2026-08-30 17:28 UTC

Download the full PDF report from the workflow artifacts.

@rubenvdlinde
rubenvdlinde merged commit 02f4130 into development Aug 30, 2026
47 checks passed
@rubenvdlinde
rubenvdlinde deleted the fix/l10n-js-catalogues branch August 30, 2026 17:39
rubenvdlinde added a commit that referenced this pull request Aug 31, 2026
The settings panel rendered entirely in English on a Dutch account: Apps,
History, Sources, Tokens, Trusted sources, Discover, Artifact cache, Automatic
updates, Security advisory checks. Every one of those was ABSENT from the
catalogue, not mistranslated.

Measured with a scanner that catches this.t( and $t( as well as bare t(, since
a naive pattern under-reports Options-API components:

  distinct t('versioniq', ...) keys in src/   181
  already in nl.json                           25
  missing                                     156

All 156 translated. en.json did not exist at all and is added as identity, so
the pair matches the rest of the fleet and check:l10n-js has both halves to
compare.

This is a SEPARATE gap from the missing-.js fault fixed in #296. That one made
27 existing translations unreachable by the browser; this one is that the other
156 strings were never written. Fixing the artefact could not have revealed
them, because before #296 all 181 rendered English identically.

Placeholders are preserved exactly ({hours}, {minutes}, {level}, {source},
{min}, {max}, {forge}, {days}, {version}, {user}, {date}, {size}, {keep},
{pattern}, {count}) -- verified programmatically, 0 mismatches.

No em-dashes in the Dutch, per the writing skill's voice rules, even where the
English source uses them; those become a colon or a full stop.

Verified: check:l10n-js exits 0 with en.js 156 keys and nl.js 183; the file the
browser loads was fetched from the running server and carries the Dutch.

Co-authored-by: Conduction Release Bot <release-bot@conduction.nl>
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