Skip to content

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

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

fix(i18n): quote raw <name> in the auto-sync profiles description#12549
pacocartones wants to merge 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
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.

i18n: raw <name> tag in featureFlags.definitions.OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES.description (all 43 locales) breaks the Feature Flags page

1 participant