Skip to content

fix(i18n): quote raw <name> in the auto-sync profiles description - #12549

Merged
diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.51from
pacocartones:fix/i18n-escape-raw-name-tag-12505
Sep 11, 2026
Merged

diegosouzapw merged 2 commits into
diegosouzapw:release/v3.8.51from
pacocartones:fix/i18n-escape-raw-name-tag-12505

Conversation

@pacocartones

Copy link
Copy Markdown
Contributor

Summary

  • featureFlags.definitions.OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES.description (src/i18n/messages/en.json:12971 and the same key in the other 41 locales) carries the literal path ~/.claude/profiles/<name>/settings.json. next-intl parses <name> as a rich-text tag; FeatureFlagsGrid.tsx:426-428 renders the description through plain t() and never passes a tag element, so the message fails to compile with INVALID_MESSAGE: UNCLOSED_TAG and the Settings → Feature Flags card shows the raw key instead of the description, in every language.
  • The placeholder is now wrapped in ICU single quotes ('<name>') in all 42 locale files and in the TypeScript default (src/shared/constants/featureFlagDefinitions.ts:613), the same form as fix(i18n): wrap ccOnboardingKeyPlaceholder in ICU single quotes across all 43 locales #12369 for ccOnboardingKeyPlaceholder. One substring changed per file, nothing else touched. Reproduced and verified against use-intl's development build (the one Turbopack dev mode loads): raw <name> → UNCLOSED_TAG + key fallback; '<name>' → the literal path renders; &lt;name&gt; → the entities are returned verbatim by t() and would be displayed as text, so the entity form used by earlier fixes is not suitable for a real file path the user is meant to copy. The production build of use-intl tolerates the raw tag on the plain t() path, which is why the symptom is context-dependent, as the report says.
  • Deliberately out of scope: the reporter's second follow-up (a CI gate that parses every en.json value with the ICU parser). tests/unit/i18n-vi-completeness.test.ts already skips any key whose English source does not parse, so that gate is a separate change with its own allowlist; it will be proposed as an issue first. This PR fixes one message and adds one focused guard.

Related Issues

Validation

  • Change type: i18n
  • Focused tests and category gates from the golden path: new test 5/5; tests/unit/i18n-vi-completeness.test.ts, i18n-cc-alias-unclosed-tags, i18n-deno-relay-unclosed-tag, i18n-placeholder-parity, i18n-ui-value-drift, i18n-translation-ratio-gate, settings-i18n-keys, i18n-config 43/43; npm run i18n:sync-ui:dry, npm run i18n:check-ui-coverage, npm run i18n:check-value-drift, npm run i18n:check-glossary all PASS; npm run check:changelog-integrity OK; node scripts/check/check-complexity-ratchets.mjs --base-ref origin/release/v3.8.51 OK; vi.json carries no __MISSING__ marker for this key
  • npm run lint — eslint and prettier run on the two touched source/test files only (exit 0); the repository-wide run was not executed for a one-substring locale change
  • Reconciled with the current active release base release/v3.8.51; focused checks rerun afterward
  • Production-code changes include a new or updated automated test in this PR
  • SonarQube is temporarily opt-in while the private project has no quota; it is not a PR gate.

Mutation: with the locale/TS change reverted the new test fails 3 of 5 cases (ICU parse, quoted form, TS default); green 5/5 with the change.

Tests Added Or Updated

  • tests/unit/i18n-feature-flag-auto-sync-profiles-tag-12505.test.ts (new, 5 cases): the key exists in all 42 locales (count from config/i18n.json, no BOM); every locale value parses with @formatjs/icu-messageformat-parser; every locale uses profiles/'<name>'/ and neither the raw nor the entity form; createTranslator renders ~/.claude/profiles/<name>/settings.json literally in every locale with no INVALID_MESSAGE; the TypeScript default parses and uses the same quoting.

