gt-next 11.3.0 → 11.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/middleware-dir/createNextMiddleware.d.ts +3 -1
- package/dist/middleware-dir/createNextMiddleware.d.ts.map +1 -1
- package/dist/middleware-dir/createNextMiddleware.js +15 -10
- package/dist/middleware-dir/createNextMiddleware.js.map +1 -1
- package/dist/middleware-dir/createNextMiddleware.mjs +15 -10
- package/dist/middleware-dir/createNextMiddleware.mjs.map +1 -1
- package/dist/middleware-dir/utils.d.ts +1 -1
- package/dist/middleware-dir/utils.d.ts.map +1 -1
- package/dist/middleware-dir/utils.js +4 -3
- package/dist/middleware-dir/utils.js.map +1 -1
- package/dist/middleware-dir/utils.mjs +4 -3
- package/dist/middleware-dir/utils.mjs.map +1 -1
- package/package.json +3 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# gt-next
|
|
2
2
|
|
|
3
|
+
## 11.4.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#2303](https://github.com/generaltranslation/gt/pull/2303) [`c552090`](https://github.com/generaltranslation/gt/commit/c55209015fbf4b79f19ffe8193dee995ed9bdf36) Thanks [@eoinest](https://github.com/eoinest)! - Add `enableSmartRouting` to `createNextMiddleware`. It defaults to `true`, preserving existing locale negotiation and default-alias behavior.
|
|
8
|
+
|
|
9
|
+
With `enableSmartRouting: false` and `prefixDefaultLocale: false`, an unprefixed URL selects the default locale for visitors who already have a locale cookie. First visits still use browser language preferences, and explicit `setLocale()` requests override the URL. Explicit default-locale prefixes redirect to the unprefixed URL. On document requests without a locale cookie, this redirect sets the default-locale cookie to preserve that choice; background fetches and existing locale cookies are left untouched. Routing with `prefixDefaultLocale: true` or `localeRouting: false` is unchanged.
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- Updated dependencies []:
|
|
14
|
+
- @generaltranslation/react-core@11.4.0
|
|
15
|
+
- gt-react@11.4.0
|
|
16
|
+
|
|
3
17
|
## 11.3.0
|
|
4
18
|
|
|
5
19
|
### Minor Changes
|
|
@@ -13,15 +13,17 @@ export type LocaleRoutes = Record<string, readonly string[]>;
|
|
|
13
13
|
*
|
|
14
14
|
* @param {boolean} [config.localeRouting=true] - Flag to enable or disable automatic locale-based routing.
|
|
15
15
|
* @param {boolean} [config.prefixDefaultLocale=false] - Flag to enable or disable prefixing the default locale to the pathname, i.e., /en/about -> /about
|
|
16
|
+
* @param {boolean} [config.enableSmartRouting=true] - Keep automatic locale negotiation. Disable to treat returning visitors' unprefixed URLs as default-locale routes.
|
|
16
17
|
* @param {boolean} [config.ignoreSourceMaps=true] - Flag to enable or disable ignoring source maps
|
|
17
18
|
* @param {PathConfig} [config.pathConfig] - Path configuration for locale routing
|
|
18
19
|
* @param {RouteOverrides} [config.routeOverrides] - Locale-relative paths to rewrite from /{locale}/{path} to /{locale}/{locale}/{path}
|
|
19
20
|
* @param {LocaleRoutes} [config.localeRoutes] - Shared paths available per locale; other paths fall back to the default locale
|
|
20
21
|
* @returns {function} - A middleware function that processes the request and response.
|
|
21
22
|
*/
|
|
22
|
-
export declare function createNextMiddleware({ localeRouting, prefixDefaultLocale, ignoreSourceMaps, pathConfig, routeOverrides, localeRoutes, }?: {
|
|
23
|
+
export declare function createNextMiddleware({ localeRouting, prefixDefaultLocale, enableSmartRouting, ignoreSourceMaps, pathConfig, routeOverrides, localeRoutes, }?: {
|
|
23
24
|
localeRouting?: boolean;
|
|
24
25
|
prefixDefaultLocale?: boolean;
|
|
26
|
+
enableSmartRouting?: boolean;
|
|
25
27
|
ignoreSourceMaps?: boolean;
|
|
26
28
|
pathConfig?: PathConfig;
|
|
27
29
|
routeOverrides?: RouteOverrides;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createNextMiddleware.d.ts","sourceRoot":"","sources":["../../src/middleware-dir/createNextMiddleware.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAS/C,OAAO,EAUL,KAAK,UAAU,EAEhB,MAAM,SAAS,CAAC;AAejB,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;AAE/D,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;AAE7D
|
|
1
|
+
{"version":3,"file":"createNextMiddleware.d.ts","sourceRoot":"","sources":["../../src/middleware-dir/createNextMiddleware.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAS/C,OAAO,EAUL,KAAK,UAAU,EAEhB,MAAM,SAAS,CAAC;AAejB,MAAM,MAAM,cAAc,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;AAE/D,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAAC;AAE7D;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,aAAoB,EACpB,mBAA2B,EAC3B,kBAAyB,EACzB,gBAAuB,EACvB,UAAe,EACf,cAAmB,EACnB,YAAiB,GAClB,GAAE;IACD,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,mBAAmB,CAAC,EAAE,OAAO,CAAC;IAC9B,kBAAkB,CAAC,EAAE,OAAO,CAAC;IAC7B,gBAAgB,CAAC,EAAE,OAAO,CAAC;IAC3B,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,YAAY,CAAC,EAAE,YAAY,CAAC;CACxB,SAmKqB,WAAW,2BA6TrC"}
|
|
@@ -24,13 +24,14 @@ const NEXT_JS_SOURCE_MAP_PATH = "/__nextjs_source-map";
|
|
|
24
24
|
*
|
|
25
25
|
* @param {boolean} [config.localeRouting=true] - Flag to enable or disable automatic locale-based routing.
|
|
26
26
|
* @param {boolean} [config.prefixDefaultLocale=false] - Flag to enable or disable prefixing the default locale to the pathname, i.e., /en/about -> /about
|
|
27
|
+
* @param {boolean} [config.enableSmartRouting=true] - Keep automatic locale negotiation. Disable to treat returning visitors' unprefixed URLs as default-locale routes.
|
|
27
28
|
* @param {boolean} [config.ignoreSourceMaps=true] - Flag to enable or disable ignoring source maps
|
|
28
29
|
* @param {PathConfig} [config.pathConfig] - Path configuration for locale routing
|
|
29
30
|
* @param {RouteOverrides} [config.routeOverrides] - Locale-relative paths to rewrite from /{locale}/{path} to /{locale}/{locale}/{path}
|
|
30
31
|
* @param {LocaleRoutes} [config.localeRoutes] - Shared paths available per locale; other paths fall back to the default locale
|
|
31
32
|
* @returns {function} - A middleware function that processes the request and response.
|
|
32
33
|
*/
|
|
33
|
-
function createNextMiddleware({ localeRouting = true, prefixDefaultLocale = false, ignoreSourceMaps = true, pathConfig = {}, routeOverrides = {}, localeRoutes = {} } = {}) {
|
|
34
|
+
function createNextMiddleware({ localeRouting = true, prefixDefaultLocale = false, enableSmartRouting = true, ignoreSourceMaps = true, pathConfig = {}, routeOverrides = {}, localeRoutes = {} } = {}) {
|
|
34
35
|
const pathRegex = require_utils_pathRegex.compilePathRegex(process.env._GENERALTRANSLATION_PATH_REGEX);
|
|
35
36
|
let envParams;
|
|
36
37
|
if (process.env._GENERALTRANSLATION_I18N_CONFIG_PARAMS) try {
|
|
@@ -95,7 +96,7 @@ function createNextMiddleware({ localeRouting = true, prefixDefaultLocale = fals
|
|
|
95
96
|
function middleware(req) {
|
|
96
97
|
if (!require_utils_pathRegex.pathnameMatchesRegex(req.nextUrl.pathname, pathRegex)) return next_server_js.NextResponse.next();
|
|
97
98
|
if (ignoreSourceMaps && req.nextUrl.pathname.startsWith(NEXT_JS_SOURCE_MAP_PATH)) return next_server_js.NextResponse.next();
|
|
98
|
-
const { userLocale: requestedLocale, pathnameLocale: pathnameRoutingLocale, unstandardizedPathnameLocale, clearResetCookie } = require_middleware_dir_utils.getLocaleFromRequest(req, defaultLocale, locales, localeRouting, prefixDefaultLocale, defaultLocalePaths, referrerLocaleCookieName, localeCookieName, resetLocaleCookieName, gt);
|
|
99
|
+
const { userLocale: requestedLocale, pathnameLocale: pathnameRoutingLocale, unstandardizedPathnameLocale, clearResetCookie } = require_middleware_dir_utils.getLocaleFromRequest(req, defaultLocale, locales, localeRouting, prefixDefaultLocale, defaultLocalePaths, referrerLocaleCookieName, localeCookieName, resetLocaleCookieName, gt, enableSmartRouting);
|
|
99
100
|
let userLocale = requestedLocale;
|
|
100
101
|
const pathnameLocale = pathnameRoutingLocale;
|
|
101
102
|
const headerList = new Headers(req.headers);
|
|
@@ -114,11 +115,15 @@ function createNextMiddleware({ localeRouting = true, prefixDefaultLocale = fals
|
|
|
114
115
|
type: "rewrite",
|
|
115
116
|
...responseConfig
|
|
116
117
|
});
|
|
117
|
-
const getRedirectResponse = (responsePath) =>
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
const getRedirectResponse = (responsePath) => {
|
|
119
|
+
const response = require_middleware_dir_utils.getResponse({
|
|
120
|
+
responsePath,
|
|
121
|
+
type: "redirect",
|
|
122
|
+
...responseConfig
|
|
123
|
+
});
|
|
124
|
+
if (!enableSmartRouting && !prefixDefaultLocale && pathnameLocale === defaultLocale && userLocale === defaultLocale && req.headers.get("sec-fetch-dest") === "document" && !req.cookies.has(localeCookieName)) response.cookies.set(localeCookieName, defaultLocale);
|
|
125
|
+
return response;
|
|
126
|
+
};
|
|
122
127
|
const getNextResponse = () => require_middleware_dir_utils.getResponse({
|
|
123
128
|
type: "next",
|
|
124
129
|
...responseConfig
|
|
@@ -150,7 +155,7 @@ function createNextMiddleware({ localeRouting = true, prefixDefaultLocale = fals
|
|
|
150
155
|
const routeOverridePath = (routeOverridePathMap ? require_middleware_dir_matchPath.getSharedPath(pagePath, routeOverridePathMap, void 0) : void 0) !== void 0 ? require_middleware_dir_pathname.applyTrailingSlash(standardizedPathname, `/${userLocale}/${userLocale}${pagePath === "/" ? "" : pagePath}`) : void 0;
|
|
151
156
|
if (localizedPathWithParameters === void 0) {
|
|
152
157
|
if (!prefixDefaultLocale && userLocale === defaultLocale) if (pathnameLocale) {
|
|
153
|
-
if (clearResetCookie) return getRedirectResponse(pathname.
|
|
158
|
+
if (clearResetCookie || !enableSmartRouting) return getRedirectResponse(pathname.slice(unstandardizedPathnameLocale.length + 1) || "/");
|
|
154
159
|
} else return getRewriteResponse(routeOverridePath || `/${userLocale}${pathname}`);
|
|
155
160
|
else if (!pathnameLocale) return getRedirectResponse(`/${userLocale}${pathname}`);
|
|
156
161
|
if (pathnameLocale && userLocale !== unstandardizedPathnameLocale) return getRedirectResponse(pathname.replace(new RegExp(`^/${unstandardizedPathnameLocale}`), `/${userLocale}`));
|
|
@@ -158,9 +163,9 @@ function createNextMiddleware({ localeRouting = true, prefixDefaultLocale = fals
|
|
|
158
163
|
return getNextResponse();
|
|
159
164
|
}
|
|
160
165
|
if (!prefixDefaultLocale && userLocale === defaultLocale) if (pathnameLocale) {
|
|
161
|
-
if (clearResetCookie) return getRedirectResponse(localizedPathWithParameters.
|
|
166
|
+
if (clearResetCookie || !enableSmartRouting) return getRedirectResponse(localizedPathWithParameters.slice(userLocale.length + 1) || "/");
|
|
162
167
|
} else {
|
|
163
|
-
const localizedPublicPath = localizedPathWithParameters.
|
|
168
|
+
const localizedPublicPath = localizedPathWithParameters.slice(userLocale.length + 1) || "/";
|
|
164
169
|
if (!pathnameLocale && require_middleware_dir_pathname.normalizePathname(localizedPublicPath) !== require_middleware_dir_pathname.normalizePathname(pathname)) return getRedirectResponse(localizedPublicPath);
|
|
165
170
|
return getRewriteResponse(routeOverridePath || sharedPathWithParameters);
|
|
166
171
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createNextMiddleware.js","names":["compilePathRegex","GTRuntime","libraryDefaultLocale","defaultLocaleCookieName","defaultResetLocaleCookieName","createUnsupportedLocalesWarning","createPathToSharedPathMap","createPathMatcher","pathnameMatchesRegex","NextResponse","getLocaleFromRequest","getResponse","getSharedPath","replaceDynamicSegments","getLocalizedPath","applyTrailingSlash","normalizePathname"],"sources":["../../src/middleware-dir/createNextMiddleware.ts"],"sourcesContent":["import { applyTrailingSlash } from './pathname';\nimport { GTRuntime } from 'generaltranslation/runtime';\nimport { libraryDefaultLocale } from 'generaltranslation/internal';\nimport { createUnsupportedLocalesWarning } from '../errors/middleware';\nimport { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\nimport {\n defaultLocaleRoutingEnabledCookieName,\n defaultReferrerLocaleCookieName,\n} from '../utils/cookies';\nimport {\n defaultLocaleCookieName,\n defaultResetLocaleCookieName,\n} from '@generaltranslation/react-core/pure';\nimport {\n normalizePathname,\n getSharedPath,\n replaceDynamicSegments,\n getLocalizedPath,\n createPathToSharedPathMap,\n createPathMatcher,\n getLocaleFromRequest,\n getResponse,\n ResponseConfig,\n type PathConfig,\n type PathMatcher,\n} from './utils';\nimport { defaultLocaleHeaderName } from '../utils/headers';\nimport type { CustomMapping } from '@generaltranslation/format/types';\nimport type { HeadersAndCookies } from '../config-dir/props/withGTConfigProps';\nimport { compilePathRegex, pathnameMatchesRegex } from '../utils/pathRegex';\n\nconst NEXT_JS_SOURCE_MAP_PATH = '/__nextjs_source-map';\n\ntype MiddlewareEnvConfig = {\n customMapping?: CustomMapping;\n defaultLocale?: string;\n locales?: string[];\n headersAndCookies?: HeadersAndCookies;\n};\n\nexport type RouteOverrides = Record<string, readonly string[]>;\n\nexport type LocaleRoutes = Record<string, readonly string[]>;\n\n/**\n * Middleware factory to create a Next.js middleware for i18n routing and locale detection.\n *\n * This middleware sets a cookie based on the locale derived from several sources\n * such as the request pathname, referer, or 'Accept-Language' header.\n * If locale routing is enabled, it redirects to the localized pathname and\n * updates the locale cookie.\n *\n * @param {boolean} [config.localeRouting=true] - Flag to enable or disable automatic locale-based routing.\n * @param {boolean} [config.prefixDefaultLocale=false] - Flag to enable or disable prefixing the default locale to the pathname, i.e., /en/about -> /about\n * @param {boolean} [config.ignoreSourceMaps=true] - Flag to enable or disable ignoring source maps\n * @param {PathConfig} [config.pathConfig] - Path configuration for locale routing\n * @param {RouteOverrides} [config.routeOverrides] - Locale-relative paths to rewrite from /{locale}/{path} to /{locale}/{locale}/{path}\n * @param {LocaleRoutes} [config.localeRoutes] - Shared paths available per locale; other paths fall back to the default locale\n * @returns {function} - A middleware function that processes the request and response.\n */\nexport function createNextMiddleware({\n localeRouting = true,\n prefixDefaultLocale = false,\n ignoreSourceMaps = true,\n pathConfig = {},\n routeOverrides = {},\n localeRoutes = {},\n}: {\n localeRouting?: boolean;\n prefixDefaultLocale?: boolean;\n ignoreSourceMaps?: boolean;\n pathConfig?: PathConfig;\n routeOverrides?: RouteOverrides;\n localeRoutes?: LocaleRoutes;\n} = {}) {\n const pathRegex = compilePathRegex(\n process.env._GENERALTRANSLATION_PATH_REGEX\n );\n\n // i18n config\n let envParams: MiddlewareEnvConfig | undefined;\n if (process.env._GENERALTRANSLATION_I18N_CONFIG_PARAMS) {\n try {\n envParams = JSON.parse(\n process.env._GENERALTRANSLATION_I18N_CONFIG_PARAMS\n );\n } catch (error) {\n console.error(`gt-next middleware:`, error);\n }\n }\n\n // gt instance\n const gt = new GTRuntime({\n customMapping: envParams?.customMapping,\n });\n\n // Configured spellings are the identity used by routes, cookies and caches.\n const defaultLocale: string =\n envParams?.defaultLocale || libraryDefaultLocale;\n const locales = Array.from(\n new Set([defaultLocale, ...(envParams?.locales || [])])\n );\n // Route entries repeat locale keys. Resolve each key once per factory instead\n // of rebuilding the full locale-resolution index for every route/locale pair.\n const resolvedConfigLocales = new Map<string, string>();\n const determineLocaleWithCache = (locale: string) => {\n const cached = resolvedConfigLocales.get(locale);\n if (cached !== undefined) return cached;\n const resolved = gt.determineLocale(locale, locales) ?? locale;\n resolvedConfigLocales.set(locale, resolved);\n return resolved;\n };\n const resolvePathConfigLocale =\n process.env._GENERALTRANSLATION_GT_SERVICES_ENABLED === 'true'\n ? determineLocaleWithCache\n : (locale: string) => locale;\n\n // cookies and header names\n const headersAndCookies = envParams?.headersAndCookies || {};\n const localeRoutingEnabledCookieName =\n headersAndCookies?.localeRoutingEnabledCookieName ||\n defaultLocaleRoutingEnabledCookieName;\n const referrerLocaleCookieName =\n headersAndCookies?.referrerLocaleCookieName ||\n defaultReferrerLocaleCookieName;\n const localeCookieName =\n headersAndCookies?.localeCookieName || defaultLocaleCookieName;\n const resetLocaleCookieName =\n headersAndCookies?.resetLocaleCookieName || defaultResetLocaleCookieName;\n const localeHeaderName =\n headersAndCookies?.localeHeaderName || defaultLocaleHeaderName;\n\n if (!gt.isValidLocale(defaultLocale))\n throw new Error(\n `gt-next middleware: defaultLocale \"${defaultLocale}\" is not a valid locale.`\n );\n\n const warningLocales = locales.filter((locale) => !gt.isValidLocale(locale));\n if (warningLocales.length)\n console.warn(createUnsupportedLocalesWarning(warningLocales));\n\n // ---------- PRE-PROCESSING PATHS ---------- //\n\n // --- localized routes --- //\n // Resolve pathConfig keys to the configured locale spelling.\n pathConfig = Object.entries(pathConfig).reduce<PathConfig>(\n (acc, [sharedPath, localizedPath]) => {\n if (typeof localizedPath === 'string') {\n acc[sharedPath] = localizedPath;\n } else {\n acc[sharedPath] = Object.entries(localizedPath).reduce<{\n [key: string]: string;\n }>((acc, [locale, localizedPath]) => {\n acc[resolvePathConfigLocale(locale)] = localizedPath;\n return acc;\n }, {});\n }\n return acc;\n },\n {}\n );\n\n // String aliases apply to every locale, including the default locale.\n // Expand a copy for lookup while retaining string target semantics in routing.\n const matcherPathConfig = Object.fromEntries(\n Object.entries(pathConfig).map(([sharedPath, localizedPath]) => [\n sharedPath,\n typeof localizedPath === 'string'\n ? Object.fromEntries(\n locales.map((locale) => [\n determineLocaleWithCache(locale),\n localizedPath === '' ? '/' : localizedPath,\n ])\n )\n : localizedPath,\n ])\n );\n\n // Create the path mapping\n const { pathToSharedPath, defaultLocalePaths } = createPathToSharedPathMap(\n matcherPathConfig,\n prefixDefaultLocale,\n defaultLocale\n );\n\n // --- route overrides --- //\n // Resolve routeOverrides keys to the configured locale spelling.\n routeOverrides = Object.entries(routeOverrides).reduce<RouteOverrides>(\n (acc, [locale, paths]) => {\n acc[resolvePathConfigLocale(locale)] = paths;\n return acc;\n },\n {}\n );\n\n // Create the route override path mapping\n const routeOverridePathMaps = Object.entries(routeOverrides).reduce<\n Record<string, PathMatcher>\n >((acc, [locale, paths]) => {\n const overridePathConfig = Object.fromEntries(\n paths.map((path) => [path, path])\n );\n acc[locale] = createPathToSharedPathMap(\n overridePathConfig,\n true,\n defaultLocale\n ).pathToSharedPath;\n return acc;\n }, {});\n\n // --- locale routes --- //\n // Compile availability separately from aliases and overrides so it cannot\n // change route precedence. An empty list deliberately matches nothing.\n const localeRoutePathMaps = new Map(\n Object.entries(localeRoutes).map(([locale, paths]) => {\n // Match request resolution so equivalent spellings use the same map key.\n const resolvedLocale = determineLocaleWithCache(locale);\n return [\n resolvedLocale,\n createPathMatcher(paths.map((path) => [path, path])),\n ];\n })\n );\n\n /**\n * Processes the incoming request to determine the user's locale and sets a locale cookie.\n * Optionally redirects the user based on the locale if locale-based routing is enabled.\n *\n * - Checks if the request URL contains a locale.\n * - Falls back to the referer URL for locale if needed.\n * - If no locale is found in the URL or referer, it checks the 'Accept-Language' header.\n * - Sets a cookie with the detected or default locale.\n * - Redirects to the correct locale route if locale routing is enabled.\n *\n * @param {NextRequest} req - The incoming request object, containing URL and headers.\n * @returns {NextResponse} - The Next.js response, either continuing the request or redirecting to the localized URL.\n */\n function middleware(req: NextRequest) {\n if (!pathnameMatchesRegex(req.nextUrl.pathname, pathRegex)) {\n return NextResponse.next();\n }\n\n // Ignore source maps\n if (\n ignoreSourceMaps &&\n req.nextUrl.pathname.startsWith(NEXT_JS_SOURCE_MAP_PATH)\n ) {\n return NextResponse.next();\n }\n\n // ---------- LOCALE DETECTION ---------- //\n\n const {\n userLocale: requestedLocale,\n pathnameLocale: pathnameRoutingLocale,\n unstandardizedPathnameLocale,\n clearResetCookie,\n } = getLocaleFromRequest(\n req,\n defaultLocale,\n locales,\n localeRouting,\n prefixDefaultLocale,\n defaultLocalePaths,\n referrerLocaleCookieName,\n localeCookieName,\n resetLocaleCookieName,\n gt\n );\n\n let userLocale = requestedLocale;\n const pathnameLocale = pathnameRoutingLocale;\n const headerList = new Headers(req.headers);\n\n const responseConfig: Omit<ResponseConfig, 'type'> = {\n originalUrl: req.nextUrl,\n headerList,\n userLocale,\n clearResetCookie,\n localeRouting,\n localeRoutingEnabledCookieName,\n resetLocaleCookieName,\n localeHeaderName,\n };\n\n const getRewriteResponse = (responsePath: string) =>\n getResponse({ responsePath, type: 'rewrite', ...responseConfig });\n\n const getRedirectResponse = (responsePath: string) =>\n getResponse({ responsePath, type: 'redirect', ...responseConfig });\n\n const getNextResponse = () =>\n getResponse({ type: 'next', ...responseConfig });\n\n if (localeRouting) {\n // ---------- GET PATHS ---------- //\n\n // get pathname\n const { pathname } = req.nextUrl;\n\n // standardize pathname (ie, /tg/welcome -> /fil/welcome), (/blog -> /blog)\n const standardizedPathname =\n pathnameLocale && pathnameLocale !== unstandardizedPathnameLocale\n ? pathname.replace(\n new RegExp(`^/${unstandardizedPathnameLocale}`),\n `/${pathnameLocale}`\n )\n : pathname;\n\n // Get the shared path for the unprefixed pathname\n const sharedPathMatch = getSharedPath(\n standardizedPathname,\n pathToSharedPath,\n pathnameLocale\n );\n const sharedPath = sharedPathMatch?.sharedPath;\n\n // Return early for a locale route that does not exist\n const localeRoutePathMap = localeRoutePathMaps.get(userLocale);\n if (userLocale !== defaultLocale && localeRoutePathMap) {\n // Resolve the shared page path without a locale prefix (e.g. /blog/hello).\n // Strip the URL's locale, which may differ from the newly selected locale.\n const sharedPagePath = sharedPathMatch\n ? replaceDynamicSegments(\n sharedPathMatch.matchedPathname,\n sharedPath || '/',\n sharedPathMatch.pathTemplate\n )\n : (pathnameLocale\n ? standardizedPathname.slice(pathnameLocale.length + 1)\n : standardizedPathname) || '/';\n\n // If the path does not exist, redirect to the default locale path\n if (\n getSharedPath(sharedPagePath, localeRoutePathMap, undefined) ===\n undefined\n ) {\n // Get the path to redirect to\n const defaultPath =\n sharedPath !== undefined\n ? getLocalizedPath(sharedPath, defaultLocale, pathConfig)\n : undefined;\n const fallbackPath =\n defaultPath !== undefined && sharedPathMatch\n ? replaceDynamicSegments(\n sharedPathMatch.matchedPathname,\n defaultPath,\n sharedPathMatch.pathTemplate\n )\n : `/${defaultLocale}${sharedPagePath === '/' ? '' : sharedPagePath}`;\n const publicFallbackPath = applyTrailingSlash(\n standardizedPathname,\n prefixDefaultLocale\n ? fallbackPath\n : fallbackPath.slice(defaultLocale.length + 1) || '/'\n );\n\n // The default locale is terminal, even if the preference/reset cookie\n // still requests an unavailable locale on the redirected request.\n userLocale = defaultLocale;\n responseConfig.userLocale = defaultLocale;\n const fallbackUrl = new URL(req.nextUrl);\n fallbackUrl.pathname = publicFallbackPath;\n if (fallbackUrl.pathname !== pathname) {\n return getRedirectResponse(publicFallbackPath);\n }\n // Already at the fallback URL: use normal default-locale routing below,\n // including its alias/override rewrite, instead of redirecting to itself.\n }\n }\n\n // Get shared path with parameters (/en/dashboard/1/custom), for rewriting localized paths\n const sharedPathWithParameters =\n sharedPathMatch !== undefined\n ? applyTrailingSlash(\n standardizedPathname,\n replaceDynamicSegments(\n sharedPathMatch.matchedPathname,\n `/${userLocale}${sharedPath}`,\n sharedPathMatch.pathTemplate\n )\n )\n : undefined;\n\n // Localized path (/en/blog, /fr/fr-about, /fr/dashboard/[id]/custom)\n const localizedPath =\n sharedPath !== undefined\n ? getLocalizedPath(sharedPath, userLocale, pathConfig)\n : undefined;\n\n // Combine localized path with dynamic parameters (/en/blog, /fr/fr-about, /fr/dashboard/1/fr-custom)\n const localizedPathWithParameters =\n localizedPath !== undefined && sharedPathMatch !== undefined\n ? applyTrailingSlash(\n standardizedPathname,\n replaceDynamicSegments(\n sharedPathMatch.matchedPathname,\n localizedPath,\n sharedPathMatch.pathTemplate\n )\n )\n : undefined;\n\n const pagePath =\n (sharedPathWithParameters?.slice(userLocale.length + 1) ??\n (pathnameLocale\n ? standardizedPathname.slice(pathnameLocale.length + 1)\n : standardizedPathname)) ||\n '/';\n const routeOverridePathMap = routeOverridePathMaps[userLocale];\n const routeOverrideMatch = routeOverridePathMap\n ? getSharedPath(pagePath, routeOverridePathMap, undefined)\n : undefined;\n const routeOverridePath =\n routeOverrideMatch !== undefined\n ? applyTrailingSlash(\n standardizedPathname,\n `/${userLocale}/${userLocale}${pagePath === '/' ? '' : pagePath}`\n )\n : undefined;\n\n // ---------- ROUTING LOGIC ---------- //\n\n // ----- CASE: no localized path exists ----- //\n\n if (localizedPathWithParameters === undefined) {\n // --- CASE: remove defaultLocale prefix --- //\n\n if (!prefixDefaultLocale && userLocale === defaultLocale) {\n if (pathnameLocale) {\n // REDIRECT CASE: used setLocale (/fr/customers -> /customers) (/en/customers -> /customers)\n if (clearResetCookie) {\n return getRedirectResponse(\n pathname.replace(\n new RegExp(`^/${unstandardizedPathnameLocale}`),\n ``\n ) || '/'\n );\n }\n } else {\n // REWRITE CASE: no pathnameLocale (/customers -> /en/customers)\n return getRewriteResponse(\n routeOverridePath || `/${userLocale}${pathname}`\n );\n }\n }\n\n // --- CASE: defaultLocale prefix --- //\n // REDIRECT CASE: no pathnameLocale (ie, /customers -> /fr/customers)\n else if (!pathnameLocale) {\n return getRedirectResponse(`/${userLocale}${pathname}`);\n }\n\n // REDIRECT CASE: wrong pathnameLocale (ie, /fr/customers -> /en/customers) (this usually happens after a locale switch)\n if (pathnameLocale && userLocale !== unstandardizedPathnameLocale) {\n return getRedirectResponse(\n pathname.replace(\n new RegExp(`^/${unstandardizedPathnameLocale}`),\n `/${userLocale}`\n )\n );\n }\n\n // BASE CASE: has pathnameLocale and it's correct\n if (routeOverridePath) {\n return getRewriteResponse(routeOverridePath);\n }\n return getNextResponse();\n }\n\n // ----- CASE: localized path exists ----- //\n\n if (!prefixDefaultLocale && userLocale === defaultLocale) {\n // --- CASE: remove defaultLocale prefix --- //\n\n if (pathnameLocale) {\n // REDIRECT CASE: remove locale prefix when setLocale is used (/en/blog -> /blog) (/fr/fr-about -> /en-about)\n if (clearResetCookie) {\n return getRedirectResponse(\n localizedPathWithParameters.replace(\n new RegExp(`^/${userLocale}`),\n ``\n ) || '/'\n );\n }\n } else {\n const localizedPublicPath =\n localizedPathWithParameters.replace(\n new RegExp(`^/${userLocale}`),\n ''\n ) || '/';\n\n // REDIRECT CASE: unprefixed pathname is wrong (/about -> /en-about)\n if (\n !pathnameLocale &&\n normalizePathname(localizedPublicPath) !==\n normalizePathname(pathname)\n ) {\n return getRedirectResponse(localizedPublicPath);\n }\n\n // REWRITE CASE: displaying correct path (/blog -> /en/blog)\n return getRewriteResponse(\n routeOverridePath || (sharedPathWithParameters as string)\n );\n }\n }\n\n // --- CASE: add defaultLocale prefix --- //\n\n // REDIRECT CASE: incorrect pathnameLocale\n if (\n normalizePathname(pathname) !==\n normalizePathname(localizedPathWithParameters)\n ) {\n return getRedirectResponse(localizedPathWithParameters);\n }\n\n if (routeOverridePath) {\n return getRewriteResponse(routeOverridePath);\n }\n\n // REWRITE CASE: displaying correct localized path, which is the same as the shared path (/fil/blog => /fil/blog) (/fr/fr-dashboard/1/fr-custom => /fr/dashboard/1/custom)\n // Next.js route identity preserves Unicode spelling, even when lookup\n // considers the public alias and shared path canonically equivalent.\n const rewriteUrl = new URL(\n sharedPathWithParameters as string,\n req.nextUrl\n );\n if (\n req.nextUrl.pathname !== rewriteUrl.pathname // no rewrite needed if it's already the shared path\n ) {\n // convert to shared path with dynamic parameters\n return getRewriteResponse(sharedPathWithParameters as string);\n }\n }\n\n // BASE CASE\n return getNextResponse();\n }\n\n return middleware;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createNextMiddleware.js","names":["compilePathRegex","GTRuntime","libraryDefaultLocale","defaultLocaleCookieName","defaultResetLocaleCookieName","createUnsupportedLocalesWarning","createPathToSharedPathMap","createPathMatcher","pathnameMatchesRegex","NextResponse","getLocaleFromRequest","getResponse","getSharedPath","replaceDynamicSegments","getLocalizedPath","applyTrailingSlash","normalizePathname"],"sources":["../../src/middleware-dir/createNextMiddleware.ts"],"sourcesContent":["import { applyTrailingSlash } from './pathname';\nimport { GTRuntime } from 'generaltranslation/runtime';\nimport { libraryDefaultLocale } from 'generaltranslation/internal';\nimport { createUnsupportedLocalesWarning } from '../errors/middleware';\nimport { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\nimport {\n defaultLocaleRoutingEnabledCookieName,\n defaultReferrerLocaleCookieName,\n} from '../utils/cookies';\nimport {\n defaultLocaleCookieName,\n defaultResetLocaleCookieName,\n} from '@generaltranslation/react-core/pure';\nimport {\n normalizePathname,\n getSharedPath,\n replaceDynamicSegments,\n getLocalizedPath,\n createPathToSharedPathMap,\n createPathMatcher,\n getLocaleFromRequest,\n getResponse,\n ResponseConfig,\n type PathConfig,\n type PathMatcher,\n} from './utils';\nimport { defaultLocaleHeaderName } from '../utils/headers';\nimport type { CustomMapping } from '@generaltranslation/format/types';\nimport type { HeadersAndCookies } from '../config-dir/props/withGTConfigProps';\nimport { compilePathRegex, pathnameMatchesRegex } from '../utils/pathRegex';\n\nconst NEXT_JS_SOURCE_MAP_PATH = '/__nextjs_source-map';\n\ntype MiddlewareEnvConfig = {\n customMapping?: CustomMapping;\n defaultLocale?: string;\n locales?: string[];\n headersAndCookies?: HeadersAndCookies;\n};\n\nexport type RouteOverrides = Record<string, readonly string[]>;\n\nexport type LocaleRoutes = Record<string, readonly string[]>;\n\n/**\n * Middleware factory to create a Next.js middleware for i18n routing and locale detection.\n *\n * This middleware sets a cookie based on the locale derived from several sources\n * such as the request pathname, referer, or 'Accept-Language' header.\n * If locale routing is enabled, it redirects to the localized pathname and\n * updates the locale cookie.\n *\n * @param {boolean} [config.localeRouting=true] - Flag to enable or disable automatic locale-based routing.\n * @param {boolean} [config.prefixDefaultLocale=false] - Flag to enable or disable prefixing the default locale to the pathname, i.e., /en/about -> /about\n * @param {boolean} [config.enableSmartRouting=true] - Keep automatic locale negotiation. Disable to treat returning visitors' unprefixed URLs as default-locale routes.\n * @param {boolean} [config.ignoreSourceMaps=true] - Flag to enable or disable ignoring source maps\n * @param {PathConfig} [config.pathConfig] - Path configuration for locale routing\n * @param {RouteOverrides} [config.routeOverrides] - Locale-relative paths to rewrite from /{locale}/{path} to /{locale}/{locale}/{path}\n * @param {LocaleRoutes} [config.localeRoutes] - Shared paths available per locale; other paths fall back to the default locale\n * @returns {function} - A middleware function that processes the request and response.\n */\nexport function createNextMiddleware({\n localeRouting = true,\n prefixDefaultLocale = false,\n enableSmartRouting = true,\n ignoreSourceMaps = true,\n pathConfig = {},\n routeOverrides = {},\n localeRoutes = {},\n}: {\n localeRouting?: boolean;\n prefixDefaultLocale?: boolean;\n enableSmartRouting?: boolean;\n ignoreSourceMaps?: boolean;\n pathConfig?: PathConfig;\n routeOverrides?: RouteOverrides;\n localeRoutes?: LocaleRoutes;\n} = {}) {\n const pathRegex = compilePathRegex(\n process.env._GENERALTRANSLATION_PATH_REGEX\n );\n\n // i18n config\n let envParams: MiddlewareEnvConfig | undefined;\n if (process.env._GENERALTRANSLATION_I18N_CONFIG_PARAMS) {\n try {\n envParams = JSON.parse(\n process.env._GENERALTRANSLATION_I18N_CONFIG_PARAMS\n );\n } catch (error) {\n console.error(`gt-next middleware:`, error);\n }\n }\n\n // gt instance\n const gt = new GTRuntime({\n customMapping: envParams?.customMapping,\n });\n\n // Configured spellings are the identity used by routes, cookies and caches.\n const defaultLocale: string =\n envParams?.defaultLocale || libraryDefaultLocale;\n const locales = Array.from(\n new Set([defaultLocale, ...(envParams?.locales || [])])\n );\n // Route entries repeat locale keys. Resolve each key once per factory instead\n // of rebuilding the full locale-resolution index for every route/locale pair.\n const resolvedConfigLocales = new Map<string, string>();\n const determineLocaleWithCache = (locale: string) => {\n const cached = resolvedConfigLocales.get(locale);\n if (cached !== undefined) return cached;\n const resolved = gt.determineLocale(locale, locales) ?? locale;\n resolvedConfigLocales.set(locale, resolved);\n return resolved;\n };\n const resolvePathConfigLocale =\n process.env._GENERALTRANSLATION_GT_SERVICES_ENABLED === 'true'\n ? determineLocaleWithCache\n : (locale: string) => locale;\n\n // cookies and header names\n const headersAndCookies = envParams?.headersAndCookies || {};\n const localeRoutingEnabledCookieName =\n headersAndCookies?.localeRoutingEnabledCookieName ||\n defaultLocaleRoutingEnabledCookieName;\n const referrerLocaleCookieName =\n headersAndCookies?.referrerLocaleCookieName ||\n defaultReferrerLocaleCookieName;\n const localeCookieName =\n headersAndCookies?.localeCookieName || defaultLocaleCookieName;\n const resetLocaleCookieName =\n headersAndCookies?.resetLocaleCookieName || defaultResetLocaleCookieName;\n const localeHeaderName =\n headersAndCookies?.localeHeaderName || defaultLocaleHeaderName;\n\n if (!gt.isValidLocale(defaultLocale))\n throw new Error(\n `gt-next middleware: defaultLocale \"${defaultLocale}\" is not a valid locale.`\n );\n\n const warningLocales = locales.filter((locale) => !gt.isValidLocale(locale));\n if (warningLocales.length)\n console.warn(createUnsupportedLocalesWarning(warningLocales));\n\n // ---------- PRE-PROCESSING PATHS ---------- //\n\n // --- localized routes --- //\n // Resolve pathConfig keys to the configured locale spelling.\n pathConfig = Object.entries(pathConfig).reduce<PathConfig>(\n (acc, [sharedPath, localizedPath]) => {\n if (typeof localizedPath === 'string') {\n acc[sharedPath] = localizedPath;\n } else {\n acc[sharedPath] = Object.entries(localizedPath).reduce<{\n [key: string]: string;\n }>((acc, [locale, localizedPath]) => {\n acc[resolvePathConfigLocale(locale)] = localizedPath;\n return acc;\n }, {});\n }\n return acc;\n },\n {}\n );\n\n // String aliases apply to every locale, including the default locale.\n // Expand a copy for lookup while retaining string target semantics in routing.\n const matcherPathConfig = Object.fromEntries(\n Object.entries(pathConfig).map(([sharedPath, localizedPath]) => [\n sharedPath,\n typeof localizedPath === 'string'\n ? Object.fromEntries(\n locales.map((locale) => [\n determineLocaleWithCache(locale),\n localizedPath === '' ? '/' : localizedPath,\n ])\n )\n : localizedPath,\n ])\n );\n\n // Create the path mapping\n const { pathToSharedPath, defaultLocalePaths } = createPathToSharedPathMap(\n matcherPathConfig,\n prefixDefaultLocale,\n defaultLocale\n );\n\n // --- route overrides --- //\n // Resolve routeOverrides keys to the configured locale spelling.\n routeOverrides = Object.entries(routeOverrides).reduce<RouteOverrides>(\n (acc, [locale, paths]) => {\n acc[resolvePathConfigLocale(locale)] = paths;\n return acc;\n },\n {}\n );\n\n // Create the route override path mapping\n const routeOverridePathMaps = Object.entries(routeOverrides).reduce<\n Record<string, PathMatcher>\n >((acc, [locale, paths]) => {\n const overridePathConfig = Object.fromEntries(\n paths.map((path) => [path, path])\n );\n acc[locale] = createPathToSharedPathMap(\n overridePathConfig,\n true,\n defaultLocale\n ).pathToSharedPath;\n return acc;\n }, {});\n\n // --- locale routes --- //\n // Compile availability separately from aliases and overrides so it cannot\n // change route precedence. An empty list deliberately matches nothing.\n const localeRoutePathMaps = new Map(\n Object.entries(localeRoutes).map(([locale, paths]) => {\n // Match request resolution so equivalent spellings use the same map key.\n const resolvedLocale = determineLocaleWithCache(locale);\n return [\n resolvedLocale,\n createPathMatcher(paths.map((path) => [path, path])),\n ];\n })\n );\n\n /**\n * Processes the incoming request to determine the user's locale and sets a locale cookie.\n * Optionally redirects the user based on the locale if locale-based routing is enabled.\n *\n * - Checks if the request URL contains a locale.\n * - Falls back to the referer URL for locale if needed.\n * - If no locale is found in the URL or referer, it checks the 'Accept-Language' header.\n * - Sets a cookie with the detected or default locale.\n * - Redirects to the correct locale route if locale routing is enabled.\n *\n * @param {NextRequest} req - The incoming request object, containing URL and headers.\n * @returns {NextResponse} - The Next.js response, either continuing the request or redirecting to the localized URL.\n */\n function middleware(req: NextRequest) {\n if (!pathnameMatchesRegex(req.nextUrl.pathname, pathRegex)) {\n return NextResponse.next();\n }\n\n // Ignore source maps\n if (\n ignoreSourceMaps &&\n req.nextUrl.pathname.startsWith(NEXT_JS_SOURCE_MAP_PATH)\n ) {\n return NextResponse.next();\n }\n\n // ---------- LOCALE DETECTION ---------- //\n\n const {\n userLocale: requestedLocale,\n pathnameLocale: pathnameRoutingLocale,\n unstandardizedPathnameLocale,\n clearResetCookie,\n } = getLocaleFromRequest(\n req,\n defaultLocale,\n locales,\n localeRouting,\n prefixDefaultLocale,\n defaultLocalePaths,\n referrerLocaleCookieName,\n localeCookieName,\n resetLocaleCookieName,\n gt,\n enableSmartRouting\n );\n\n let userLocale = requestedLocale;\n const pathnameLocale = pathnameRoutingLocale;\n const headerList = new Headers(req.headers);\n\n const responseConfig: Omit<ResponseConfig, 'type'> = {\n originalUrl: req.nextUrl,\n headerList,\n userLocale,\n clearResetCookie,\n localeRouting,\n localeRoutingEnabledCookieName,\n resetLocaleCookieName,\n localeHeaderName,\n };\n\n const getRewriteResponse = (responsePath: string) =>\n getResponse({ responsePath, type: 'rewrite', ...responseConfig });\n\n const getRedirectResponse = (responsePath: string) => {\n const response = getResponse({\n responsePath,\n type: 'redirect',\n ...responseConfig,\n });\n if (\n !enableSmartRouting &&\n !prefixDefaultLocale &&\n pathnameLocale === defaultLocale &&\n userLocale === defaultLocale &&\n req.headers.get('sec-fetch-dest') === 'document' &&\n !req.cookies.has(localeCookieName)\n ) {\n // Preserve explicit /en intent across a first document redirect.\n // Background fetches and returning visits must not change the cookie.\n response.cookies.set(localeCookieName, defaultLocale);\n }\n return response;\n };\n\n const getNextResponse = () =>\n getResponse({ type: 'next', ...responseConfig });\n\n if (localeRouting) {\n // ---------- GET PATHS ---------- //\n\n // get pathname\n const { pathname } = req.nextUrl;\n\n // standardize pathname (ie, /tg/welcome -> /fil/welcome), (/blog -> /blog)\n const standardizedPathname =\n pathnameLocale && pathnameLocale !== unstandardizedPathnameLocale\n ? pathname.replace(\n new RegExp(`^/${unstandardizedPathnameLocale}`),\n `/${pathnameLocale}`\n )\n : pathname;\n\n // Get the shared path for the unprefixed pathname\n const sharedPathMatch = getSharedPath(\n standardizedPathname,\n pathToSharedPath,\n pathnameLocale\n );\n const sharedPath = sharedPathMatch?.sharedPath;\n\n // Return early for a locale route that does not exist\n const localeRoutePathMap = localeRoutePathMaps.get(userLocale);\n if (userLocale !== defaultLocale && localeRoutePathMap) {\n // Resolve the shared page path without a locale prefix (e.g. /blog/hello).\n // Strip the URL's locale, which may differ from the newly selected locale.\n const sharedPagePath = sharedPathMatch\n ? replaceDynamicSegments(\n sharedPathMatch.matchedPathname,\n sharedPath || '/',\n sharedPathMatch.pathTemplate\n )\n : (pathnameLocale\n ? standardizedPathname.slice(pathnameLocale.length + 1)\n : standardizedPathname) || '/';\n\n // If the path does not exist, redirect to the default locale path\n if (\n getSharedPath(sharedPagePath, localeRoutePathMap, undefined) ===\n undefined\n ) {\n // Get the path to redirect to\n const defaultPath =\n sharedPath !== undefined\n ? getLocalizedPath(sharedPath, defaultLocale, pathConfig)\n : undefined;\n const fallbackPath =\n defaultPath !== undefined && sharedPathMatch\n ? replaceDynamicSegments(\n sharedPathMatch.matchedPathname,\n defaultPath,\n sharedPathMatch.pathTemplate\n )\n : `/${defaultLocale}${sharedPagePath === '/' ? '' : sharedPagePath}`;\n const publicFallbackPath = applyTrailingSlash(\n standardizedPathname,\n prefixDefaultLocale\n ? fallbackPath\n : fallbackPath.slice(defaultLocale.length + 1) || '/'\n );\n\n // The default locale is terminal, even if the preference/reset cookie\n // still requests an unavailable locale on the redirected request.\n userLocale = defaultLocale;\n responseConfig.userLocale = defaultLocale;\n const fallbackUrl = new URL(req.nextUrl);\n fallbackUrl.pathname = publicFallbackPath;\n if (fallbackUrl.pathname !== pathname) {\n return getRedirectResponse(publicFallbackPath);\n }\n // Already at the fallback URL: use normal default-locale routing below,\n // including its alias/override rewrite, instead of redirecting to itself.\n }\n }\n\n // Get shared path with parameters (/en/dashboard/1/custom), for rewriting localized paths\n const sharedPathWithParameters =\n sharedPathMatch !== undefined\n ? applyTrailingSlash(\n standardizedPathname,\n replaceDynamicSegments(\n sharedPathMatch.matchedPathname,\n `/${userLocale}${sharedPath}`,\n sharedPathMatch.pathTemplate\n )\n )\n : undefined;\n\n // Localized path (/en/blog, /fr/fr-about, /fr/dashboard/[id]/custom)\n const localizedPath =\n sharedPath !== undefined\n ? getLocalizedPath(sharedPath, userLocale, pathConfig)\n : undefined;\n\n // Combine localized path with dynamic parameters (/en/blog, /fr/fr-about, /fr/dashboard/1/fr-custom)\n const localizedPathWithParameters =\n localizedPath !== undefined && sharedPathMatch !== undefined\n ? applyTrailingSlash(\n standardizedPathname,\n replaceDynamicSegments(\n sharedPathMatch.matchedPathname,\n localizedPath,\n sharedPathMatch.pathTemplate\n )\n )\n : undefined;\n\n const pagePath =\n (sharedPathWithParameters?.slice(userLocale.length + 1) ??\n (pathnameLocale\n ? standardizedPathname.slice(pathnameLocale.length + 1)\n : standardizedPathname)) ||\n '/';\n const routeOverridePathMap = routeOverridePathMaps[userLocale];\n const routeOverrideMatch = routeOverridePathMap\n ? getSharedPath(pagePath, routeOverridePathMap, undefined)\n : undefined;\n const routeOverridePath =\n routeOverrideMatch !== undefined\n ? applyTrailingSlash(\n standardizedPathname,\n `/${userLocale}/${userLocale}${pagePath === '/' ? '' : pagePath}`\n )\n : undefined;\n\n // ---------- ROUTING LOGIC ---------- //\n\n // ----- CASE: no localized path exists ----- //\n\n if (localizedPathWithParameters === undefined) {\n // --- CASE: remove defaultLocale prefix --- //\n\n if (!prefixDefaultLocale && userLocale === defaultLocale) {\n if (pathnameLocale) {\n // REDIRECT CASE: explicit default locale or setLocale (/en/customers -> /customers)\n if (clearResetCookie || !enableSmartRouting) {\n return getRedirectResponse(\n pathname.slice(\n (unstandardizedPathnameLocale as string).length + 1\n ) || '/'\n );\n }\n } else {\n // REWRITE CASE: no pathnameLocale (/customers -> /en/customers)\n return getRewriteResponse(\n routeOverridePath || `/${userLocale}${pathname}`\n );\n }\n }\n\n // --- CASE: defaultLocale prefix --- //\n // REDIRECT CASE: no pathnameLocale (ie, /customers -> /fr/customers)\n else if (!pathnameLocale) {\n return getRedirectResponse(`/${userLocale}${pathname}`);\n }\n\n // REDIRECT CASE: wrong pathnameLocale (ie, /fr/customers -> /en/customers) (this usually happens after a locale switch)\n if (pathnameLocale && userLocale !== unstandardizedPathnameLocale) {\n return getRedirectResponse(\n pathname.replace(\n new RegExp(`^/${unstandardizedPathnameLocale}`),\n `/${userLocale}`\n )\n );\n }\n\n // BASE CASE: has pathnameLocale and it's correct\n if (routeOverridePath) {\n return getRewriteResponse(routeOverridePath);\n }\n return getNextResponse();\n }\n\n // ----- CASE: localized path exists ----- //\n\n if (!prefixDefaultLocale && userLocale === defaultLocale) {\n // --- CASE: remove defaultLocale prefix --- //\n\n if (pathnameLocale) {\n // REDIRECT CASE: explicit default locale or setLocale (/en/blog -> /blog)\n if (clearResetCookie || !enableSmartRouting) {\n return getRedirectResponse(\n localizedPathWithParameters.slice(userLocale.length + 1) || '/'\n );\n }\n } else {\n const localizedPublicPath =\n localizedPathWithParameters.slice(userLocale.length + 1) || '/';\n\n // REDIRECT CASE: unprefixed pathname is wrong (/about -> /en-about)\n if (\n !pathnameLocale &&\n normalizePathname(localizedPublicPath) !==\n normalizePathname(pathname)\n ) {\n return getRedirectResponse(localizedPublicPath);\n }\n\n // REWRITE CASE: displaying correct path (/blog -> /en/blog)\n return getRewriteResponse(\n routeOverridePath || (sharedPathWithParameters as string)\n );\n }\n }\n\n // --- CASE: add defaultLocale prefix --- //\n\n // REDIRECT CASE: incorrect pathnameLocale\n if (\n normalizePathname(pathname) !==\n normalizePathname(localizedPathWithParameters)\n ) {\n return getRedirectResponse(localizedPathWithParameters);\n }\n\n if (routeOverridePath) {\n return getRewriteResponse(routeOverridePath);\n }\n\n // REWRITE CASE: displaying correct localized path, which is the same as the shared path (/fil/blog => /fil/blog) (/fr/fr-dashboard/1/fr-custom => /fr/dashboard/1/custom)\n // Next.js route identity preserves Unicode spelling, even when lookup\n // considers the public alias and shared path canonically equivalent.\n const rewriteUrl = new URL(\n sharedPathWithParameters as string,\n req.nextUrl\n );\n if (\n req.nextUrl.pathname !== rewriteUrl.pathname // no rewrite needed if it's already the shared path\n ) {\n // convert to shared path with dynamic parameters\n return getRewriteResponse(sharedPathWithParameters as string);\n }\n }\n\n // BASE CASE\n return getNextResponse();\n }\n\n return middleware;\n}\n"],"mappings":"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"}
|
|
@@ -22,13 +22,14 @@ const NEXT_JS_SOURCE_MAP_PATH = "/__nextjs_source-map";
|
|
|
22
22
|
*
|
|
23
23
|
* @param {boolean} [config.localeRouting=true] - Flag to enable or disable automatic locale-based routing.
|
|
24
24
|
* @param {boolean} [config.prefixDefaultLocale=false] - Flag to enable or disable prefixing the default locale to the pathname, i.e., /en/about -> /about
|
|
25
|
+
* @param {boolean} [config.enableSmartRouting=true] - Keep automatic locale negotiation. Disable to treat returning visitors' unprefixed URLs as default-locale routes.
|
|
25
26
|
* @param {boolean} [config.ignoreSourceMaps=true] - Flag to enable or disable ignoring source maps
|
|
26
27
|
* @param {PathConfig} [config.pathConfig] - Path configuration for locale routing
|
|
27
28
|
* @param {RouteOverrides} [config.routeOverrides] - Locale-relative paths to rewrite from /{locale}/{path} to /{locale}/{locale}/{path}
|
|
28
29
|
* @param {LocaleRoutes} [config.localeRoutes] - Shared paths available per locale; other paths fall back to the default locale
|
|
29
30
|
* @returns {function} - A middleware function that processes the request and response.
|
|
30
31
|
*/
|
|
31
|
-
function createNextMiddleware({ localeRouting = true, prefixDefaultLocale = false, ignoreSourceMaps = true, pathConfig = {}, routeOverrides = {}, localeRoutes = {} } = {}) {
|
|
32
|
+
function createNextMiddleware({ localeRouting = true, prefixDefaultLocale = false, enableSmartRouting = true, ignoreSourceMaps = true, pathConfig = {}, routeOverrides = {}, localeRoutes = {} } = {}) {
|
|
32
33
|
const pathRegex = compilePathRegex(process.env._GENERALTRANSLATION_PATH_REGEX);
|
|
33
34
|
let envParams;
|
|
34
35
|
if (process.env._GENERALTRANSLATION_I18N_CONFIG_PARAMS) try {
|
|
@@ -93,7 +94,7 @@ function createNextMiddleware({ localeRouting = true, prefixDefaultLocale = fals
|
|
|
93
94
|
function middleware(req) {
|
|
94
95
|
if (!pathnameMatchesRegex(req.nextUrl.pathname, pathRegex)) return NextResponse.next();
|
|
95
96
|
if (ignoreSourceMaps && req.nextUrl.pathname.startsWith(NEXT_JS_SOURCE_MAP_PATH)) return NextResponse.next();
|
|
96
|
-
const { userLocale: requestedLocale, pathnameLocale: pathnameRoutingLocale, unstandardizedPathnameLocale, clearResetCookie } = getLocaleFromRequest(req, defaultLocale, locales, localeRouting, prefixDefaultLocale, defaultLocalePaths, referrerLocaleCookieName, localeCookieName, resetLocaleCookieName, gt);
|
|
97
|
+
const { userLocale: requestedLocale, pathnameLocale: pathnameRoutingLocale, unstandardizedPathnameLocale, clearResetCookie } = getLocaleFromRequest(req, defaultLocale, locales, localeRouting, prefixDefaultLocale, defaultLocalePaths, referrerLocaleCookieName, localeCookieName, resetLocaleCookieName, gt, enableSmartRouting);
|
|
97
98
|
let userLocale = requestedLocale;
|
|
98
99
|
const pathnameLocale = pathnameRoutingLocale;
|
|
99
100
|
const headerList = new Headers(req.headers);
|
|
@@ -112,11 +113,15 @@ function createNextMiddleware({ localeRouting = true, prefixDefaultLocale = fals
|
|
|
112
113
|
type: "rewrite",
|
|
113
114
|
...responseConfig
|
|
114
115
|
});
|
|
115
|
-
const getRedirectResponse = (responsePath) =>
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
116
|
+
const getRedirectResponse = (responsePath) => {
|
|
117
|
+
const response = getResponse({
|
|
118
|
+
responsePath,
|
|
119
|
+
type: "redirect",
|
|
120
|
+
...responseConfig
|
|
121
|
+
});
|
|
122
|
+
if (!enableSmartRouting && !prefixDefaultLocale && pathnameLocale === defaultLocale && userLocale === defaultLocale && req.headers.get("sec-fetch-dest") === "document" && !req.cookies.has(localeCookieName)) response.cookies.set(localeCookieName, defaultLocale);
|
|
123
|
+
return response;
|
|
124
|
+
};
|
|
120
125
|
const getNextResponse = () => getResponse({
|
|
121
126
|
type: "next",
|
|
122
127
|
...responseConfig
|
|
@@ -148,7 +153,7 @@ function createNextMiddleware({ localeRouting = true, prefixDefaultLocale = fals
|
|
|
148
153
|
const routeOverridePath = (routeOverridePathMap ? getSharedPath(pagePath, routeOverridePathMap, void 0) : void 0) !== void 0 ? applyTrailingSlash(standardizedPathname, `/${userLocale}/${userLocale}${pagePath === "/" ? "" : pagePath}`) : void 0;
|
|
149
154
|
if (localizedPathWithParameters === void 0) {
|
|
150
155
|
if (!prefixDefaultLocale && userLocale === defaultLocale) if (pathnameLocale) {
|
|
151
|
-
if (clearResetCookie) return getRedirectResponse(pathname.
|
|
156
|
+
if (clearResetCookie || !enableSmartRouting) return getRedirectResponse(pathname.slice(unstandardizedPathnameLocale.length + 1) || "/");
|
|
152
157
|
} else return getRewriteResponse(routeOverridePath || `/${userLocale}${pathname}`);
|
|
153
158
|
else if (!pathnameLocale) return getRedirectResponse(`/${userLocale}${pathname}`);
|
|
154
159
|
if (pathnameLocale && userLocale !== unstandardizedPathnameLocale) return getRedirectResponse(pathname.replace(new RegExp(`^/${unstandardizedPathnameLocale}`), `/${userLocale}`));
|
|
@@ -156,9 +161,9 @@ function createNextMiddleware({ localeRouting = true, prefixDefaultLocale = fals
|
|
|
156
161
|
return getNextResponse();
|
|
157
162
|
}
|
|
158
163
|
if (!prefixDefaultLocale && userLocale === defaultLocale) if (pathnameLocale) {
|
|
159
|
-
if (clearResetCookie) return getRedirectResponse(localizedPathWithParameters.
|
|
164
|
+
if (clearResetCookie || !enableSmartRouting) return getRedirectResponse(localizedPathWithParameters.slice(userLocale.length + 1) || "/");
|
|
160
165
|
} else {
|
|
161
|
-
const localizedPublicPath = localizedPathWithParameters.
|
|
166
|
+
const localizedPublicPath = localizedPathWithParameters.slice(userLocale.length + 1) || "/";
|
|
162
167
|
if (!pathnameLocale && normalizePathname(localizedPublicPath) !== normalizePathname(pathname)) return getRedirectResponse(localizedPublicPath);
|
|
163
168
|
return getRewriteResponse(routeOverridePath || sharedPathWithParameters);
|
|
164
169
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createNextMiddleware.mjs","names":[],"sources":["../../src/middleware-dir/createNextMiddleware.ts"],"sourcesContent":["import { applyTrailingSlash } from './pathname';\nimport { GTRuntime } from 'generaltranslation/runtime';\nimport { libraryDefaultLocale } from 'generaltranslation/internal';\nimport { createUnsupportedLocalesWarning } from '../errors/middleware';\nimport { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\nimport {\n defaultLocaleRoutingEnabledCookieName,\n defaultReferrerLocaleCookieName,\n} from '../utils/cookies';\nimport {\n defaultLocaleCookieName,\n defaultResetLocaleCookieName,\n} from '@generaltranslation/react-core/pure';\nimport {\n normalizePathname,\n getSharedPath,\n replaceDynamicSegments,\n getLocalizedPath,\n createPathToSharedPathMap,\n createPathMatcher,\n getLocaleFromRequest,\n getResponse,\n ResponseConfig,\n type PathConfig,\n type PathMatcher,\n} from './utils';\nimport { defaultLocaleHeaderName } from '../utils/headers';\nimport type { CustomMapping } from '@generaltranslation/format/types';\nimport type { HeadersAndCookies } from '../config-dir/props/withGTConfigProps';\nimport { compilePathRegex, pathnameMatchesRegex } from '../utils/pathRegex';\n\nconst NEXT_JS_SOURCE_MAP_PATH = '/__nextjs_source-map';\n\ntype MiddlewareEnvConfig = {\n customMapping?: CustomMapping;\n defaultLocale?: string;\n locales?: string[];\n headersAndCookies?: HeadersAndCookies;\n};\n\nexport type RouteOverrides = Record<string, readonly string[]>;\n\nexport type LocaleRoutes = Record<string, readonly string[]>;\n\n/**\n * Middleware factory to create a Next.js middleware for i18n routing and locale detection.\n *\n * This middleware sets a cookie based on the locale derived from several sources\n * such as the request pathname, referer, or 'Accept-Language' header.\n * If locale routing is enabled, it redirects to the localized pathname and\n * updates the locale cookie.\n *\n * @param {boolean} [config.localeRouting=true] - Flag to enable or disable automatic locale-based routing.\n * @param {boolean} [config.prefixDefaultLocale=false] - Flag to enable or disable prefixing the default locale to the pathname, i.e., /en/about -> /about\n * @param {boolean} [config.ignoreSourceMaps=true] - Flag to enable or disable ignoring source maps\n * @param {PathConfig} [config.pathConfig] - Path configuration for locale routing\n * @param {RouteOverrides} [config.routeOverrides] - Locale-relative paths to rewrite from /{locale}/{path} to /{locale}/{locale}/{path}\n * @param {LocaleRoutes} [config.localeRoutes] - Shared paths available per locale; other paths fall back to the default locale\n * @returns {function} - A middleware function that processes the request and response.\n */\nexport function createNextMiddleware({\n localeRouting = true,\n prefixDefaultLocale = false,\n ignoreSourceMaps = true,\n pathConfig = {},\n routeOverrides = {},\n localeRoutes = {},\n}: {\n localeRouting?: boolean;\n prefixDefaultLocale?: boolean;\n ignoreSourceMaps?: boolean;\n pathConfig?: PathConfig;\n routeOverrides?: RouteOverrides;\n localeRoutes?: LocaleRoutes;\n} = {}) {\n const pathRegex = compilePathRegex(\n process.env._GENERALTRANSLATION_PATH_REGEX\n );\n\n // i18n config\n let envParams: MiddlewareEnvConfig | undefined;\n if (process.env._GENERALTRANSLATION_I18N_CONFIG_PARAMS) {\n try {\n envParams = JSON.parse(\n process.env._GENERALTRANSLATION_I18N_CONFIG_PARAMS\n );\n } catch (error) {\n console.error(`gt-next middleware:`, error);\n }\n }\n\n // gt instance\n const gt = new GTRuntime({\n customMapping: envParams?.customMapping,\n });\n\n // Configured spellings are the identity used by routes, cookies and caches.\n const defaultLocale: string =\n envParams?.defaultLocale || libraryDefaultLocale;\n const locales = Array.from(\n new Set([defaultLocale, ...(envParams?.locales || [])])\n );\n // Route entries repeat locale keys. Resolve each key once per factory instead\n // of rebuilding the full locale-resolution index for every route/locale pair.\n const resolvedConfigLocales = new Map<string, string>();\n const determineLocaleWithCache = (locale: string) => {\n const cached = resolvedConfigLocales.get(locale);\n if (cached !== undefined) return cached;\n const resolved = gt.determineLocale(locale, locales) ?? locale;\n resolvedConfigLocales.set(locale, resolved);\n return resolved;\n };\n const resolvePathConfigLocale =\n process.env._GENERALTRANSLATION_GT_SERVICES_ENABLED === 'true'\n ? determineLocaleWithCache\n : (locale: string) => locale;\n\n // cookies and header names\n const headersAndCookies = envParams?.headersAndCookies || {};\n const localeRoutingEnabledCookieName =\n headersAndCookies?.localeRoutingEnabledCookieName ||\n defaultLocaleRoutingEnabledCookieName;\n const referrerLocaleCookieName =\n headersAndCookies?.referrerLocaleCookieName ||\n defaultReferrerLocaleCookieName;\n const localeCookieName =\n headersAndCookies?.localeCookieName || defaultLocaleCookieName;\n const resetLocaleCookieName =\n headersAndCookies?.resetLocaleCookieName || defaultResetLocaleCookieName;\n const localeHeaderName =\n headersAndCookies?.localeHeaderName || defaultLocaleHeaderName;\n\n if (!gt.isValidLocale(defaultLocale))\n throw new Error(\n `gt-next middleware: defaultLocale \"${defaultLocale}\" is not a valid locale.`\n );\n\n const warningLocales = locales.filter((locale) => !gt.isValidLocale(locale));\n if (warningLocales.length)\n console.warn(createUnsupportedLocalesWarning(warningLocales));\n\n // ---------- PRE-PROCESSING PATHS ---------- //\n\n // --- localized routes --- //\n // Resolve pathConfig keys to the configured locale spelling.\n pathConfig = Object.entries(pathConfig).reduce<PathConfig>(\n (acc, [sharedPath, localizedPath]) => {\n if (typeof localizedPath === 'string') {\n acc[sharedPath] = localizedPath;\n } else {\n acc[sharedPath] = Object.entries(localizedPath).reduce<{\n [key: string]: string;\n }>((acc, [locale, localizedPath]) => {\n acc[resolvePathConfigLocale(locale)] = localizedPath;\n return acc;\n }, {});\n }\n return acc;\n },\n {}\n );\n\n // String aliases apply to every locale, including the default locale.\n // Expand a copy for lookup while retaining string target semantics in routing.\n const matcherPathConfig = Object.fromEntries(\n Object.entries(pathConfig).map(([sharedPath, localizedPath]) => [\n sharedPath,\n typeof localizedPath === 'string'\n ? Object.fromEntries(\n locales.map((locale) => [\n determineLocaleWithCache(locale),\n localizedPath === '' ? '/' : localizedPath,\n ])\n )\n : localizedPath,\n ])\n );\n\n // Create the path mapping\n const { pathToSharedPath, defaultLocalePaths } = createPathToSharedPathMap(\n matcherPathConfig,\n prefixDefaultLocale,\n defaultLocale\n );\n\n // --- route overrides --- //\n // Resolve routeOverrides keys to the configured locale spelling.\n routeOverrides = Object.entries(routeOverrides).reduce<RouteOverrides>(\n (acc, [locale, paths]) => {\n acc[resolvePathConfigLocale(locale)] = paths;\n return acc;\n },\n {}\n );\n\n // Create the route override path mapping\n const routeOverridePathMaps = Object.entries(routeOverrides).reduce<\n Record<string, PathMatcher>\n >((acc, [locale, paths]) => {\n const overridePathConfig = Object.fromEntries(\n paths.map((path) => [path, path])\n );\n acc[locale] = createPathToSharedPathMap(\n overridePathConfig,\n true,\n defaultLocale\n ).pathToSharedPath;\n return acc;\n }, {});\n\n // --- locale routes --- //\n // Compile availability separately from aliases and overrides so it cannot\n // change route precedence. An empty list deliberately matches nothing.\n const localeRoutePathMaps = new Map(\n Object.entries(localeRoutes).map(([locale, paths]) => {\n // Match request resolution so equivalent spellings use the same map key.\n const resolvedLocale = determineLocaleWithCache(locale);\n return [\n resolvedLocale,\n createPathMatcher(paths.map((path) => [path, path])),\n ];\n })\n );\n\n /**\n * Processes the incoming request to determine the user's locale and sets a locale cookie.\n * Optionally redirects the user based on the locale if locale-based routing is enabled.\n *\n * - Checks if the request URL contains a locale.\n * - Falls back to the referer URL for locale if needed.\n * - If no locale is found in the URL or referer, it checks the 'Accept-Language' header.\n * - Sets a cookie with the detected or default locale.\n * - Redirects to the correct locale route if locale routing is enabled.\n *\n * @param {NextRequest} req - The incoming request object, containing URL and headers.\n * @returns {NextResponse} - The Next.js response, either continuing the request or redirecting to the localized URL.\n */\n function middleware(req: NextRequest) {\n if (!pathnameMatchesRegex(req.nextUrl.pathname, pathRegex)) {\n return NextResponse.next();\n }\n\n // Ignore source maps\n if (\n ignoreSourceMaps &&\n req.nextUrl.pathname.startsWith(NEXT_JS_SOURCE_MAP_PATH)\n ) {\n return NextResponse.next();\n }\n\n // ---------- LOCALE DETECTION ---------- //\n\n const {\n userLocale: requestedLocale,\n pathnameLocale: pathnameRoutingLocale,\n unstandardizedPathnameLocale,\n clearResetCookie,\n } = getLocaleFromRequest(\n req,\n defaultLocale,\n locales,\n localeRouting,\n prefixDefaultLocale,\n defaultLocalePaths,\n referrerLocaleCookieName,\n localeCookieName,\n resetLocaleCookieName,\n gt\n );\n\n let userLocale = requestedLocale;\n const pathnameLocale = pathnameRoutingLocale;\n const headerList = new Headers(req.headers);\n\n const responseConfig: Omit<ResponseConfig, 'type'> = {\n originalUrl: req.nextUrl,\n headerList,\n userLocale,\n clearResetCookie,\n localeRouting,\n localeRoutingEnabledCookieName,\n resetLocaleCookieName,\n localeHeaderName,\n };\n\n const getRewriteResponse = (responsePath: string) =>\n getResponse({ responsePath, type: 'rewrite', ...responseConfig });\n\n const getRedirectResponse = (responsePath: string) =>\n getResponse({ responsePath, type: 'redirect', ...responseConfig });\n\n const getNextResponse = () =>\n getResponse({ type: 'next', ...responseConfig });\n\n if (localeRouting) {\n // ---------- GET PATHS ---------- //\n\n // get pathname\n const { pathname } = req.nextUrl;\n\n // standardize pathname (ie, /tg/welcome -> /fil/welcome), (/blog -> /blog)\n const standardizedPathname =\n pathnameLocale && pathnameLocale !== unstandardizedPathnameLocale\n ? pathname.replace(\n new RegExp(`^/${unstandardizedPathnameLocale}`),\n `/${pathnameLocale}`\n )\n : pathname;\n\n // Get the shared path for the unprefixed pathname\n const sharedPathMatch = getSharedPath(\n standardizedPathname,\n pathToSharedPath,\n pathnameLocale\n );\n const sharedPath = sharedPathMatch?.sharedPath;\n\n // Return early for a locale route that does not exist\n const localeRoutePathMap = localeRoutePathMaps.get(userLocale);\n if (userLocale !== defaultLocale && localeRoutePathMap) {\n // Resolve the shared page path without a locale prefix (e.g. /blog/hello).\n // Strip the URL's locale, which may differ from the newly selected locale.\n const sharedPagePath = sharedPathMatch\n ? replaceDynamicSegments(\n sharedPathMatch.matchedPathname,\n sharedPath || '/',\n sharedPathMatch.pathTemplate\n )\n : (pathnameLocale\n ? standardizedPathname.slice(pathnameLocale.length + 1)\n : standardizedPathname) || '/';\n\n // If the path does not exist, redirect to the default locale path\n if (\n getSharedPath(sharedPagePath, localeRoutePathMap, undefined) ===\n undefined\n ) {\n // Get the path to redirect to\n const defaultPath =\n sharedPath !== undefined\n ? getLocalizedPath(sharedPath, defaultLocale, pathConfig)\n : undefined;\n const fallbackPath =\n defaultPath !== undefined && sharedPathMatch\n ? replaceDynamicSegments(\n sharedPathMatch.matchedPathname,\n defaultPath,\n sharedPathMatch.pathTemplate\n )\n : `/${defaultLocale}${sharedPagePath === '/' ? '' : sharedPagePath}`;\n const publicFallbackPath = applyTrailingSlash(\n standardizedPathname,\n prefixDefaultLocale\n ? fallbackPath\n : fallbackPath.slice(defaultLocale.length + 1) || '/'\n );\n\n // The default locale is terminal, even if the preference/reset cookie\n // still requests an unavailable locale on the redirected request.\n userLocale = defaultLocale;\n responseConfig.userLocale = defaultLocale;\n const fallbackUrl = new URL(req.nextUrl);\n fallbackUrl.pathname = publicFallbackPath;\n if (fallbackUrl.pathname !== pathname) {\n return getRedirectResponse(publicFallbackPath);\n }\n // Already at the fallback URL: use normal default-locale routing below,\n // including its alias/override rewrite, instead of redirecting to itself.\n }\n }\n\n // Get shared path with parameters (/en/dashboard/1/custom), for rewriting localized paths\n const sharedPathWithParameters =\n sharedPathMatch !== undefined\n ? applyTrailingSlash(\n standardizedPathname,\n replaceDynamicSegments(\n sharedPathMatch.matchedPathname,\n `/${userLocale}${sharedPath}`,\n sharedPathMatch.pathTemplate\n )\n )\n : undefined;\n\n // Localized path (/en/blog, /fr/fr-about, /fr/dashboard/[id]/custom)\n const localizedPath =\n sharedPath !== undefined\n ? getLocalizedPath(sharedPath, userLocale, pathConfig)\n : undefined;\n\n // Combine localized path with dynamic parameters (/en/blog, /fr/fr-about, /fr/dashboard/1/fr-custom)\n const localizedPathWithParameters =\n localizedPath !== undefined && sharedPathMatch !== undefined\n ? applyTrailingSlash(\n standardizedPathname,\n replaceDynamicSegments(\n sharedPathMatch.matchedPathname,\n localizedPath,\n sharedPathMatch.pathTemplate\n )\n )\n : undefined;\n\n const pagePath =\n (sharedPathWithParameters?.slice(userLocale.length + 1) ??\n (pathnameLocale\n ? standardizedPathname.slice(pathnameLocale.length + 1)\n : standardizedPathname)) ||\n '/';\n const routeOverridePathMap = routeOverridePathMaps[userLocale];\n const routeOverrideMatch = routeOverridePathMap\n ? getSharedPath(pagePath, routeOverridePathMap, undefined)\n : undefined;\n const routeOverridePath =\n routeOverrideMatch !== undefined\n ? applyTrailingSlash(\n standardizedPathname,\n `/${userLocale}/${userLocale}${pagePath === '/' ? '' : pagePath}`\n )\n : undefined;\n\n // ---------- ROUTING LOGIC ---------- //\n\n // ----- CASE: no localized path exists ----- //\n\n if (localizedPathWithParameters === undefined) {\n // --- CASE: remove defaultLocale prefix --- //\n\n if (!prefixDefaultLocale && userLocale === defaultLocale) {\n if (pathnameLocale) {\n // REDIRECT CASE: used setLocale (/fr/customers -> /customers) (/en/customers -> /customers)\n if (clearResetCookie) {\n return getRedirectResponse(\n pathname.replace(\n new RegExp(`^/${unstandardizedPathnameLocale}`),\n ``\n ) || '/'\n );\n }\n } else {\n // REWRITE CASE: no pathnameLocale (/customers -> /en/customers)\n return getRewriteResponse(\n routeOverridePath || `/${userLocale}${pathname}`\n );\n }\n }\n\n // --- CASE: defaultLocale prefix --- //\n // REDIRECT CASE: no pathnameLocale (ie, /customers -> /fr/customers)\n else if (!pathnameLocale) {\n return getRedirectResponse(`/${userLocale}${pathname}`);\n }\n\n // REDIRECT CASE: wrong pathnameLocale (ie, /fr/customers -> /en/customers) (this usually happens after a locale switch)\n if (pathnameLocale && userLocale !== unstandardizedPathnameLocale) {\n return getRedirectResponse(\n pathname.replace(\n new RegExp(`^/${unstandardizedPathnameLocale}`),\n `/${userLocale}`\n )\n );\n }\n\n // BASE CASE: has pathnameLocale and it's correct\n if (routeOverridePath) {\n return getRewriteResponse(routeOverridePath);\n }\n return getNextResponse();\n }\n\n // ----- CASE: localized path exists ----- //\n\n if (!prefixDefaultLocale && userLocale === defaultLocale) {\n // --- CASE: remove defaultLocale prefix --- //\n\n if (pathnameLocale) {\n // REDIRECT CASE: remove locale prefix when setLocale is used (/en/blog -> /blog) (/fr/fr-about -> /en-about)\n if (clearResetCookie) {\n return getRedirectResponse(\n localizedPathWithParameters.replace(\n new RegExp(`^/${userLocale}`),\n ``\n ) || '/'\n );\n }\n } else {\n const localizedPublicPath =\n localizedPathWithParameters.replace(\n new RegExp(`^/${userLocale}`),\n ''\n ) || '/';\n\n // REDIRECT CASE: unprefixed pathname is wrong (/about -> /en-about)\n if (\n !pathnameLocale &&\n normalizePathname(localizedPublicPath) !==\n normalizePathname(pathname)\n ) {\n return getRedirectResponse(localizedPublicPath);\n }\n\n // REWRITE CASE: displaying correct path (/blog -> /en/blog)\n return getRewriteResponse(\n routeOverridePath || (sharedPathWithParameters as string)\n );\n }\n }\n\n // --- CASE: add defaultLocale prefix --- //\n\n // REDIRECT CASE: incorrect pathnameLocale\n if (\n normalizePathname(pathname) !==\n normalizePathname(localizedPathWithParameters)\n ) {\n return getRedirectResponse(localizedPathWithParameters);\n }\n\n if (routeOverridePath) {\n return getRewriteResponse(routeOverridePath);\n }\n\n // REWRITE CASE: displaying correct localized path, which is the same as the shared path (/fil/blog => /fil/blog) (/fr/fr-dashboard/1/fr-custom => /fr/dashboard/1/custom)\n // Next.js route identity preserves Unicode spelling, even when lookup\n // considers the public alias and shared path canonically equivalent.\n const rewriteUrl = new URL(\n sharedPathWithParameters as string,\n req.nextUrl\n );\n if (\n req.nextUrl.pathname !== rewriteUrl.pathname // no rewrite needed if it's already the shared path\n ) {\n // convert to shared path with dynamic parameters\n return getRewriteResponse(sharedPathWithParameters as string);\n }\n }\n\n // BASE CASE\n return getNextResponse();\n }\n\n return middleware;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createNextMiddleware.mjs","names":[],"sources":["../../src/middleware-dir/createNextMiddleware.ts"],"sourcesContent":["import { applyTrailingSlash } from './pathname';\nimport { GTRuntime } from 'generaltranslation/runtime';\nimport { libraryDefaultLocale } from 'generaltranslation/internal';\nimport { createUnsupportedLocalesWarning } from '../errors/middleware';\nimport { NextResponse } from 'next/server';\nimport type { NextRequest } from 'next/server';\nimport {\n defaultLocaleRoutingEnabledCookieName,\n defaultReferrerLocaleCookieName,\n} from '../utils/cookies';\nimport {\n defaultLocaleCookieName,\n defaultResetLocaleCookieName,\n} from '@generaltranslation/react-core/pure';\nimport {\n normalizePathname,\n getSharedPath,\n replaceDynamicSegments,\n getLocalizedPath,\n createPathToSharedPathMap,\n createPathMatcher,\n getLocaleFromRequest,\n getResponse,\n ResponseConfig,\n type PathConfig,\n type PathMatcher,\n} from './utils';\nimport { defaultLocaleHeaderName } from '../utils/headers';\nimport type { CustomMapping } from '@generaltranslation/format/types';\nimport type { HeadersAndCookies } from '../config-dir/props/withGTConfigProps';\nimport { compilePathRegex, pathnameMatchesRegex } from '../utils/pathRegex';\n\nconst NEXT_JS_SOURCE_MAP_PATH = '/__nextjs_source-map';\n\ntype MiddlewareEnvConfig = {\n customMapping?: CustomMapping;\n defaultLocale?: string;\n locales?: string[];\n headersAndCookies?: HeadersAndCookies;\n};\n\nexport type RouteOverrides = Record<string, readonly string[]>;\n\nexport type LocaleRoutes = Record<string, readonly string[]>;\n\n/**\n * Middleware factory to create a Next.js middleware for i18n routing and locale detection.\n *\n * This middleware sets a cookie based on the locale derived from several sources\n * such as the request pathname, referer, or 'Accept-Language' header.\n * If locale routing is enabled, it redirects to the localized pathname and\n * updates the locale cookie.\n *\n * @param {boolean} [config.localeRouting=true] - Flag to enable or disable automatic locale-based routing.\n * @param {boolean} [config.prefixDefaultLocale=false] - Flag to enable or disable prefixing the default locale to the pathname, i.e., /en/about -> /about\n * @param {boolean} [config.enableSmartRouting=true] - Keep automatic locale negotiation. Disable to treat returning visitors' unprefixed URLs as default-locale routes.\n * @param {boolean} [config.ignoreSourceMaps=true] - Flag to enable or disable ignoring source maps\n * @param {PathConfig} [config.pathConfig] - Path configuration for locale routing\n * @param {RouteOverrides} [config.routeOverrides] - Locale-relative paths to rewrite from /{locale}/{path} to /{locale}/{locale}/{path}\n * @param {LocaleRoutes} [config.localeRoutes] - Shared paths available per locale; other paths fall back to the default locale\n * @returns {function} - A middleware function that processes the request and response.\n */\nexport function createNextMiddleware({\n localeRouting = true,\n prefixDefaultLocale = false,\n enableSmartRouting = true,\n ignoreSourceMaps = true,\n pathConfig = {},\n routeOverrides = {},\n localeRoutes = {},\n}: {\n localeRouting?: boolean;\n prefixDefaultLocale?: boolean;\n enableSmartRouting?: boolean;\n ignoreSourceMaps?: boolean;\n pathConfig?: PathConfig;\n routeOverrides?: RouteOverrides;\n localeRoutes?: LocaleRoutes;\n} = {}) {\n const pathRegex = compilePathRegex(\n process.env._GENERALTRANSLATION_PATH_REGEX\n );\n\n // i18n config\n let envParams: MiddlewareEnvConfig | undefined;\n if (process.env._GENERALTRANSLATION_I18N_CONFIG_PARAMS) {\n try {\n envParams = JSON.parse(\n process.env._GENERALTRANSLATION_I18N_CONFIG_PARAMS\n );\n } catch (error) {\n console.error(`gt-next middleware:`, error);\n }\n }\n\n // gt instance\n const gt = new GTRuntime({\n customMapping: envParams?.customMapping,\n });\n\n // Configured spellings are the identity used by routes, cookies and caches.\n const defaultLocale: string =\n envParams?.defaultLocale || libraryDefaultLocale;\n const locales = Array.from(\n new Set([defaultLocale, ...(envParams?.locales || [])])\n );\n // Route entries repeat locale keys. Resolve each key once per factory instead\n // of rebuilding the full locale-resolution index for every route/locale pair.\n const resolvedConfigLocales = new Map<string, string>();\n const determineLocaleWithCache = (locale: string) => {\n const cached = resolvedConfigLocales.get(locale);\n if (cached !== undefined) return cached;\n const resolved = gt.determineLocale(locale, locales) ?? locale;\n resolvedConfigLocales.set(locale, resolved);\n return resolved;\n };\n const resolvePathConfigLocale =\n process.env._GENERALTRANSLATION_GT_SERVICES_ENABLED === 'true'\n ? determineLocaleWithCache\n : (locale: string) => locale;\n\n // cookies and header names\n const headersAndCookies = envParams?.headersAndCookies || {};\n const localeRoutingEnabledCookieName =\n headersAndCookies?.localeRoutingEnabledCookieName ||\n defaultLocaleRoutingEnabledCookieName;\n const referrerLocaleCookieName =\n headersAndCookies?.referrerLocaleCookieName ||\n defaultReferrerLocaleCookieName;\n const localeCookieName =\n headersAndCookies?.localeCookieName || defaultLocaleCookieName;\n const resetLocaleCookieName =\n headersAndCookies?.resetLocaleCookieName || defaultResetLocaleCookieName;\n const localeHeaderName =\n headersAndCookies?.localeHeaderName || defaultLocaleHeaderName;\n\n if (!gt.isValidLocale(defaultLocale))\n throw new Error(\n `gt-next middleware: defaultLocale \"${defaultLocale}\" is not a valid locale.`\n );\n\n const warningLocales = locales.filter((locale) => !gt.isValidLocale(locale));\n if (warningLocales.length)\n console.warn(createUnsupportedLocalesWarning(warningLocales));\n\n // ---------- PRE-PROCESSING PATHS ---------- //\n\n // --- localized routes --- //\n // Resolve pathConfig keys to the configured locale spelling.\n pathConfig = Object.entries(pathConfig).reduce<PathConfig>(\n (acc, [sharedPath, localizedPath]) => {\n if (typeof localizedPath === 'string') {\n acc[sharedPath] = localizedPath;\n } else {\n acc[sharedPath] = Object.entries(localizedPath).reduce<{\n [key: string]: string;\n }>((acc, [locale, localizedPath]) => {\n acc[resolvePathConfigLocale(locale)] = localizedPath;\n return acc;\n }, {});\n }\n return acc;\n },\n {}\n );\n\n // String aliases apply to every locale, including the default locale.\n // Expand a copy for lookup while retaining string target semantics in routing.\n const matcherPathConfig = Object.fromEntries(\n Object.entries(pathConfig).map(([sharedPath, localizedPath]) => [\n sharedPath,\n typeof localizedPath === 'string'\n ? Object.fromEntries(\n locales.map((locale) => [\n determineLocaleWithCache(locale),\n localizedPath === '' ? '/' : localizedPath,\n ])\n )\n : localizedPath,\n ])\n );\n\n // Create the path mapping\n const { pathToSharedPath, defaultLocalePaths } = createPathToSharedPathMap(\n matcherPathConfig,\n prefixDefaultLocale,\n defaultLocale\n );\n\n // --- route overrides --- //\n // Resolve routeOverrides keys to the configured locale spelling.\n routeOverrides = Object.entries(routeOverrides).reduce<RouteOverrides>(\n (acc, [locale, paths]) => {\n acc[resolvePathConfigLocale(locale)] = paths;\n return acc;\n },\n {}\n );\n\n // Create the route override path mapping\n const routeOverridePathMaps = Object.entries(routeOverrides).reduce<\n Record<string, PathMatcher>\n >((acc, [locale, paths]) => {\n const overridePathConfig = Object.fromEntries(\n paths.map((path) => [path, path])\n );\n acc[locale] = createPathToSharedPathMap(\n overridePathConfig,\n true,\n defaultLocale\n ).pathToSharedPath;\n return acc;\n }, {});\n\n // --- locale routes --- //\n // Compile availability separately from aliases and overrides so it cannot\n // change route precedence. An empty list deliberately matches nothing.\n const localeRoutePathMaps = new Map(\n Object.entries(localeRoutes).map(([locale, paths]) => {\n // Match request resolution so equivalent spellings use the same map key.\n const resolvedLocale = determineLocaleWithCache(locale);\n return [\n resolvedLocale,\n createPathMatcher(paths.map((path) => [path, path])),\n ];\n })\n );\n\n /**\n * Processes the incoming request to determine the user's locale and sets a locale cookie.\n * Optionally redirects the user based on the locale if locale-based routing is enabled.\n *\n * - Checks if the request URL contains a locale.\n * - Falls back to the referer URL for locale if needed.\n * - If no locale is found in the URL or referer, it checks the 'Accept-Language' header.\n * - Sets a cookie with the detected or default locale.\n * - Redirects to the correct locale route if locale routing is enabled.\n *\n * @param {NextRequest} req - The incoming request object, containing URL and headers.\n * @returns {NextResponse} - The Next.js response, either continuing the request or redirecting to the localized URL.\n */\n function middleware(req: NextRequest) {\n if (!pathnameMatchesRegex(req.nextUrl.pathname, pathRegex)) {\n return NextResponse.next();\n }\n\n // Ignore source maps\n if (\n ignoreSourceMaps &&\n req.nextUrl.pathname.startsWith(NEXT_JS_SOURCE_MAP_PATH)\n ) {\n return NextResponse.next();\n }\n\n // ---------- LOCALE DETECTION ---------- //\n\n const {\n userLocale: requestedLocale,\n pathnameLocale: pathnameRoutingLocale,\n unstandardizedPathnameLocale,\n clearResetCookie,\n } = getLocaleFromRequest(\n req,\n defaultLocale,\n locales,\n localeRouting,\n prefixDefaultLocale,\n defaultLocalePaths,\n referrerLocaleCookieName,\n localeCookieName,\n resetLocaleCookieName,\n gt,\n enableSmartRouting\n );\n\n let userLocale = requestedLocale;\n const pathnameLocale = pathnameRoutingLocale;\n const headerList = new Headers(req.headers);\n\n const responseConfig: Omit<ResponseConfig, 'type'> = {\n originalUrl: req.nextUrl,\n headerList,\n userLocale,\n clearResetCookie,\n localeRouting,\n localeRoutingEnabledCookieName,\n resetLocaleCookieName,\n localeHeaderName,\n };\n\n const getRewriteResponse = (responsePath: string) =>\n getResponse({ responsePath, type: 'rewrite', ...responseConfig });\n\n const getRedirectResponse = (responsePath: string) => {\n const response = getResponse({\n responsePath,\n type: 'redirect',\n ...responseConfig,\n });\n if (\n !enableSmartRouting &&\n !prefixDefaultLocale &&\n pathnameLocale === defaultLocale &&\n userLocale === defaultLocale &&\n req.headers.get('sec-fetch-dest') === 'document' &&\n !req.cookies.has(localeCookieName)\n ) {\n // Preserve explicit /en intent across a first document redirect.\n // Background fetches and returning visits must not change the cookie.\n response.cookies.set(localeCookieName, defaultLocale);\n }\n return response;\n };\n\n const getNextResponse = () =>\n getResponse({ type: 'next', ...responseConfig });\n\n if (localeRouting) {\n // ---------- GET PATHS ---------- //\n\n // get pathname\n const { pathname } = req.nextUrl;\n\n // standardize pathname (ie, /tg/welcome -> /fil/welcome), (/blog -> /blog)\n const standardizedPathname =\n pathnameLocale && pathnameLocale !== unstandardizedPathnameLocale\n ? pathname.replace(\n new RegExp(`^/${unstandardizedPathnameLocale}`),\n `/${pathnameLocale}`\n )\n : pathname;\n\n // Get the shared path for the unprefixed pathname\n const sharedPathMatch = getSharedPath(\n standardizedPathname,\n pathToSharedPath,\n pathnameLocale\n );\n const sharedPath = sharedPathMatch?.sharedPath;\n\n // Return early for a locale route that does not exist\n const localeRoutePathMap = localeRoutePathMaps.get(userLocale);\n if (userLocale !== defaultLocale && localeRoutePathMap) {\n // Resolve the shared page path without a locale prefix (e.g. /blog/hello).\n // Strip the URL's locale, which may differ from the newly selected locale.\n const sharedPagePath = sharedPathMatch\n ? replaceDynamicSegments(\n sharedPathMatch.matchedPathname,\n sharedPath || '/',\n sharedPathMatch.pathTemplate\n )\n : (pathnameLocale\n ? standardizedPathname.slice(pathnameLocale.length + 1)\n : standardizedPathname) || '/';\n\n // If the path does not exist, redirect to the default locale path\n if (\n getSharedPath(sharedPagePath, localeRoutePathMap, undefined) ===\n undefined\n ) {\n // Get the path to redirect to\n const defaultPath =\n sharedPath !== undefined\n ? getLocalizedPath(sharedPath, defaultLocale, pathConfig)\n : undefined;\n const fallbackPath =\n defaultPath !== undefined && sharedPathMatch\n ? replaceDynamicSegments(\n sharedPathMatch.matchedPathname,\n defaultPath,\n sharedPathMatch.pathTemplate\n )\n : `/${defaultLocale}${sharedPagePath === '/' ? '' : sharedPagePath}`;\n const publicFallbackPath = applyTrailingSlash(\n standardizedPathname,\n prefixDefaultLocale\n ? fallbackPath\n : fallbackPath.slice(defaultLocale.length + 1) || '/'\n );\n\n // The default locale is terminal, even if the preference/reset cookie\n // still requests an unavailable locale on the redirected request.\n userLocale = defaultLocale;\n responseConfig.userLocale = defaultLocale;\n const fallbackUrl = new URL(req.nextUrl);\n fallbackUrl.pathname = publicFallbackPath;\n if (fallbackUrl.pathname !== pathname) {\n return getRedirectResponse(publicFallbackPath);\n }\n // Already at the fallback URL: use normal default-locale routing below,\n // including its alias/override rewrite, instead of redirecting to itself.\n }\n }\n\n // Get shared path with parameters (/en/dashboard/1/custom), for rewriting localized paths\n const sharedPathWithParameters =\n sharedPathMatch !== undefined\n ? applyTrailingSlash(\n standardizedPathname,\n replaceDynamicSegments(\n sharedPathMatch.matchedPathname,\n `/${userLocale}${sharedPath}`,\n sharedPathMatch.pathTemplate\n )\n )\n : undefined;\n\n // Localized path (/en/blog, /fr/fr-about, /fr/dashboard/[id]/custom)\n const localizedPath =\n sharedPath !== undefined\n ? getLocalizedPath(sharedPath, userLocale, pathConfig)\n : undefined;\n\n // Combine localized path with dynamic parameters (/en/blog, /fr/fr-about, /fr/dashboard/1/fr-custom)\n const localizedPathWithParameters =\n localizedPath !== undefined && sharedPathMatch !== undefined\n ? applyTrailingSlash(\n standardizedPathname,\n replaceDynamicSegments(\n sharedPathMatch.matchedPathname,\n localizedPath,\n sharedPathMatch.pathTemplate\n )\n )\n : undefined;\n\n const pagePath =\n (sharedPathWithParameters?.slice(userLocale.length + 1) ??\n (pathnameLocale\n ? standardizedPathname.slice(pathnameLocale.length + 1)\n : standardizedPathname)) ||\n '/';\n const routeOverridePathMap = routeOverridePathMaps[userLocale];\n const routeOverrideMatch = routeOverridePathMap\n ? getSharedPath(pagePath, routeOverridePathMap, undefined)\n : undefined;\n const routeOverridePath =\n routeOverrideMatch !== undefined\n ? applyTrailingSlash(\n standardizedPathname,\n `/${userLocale}/${userLocale}${pagePath === '/' ? '' : pagePath}`\n )\n : undefined;\n\n // ---------- ROUTING LOGIC ---------- //\n\n // ----- CASE: no localized path exists ----- //\n\n if (localizedPathWithParameters === undefined) {\n // --- CASE: remove defaultLocale prefix --- //\n\n if (!prefixDefaultLocale && userLocale === defaultLocale) {\n if (pathnameLocale) {\n // REDIRECT CASE: explicit default locale or setLocale (/en/customers -> /customers)\n if (clearResetCookie || !enableSmartRouting) {\n return getRedirectResponse(\n pathname.slice(\n (unstandardizedPathnameLocale as string).length + 1\n ) || '/'\n );\n }\n } else {\n // REWRITE CASE: no pathnameLocale (/customers -> /en/customers)\n return getRewriteResponse(\n routeOverridePath || `/${userLocale}${pathname}`\n );\n }\n }\n\n // --- CASE: defaultLocale prefix --- //\n // REDIRECT CASE: no pathnameLocale (ie, /customers -> /fr/customers)\n else if (!pathnameLocale) {\n return getRedirectResponse(`/${userLocale}${pathname}`);\n }\n\n // REDIRECT CASE: wrong pathnameLocale (ie, /fr/customers -> /en/customers) (this usually happens after a locale switch)\n if (pathnameLocale && userLocale !== unstandardizedPathnameLocale) {\n return getRedirectResponse(\n pathname.replace(\n new RegExp(`^/${unstandardizedPathnameLocale}`),\n `/${userLocale}`\n )\n );\n }\n\n // BASE CASE: has pathnameLocale and it's correct\n if (routeOverridePath) {\n return getRewriteResponse(routeOverridePath);\n }\n return getNextResponse();\n }\n\n // ----- CASE: localized path exists ----- //\n\n if (!prefixDefaultLocale && userLocale === defaultLocale) {\n // --- CASE: remove defaultLocale prefix --- //\n\n if (pathnameLocale) {\n // REDIRECT CASE: explicit default locale or setLocale (/en/blog -> /blog)\n if (clearResetCookie || !enableSmartRouting) {\n return getRedirectResponse(\n localizedPathWithParameters.slice(userLocale.length + 1) || '/'\n );\n }\n } else {\n const localizedPublicPath =\n localizedPathWithParameters.slice(userLocale.length + 1) || '/';\n\n // REDIRECT CASE: unprefixed pathname is wrong (/about -> /en-about)\n if (\n !pathnameLocale &&\n normalizePathname(localizedPublicPath) !==\n normalizePathname(pathname)\n ) {\n return getRedirectResponse(localizedPublicPath);\n }\n\n // REWRITE CASE: displaying correct path (/blog -> /en/blog)\n return getRewriteResponse(\n routeOverridePath || (sharedPathWithParameters as string)\n );\n }\n }\n\n // --- CASE: add defaultLocale prefix --- //\n\n // REDIRECT CASE: incorrect pathnameLocale\n if (\n normalizePathname(pathname) !==\n normalizePathname(localizedPathWithParameters)\n ) {\n return getRedirectResponse(localizedPathWithParameters);\n }\n\n if (routeOverridePath) {\n return getRewriteResponse(routeOverridePath);\n }\n\n // REWRITE CASE: displaying correct localized path, which is the same as the shared path (/fil/blog => /fil/blog) (/fr/fr-dashboard/1/fr-custom => /fr/dashboard/1/custom)\n // Next.js route identity preserves Unicode spelling, even when lookup\n // considers the public alias and shared path canonically equivalent.\n const rewriteUrl = new URL(\n sharedPathWithParameters as string,\n req.nextUrl\n );\n if (\n req.nextUrl.pathname !== rewriteUrl.pathname // no rewrite needed if it's already the shared path\n ) {\n // convert to shared path with dynamic parameters\n return getRewriteResponse(sharedPathWithParameters as string);\n }\n }\n\n // BASE CASE\n return getNextResponse();\n }\n\n return middleware;\n}\n"],"mappings":"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"}
|
|
@@ -38,7 +38,7 @@ export declare function getLocalizedPath(sharedPath: string, locale: string, pat
|
|
|
38
38
|
/**
|
|
39
39
|
* Resolves the request locale from its pathname, cookies, and headers.
|
|
40
40
|
*/
|
|
41
|
-
export declare function getLocaleFromRequest(req: NextRequest, defaultLocale: string, approvedLocales: string[], localeRouting: boolean, prefixDefaultLocale: boolean, defaultLocalePaths: PathMatcher, referrerLocaleCookieName: string, localeCookieName: string, resetLocaleCookieName: string, gt: GTRuntime): {
|
|
41
|
+
export declare function getLocaleFromRequest(req: NextRequest, defaultLocale: string, approvedLocales: string[], localeRouting: boolean, prefixDefaultLocale: boolean, defaultLocalePaths: PathMatcher, referrerLocaleCookieName: string, localeCookieName: string, resetLocaleCookieName: string, gt: GTRuntime, enableSmartRouting?: boolean): {
|
|
42
42
|
userLocale: string;
|
|
43
43
|
pathnameLocale: string | undefined;
|
|
44
44
|
unstandardizedPathnameLocale: string | null | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/middleware-dir/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAExD,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EAEzB,iBAAiB,EACjB,KAAK,UAAU,EACf,KAAK,WAAW,EACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,aAAa,EACb,iBAAiB,GAClB,CAAC;AACF,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;AAExC,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,8BAA8B,EAAE,MAAM,CAAC;IACvC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAeF,mEAAmE;AACnE,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,WAAW,EACX,YAAmC,EACnC,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,8BAA8B,EAC9B,qBAAqB,EACrB,gBAAgB,GACjB,EAAE,cAAc,GAAG,YAAY,CAAC,OAAO,CAAC,CAwCxC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG7D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,MAAM,EAAE,CAiBV;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,kBAAkB,SAAe,GAChC,MAAM,CA8BR;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,GACrB,MAAM,GAAG,SAAS,CAapB;AAeD;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,WAAW,EAChB,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,MAAM,EAAE,EACzB,aAAa,EAAE,OAAO,EACtB,mBAAmB,EAAE,OAAO,EAC5B,kBAAkB,EAAE,WAAW,EAC/B,wBAAwB,EAAE,MAAM,EAChC,gBAAgB,EAAE,MAAM,EACxB,qBAAqB,EAAE,MAAM,EAC7B,EAAE,EAAE,SAAS,
|
|
1
|
+
{"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../src/middleware-dir/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,OAAO,EAAE,MAAM,+BAA+B,CAAC;AAExD,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EAEzB,iBAAiB,EACjB,KAAK,UAAU,EACf,KAAK,WAAW,EACjB,MAAM,qBAAqB,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAG5C,OAAO,EACL,iBAAiB,EACjB,yBAAyB,EACzB,aAAa,EACb,iBAAiB,GAClB,CAAC;AACF,YAAY,EAAE,UAAU,EAAE,WAAW,EAAE,CAAC;AAExC,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;IACtC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,OAAO,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,gBAAgB,EAAE,OAAO,CAAC;IAC1B,UAAU,EAAE,OAAO,CAAC;IACpB,aAAa,EAAE,OAAO,CAAC;IACvB,8BAA8B,EAAE,MAAM,CAAC;IACvC,qBAAqB,EAAE,MAAM,CAAC;IAC9B,gBAAgB,EAAE,MAAM,CAAC;CAC1B,CAAC;AAeF,mEAAmE;AACnE,wBAAgB,WAAW,CAAC,EAC1B,IAAI,EACJ,WAAW,EACX,YAAmC,EACnC,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,8BAA8B,EAC9B,qBAAqB,EACrB,gBAAgB,GACjB,EAAE,cAAc,GAAG,YAAY,CAAC,OAAO,CAAC,CAwCxC;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAG7D;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,MAAM,EACpB,IAAI,EAAE,MAAM,GACX,MAAM,EAAE,CAiBV;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,MAAM,EACZ,YAAY,EAAE,MAAM,EACpB,kBAAkB,SAAe,GAChC,MAAM,CA8BR;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAC9B,UAAU,EAAE,MAAM,EAClB,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,UAAU,GACrB,MAAM,GAAG,SAAS,CAapB;AAeD;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,WAAW,EAChB,aAAa,EAAE,MAAM,EACrB,eAAe,EAAE,MAAM,EAAE,EACzB,aAAa,EAAE,OAAO,EACtB,mBAAmB,EAAE,OAAO,EAC5B,kBAAkB,EAAE,WAAW,EAC/B,wBAAwB,EAAE,MAAM,EAChC,gBAAgB,EAAE,MAAM,EACxB,qBAAqB,EAAE,MAAM,EAC7B,EAAE,EAAE,SAAS,EACb,kBAAkB,UAAO,GACxB;IACD,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,GAAG,SAAS,CAAC;IACnC,4BAA4B,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC;IACxD,gBAAgB,EAAE,OAAO,CAAC;CAC3B,CA0FA"}
|
|
@@ -92,7 +92,7 @@ function inDefaultLocalePaths(pathname, defaultLocalePaths) {
|
|
|
92
92
|
/**
|
|
93
93
|
* Resolves the request locale from its pathname, cookies, and headers.
|
|
94
94
|
*/
|
|
95
|
-
function getLocaleFromRequest(req, defaultLocale, approvedLocales, localeRouting, prefixDefaultLocale, defaultLocalePaths, referrerLocaleCookieName, localeCookieName, resetLocaleCookieName, gt) {
|
|
95
|
+
function getLocaleFromRequest(req, defaultLocale, approvedLocales, localeRouting, prefixDefaultLocale, defaultLocalePaths, referrerLocaleCookieName, localeCookieName, resetLocaleCookieName, gt, enableSmartRouting = true) {
|
|
96
96
|
const headerList = new Headers(req.headers);
|
|
97
97
|
const candidates = [];
|
|
98
98
|
let clearResetCookie = false;
|
|
@@ -105,14 +105,15 @@ function getLocaleFromRequest(req, defaultLocale, approvedLocales, localeRouting
|
|
|
105
105
|
if (pathnameLocale) candidates.push(pathnameLocale);
|
|
106
106
|
}
|
|
107
107
|
}
|
|
108
|
-
if (localeRouting && !prefixDefaultLocale && !pathnameLocale && inDefaultLocalePaths(pathname, defaultLocalePaths)) candidates.push(defaultLocale);
|
|
109
108
|
const cookieLocale = req.cookies.get(localeCookieName);
|
|
109
|
+
const preferDefaultLocale = !enableSmartRouting && localeRouting && !prefixDefaultLocale;
|
|
110
|
+
if (localeRouting && !prefixDefaultLocale && !pathnameLocale && (preferDefaultLocale ? Boolean(cookieLocale?.value) : inDefaultLocalePaths(pathname, defaultLocalePaths))) candidates.push(defaultLocale);
|
|
110
111
|
if (cookieLocale?.value && gt.isValidLocale(cookieLocale?.value)) if (req.cookies.get(resetLocaleCookieName)?.value) {
|
|
111
112
|
candidates.unshift(cookieLocale.value);
|
|
112
113
|
clearResetCookie = true;
|
|
113
114
|
} else candidates.push(cookieLocale.value);
|
|
114
115
|
const referrerLocaleCookie = req.cookies.get(referrerLocaleCookieName);
|
|
115
|
-
if (referrerLocaleCookie?.value && gt.isValidLocale(referrerLocaleCookie.value) && !clearResetCookie) {
|
|
116
|
+
if (referrerLocaleCookie?.value && gt.isValidLocale(referrerLocaleCookie.value) && !clearResetCookie && (!preferDefaultLocale || cookieLocale?.value)) {
|
|
116
117
|
const referrerLocale = referrerLocaleCookie.value;
|
|
117
118
|
if (gt.determineLocale([referrerLocale], approvedLocales)) candidates.push(referrerLocale);
|
|
118
119
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":["applyTrailingSlash","NextResponse","stripTrailingSlashes","getDynamicSegmentType","getSharedPath"],"sources":["../../src/middleware-dir/utils.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\nimport { GTRuntime } from 'generaltranslation/runtime';\nimport { NextURL } from 'next/dist/server/web/next-url';\nimport { parseAcceptLanguage } from 'gt-i18n/internal';\nimport {\n createPathMatcher,\n createPathToSharedPathMap,\n getDynamicSegmentType,\n normalizePathname,\n type PathConfig,\n type PathMatcher,\n} from './createPathMatcher';\nimport { getSharedPath } from './matchPath';\nimport { applyTrailingSlash, stripTrailingSlashes } from './pathname';\n\nexport {\n createPathMatcher,\n createPathToSharedPathMap,\n getSharedPath,\n normalizePathname,\n};\nexport type { PathConfig, PathMatcher };\n\nexport type ResponseConfig = {\n type: 'next' | 'rewrite' | 'redirect';\n responsePath?: string;\n originalUrl: NextURL;\n userLocale: string;\n clearResetCookie: boolean;\n headerList: Headers;\n localeRouting: boolean;\n localeRoutingEnabledCookieName: string;\n resetLocaleCookieName: string;\n localeHeaderName: string;\n};\n\nfunction applyBasePath(responseUrl: URL, originalUrl: NextURL) {\n const { basePath } = originalUrl;\n if (!basePath || responseUrl.origin !== originalUrl.origin) {\n return;\n }\n // Middleware targets are app-relative, even when a route repeats the base path.\n // Preserve the request's slash style when targeting the app root.\n responseUrl.pathname =\n responseUrl.pathname === '/'\n ? applyTrailingSlash(new URL(originalUrl).pathname, basePath)\n : `${basePath}${responseUrl.pathname}`;\n}\n\n/** Creates a middleware response and attaches GT routing state. */\nexport function getResponse({\n type,\n originalUrl,\n responsePath = originalUrl.pathname,\n userLocale,\n clearResetCookie,\n headerList,\n localeRouting,\n localeRoutingEnabledCookieName,\n resetLocaleCookieName,\n localeHeaderName,\n}: ResponseConfig): NextResponse<unknown> {\n // Get Response\n let response;\n if (type === 'next') {\n response = NextResponse.next({\n request: {\n headers: headerList,\n },\n });\n } else {\n // Assign the pathname to preserve the request origin: resolving a path\n // starting with // or /\\ as a URL can turn it into an external destination.\n const responseUrl = new URL(originalUrl);\n responseUrl.pathname = responsePath;\n applyBasePath(responseUrl, originalUrl);\n responseUrl.search = originalUrl.search;\n response =\n type === 'rewrite'\n ? NextResponse.rewrite(responseUrl, {\n request: {\n headers: headerList,\n },\n })\n : NextResponse.redirect(responseUrl);\n }\n\n // Set Headers & Cookies\n response.headers.set(localeHeaderName, userLocale);\n response.cookies.set(\n localeRoutingEnabledCookieName,\n localeRouting.toString()\n );\n // Clear the setLocale reset cookie once it has been processed. The locale\n // cookie must be kept: the client re-reads it on every render, and deleting\n // it here races with concurrent prefetch responses after a locale switch,\n // dropping client components back to the browser's default locale.\n if (clearResetCookie && type !== 'redirect') {\n response.cookies.delete(resetLocaleCookieName);\n }\n return response;\n}\n\n/**\n * Extracts the locale from the given pathname.\n */\nexport function extractLocale(pathname: string): string | null {\n const matches = pathname.match(/^\\/([^/]+)(?:\\/|$)/);\n return matches ? matches[1] : null;\n}\n\n/**\n * Extracts dynamic parameters from a path based on a shared path pattern.\n */\nexport function extractDynamicParams(\n templatePath: string,\n path: string\n): string[] {\n if (!templatePath.includes('[')) return [];\n\n const params: string[] = [];\n const pathSegments = stripTrailingSlashes(path).split('/');\n const sharedSegments = templatePath.split('/');\n\n sharedSegments.forEach((segment, index) => {\n const segmentType = getDynamicSegmentType(segment);\n if (segmentType === 'catch-all' || segmentType === 'optional-catch-all') {\n params.push(pathSegments.slice(index).join('/'));\n } else if (segmentType === 'dynamic') {\n params.push(pathSegments[index]);\n }\n });\n\n return params;\n}\n\n/**\n * Replaces dynamic segments in a path with their actual values.\n */\nexport function replaceDynamicSegments(\n path: string,\n templatePath: string,\n sourceTemplatePath = templatePath\n): string {\n if (!templatePath.includes('[')) {\n return applyTrailingSlash(path, templatePath);\n }\n\n const params = extractDynamicParams(sourceTemplatePath, path);\n let paramIndex = 0;\n const resultSegments: string[] = [];\n\n for (const segment of templatePath.split('/')) {\n const segmentType = getDynamicSegmentType(segment);\n if (!segmentType) {\n resultSegments.push(segment);\n continue;\n }\n\n const param = params[paramIndex++];\n if (segmentType === 'optional-catch-all' && !param) continue;\n if (!param) {\n resultSegments.push(segment);\n continue;\n }\n if (segmentType === 'catch-all' || segmentType === 'optional-catch-all') {\n resultSegments.push(...param.split('/'));\n } else {\n resultSegments.push(param);\n }\n }\n\n return applyTrailingSlash(path, resultSegments.join('/') || '/');\n}\n\n/**\n * Gets the full localized path given a shared path and locale.\n */\nexport function getLocalizedPath(\n sharedPath: string,\n locale: string,\n pathConfig: PathConfig\n): string | undefined {\n const localizedPath = pathConfig[sharedPath];\n let path: string | undefined;\n\n if (typeof localizedPath === 'string') {\n path = `/${locale}${localizedPath}`;\n } else if (typeof localizedPath === 'object') {\n path = localizedPath[locale]\n ? `/${locale}${localizedPath[locale]}`\n : `/${locale}${sharedPath}`;\n }\n\n return path;\n}\n\n/**\n * Checks whether a pathname matches an unprefixed default-locale path.\n */\nfunction inDefaultLocalePaths(\n pathname: string,\n defaultLocalePaths: PathMatcher\n): boolean {\n return (\n getSharedPath(pathname, defaultLocalePaths, undefined)?.sharedPath !==\n undefined\n );\n}\n\n/**\n * Resolves the request locale from its pathname, cookies, and headers.\n */\nexport function getLocaleFromRequest(\n req: NextRequest,\n defaultLocale: string,\n approvedLocales: string[],\n localeRouting: boolean,\n prefixDefaultLocale: boolean,\n defaultLocalePaths: PathMatcher,\n referrerLocaleCookieName: string,\n localeCookieName: string,\n resetLocaleCookieName: string,\n gt: GTRuntime\n): {\n userLocale: string;\n pathnameLocale: string | undefined;\n unstandardizedPathnameLocale: string | null | undefined;\n clearResetCookie: boolean;\n} {\n const headerList = new Headers(req.headers);\n const candidates: string[] = [];\n let clearResetCookie = false;\n const { pathname } = req.nextUrl;\n\n // Check pathname locales\n let pathnameLocale, unstandardizedPathnameLocale;\n if (localeRouting) {\n unstandardizedPathnameLocale = extractLocale(pathname);\n if (\n unstandardizedPathnameLocale &&\n gt.isValidLocale(unstandardizedPathnameLocale)\n ) {\n pathnameLocale = gt.determineLocale(\n unstandardizedPathnameLocale,\n approvedLocales\n );\n if (pathnameLocale) candidates.push(pathnameLocale);\n }\n }\n\n // Check pathname for a customized unprefixed default locale path (e.g. /en-about , /en-dashboard/1/en-custom)\n if (\n localeRouting &&\n !prefixDefaultLocale &&\n !pathnameLocale &&\n inDefaultLocalePaths(pathname, defaultLocalePaths)\n ) {\n candidates.push(defaultLocale); // will override other candidates\n }\n\n // Check cookie locale\n const cookieLocale = req.cookies.get(localeCookieName);\n if (cookieLocale?.value && gt.isValidLocale(cookieLocale?.value)) {\n const resetCookie = req.cookies.get(resetLocaleCookieName);\n if (resetCookie?.value) {\n // Add this back in when we support custom getLocale\n // addedCustomLocale\n // ? candidates.splice(1, 0, cookieLocale.value)\n // : candidates.unshift(cookieLocale.value);\n candidates.unshift(cookieLocale.value);\n clearResetCookie = true;\n } else {\n candidates.push(cookieLocale.value);\n }\n }\n\n // Check referrer locale\n const referrerLocaleCookie = req.cookies.get(referrerLocaleCookieName);\n if (\n referrerLocaleCookie?.value &&\n gt.isValidLocale(referrerLocaleCookie.value) &&\n !clearResetCookie\n ) {\n const referrerLocale = referrerLocaleCookie.value;\n if (gt.determineLocale([referrerLocale], approvedLocales)) {\n candidates.push(referrerLocale);\n }\n }\n\n // Get locales from accept-language header\n if (process.env._GENERALTRANSLATION_IGNORE_BROWSER_LOCALES === 'false') {\n candidates.push(...parseAcceptLanguage(headerList.get('accept-language')));\n }\n\n // Get default locale\n candidates.push(defaultLocale);\n\n // determine userLocale\n const userLocale =\n gt.determineLocale(\n candidates.filter((locale) => gt.isValidLocale(locale)),\n approvedLocales\n ) || defaultLocale;\n\n return {\n userLocale,\n pathnameLocale,\n unstandardizedPathnameLocale,\n clearResetCookie,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","names":["applyTrailingSlash","NextResponse","stripTrailingSlashes","getDynamicSegmentType","getSharedPath"],"sources":["../../src/middleware-dir/utils.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\nimport { GTRuntime } from 'generaltranslation/runtime';\nimport { NextURL } from 'next/dist/server/web/next-url';\nimport { parseAcceptLanguage } from 'gt-i18n/internal';\nimport {\n createPathMatcher,\n createPathToSharedPathMap,\n getDynamicSegmentType,\n normalizePathname,\n type PathConfig,\n type PathMatcher,\n} from './createPathMatcher';\nimport { getSharedPath } from './matchPath';\nimport { applyTrailingSlash, stripTrailingSlashes } from './pathname';\n\nexport {\n createPathMatcher,\n createPathToSharedPathMap,\n getSharedPath,\n normalizePathname,\n};\nexport type { PathConfig, PathMatcher };\n\nexport type ResponseConfig = {\n type: 'next' | 'rewrite' | 'redirect';\n responsePath?: string;\n originalUrl: NextURL;\n userLocale: string;\n clearResetCookie: boolean;\n headerList: Headers;\n localeRouting: boolean;\n localeRoutingEnabledCookieName: string;\n resetLocaleCookieName: string;\n localeHeaderName: string;\n};\n\nfunction applyBasePath(responseUrl: URL, originalUrl: NextURL) {\n const { basePath } = originalUrl;\n if (!basePath || responseUrl.origin !== originalUrl.origin) {\n return;\n }\n // Middleware targets are app-relative, even when a route repeats the base path.\n // Preserve the request's slash style when targeting the app root.\n responseUrl.pathname =\n responseUrl.pathname === '/'\n ? applyTrailingSlash(new URL(originalUrl).pathname, basePath)\n : `${basePath}${responseUrl.pathname}`;\n}\n\n/** Creates a middleware response and attaches GT routing state. */\nexport function getResponse({\n type,\n originalUrl,\n responsePath = originalUrl.pathname,\n userLocale,\n clearResetCookie,\n headerList,\n localeRouting,\n localeRoutingEnabledCookieName,\n resetLocaleCookieName,\n localeHeaderName,\n}: ResponseConfig): NextResponse<unknown> {\n // Get Response\n let response;\n if (type === 'next') {\n response = NextResponse.next({\n request: {\n headers: headerList,\n },\n });\n } else {\n // Assign the pathname to preserve the request origin: resolving a path\n // starting with // or /\\ as a URL can turn it into an external destination.\n const responseUrl = new URL(originalUrl);\n responseUrl.pathname = responsePath;\n applyBasePath(responseUrl, originalUrl);\n responseUrl.search = originalUrl.search;\n response =\n type === 'rewrite'\n ? NextResponse.rewrite(responseUrl, {\n request: {\n headers: headerList,\n },\n })\n : NextResponse.redirect(responseUrl);\n }\n\n // Set Headers & Cookies\n response.headers.set(localeHeaderName, userLocale);\n response.cookies.set(\n localeRoutingEnabledCookieName,\n localeRouting.toString()\n );\n // Clear the setLocale reset cookie once it has been processed. The locale\n // cookie must be kept: the client re-reads it on every render, and deleting\n // it here races with concurrent prefetch responses after a locale switch,\n // dropping client components back to the browser's default locale.\n if (clearResetCookie && type !== 'redirect') {\n response.cookies.delete(resetLocaleCookieName);\n }\n return response;\n}\n\n/**\n * Extracts the locale from the given pathname.\n */\nexport function extractLocale(pathname: string): string | null {\n const matches = pathname.match(/^\\/([^/]+)(?:\\/|$)/);\n return matches ? matches[1] : null;\n}\n\n/**\n * Extracts dynamic parameters from a path based on a shared path pattern.\n */\nexport function extractDynamicParams(\n templatePath: string,\n path: string\n): string[] {\n if (!templatePath.includes('[')) return [];\n\n const params: string[] = [];\n const pathSegments = stripTrailingSlashes(path).split('/');\n const sharedSegments = templatePath.split('/');\n\n sharedSegments.forEach((segment, index) => {\n const segmentType = getDynamicSegmentType(segment);\n if (segmentType === 'catch-all' || segmentType === 'optional-catch-all') {\n params.push(pathSegments.slice(index).join('/'));\n } else if (segmentType === 'dynamic') {\n params.push(pathSegments[index]);\n }\n });\n\n return params;\n}\n\n/**\n * Replaces dynamic segments in a path with their actual values.\n */\nexport function replaceDynamicSegments(\n path: string,\n templatePath: string,\n sourceTemplatePath = templatePath\n): string {\n if (!templatePath.includes('[')) {\n return applyTrailingSlash(path, templatePath);\n }\n\n const params = extractDynamicParams(sourceTemplatePath, path);\n let paramIndex = 0;\n const resultSegments: string[] = [];\n\n for (const segment of templatePath.split('/')) {\n const segmentType = getDynamicSegmentType(segment);\n if (!segmentType) {\n resultSegments.push(segment);\n continue;\n }\n\n const param = params[paramIndex++];\n if (segmentType === 'optional-catch-all' && !param) continue;\n if (!param) {\n resultSegments.push(segment);\n continue;\n }\n if (segmentType === 'catch-all' || segmentType === 'optional-catch-all') {\n resultSegments.push(...param.split('/'));\n } else {\n resultSegments.push(param);\n }\n }\n\n return applyTrailingSlash(path, resultSegments.join('/') || '/');\n}\n\n/**\n * Gets the full localized path given a shared path and locale.\n */\nexport function getLocalizedPath(\n sharedPath: string,\n locale: string,\n pathConfig: PathConfig\n): string | undefined {\n const localizedPath = pathConfig[sharedPath];\n let path: string | undefined;\n\n if (typeof localizedPath === 'string') {\n path = `/${locale}${localizedPath}`;\n } else if (typeof localizedPath === 'object') {\n path = localizedPath[locale]\n ? `/${locale}${localizedPath[locale]}`\n : `/${locale}${sharedPath}`;\n }\n\n return path;\n}\n\n/**\n * Checks whether a pathname matches an unprefixed default-locale path.\n */\nfunction inDefaultLocalePaths(\n pathname: string,\n defaultLocalePaths: PathMatcher\n): boolean {\n return (\n getSharedPath(pathname, defaultLocalePaths, undefined)?.sharedPath !==\n undefined\n );\n}\n\n/**\n * Resolves the request locale from its pathname, cookies, and headers.\n */\nexport function getLocaleFromRequest(\n req: NextRequest,\n defaultLocale: string,\n approvedLocales: string[],\n localeRouting: boolean,\n prefixDefaultLocale: boolean,\n defaultLocalePaths: PathMatcher,\n referrerLocaleCookieName: string,\n localeCookieName: string,\n resetLocaleCookieName: string,\n gt: GTRuntime,\n enableSmartRouting = true\n): {\n userLocale: string;\n pathnameLocale: string | undefined;\n unstandardizedPathnameLocale: string | null | undefined;\n clearResetCookie: boolean;\n} {\n const headerList = new Headers(req.headers);\n const candidates: string[] = [];\n let clearResetCookie = false;\n const { pathname } = req.nextUrl;\n\n // Check pathname locales\n let pathnameLocale, unstandardizedPathnameLocale;\n if (localeRouting) {\n unstandardizedPathnameLocale = extractLocale(pathname);\n if (\n unstandardizedPathnameLocale &&\n gt.isValidLocale(unstandardizedPathnameLocale)\n ) {\n pathnameLocale = gt.determineLocale(\n unstandardizedPathnameLocale,\n approvedLocales\n );\n if (pathnameLocale) candidates.push(pathnameLocale);\n }\n }\n\n const cookieLocale = req.cookies.get(localeCookieName);\n const preferDefaultLocale =\n !enableSmartRouting && localeRouting && !prefixDefaultLocale;\n // With smart routing disabled, returning visitors choose the default locale\n // by visiting an unprefixed URL.\n // First visits still negotiate their language; explicit resets override below.\n // Check pathname for a customized unprefixed default locale path (e.g. /en-about , /en-dashboard/1/en-custom)\n if (\n localeRouting &&\n !prefixDefaultLocale &&\n !pathnameLocale &&\n (preferDefaultLocale\n ? Boolean(cookieLocale?.value) // skip this condition when preferDefaultLocale is true and first request (no cookieLocale)\n : inDefaultLocalePaths(pathname, defaultLocalePaths))\n ) {\n candidates.push(defaultLocale); // will override other candidates\n }\n\n // Check cookie locale\n if (cookieLocale?.value && gt.isValidLocale(cookieLocale?.value)) {\n const resetCookie = req.cookies.get(resetLocaleCookieName);\n if (resetCookie?.value) {\n // Add this back in when we support custom getLocale\n // addedCustomLocale\n // ? candidates.splice(1, 0, cookieLocale.value)\n // : candidates.unshift(cookieLocale.value);\n candidates.unshift(cookieLocale.value);\n clearResetCookie = true;\n } else {\n candidates.push(cookieLocale.value);\n }\n }\n\n // Check referrer locale\n const referrerLocaleCookie = req.cookies.get(referrerLocaleCookieName);\n if (\n referrerLocaleCookie?.value &&\n gt.isValidLocale(referrerLocaleCookie.value) &&\n !clearResetCookie &&\n (!preferDefaultLocale || cookieLocale?.value) // skip this condition when preferDefaultLocale is true and first request (no cookieLocale)\n ) {\n const referrerLocale = referrerLocaleCookie.value;\n if (gt.determineLocale([referrerLocale], approvedLocales)) {\n candidates.push(referrerLocale);\n }\n }\n\n // Get locales from accept-language header\n if (process.env._GENERALTRANSLATION_IGNORE_BROWSER_LOCALES === 'false') {\n candidates.push(...parseAcceptLanguage(headerList.get('accept-language')));\n }\n\n // Get default locale\n candidates.push(defaultLocale);\n\n // determine userLocale\n const userLocale =\n gt.determineLocale(\n candidates.filter((locale) => gt.isValidLocale(locale)),\n approvedLocales\n ) || defaultLocale;\n\n return {\n userLocale,\n pathnameLocale,\n unstandardizedPathnameLocale,\n clearResetCookie,\n };\n}\n"],"mappings":"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"}
|
|
@@ -90,7 +90,7 @@ function inDefaultLocalePaths(pathname, defaultLocalePaths) {
|
|
|
90
90
|
/**
|
|
91
91
|
* Resolves the request locale from its pathname, cookies, and headers.
|
|
92
92
|
*/
|
|
93
|
-
function getLocaleFromRequest(req, defaultLocale, approvedLocales, localeRouting, prefixDefaultLocale, defaultLocalePaths, referrerLocaleCookieName, localeCookieName, resetLocaleCookieName, gt) {
|
|
93
|
+
function getLocaleFromRequest(req, defaultLocale, approvedLocales, localeRouting, prefixDefaultLocale, defaultLocalePaths, referrerLocaleCookieName, localeCookieName, resetLocaleCookieName, gt, enableSmartRouting = true) {
|
|
94
94
|
const headerList = new Headers(req.headers);
|
|
95
95
|
const candidates = [];
|
|
96
96
|
let clearResetCookie = false;
|
|
@@ -103,14 +103,15 @@ function getLocaleFromRequest(req, defaultLocale, approvedLocales, localeRouting
|
|
|
103
103
|
if (pathnameLocale) candidates.push(pathnameLocale);
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
-
if (localeRouting && !prefixDefaultLocale && !pathnameLocale && inDefaultLocalePaths(pathname, defaultLocalePaths)) candidates.push(defaultLocale);
|
|
107
106
|
const cookieLocale = req.cookies.get(localeCookieName);
|
|
107
|
+
const preferDefaultLocale = !enableSmartRouting && localeRouting && !prefixDefaultLocale;
|
|
108
|
+
if (localeRouting && !prefixDefaultLocale && !pathnameLocale && (preferDefaultLocale ? Boolean(cookieLocale?.value) : inDefaultLocalePaths(pathname, defaultLocalePaths))) candidates.push(defaultLocale);
|
|
108
109
|
if (cookieLocale?.value && gt.isValidLocale(cookieLocale?.value)) if (req.cookies.get(resetLocaleCookieName)?.value) {
|
|
109
110
|
candidates.unshift(cookieLocale.value);
|
|
110
111
|
clearResetCookie = true;
|
|
111
112
|
} else candidates.push(cookieLocale.value);
|
|
112
113
|
const referrerLocaleCookie = req.cookies.get(referrerLocaleCookieName);
|
|
113
|
-
if (referrerLocaleCookie?.value && gt.isValidLocale(referrerLocaleCookie.value) && !clearResetCookie) {
|
|
114
|
+
if (referrerLocaleCookie?.value && gt.isValidLocale(referrerLocaleCookie.value) && !clearResetCookie && (!preferDefaultLocale || cookieLocale?.value)) {
|
|
114
115
|
const referrerLocale = referrerLocaleCookie.value;
|
|
115
116
|
if (gt.determineLocale([referrerLocale], approvedLocales)) candidates.push(referrerLocale);
|
|
116
117
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.mjs","names":[],"sources":["../../src/middleware-dir/utils.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\nimport { GTRuntime } from 'generaltranslation/runtime';\nimport { NextURL } from 'next/dist/server/web/next-url';\nimport { parseAcceptLanguage } from 'gt-i18n/internal';\nimport {\n createPathMatcher,\n createPathToSharedPathMap,\n getDynamicSegmentType,\n normalizePathname,\n type PathConfig,\n type PathMatcher,\n} from './createPathMatcher';\nimport { getSharedPath } from './matchPath';\nimport { applyTrailingSlash, stripTrailingSlashes } from './pathname';\n\nexport {\n createPathMatcher,\n createPathToSharedPathMap,\n getSharedPath,\n normalizePathname,\n};\nexport type { PathConfig, PathMatcher };\n\nexport type ResponseConfig = {\n type: 'next' | 'rewrite' | 'redirect';\n responsePath?: string;\n originalUrl: NextURL;\n userLocale: string;\n clearResetCookie: boolean;\n headerList: Headers;\n localeRouting: boolean;\n localeRoutingEnabledCookieName: string;\n resetLocaleCookieName: string;\n localeHeaderName: string;\n};\n\nfunction applyBasePath(responseUrl: URL, originalUrl: NextURL) {\n const { basePath } = originalUrl;\n if (!basePath || responseUrl.origin !== originalUrl.origin) {\n return;\n }\n // Middleware targets are app-relative, even when a route repeats the base path.\n // Preserve the request's slash style when targeting the app root.\n responseUrl.pathname =\n responseUrl.pathname === '/'\n ? applyTrailingSlash(new URL(originalUrl).pathname, basePath)\n : `${basePath}${responseUrl.pathname}`;\n}\n\n/** Creates a middleware response and attaches GT routing state. */\nexport function getResponse({\n type,\n originalUrl,\n responsePath = originalUrl.pathname,\n userLocale,\n clearResetCookie,\n headerList,\n localeRouting,\n localeRoutingEnabledCookieName,\n resetLocaleCookieName,\n localeHeaderName,\n}: ResponseConfig): NextResponse<unknown> {\n // Get Response\n let response;\n if (type === 'next') {\n response = NextResponse.next({\n request: {\n headers: headerList,\n },\n });\n } else {\n // Assign the pathname to preserve the request origin: resolving a path\n // starting with // or /\\ as a URL can turn it into an external destination.\n const responseUrl = new URL(originalUrl);\n responseUrl.pathname = responsePath;\n applyBasePath(responseUrl, originalUrl);\n responseUrl.search = originalUrl.search;\n response =\n type === 'rewrite'\n ? NextResponse.rewrite(responseUrl, {\n request: {\n headers: headerList,\n },\n })\n : NextResponse.redirect(responseUrl);\n }\n\n // Set Headers & Cookies\n response.headers.set(localeHeaderName, userLocale);\n response.cookies.set(\n localeRoutingEnabledCookieName,\n localeRouting.toString()\n );\n // Clear the setLocale reset cookie once it has been processed. The locale\n // cookie must be kept: the client re-reads it on every render, and deleting\n // it here races with concurrent prefetch responses after a locale switch,\n // dropping client components back to the browser's default locale.\n if (clearResetCookie && type !== 'redirect') {\n response.cookies.delete(resetLocaleCookieName);\n }\n return response;\n}\n\n/**\n * Extracts the locale from the given pathname.\n */\nexport function extractLocale(pathname: string): string | null {\n const matches = pathname.match(/^\\/([^/]+)(?:\\/|$)/);\n return matches ? matches[1] : null;\n}\n\n/**\n * Extracts dynamic parameters from a path based on a shared path pattern.\n */\nexport function extractDynamicParams(\n templatePath: string,\n path: string\n): string[] {\n if (!templatePath.includes('[')) return [];\n\n const params: string[] = [];\n const pathSegments = stripTrailingSlashes(path).split('/');\n const sharedSegments = templatePath.split('/');\n\n sharedSegments.forEach((segment, index) => {\n const segmentType = getDynamicSegmentType(segment);\n if (segmentType === 'catch-all' || segmentType === 'optional-catch-all') {\n params.push(pathSegments.slice(index).join('/'));\n } else if (segmentType === 'dynamic') {\n params.push(pathSegments[index]);\n }\n });\n\n return params;\n}\n\n/**\n * Replaces dynamic segments in a path with their actual values.\n */\nexport function replaceDynamicSegments(\n path: string,\n templatePath: string,\n sourceTemplatePath = templatePath\n): string {\n if (!templatePath.includes('[')) {\n return applyTrailingSlash(path, templatePath);\n }\n\n const params = extractDynamicParams(sourceTemplatePath, path);\n let paramIndex = 0;\n const resultSegments: string[] = [];\n\n for (const segment of templatePath.split('/')) {\n const segmentType = getDynamicSegmentType(segment);\n if (!segmentType) {\n resultSegments.push(segment);\n continue;\n }\n\n const param = params[paramIndex++];\n if (segmentType === 'optional-catch-all' && !param) continue;\n if (!param) {\n resultSegments.push(segment);\n continue;\n }\n if (segmentType === 'catch-all' || segmentType === 'optional-catch-all') {\n resultSegments.push(...param.split('/'));\n } else {\n resultSegments.push(param);\n }\n }\n\n return applyTrailingSlash(path, resultSegments.join('/') || '/');\n}\n\n/**\n * Gets the full localized path given a shared path and locale.\n */\nexport function getLocalizedPath(\n sharedPath: string,\n locale: string,\n pathConfig: PathConfig\n): string | undefined {\n const localizedPath = pathConfig[sharedPath];\n let path: string | undefined;\n\n if (typeof localizedPath === 'string') {\n path = `/${locale}${localizedPath}`;\n } else if (typeof localizedPath === 'object') {\n path = localizedPath[locale]\n ? `/${locale}${localizedPath[locale]}`\n : `/${locale}${sharedPath}`;\n }\n\n return path;\n}\n\n/**\n * Checks whether a pathname matches an unprefixed default-locale path.\n */\nfunction inDefaultLocalePaths(\n pathname: string,\n defaultLocalePaths: PathMatcher\n): boolean {\n return (\n getSharedPath(pathname, defaultLocalePaths, undefined)?.sharedPath !==\n undefined\n );\n}\n\n/**\n * Resolves the request locale from its pathname, cookies, and headers.\n */\nexport function getLocaleFromRequest(\n req: NextRequest,\n defaultLocale: string,\n approvedLocales: string[],\n localeRouting: boolean,\n prefixDefaultLocale: boolean,\n defaultLocalePaths: PathMatcher,\n referrerLocaleCookieName: string,\n localeCookieName: string,\n resetLocaleCookieName: string,\n gt: GTRuntime\n): {\n userLocale: string;\n pathnameLocale: string | undefined;\n unstandardizedPathnameLocale: string | null | undefined;\n clearResetCookie: boolean;\n} {\n const headerList = new Headers(req.headers);\n const candidates: string[] = [];\n let clearResetCookie = false;\n const { pathname } = req.nextUrl;\n\n // Check pathname locales\n let pathnameLocale, unstandardizedPathnameLocale;\n if (localeRouting) {\n unstandardizedPathnameLocale = extractLocale(pathname);\n if (\n unstandardizedPathnameLocale &&\n gt.isValidLocale(unstandardizedPathnameLocale)\n ) {\n pathnameLocale = gt.determineLocale(\n unstandardizedPathnameLocale,\n approvedLocales\n );\n if (pathnameLocale) candidates.push(pathnameLocale);\n }\n }\n\n // Check pathname for a customized unprefixed default locale path (e.g. /en-about , /en-dashboard/1/en-custom)\n if (\n localeRouting &&\n !prefixDefaultLocale &&\n !pathnameLocale &&\n inDefaultLocalePaths(pathname, defaultLocalePaths)\n ) {\n candidates.push(defaultLocale); // will override other candidates\n }\n\n // Check cookie locale\n const cookieLocale = req.cookies.get(localeCookieName);\n if (cookieLocale?.value && gt.isValidLocale(cookieLocale?.value)) {\n const resetCookie = req.cookies.get(resetLocaleCookieName);\n if (resetCookie?.value) {\n // Add this back in when we support custom getLocale\n // addedCustomLocale\n // ? candidates.splice(1, 0, cookieLocale.value)\n // : candidates.unshift(cookieLocale.value);\n candidates.unshift(cookieLocale.value);\n clearResetCookie = true;\n } else {\n candidates.push(cookieLocale.value);\n }\n }\n\n // Check referrer locale\n const referrerLocaleCookie = req.cookies.get(referrerLocaleCookieName);\n if (\n referrerLocaleCookie?.value &&\n gt.isValidLocale(referrerLocaleCookie.value) &&\n !clearResetCookie\n ) {\n const referrerLocale = referrerLocaleCookie.value;\n if (gt.determineLocale([referrerLocale], approvedLocales)) {\n candidates.push(referrerLocale);\n }\n }\n\n // Get locales from accept-language header\n if (process.env._GENERALTRANSLATION_IGNORE_BROWSER_LOCALES === 'false') {\n candidates.push(...parseAcceptLanguage(headerList.get('accept-language')));\n }\n\n // Get default locale\n candidates.push(defaultLocale);\n\n // determine userLocale\n const userLocale =\n gt.determineLocale(\n candidates.filter((locale) => gt.isValidLocale(locale)),\n approvedLocales\n ) || defaultLocale;\n\n return {\n userLocale,\n pathnameLocale,\n unstandardizedPathnameLocale,\n clearResetCookie,\n };\n}\n"],"mappings":";;;;;;AAoCA,SAAS,cAAc,aAAkB,aAAsB;CAC7D,MAAM,EAAE,aAAa;AACrB,KAAI,CAAC,YAAY,YAAY,WAAW,YAAY,OAClD;AAIF,aAAY,WACV,YAAY,aAAa,MACrB,mBAAmB,IAAI,IAAI,YAAY,CAAC,UAAU,SAAS,GAC3D,GAAG,WAAW,YAAY;;;AAIlC,SAAgB,YAAY,EAC1B,MACA,aACA,eAAe,YAAY,UAC3B,YACA,kBACA,YACA,eACA,gCACA,uBACA,oBACwC;CAExC,IAAI;AACJ,KAAI,SAAS,OACX,YAAW,aAAa,KAAK,EAC3B,SAAS,EACP,SAAS,YACV,EACF,CAAC;MACG;EAGL,MAAM,cAAc,IAAI,IAAI,YAAY;AACxC,cAAY,WAAW;AACvB,gBAAc,aAAa,YAAY;AACvC,cAAY,SAAS,YAAY;AACjC,aACE,SAAS,YACL,aAAa,QAAQ,aAAa,EAChC,SAAS,EACP,SAAS,YACV,EACF,CAAC,GACF,aAAa,SAAS,YAAY;;AAI1C,UAAS,QAAQ,IAAI,kBAAkB,WAAW;AAClD,UAAS,QAAQ,IACf,gCACA,cAAc,UAAU,CACzB;AAKD,KAAI,oBAAoB,SAAS,WAC/B,UAAS,QAAQ,OAAO,sBAAsB;AAEhD,QAAO;;;;;AAMT,SAAgB,cAAc,UAAiC;CAC7D,MAAM,UAAU,SAAS,MAAM,qBAAqB;AACpD,QAAO,UAAU,QAAQ,KAAK;;;;;AAMhC,SAAgB,qBACd,cACA,MACU;AACV,KAAI,CAAC,aAAa,SAAS,IAAI,CAAE,QAAO,EAAE;CAE1C,MAAM,SAAmB,EAAE;CAC3B,MAAM,eAAe,qBAAqB,KAAK,CAAC,MAAM,IAAI;AACnC,cAAa,MAAM,IAE5B,CAAC,SAAS,SAAS,UAAU;EACzC,MAAM,cAAc,sBAAsB,QAAQ;AAClD,MAAI,gBAAgB,eAAe,gBAAgB,qBACjD,QAAO,KAAK,aAAa,MAAM,MAAM,CAAC,KAAK,IAAI,CAAC;WACvC,gBAAgB,UACzB,QAAO,KAAK,aAAa,OAAO;GAElC;AAEF,QAAO;;;;;AAMT,SAAgB,uBACd,MACA,cACA,qBAAqB,cACb;AACR,KAAI,CAAC,aAAa,SAAS,IAAI,CAC7B,QAAO,mBAAmB,MAAM,aAAa;CAG/C,MAAM,SAAS,qBAAqB,oBAAoB,KAAK;CAC7D,IAAI,aAAa;CACjB,MAAM,iBAA2B,EAAE;AAEnC,MAAK,MAAM,WAAW,aAAa,MAAM,IAAI,EAAE;EAC7C,MAAM,cAAc,sBAAsB,QAAQ;AAClD,MAAI,CAAC,aAAa;AAChB,kBAAe,KAAK,QAAQ;AAC5B;;EAGF,MAAM,QAAQ,OAAO;AACrB,MAAI,gBAAgB,wBAAwB,CAAC,MAAO;AACpD,MAAI,CAAC,OAAO;AACV,kBAAe,KAAK,QAAQ;AAC5B;;AAEF,MAAI,gBAAgB,eAAe,gBAAgB,qBACjD,gBAAe,KAAK,GAAG,MAAM,MAAM,IAAI,CAAC;MAExC,gBAAe,KAAK,MAAM;;AAI9B,QAAO,mBAAmB,MAAM,eAAe,KAAK,IAAI,IAAI,IAAI;;;;;AAMlE,SAAgB,iBACd,YACA,QACA,YACoB;CACpB,MAAM,gBAAgB,WAAW;CACjC,IAAI;AAEJ,KAAI,OAAO,kBAAkB,SAC3B,QAAO,IAAI,SAAS;UACX,OAAO,kBAAkB,SAClC,QAAO,cAAc,UACjB,IAAI,SAAS,cAAc,YAC3B,IAAI,SAAS;AAGnB,QAAO;;;;;AAMT,SAAS,qBACP,UACA,oBACS;AACT,QACE,cAAc,UAAU,oBAAoB,KAAA,EAAU,EAAE,eACxD,KAAA;;;;;AAOJ,SAAgB,qBACd,KACA,eACA,iBACA,eACA,qBACA,oBACA,0BACA,kBACA,uBACA,IAMA;CACA,MAAM,aAAa,IAAI,QAAQ,IAAI,QAAQ;CAC3C,MAAM,aAAuB,EAAE;CAC/B,IAAI,mBAAmB;CACvB,MAAM,EAAE,aAAa,IAAI;CAGzB,IAAI,gBAAgB;AACpB,KAAI,eAAe;AACjB,iCAA+B,cAAc,SAAS;AACtD,MACE,gCACA,GAAG,cAAc,6BAA6B,EAC9C;AACA,oBAAiB,GAAG,gBAClB,8BACA,gBACD;AACD,OAAI,eAAgB,YAAW,KAAK,eAAe;;;AAKvD,KACE,iBACA,CAAC,uBACD,CAAC,kBACD,qBAAqB,UAAU,mBAAmB,CAElD,YAAW,KAAK,cAAc;CAIhC,MAAM,eAAe,IAAI,QAAQ,IAAI,iBAAiB;AACtD,KAAI,cAAc,SAAS,GAAG,cAAc,cAAc,MAAM,CAE9D,KADoB,IAAI,QAAQ,IAAI,sBACrB,EAAE,OAAO;AAKtB,aAAW,QAAQ,aAAa,MAAM;AACtC,qBAAmB;OAEnB,YAAW,KAAK,aAAa,MAAM;CAKvC,MAAM,uBAAuB,IAAI,QAAQ,IAAI,yBAAyB;AACtE,KACE,sBAAsB,SACtB,GAAG,cAAc,qBAAqB,MAAM,IAC5C,CAAC,kBACD;EACA,MAAM,iBAAiB,qBAAqB;AAC5C,MAAI,GAAG,gBAAgB,CAAC,eAAe,EAAE,gBAAgB,CACvD,YAAW,KAAK,eAAe;;AAKnC,KAAI,QAAQ,IAAI,+CAA+C,QAC7D,YAAW,KAAK,GAAG,oBAAoB,WAAW,IAAI,kBAAkB,CAAC,CAAC;AAI5E,YAAW,KAAK,cAAc;AAS9B,QAAO;EACL,YANA,GAAG,gBACD,WAAW,QAAQ,WAAW,GAAG,cAAc,OAAO,CAAC,EACvD,gBACD,IAAI;EAIL;EACA;EACA;EACD"}
|
|
1
|
+
{"version":3,"file":"utils.mjs","names":[],"sources":["../../src/middleware-dir/utils.ts"],"sourcesContent":["import { NextRequest, NextResponse } from 'next/server';\nimport { GTRuntime } from 'generaltranslation/runtime';\nimport { NextURL } from 'next/dist/server/web/next-url';\nimport { parseAcceptLanguage } from 'gt-i18n/internal';\nimport {\n createPathMatcher,\n createPathToSharedPathMap,\n getDynamicSegmentType,\n normalizePathname,\n type PathConfig,\n type PathMatcher,\n} from './createPathMatcher';\nimport { getSharedPath } from './matchPath';\nimport { applyTrailingSlash, stripTrailingSlashes } from './pathname';\n\nexport {\n createPathMatcher,\n createPathToSharedPathMap,\n getSharedPath,\n normalizePathname,\n};\nexport type { PathConfig, PathMatcher };\n\nexport type ResponseConfig = {\n type: 'next' | 'rewrite' | 'redirect';\n responsePath?: string;\n originalUrl: NextURL;\n userLocale: string;\n clearResetCookie: boolean;\n headerList: Headers;\n localeRouting: boolean;\n localeRoutingEnabledCookieName: string;\n resetLocaleCookieName: string;\n localeHeaderName: string;\n};\n\nfunction applyBasePath(responseUrl: URL, originalUrl: NextURL) {\n const { basePath } = originalUrl;\n if (!basePath || responseUrl.origin !== originalUrl.origin) {\n return;\n }\n // Middleware targets are app-relative, even when a route repeats the base path.\n // Preserve the request's slash style when targeting the app root.\n responseUrl.pathname =\n responseUrl.pathname === '/'\n ? applyTrailingSlash(new URL(originalUrl).pathname, basePath)\n : `${basePath}${responseUrl.pathname}`;\n}\n\n/** Creates a middleware response and attaches GT routing state. */\nexport function getResponse({\n type,\n originalUrl,\n responsePath = originalUrl.pathname,\n userLocale,\n clearResetCookie,\n headerList,\n localeRouting,\n localeRoutingEnabledCookieName,\n resetLocaleCookieName,\n localeHeaderName,\n}: ResponseConfig): NextResponse<unknown> {\n // Get Response\n let response;\n if (type === 'next') {\n response = NextResponse.next({\n request: {\n headers: headerList,\n },\n });\n } else {\n // Assign the pathname to preserve the request origin: resolving a path\n // starting with // or /\\ as a URL can turn it into an external destination.\n const responseUrl = new URL(originalUrl);\n responseUrl.pathname = responsePath;\n applyBasePath(responseUrl, originalUrl);\n responseUrl.search = originalUrl.search;\n response =\n type === 'rewrite'\n ? NextResponse.rewrite(responseUrl, {\n request: {\n headers: headerList,\n },\n })\n : NextResponse.redirect(responseUrl);\n }\n\n // Set Headers & Cookies\n response.headers.set(localeHeaderName, userLocale);\n response.cookies.set(\n localeRoutingEnabledCookieName,\n localeRouting.toString()\n );\n // Clear the setLocale reset cookie once it has been processed. The locale\n // cookie must be kept: the client re-reads it on every render, and deleting\n // it here races with concurrent prefetch responses after a locale switch,\n // dropping client components back to the browser's default locale.\n if (clearResetCookie && type !== 'redirect') {\n response.cookies.delete(resetLocaleCookieName);\n }\n return response;\n}\n\n/**\n * Extracts the locale from the given pathname.\n */\nexport function extractLocale(pathname: string): string | null {\n const matches = pathname.match(/^\\/([^/]+)(?:\\/|$)/);\n return matches ? matches[1] : null;\n}\n\n/**\n * Extracts dynamic parameters from a path based on a shared path pattern.\n */\nexport function extractDynamicParams(\n templatePath: string,\n path: string\n): string[] {\n if (!templatePath.includes('[')) return [];\n\n const params: string[] = [];\n const pathSegments = stripTrailingSlashes(path).split('/');\n const sharedSegments = templatePath.split('/');\n\n sharedSegments.forEach((segment, index) => {\n const segmentType = getDynamicSegmentType(segment);\n if (segmentType === 'catch-all' || segmentType === 'optional-catch-all') {\n params.push(pathSegments.slice(index).join('/'));\n } else if (segmentType === 'dynamic') {\n params.push(pathSegments[index]);\n }\n });\n\n return params;\n}\n\n/**\n * Replaces dynamic segments in a path with their actual values.\n */\nexport function replaceDynamicSegments(\n path: string,\n templatePath: string,\n sourceTemplatePath = templatePath\n): string {\n if (!templatePath.includes('[')) {\n return applyTrailingSlash(path, templatePath);\n }\n\n const params = extractDynamicParams(sourceTemplatePath, path);\n let paramIndex = 0;\n const resultSegments: string[] = [];\n\n for (const segment of templatePath.split('/')) {\n const segmentType = getDynamicSegmentType(segment);\n if (!segmentType) {\n resultSegments.push(segment);\n continue;\n }\n\n const param = params[paramIndex++];\n if (segmentType === 'optional-catch-all' && !param) continue;\n if (!param) {\n resultSegments.push(segment);\n continue;\n }\n if (segmentType === 'catch-all' || segmentType === 'optional-catch-all') {\n resultSegments.push(...param.split('/'));\n } else {\n resultSegments.push(param);\n }\n }\n\n return applyTrailingSlash(path, resultSegments.join('/') || '/');\n}\n\n/**\n * Gets the full localized path given a shared path and locale.\n */\nexport function getLocalizedPath(\n sharedPath: string,\n locale: string,\n pathConfig: PathConfig\n): string | undefined {\n const localizedPath = pathConfig[sharedPath];\n let path: string | undefined;\n\n if (typeof localizedPath === 'string') {\n path = `/${locale}${localizedPath}`;\n } else if (typeof localizedPath === 'object') {\n path = localizedPath[locale]\n ? `/${locale}${localizedPath[locale]}`\n : `/${locale}${sharedPath}`;\n }\n\n return path;\n}\n\n/**\n * Checks whether a pathname matches an unprefixed default-locale path.\n */\nfunction inDefaultLocalePaths(\n pathname: string,\n defaultLocalePaths: PathMatcher\n): boolean {\n return (\n getSharedPath(pathname, defaultLocalePaths, undefined)?.sharedPath !==\n undefined\n );\n}\n\n/**\n * Resolves the request locale from its pathname, cookies, and headers.\n */\nexport function getLocaleFromRequest(\n req: NextRequest,\n defaultLocale: string,\n approvedLocales: string[],\n localeRouting: boolean,\n prefixDefaultLocale: boolean,\n defaultLocalePaths: PathMatcher,\n referrerLocaleCookieName: string,\n localeCookieName: string,\n resetLocaleCookieName: string,\n gt: GTRuntime,\n enableSmartRouting = true\n): {\n userLocale: string;\n pathnameLocale: string | undefined;\n unstandardizedPathnameLocale: string | null | undefined;\n clearResetCookie: boolean;\n} {\n const headerList = new Headers(req.headers);\n const candidates: string[] = [];\n let clearResetCookie = false;\n const { pathname } = req.nextUrl;\n\n // Check pathname locales\n let pathnameLocale, unstandardizedPathnameLocale;\n if (localeRouting) {\n unstandardizedPathnameLocale = extractLocale(pathname);\n if (\n unstandardizedPathnameLocale &&\n gt.isValidLocale(unstandardizedPathnameLocale)\n ) {\n pathnameLocale = gt.determineLocale(\n unstandardizedPathnameLocale,\n approvedLocales\n );\n if (pathnameLocale) candidates.push(pathnameLocale);\n }\n }\n\n const cookieLocale = req.cookies.get(localeCookieName);\n const preferDefaultLocale =\n !enableSmartRouting && localeRouting && !prefixDefaultLocale;\n // With smart routing disabled, returning visitors choose the default locale\n // by visiting an unprefixed URL.\n // First visits still negotiate their language; explicit resets override below.\n // Check pathname for a customized unprefixed default locale path (e.g. /en-about , /en-dashboard/1/en-custom)\n if (\n localeRouting &&\n !prefixDefaultLocale &&\n !pathnameLocale &&\n (preferDefaultLocale\n ? Boolean(cookieLocale?.value) // skip this condition when preferDefaultLocale is true and first request (no cookieLocale)\n : inDefaultLocalePaths(pathname, defaultLocalePaths))\n ) {\n candidates.push(defaultLocale); // will override other candidates\n }\n\n // Check cookie locale\n if (cookieLocale?.value && gt.isValidLocale(cookieLocale?.value)) {\n const resetCookie = req.cookies.get(resetLocaleCookieName);\n if (resetCookie?.value) {\n // Add this back in when we support custom getLocale\n // addedCustomLocale\n // ? candidates.splice(1, 0, cookieLocale.value)\n // : candidates.unshift(cookieLocale.value);\n candidates.unshift(cookieLocale.value);\n clearResetCookie = true;\n } else {\n candidates.push(cookieLocale.value);\n }\n }\n\n // Check referrer locale\n const referrerLocaleCookie = req.cookies.get(referrerLocaleCookieName);\n if (\n referrerLocaleCookie?.value &&\n gt.isValidLocale(referrerLocaleCookie.value) &&\n !clearResetCookie &&\n (!preferDefaultLocale || cookieLocale?.value) // skip this condition when preferDefaultLocale is true and first request (no cookieLocale)\n ) {\n const referrerLocale = referrerLocaleCookie.value;\n if (gt.determineLocale([referrerLocale], approvedLocales)) {\n candidates.push(referrerLocale);\n }\n }\n\n // Get locales from accept-language header\n if (process.env._GENERALTRANSLATION_IGNORE_BROWSER_LOCALES === 'false') {\n candidates.push(...parseAcceptLanguage(headerList.get('accept-language')));\n }\n\n // Get default locale\n candidates.push(defaultLocale);\n\n // determine userLocale\n const userLocale =\n gt.determineLocale(\n candidates.filter((locale) => gt.isValidLocale(locale)),\n approvedLocales\n ) || defaultLocale;\n\n return {\n userLocale,\n pathnameLocale,\n unstandardizedPathnameLocale,\n clearResetCookie,\n };\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gt-next",
|
|
3
|
-
"version": "11.
|
|
3
|
+
"version": "11.4.0",
|
|
4
4
|
"description": "A Next.js library for automatic internationalization.",
|
|
5
5
|
"main": "dist/index.server.js",
|
|
6
6
|
"peerDependencies": {
|
|
@@ -29,9 +29,9 @@
|
|
|
29
29
|
],
|
|
30
30
|
"dependencies": {
|
|
31
31
|
"@generaltranslation/format": "0.1.9",
|
|
32
|
-
"@generaltranslation/react-core": "11.
|
|
32
|
+
"@generaltranslation/react-core": "11.4.0",
|
|
33
33
|
"generaltranslation": "9.4.1",
|
|
34
|
-
"gt-react": "11.
|
|
34
|
+
"gt-react": "11.4.0",
|
|
35
35
|
"gt-i18n": "1.0.28"
|
|
36
36
|
},
|
|
37
37
|
"repository": {
|