Skip to content

fix: transform JSX macros nested inside expression placeholders - #279

Merged
andrii-bodnar merged 2 commits into
lingui:mainfrom
cpruijsen:fix/issue-16-57a43fb6
Oct 8, 2026
Merged

andrii-bodnar merged 2 commits into
lingui:mainfrom
cpruijsen:fix/issue-16-57a43fb6

Conversation

@cpruijsen

Copy link
Copy Markdown
Contributor

Fixes #16

<Trans>, <Plural>, <Select> and <SelectOrdinal> inside a JSX expression placeholder are now transformed. Before this change, they were emitted as-is, for example in {cond ? <Trans>a</Trans> : <Trans>b</Trans>}, {cond && <Trans>a</Trans>} or {items.map(i => <Trans>{i}</Trans>)}. The macro import was still removed, so the output referenced an element with no import.

Cause

fold_jsx_element returned the result of transform_jsx_macro without folding it again. transform_jsx_macro copies placeholder expressions verbatim into values and components, so the folder never visited macros inside them. The other fold_* methods in the same impl Fold call fold_children_with(self) after they transform.

Fix

Both macro branches in fold_jsx_element now call fold_children_with(self) on the produced element. The fold runs after the message is built, so a direct child macro such as <Trans>You have <Plural .../></Trans> is still inlined into the parent message. The produced element uses the runtime Trans identifier, so the fold does not match it again.

Before:

values: { 0: props.world ? <Trans>world</Trans> : <Trans>guys</Trans> }

After:

values: {
  0: props.world
    ? <Trans_ {.../*i18n*/ { id: "ELi2P3", message: "world" }}/>
    : <Trans_ {.../*i18n*/ { id: "39nd-2", message: "guys" }}/>
}

The nested descriptors keep their /*i18n*/ comments, so the extractor can find these messages too.

Scope

  • The fold runs on the produced element, not the source element. Folding the source children first would compile a direct child <Plural> or <Trans> into a runtime element, and it would no longer be inlined into the parent message.
  • The fold covers all children of the produced element, not only values and components. This also fixes macros in the preserved render and component attributes, for example render={() => <Trans>x</Trans>}. I can limit it to the descriptor if you prefer a narrower change.

Tests

  • New snapshot test jsx_macros_inside_expression_are_transformed covers the repro from the issue and the && form. Without the fix, it shows the untransformed <Trans> in values.
  • All existing lingui_macro and lingui_extractor snapshots are unchanged.
  • cargo fmt --check and cargo clippy -p lingui_macro --all-targets --all-features -- -D warnings pass.

fold_jsx_element returned transform_jsx_macro(el) for Trans and ICU
choice elements without folding the produced element's children, so
macro elements captured inside values/components expressions (e.g.
{cond ? <Trans/> : <Trans/>} or {cond && <Trans/>}) were emitted
untransformed after the macro import had been removed.

Fold the produced element with self so nested macros inside those
expressions are transformed recursively, matching how every other
fold_* method in the file recurses after transforming.

Fixes lingui#16
@codecov

codecov Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 94.44%. Comparing base (673f2fd) to head (911fe12).

Additional details and impacted files

Impacted file tree graph

@@           Coverage Diff           @@
##             main     #279   +/-   ##
=======================================
  Coverage   94.44%   94.44%           
=======================================
  Files          11       11           
  Lines        2518     2518           
=======================================
  Hits         2378     2378           
  Misses        140      140           
Flag Coverage Δ
unittests 94.44% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
crates/lingui_macro/src/lib.rs 91.47% <100.00%> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@andrii-bodnar
andrii-bodnar merged commit 32bb27a into lingui:main Oct 8, 2026
28 checks passed
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.

Trans macro inside ConditionalExpressions are not processed

3 participants