Coverage Notes

  • src/shared/constants/featureFlagDefinitions.ts: one string literal changed; the new test imports the module and asserts the OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description. No logic changed, no coverage moved.
  • src/i18n/messages/*.json are data files, covered by the new test and by the existing i18n-vi-completeness ICU-regression case, which now actively checks this key because the English source parses.

Reviewer Notes

The `featureFlags.definitions.OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES.description`
message carries the literal path `~/.claude/profiles/<name>/settings.json`.
next-intl parses `<name>` as a rich-text tag; `FeatureFlagsGrid.tsx` renders
the description through plain `t()` and never passes a tag element, so the
message fails to compile with `INVALID_MESSAGE: UNCLOSED_TAG` and the card
shows the raw key in every locale.

Wrap the placeholder in ICU single quotes (`'<name>'`) in all 42 locales and
in the TypeScript default so the angle brackets render literally. HTML
entities are not an option for a real file path: `t()` returns them verbatim.

Add a regression test that checks the key in every locale parses as ICU,
uses the quoted form, renders the literal path through `createTranslator`,
and that the TypeScript default matches.

Closes diegosouzapw#12505
@diegosouzapw
diegosouzapw merged commit ba1ee66 into diegosouzapw:release/v3.8.51 Sep 11, 2026
16 checks passed
Githab-capibara added a commit to Githab-capibara/OmniRoute that referenced this pull request Sep 17, 2026
…egosouzapw#12549)

Merged for the half that is still live. The 42 locale catalogs were fixed upstream while this sat open — all 51 now carry `'<name>'` — but the TypeScript default at `featureFlagDefinitions.ts:625` still had the raw tag, so the Feature Flags card kept failing to compile wherever the default is the source. Against the current tip this PR lands exactly three files: that one-character fix, the changelog fragment, and your 135-line regression test, with zero locale files touched.

The analysis is what makes it worth keeping. `next-intl` parsing `<name>` as a rich-text tag, `FeatureFlagsGrid.tsx` rendering the description through plain `t()` with no tag element, and the result being `INVALID_MESSAGE: UNCLOSED_TAG` — the card showing the raw key instead of the description, in every language — is a failure mode that is easy to misread as a missing translation. Verifying it against `use-intl`'s `development` build, the one Turbopack dev mode actually loads, is the detail that makes the reproduction trustworthy. Wrapping in ICU single quotes matches what diegosouzapw#12369 did for `ccOnboardingKeyPlaceholder`.

5/5 on the regression suite against the tip.

---

⚠️ base-red inherited: diegosouzapw#12732.

Thanks @pacocartones — 18 more of yours merged today.
muhamadgalihsaputra pushed a commit to niyatna/NiyatnaRoute that referenced this pull request Sep 27, 2026
…egosouzapw#12549)

Merged for the half that is still live. The 42 locale catalogs were fixed upstream while this sat open — all 51 now carry `'<name>'` — but the TypeScript default at `featureFlagDefinitions.ts:625` still had the raw tag, so the Feature Flags card kept failing to compile wherever the default is the source. Against the current tip this PR lands exactly three files: that one-character fix, the changelog fragment, and your 135-line regression test, with zero locale files touched.

The analysis is what makes it worth keeping. `next-intl` parsing `<name>` as a rich-text tag, `FeatureFlagsGrid.tsx` rendering the description through plain `t()` with no tag element, and the result being `INVALID_MESSAGE: UNCLOSED_TAG` — the card showing the raw key instead of the description, in every language — is a failure mode that is easy to misread as a missing translation. Verifying it against `use-intl`'s `development` build, the one Turbopack dev mode actually loads, is the detail that makes the reproduction trustworthy. Wrapping in ICU single quotes matches what diegosouzapw#12369 did for `ccOnboardingKeyPlaceholder`.

5/5 on the regression suite against the tip.

---

⚠️ base-red inherited: diegosouzapw#12732.

Thanks @pacocartones — 18 more of yours merged today.
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.

fix: escape raw <name> tag in OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES description across 43 locales

2 participants