Skip to content

feat(i18n): detect JSX, concat and whole-word plural glue (#5818) - #5832

Merged
bolichen97 merged 2 commits into
mainfrom
fix/plural-detector-sibling-spellings-5818
Aug 26, 2026
Merged

feat(i18n): detect JSX, concat and whole-word plural glue (#5818)#5832
bolichen97 merged 2 commits into
mainfrom
fix/plural-detector-sibling-spellings-5818

Conversation

@CrysisDeu

Copy link
Copy Markdown
Collaborator

Summary

The [plurals-hardcoded] ceiling tier (introduced in #5810) only saw the template-literal spelling of hardcoded plural glue. Three sibling spellings of the same root cause — a plural form chosen in JavaScript outside any translate call — reported zero to both gate tiers. This PR widens the detector; converting the flagged sites themselves is #5820 (parked on #5811).

Detector (website/scripts/lib/hardcoded-plural.mjs):

  • JSX-text glue{n} agent{n > 1 ? 's' : ''} with no i18nT. The noun run may not contain braces or angle brackets, so a match never crosses an element boundary, and the letter-flush-against-the-brace requirement keeps the i18nT-adjacent form (owned by the hard-zero tier) and the template spelling from double-billing.
  • String concatenation'agent' + (n > 1 ? 's' : '').
  • Whole-word ternaryn === 1 ? 'line' : 'lines'. The plural arm must be, by regex backreference, the s-suffixed or y→ies form of the singular arm, so a ternary choosing two unrelated words never counts. Patterns are anchored on the comparison operator (no free-form lazy prefix), keeping the scan linear; the > spellings carry a lookbehind excluding =>/>>.
  • A linear lexical region scan (scanRegions) keeps a code sample quoted in a comment from counting for the new spellings, and one quoted in a string/template text from counting as a whole-word site — with the ceiling pinned at the live count, documenting the defect must never redden CI.
  • The existing template-literal patterns, their behavior, and their tests are unchanged.

All three comparison spellings (> 1, !== 1, inverted === 1) and whitespace/quote tolerance carry over to every new variant.

Ceiling (website/scripts/i18n-plural-codemod.mjs): HARDCODED_CEILING re-measured against main with the widened set: 37 → 39. Breakdown: 36 template-literal sites (the template tier itself had ratcheted 37→36 upstream, so +3 nets to +2), plus the 3 whole-word sites the widening makes visible (PastedChip.tsx:96, SecurityPanel.tsx:525, AgentImportFlow.tsx:726). JSX-text glue and concatenation have zero live sites. Growth-only semantics unchanged.

Docs: docs/ci/i18n-gates.md and website/docs/i18n-catalog.md now state covered and uncovered spellings explicitly (tag-boundary, literal-noun, irregular-morphology, and brace limits are named so a miss is triaged as a known gap, not a malfunction).

Testing

  • npx vitest run src/test/hardcodedPlural.test.ts — 57 tests: per-variant true positives (including the three live sites verbatim), false-positive pins (unrelated-word ternaries, conditional copy, comment/string samples, arrow/shift operators, mixed quotes, cross-tag-boundary), and a linear-time regression test (64 KiB non-matching line under a generous absolute ceiling; 128 KiB measured at 3.5 ms).
  • node scripts/i18n-plural-codemod.mjs --check — green at exactly 39/39; per-spelling breakdown re-verified independently by the Opus review lane.
  • npx tsc -b, full npm run i18n:check runner — green.

Pre-push review

Two model-pinned reviewers ran before this PR opened:

  • GPT (gpt-5.6-sol): BLOCK with 2 High findings — comment/string false positives at zero slack, and quadratic scan from an unanchored lazy prefix. Both fixed (region-scan filter; operator-anchored patterns) and pinned with tests.
  • Opus (claude-opus-5): PASS, 0 blocking, 5 advisory. A1 (aggregate-ceiling wording), A2 (37→36 absorption note), A3 (literal-noun known limit) adopted; A4/A5 were resolved by the GPT fixes.

Closes #5818

The [plurals-hardcoded] ceiling tier only saw the template-literal
spelling of hardcoded plural glue. Three sibling spellings of the same
root cause (a plural form chosen in JavaScript outside any translate
call) reported zero to both gate tiers:

1. JSX-text glue: {n} agent{n > 1 ? 's' : ''} with no i18nT
2. String concatenation: 'agent' + (n > 1 ? 's' : '')
3. Whole-word ternary: n === 1 ? 'line' : 'lines'

This widens scripts/lib/hardcoded-plural.mjs with one pattern group per
spelling, keeping the existing template-literal patterns and their tests
unchanged. All three comparison spellings (> 1, !== 1, inverted === 1)
and whitespace/quote tolerance carry over to the new variants. The
whole-word variant requires, by regex backreference, that the plural arm
be the s-suffixed (or y-to-ies) form of the singular arm, so a ternary
choosing two unrelated words never counts as a plural pair.

HARDCODED_CEILING is re-measured against main with the widened set:
36 existing template-literal sites plus the 3 whole-word sites the
widening makes visible (PastedChip.tsx:96, SecurityPanel.tsx:525,
AgentImportFlow.tsx:726). JSX-text glue and concatenation have zero
live sites, so a new site in them fails the check while the aggregate
count sits at the ceiling.

Detector-only change: converting the flagged sites themselves is
issue #5820, parked on PR #5811.

Closes #5818
@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

UX Review (Fable 5) — ✅ PASS

UX-level review of 83044cb5cc37562dfb45735290b30c23c8eb73b5 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

UX-Verdict: PASS

Pure lint-tooling change — detector regexes, docs, and tests; no rendered UI, string, or flow a product user ever sees.

[UX-REVIEWED] 83044cb

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

GPT 5.6 Review — ✅ no blocking findings

GPT 5.6 completed its review of 83044cb5cc37562dfb45735290b30c23c8eb73b5 and found no blocking issues.

This comment is updated in place on each push.

Review details

FINDING -- website/scripts/lib/hardcoded-plural.mjs:249 -- checking only regions[m.index] lets a concat match consume // n > 1 ? 's' : '', falsely exceeding the exact ceiling -> Fix: validate the matched comparison operator’s region too.
[GPT-REVIEWED] 83044cb

False positive or not applicable? A repository writer can comment:
/ai-review override gpt 83044cb5cc37562dfb45735290b30c23c8eb73b5: <one-sentence reason>

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Design Review (Fable 5) — ✅ PASS

Design-level review of 83044cb5cc37562dfb45735290b30c23c8eb73b5 — updated in place on each push. A BLOCK verdict blocks PR readiness; PASS/CONCERNS are advisory.

Design-Verdict: PASS

A real detection gap, closed with the same lexical ratchet philosophy the gate already runs on, with misses documented and false positives pinned.

Watch

Suggestions

  • JSX-text glue and concatenation have zero live sites — bill those two groups to the hard-zero tier instead of the shared ceiling. The ceiling comment itself concedes the cost of pooling ("unclaimed slack from a converted site in one spelling can absorb a new site in another until the constant is tightened"); the tier-1 rationale for not going hard-zero (frozen debt would fail on arrival) doesn't apply to empty groups, and the groups are already separate exports, so this deletes the absorption window at near-zero cost.

[DESIGN-REVIEWED] 83044cb

@github-actions

github-actions Bot commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Opus 4.8 Review — ✅ no blocking findings

Reviewed 83044cb5cc37562dfb45735290b30c23c8eb73b5 — this comment is updated in place on each push.

Review details

No findings.

[OPUS-REVIEWED] 83044cb

Verdict parsed from the review's SHA-scoped output markers for commit 83044cb5cc37562dfb45735290b30c23c8eb73b5.

False positive or not applicable? A repository writer can comment:
/ai-review override fable 83044cb5cc37562dfb45735290b30c23c8eb73b5: <one-sentence reason>

@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision and removed readiness: checking Automated validation is still running labels Aug 25, 2026
CrysisDeu pushed a commit that referenced this pull request Aug 25, 2026
Ten user-visible sites still built their count labels with fully hardcoded
template-literal plural glue (`${n} issue${n === 1 ? '' : 's'}`) or its
uninflected sibling (`${n} more`) — the label assembled in JS, in English,
entirely outside any translate call — so every non-English locale rendered
mixed-language text. Sites converted, per the pattern PR #5811 established:

- App.tsx: the nav overflow toggle's "Show N more apps" title and its visible
  "N more" label, and the notification bell's "N notifications" title
- AppsPage.tsx: the "Updated N apps." success message
- issue-radar TaggingView: the "Labelled N issues[; M could not be updated]"
  banner (both branches) and the "Apply N suggestions" button
- issue-radar LabelsPanel: the "N open issues" chip title, in both its
  with-description and no-description branches
- issue-radar PrList: the "N files changed" diff-stat title
- file-explorer SearchPanel: the "N results" status line and its "(capped)"
  truncation suffix

Each site now passes the count into one i18nT(key, { count }) call. The
SearchPanel count reuses the existing components.discoverySearchBar.result
base rather than duplicating it (both sites carry a shared-owner note); the
other sites add ten new plural bases with per-language plural forms across
all 12 catalogs (categories exactly per each language's own Intl.PluralRules:
ru 4 forms, es/fr/it/pt 3, en/de/hi/bn 2, zh-CN/ja/ko 1), registered in
pluralKeys.json, plus one plain key, with en-XA regenerated.

The nav overflow toggle's accessible name now equals its visible label while
the label is shown (WCAG 2.5.3 Label in Name): converting only the aria-label
would have localized the name while the visible text stayed English, letting
the two diverge in every non-English locale. Its title keeps the fuller
"Show N more apps" phrasing for icon-only (collapsed) mode.

The [plurals-hardcoded] growth-only ceiling ratchets down 37 -> 7, the count
the gate itself reports after the conversion (the detector lib is untouched;
widening it is #5818 / PR #5832).

The remaining detector hits are model-directed prompt text (CommentOverlay,
ArtifactDetailPage, design-tweak prompts), which is deliberately English and
not user-visible locale copy.

Closes #5820
CrysisDeu pushed a commit that referenced this pull request Aug 25, 2026
Ten user-visible sites still built their count labels with fully hardcoded
template-literal plural glue (`${n} issue${n === 1 ? '' : 's'}`) or its
uninflected sibling (`${n} more`) — the label assembled in JS, in English,
entirely outside any translate call — so every non-English locale rendered
mixed-language text. Sites converted, per the pattern PR #5811 established:

- App.tsx: the nav overflow toggle's "Show N more apps" title and its visible
  "N more" label, and the notification bell's "N notifications" title
- AppsPage.tsx: the "Updated N apps." success message
- issue-radar TaggingView: the "Labelled N issues[; M could not be updated]"
  banner (both branches) and the "Apply N suggestions" button
- issue-radar LabelsPanel: the "N open issues" chip title, in both its
  with-description and no-description branches
- issue-radar PrList: the "N files changed" diff-stat title
- file-explorer SearchPanel: the "N results" status line and its "(capped)"
  truncation suffix

Each site now passes the count into one i18nT(key, { count }) call. The
SearchPanel count reuses the existing components.discoverySearchBar.result
base rather than duplicating it (both sites carry a shared-owner note); the
other sites add ten new plural bases with per-language plural forms across
all 12 catalogs (categories exactly per each language's own Intl.PluralRules:
ru 4 forms, es/fr/it/pt 3, en/de/hi/bn 2, zh-CN/ja/ko 1), registered in
pluralKeys.json, plus one plain key, with en-XA regenerated.

The nav overflow toggle's accessible name now equals its visible label while
the label is shown (WCAG 2.5.3 Label in Name): converting only the aria-label
would have localized the name while the visible text stayed English, letting
the two diverge in every non-English locale. Its title keeps the fuller
"Show N more apps" phrasing for icon-only (collapsed) mode.

The [plurals-hardcoded] growth-only ceiling ratchets down 37 -> 7, the count
the gate itself reports after the conversion (the detector lib is untouched;
widening it is #5818 / PR #5832).

The remaining detector hits are model-directed prompt text (CommentOverlay,
ArtifactDetailPage, design-tweak prompts), which is deliberately English and
not user-visible locale copy.

Closes #5820
bolichen97 pushed a commit that referenced this pull request Aug 26, 2026
…#5876)

Ten user-visible sites still built their count labels with fully hardcoded
template-literal plural glue (`${n} issue${n === 1 ? '' : 's'}`) or its
uninflected sibling (`${n} more`) — the label assembled in JS, in English,
entirely outside any translate call — so every non-English locale rendered
mixed-language text. Sites converted, per the pattern PR #5811 established:

- App.tsx: the nav overflow toggle's "Show N more apps" title and its visible
  "N more" label, and the notification bell's "N notifications" title
- AppsPage.tsx: the "Updated N apps." success message
- issue-radar TaggingView: the "Labelled N issues[; M could not be updated]"
  banner (both branches) and the "Apply N suggestions" button
- issue-radar LabelsPanel: the "N open issues" chip title, in both its
  with-description and no-description branches
- issue-radar PrList: the "N files changed" diff-stat title
- file-explorer SearchPanel: the "N results" status line and its "(capped)"
  truncation suffix

Each site now passes the count into one i18nT(key, { count }) call. The
SearchPanel count reuses the existing components.discoverySearchBar.result
base rather than duplicating it (both sites carry a shared-owner note); the
other sites add ten new plural bases with per-language plural forms across
all 12 catalogs (categories exactly per each language's own Intl.PluralRules:
ru 4 forms, es/fr/it/pt 3, en/de/hi/bn 2, zh-CN/ja/ko 1), registered in
pluralKeys.json, plus one plain key, with en-XA regenerated.

The nav overflow toggle's accessible name now equals its visible label while
the label is shown (WCAG 2.5.3 Label in Name): converting only the aria-label
would have localized the name while the visible text stayed English, letting
the two diverge in every non-English locale. Its title keeps the fuller
"Show N more apps" phrasing for icon-only (collapsed) mode.

The [plurals-hardcoded] growth-only ceiling ratchets down 37 -> 7, the count
the gate itself reports after the conversion (the detector lib is untouched;
widening it is #5818 / PR #5832).

The remaining detector hits are model-directed prompt text (CommentOverlay,
ArtifactDetailPage, design-tweak prompts), which is deliberately English and
not user-visible locale copy.

Closes #5820

Co-authored-by: Zezhen Xu <zezhexu@dev-dsk-zezhexu-2b-15d11a49.us-west-2.amazon.com>
@github-actions github-actions Bot added the merge conflict Branch has merge conflicts with its base — author must resolve before merge label Aug 26, 2026
@bolichen97
bolichen97 enabled auto-merge (squash) August 26, 2026 04:44
bolichen97
bolichen97 previously approved these changes Aug 26, 2026
# Conflicts:
#	website/scripts/i18n-plural-codemod.mjs
@github-actions github-actions Bot added readiness: checking Automated validation is still running and removed readiness: passed Eligible automated validation passed for the current revision labels Aug 26, 2026

@bolichen97 bolichen97 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Re-approved after conflict resolution against the latest main; focused validation passed.

@github-actions github-actions Bot added readiness: passed Eligible automated validation passed for the current revision and removed merge conflict Branch has merge conflicts with its base — author must resolve before merge readiness: checking Automated validation is still running labels Aug 26, 2026
@bolichen97
bolichen97 merged commit c3b715b into main Aug 26, 2026
66 checks passed
@bolichen97
bolichen97 deleted the fix/plural-detector-sibling-spellings-5818 branch August 26, 2026 17:10
@github-actions github-actions Bot removed the readiness: passed Eligible automated validation passed for the current revision label Aug 26, 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.

i18n: plurals-hardcoded detector misses JSX-text, concat, and whole-word plural spellings

3 participants