Commit af83b87
authored
fix(nextjs): Inconsistent transaction naming for i18n routing (#17927)
### Problem
When using Next.js 15 App Router with `next-intl` and `localePrefix:
"as-needed"`, Web Vitals and transaction names were inconsistent across
locales:
- `/foo` (default locale, no prefix) → Transaction: `/:locale` ❌
- `/ar/foo` (non-default locale, with prefix) → Transaction:
`/:locale/foo` ✅
This caused all default locale pages to collapse into a single
`/:locale` transaction, making Web Vitals data unusable for apps with
i18n routing.
After investigation it seems like the route parameterization logic
couldn't match `/foo` (1 segment) to the `/:locale/foo` pattern (expects
2 segments) because the locale prefix is omitted in default locale URLs.
### Solution
Implemented enhanced route matching with automatic i18n prefix
detection:
1. **Route Manifest Metadata** - Added `hasOptionalPrefix` flag to route
info to identify routes with common i18n parameter names (`locale`,
`lang`, `language`)
2. **Smart Fallback Matching** - When a route doesn't match directly,
the matcher now tries prepending a placeholder segment for routes
flagged with `hasOptionalPrefix`
- Example: `/foo` → tries matching as `/PLACEHOLDER/foo` → matches
`/:locale/foo` ✓
3. **Updated Specificity Scoring** - changed route specificity
calculation to prefer longer routes when dynamic segment counts are
equal
- Example: `/:locale/foo` (2 segments) now beats `/:locale` (1 segment)
### Result
**After fix:**
```
URL: /foo → Transaction: /:locale/foo ✅
URL: /ar/foo → Transaction: /:locale/foo ✅
URL: /products → Transaction: /:locale/products ✅
URL: /ar/products → Transaction: /:locale/products ✅
```
All routes now consistently use the same parameterized transaction name
regardless of locale, making Web Vitals properly grouped and usable.
### Backwards Compatibility
- No breaking changes - only applies when direct matching would fail
- Only affects routes with first param named `locale`/`lang`/`language`
- Non-i18n apps completely unaffected
- Direct matches always take precedence over optional prefix matching
Fixes #17775
---
Maybe we should make certain aspects of this configurable, like the
`['locale', 'lang', 'language']` collection1 parent 66dc9a2 commit af83b87
File tree
29 files changed
+775
-3
lines changed- dev-packages/e2e-tests/test-applications
- nextjs-15-intl
- app
- [locale]
- i18n-test
- i18n
- tests
- nextjs-15
- app/[locale]/i18n-test
- tests
- packages/nextjs
- src
- client/routing
- config/manifest
- test
- client
- config/manifest/suites
- base-path
- catchall-at-root
- catchall
- dynamic
- route-groups
29 files changed
+775
-3
lines changedLines changed: 51 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 23 | + | |
| 24 | + | |
| 25 | + | |
| 26 | + | |
| 27 | + | |
| 28 | + | |
| 29 | + | |
| 30 | + | |
| 31 | + | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
| 35 | + | |
| 36 | + | |
| 37 | + | |
| 38 | + | |
| 39 | + | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
| 46 | + | |
| 47 | + | |
| 48 | + | |
| 49 | + | |
| 50 | + | |
| 51 | + | |
Lines changed: 2 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
Lines changed: 9 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
Lines changed: 9 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
Lines changed: 11 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
Lines changed: 14 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
Lines changed: 10 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
Lines changed: 11 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
Lines changed: 13 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
Lines changed: 8 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
0 commit comments