Skip to content

feat: render invalid ICU placeholders as plain text#10

Open
bdshadow wants to merge 1 commit into
mainfrom
bdshadow/rtl-incomplete-expression
Open

feat: render invalid ICU placeholders as plain text#10
bdshadow wants to merge 1 commit into
mainfrom
bdshadow/rtl-incomplete-expression

Conversation

@bdshadow

@bdshadow bdshadow commented Jul 1, 2026

Copy link
Copy Markdown
Member

Problem

Invalid ICU expressions like {placeholder:space} (a colon isn't valid ICU) parsed as a partial {placeholder Expression plus trailing :space} text. In an RTL editor, htmlIsolatesPlugin puts the Expression in an LTR isolate and the trailing text in an RTL isolate, splitting the single token across two bidi boundaries and scrambling it.

Fix

Invalid {...} contents now parse leniently as a single Expression containing an InvalidExpressionBody (no Param, no styled tokens):

  • No chip in placeholder mode — getPlaceholders skips it (inner node isn't FormatExpression).
  • No highlighting in syntax mode — there are no Param/styled child tokens.
  • Not split — the whole {...} stays one node, so RTL bidi isolation keeps it intact.
  • Valid placeholders / plurals / selects are unchanged.

Mechanism: an extend: true external tokenizer forks the parse, and InvalidExpressionBody's @dynamicPrecedence=-1 makes a real format/select parse win whenever one exists. ICU validity itself is still reported by the formatjs-based linter.

Adds getInvalidPlaceholders(text, nested?) to detect such expressions (used by the platform to warn the user).

Tests

166 tests pass, including new parser cases and a getInvalidPlaceholders suite. tsc, eslint, and vite build are clean.

Invalid ICU expressions like `{placeholder:space}` used to parse as a
partial `{placeholder` Expression plus trailing text. In RTL editors the
two halves ended up in different bidi-isolation runs, scrambling the token.

They now parse leniently as a single Expression with no styled child
tokens, so they render as plain text — no chip, no highlighting — and stay
intact in RTL. Valid placeholders are unchanged. Adds `getInvalidPlaceholders`
to detect them.
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