Skip to content

fix: keep hook-bound nested plurals intact in function expressions - #292

Closed
Aryan-Mi wants to merge 1 commit into
lingui:mainfrom
Aryan-Mi:fix/hook-t-nested-plurals
Closed

Aryan-Mi wants to merge 1 commit into
lingui:mainfrom
Aryan-Mi:fix/hook-t-nested-plurals

Conversation

@Aryan-Mi

@Aryan-Mi Aryan-Mi commented Nov 4, 2026

Copy link
Copy Markdown

A nested plural() in a hook-bound t template can become two messages instead of one. This reproduces with native-tools 0.1.6, the latest release 0.2.0, and current main.

import { useLingui } from '@lingui/react/macro';
import { plural } from '@lingui/core/macro';

const Component = wrap(() => {
  const { t } = useLingui();
  return t`Selected ${plural(count, { one: '# item', other: '# items' })}`;
});

wrap can be memo, forwardRef, or an arbitrary function. A standalone function expression and an object method also reproduce it. A plain function declaration, an unwrapped arrow, and an imported core t keep the message intact.

Babel and CLI extraction produce one message, Selected {count, plural, one {# item} other {# items}}, with ID oDeoLE. The native transform instead emits Selected {0} with ID pEM-My around a separately translated plural with ID Md_eZS. A catalog containing the extracted translation cannot satisfy those lookups. I verified English fallback text with full descriptors and the raw ID pEM-My with production ID-only descriptors.

LinguiMacroFolder enters hook scopes, but its recursive JsMacroFolder can reach a function through an enclosing expression first. It compiles plural() before the hook's t binding is registered. Move the existing hook and function-entry handling into JsMacroFolder, so function entry happens before its expressions are transformed regardless of how traversal reaches the function. Keep the outer folder's function-declaration entry point and remove the arrow-only special case.

Eight snapshot tests cover the failing forms, nested hook scopes, and controls for an outer hook translator and an imported core translator. All existing snapshots stay unchanged. Against unmodified main, six new tests fail and the two controls pass. With the fix, all eight pass.

Validation:

  • cargo test --workspace: 338 tests passed.
  • cargo fmt --all --check: passed.
  • cargo clippy --all-targets --all-features -- -D warnings: passed.
  • cargo build -p lingui_native_tools: passed.
  • vitest run --config vitest.config.ts in packages/native-tools, using the locally built native library: 43 tests passed.
  • Isolated CLI extraction and runtime comparison: the fixed native library uses oDeoLE and displays its translation with both full and ID-only descriptors.

The WASM package's JavaScript e2e suite and release build were not run. The shared Rust transform is covered by the workspace tests and the native binding tests above.

AI assistance: this change and its regression tests were developed and validated with OpenAI Codex.

@Aryan-Mi Aryan-Mi closed this Nov 4, 2026
@Aryan-Mi
Aryan-Mi deleted the fix/hook-t-nested-plurals branch November 4, 2026 10:31
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