Skip to content

fix(extractor): route each t alias to its own useTranslate namespace#204

Closed
bdshadow wants to merge 1 commit into
bdshadow/resolve-const-namespacefrom
bdshadow/multi-namespace-aliases
Closed

fix(extractor): route each t alias to its own useTranslate namespace#204
bdshadow wants to merge 1 commit into
bdshadow/resolve-const-namespacefrom
bdshadow/multi-namespace-aliases

Conversation

@bdshadow
Copy link
Copy Markdown
Member

Problem

Reported by a customer: a file with two useTranslate calls in the
same scope misroutes keys.

const { t } = useTranslate('defaultNamespace');
const { t: tCommon } = useTranslate('common');

<span>{t('some.key.from.default')}</span>
<span>{tCommon('some.shared.label')}</span>

tolgee extract print:

1 key found in src/components/Example.tsx:
line 14: some.key.from.default
namespace: common
Total unique keys found: 1

t(...) is attributed to common (the namespace of the last
useTranslate), and tCommon(...) is silently dropped — no
warning. The "component-specific NS plus a shared common NS"
pattern is hard to avoid in a real codebase, so this hits people
quickly.

Root cause

Two interlocking bugs:

  1. tFunctionMerger only matches the literal name t
    (token === 't'). Any aliased name like tCommon is invisible to
    the parser, so the call never produces a keyInfo.
  2. generateReport threads "most recent nsInfo" as contextNs for
    all sibling nodes. Two useTranslate(...) declarations collapse
    onto whichever appeared last.

Solution

A small source-level pre-pass (extractTranslateAliases.ts) detects
all const { t [, ...] } = useTranslate(...) and
const { t: alias } = useTranslate(...) shapes, plus the
const t = await getTranslate(...) variant. For each, it records:

  • aliasMap: alias name → resolved namespace literal
  • lineAliasMap: line of the useTranslate( call → alias

The namespace argument is resolved against the const map from
#202 (so useTranslate(NS.COMMON) works once that PR lands).

After the main merger pipeline, the parser walks the merged tokens
once and retags any function.call(<alias>) + expression.begin pair
as a trigger.t.function token whose .token is <alias>(. The
existing tFunction rule strips the trailing ( to recover the alias
and attaches it to the produced keyInfo. The useTranslate rule
looks up lineAliasMap[currentLine] and attaches the alias to its
nsInfo.

generateReport.reportGeneral now carries a Map<alias, nsInfo>
alongside the existing "most-recent nsInfo" walker. When a keyInfo
arrives with an alias, the reporter prefers the matching nsInfo over
the latest-sibling heuristic.

Scope and limitations

  • File-level alias tracking. A useTranslate inside one function and
    a same-named alias inside a different function will still share the
    binding. In practice, alias names like tCommon are unique per file.
  • Single-line useTranslate(...) calls. Multi-line argument lists are
    handled, but the lineAliasMap keys on the line of the opening (
    — collisions on the same line aren't disambiguated.
  • Built on top of feat(extractor): resolve const namespace identifiers and NS.KEY member access #202; rebase target is
    bdshadow/resolve-const-namespace.

Verification

  • npm run test:unit — 613 passed (was 597; +16 new alias tests
    across four file extensions).
  • Customer reproduction now extracts both keys with the right
    namespaces and zero warnings.

Test plan

  • Customer pattern reproduces and resolves correctly.
  • Existing single-namespace files behave identically.
  • const t = await getTranslate('ns') style (no destructure)
    continues to work.

Depends on

A file with multiple useTranslate calls in the same scope used to
attribute every t() call to whichever useTranslate appeared last, and
silently dropped any aliased call (`const { t: tCommon } = ...`).

The pre-pass now records each destructured alias and the namespace it
came from. Aliased call sites are retagged as trigger.t.function so
the existing rule pipeline handles them; the resulting keyInfo and
nsInfo carry the alias, and the reporter matches them up instead of
relying on "latest nsInfo wins."

Resolves the customer report on bdshadow/resolve-const-namespace
about multi-namespace components.
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 22, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3f8a07bb-ee94-4d5e-a636-6427b3b0ec2d

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch bdshadow/multi-namespace-aliases

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@bdshadow bdshadow closed this May 22, 2026
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