Skip to content

Client route parameterization names everything /:lng/:notFound* when the locale param is [lng] and the default locale has no URL prefix #23488

Description

@otaviosoares

Is there an existing issue for this?

How do you use Sentry?

Self-hosted / on-premise

Which SDK are you using?

@sentry/nextjs

SDK Version

10.70.0

Framework Version

Next.js 16.2.6 (App Router)

Link to Sentry event

(self-hosted)

Steps to Reproduce

Our app dir uses [lng] as the locale param (i18next convention), with a catch-all for hard 404s:

app/
  [lng]/
    page.tsx
    guides/[category]/[...rest]/page.tsx
    [...notFound]/page.tsx

The default locale is served without a prefix: /guides/renting/foo for en, /fr/guides/renting/foo for fr.

Build with route manifest injection enabled (the default) and load any default-locale page with 2+ path segments.

Expected Result

/guides/renting/foo gets named /:lng/guides/:category/:rest*, same as the prefixed locales.

Actual Result

It gets named /:lng/:notFound*. Same for basically every unprefixed URL with 2+ segments, so most of our traffic (default locale) reports transactions and error culprits pointing at the 404 catch-all.

Cause: findMatchingRoutes has a retry that prepends SENTRY_OPTIONAL_PREFIX for exactly this unprefixed-default-locale case, but it only runs for routes with hasOptionalPrefix, and createRouteManifest.ts only sets that flag when the first param is literally locale, lang or language:

// packages/nextjs/src/config/manifest/createRouteManifest.ts
function hasOptionalPrefix(paramNames: string[]): boolean {
  const firstParam = paramNames[0];
  ...
  return firstParam === 'locale' || firstParam === 'lang' || firstParam === 'language';
}

lng isn't in the list, even though it's i18next's own option name and the folder name used in the next-i18next examples. So for [lng] projects the regexes never match unprefixed paths and the catch-all wins. Only workaround right now is routeManifestInjection: false, which drops parameterization entirely.

Proposed fix

Add 'lng' to the list, and/or make it configurable. Already raised in the #17927 description:

Maybe we should make certain aspects of this configurable, like the ['locale', 'lang', 'language'] collection

e.g.:

routeManifestInjection: {
  localeParamNames: ['lng']
}

Can open a PR for either.

Metadata

Metadata

Assignees

No one assigned

    Projects

    Status
    Waiting for: Product Owner

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions