next-intlayer
Version:
Simplify internationalization i18n in Next.js with context providers, hooks, locale detection, and multilingual content integration.
244 lines (242 loc) • 11.4 kB
JavaScript
const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
const require_proxy_localeDetector = require('./localeDetector.cjs');
let __intlayer_config_built = require("@intlayer/config/built");
__intlayer_config_built = require_rolldown_runtime.__toESM(__intlayer_config_built);
let __intlayer_core = require("@intlayer/core");
let __intlayer_config_client = require("@intlayer/config/client");
let next_server = require("next/server");
//#region src/proxy/intlayerProxy.ts
const { internationalization, routing } = __intlayer_config_built.default ?? {};
const { locales, defaultLocale } = internationalization ?? {};
const { basePath, mode } = routing ?? {};
const effectiveMode = mode ?? __intlayer_config_client.DefaultValues.Routing.ROUTING_MODE;
const noPrefix = effectiveMode === "no-prefix" || effectiveMode === "search-params";
const prefixDefault = effectiveMode === "prefix-all";
/**
* Detects if the request is a prefetch request from Next.js.
*
* Next.js prefetch requests can be identified by several headers:
* - purpose: 'prefetch' (standard prefetch header)
* - next-router-prefetch: '1' (Next.js router prefetch)
* - next-url: present (Next.js internal navigation)
*
* During prefetch, we should ignore cookie-based locale detection
* to prevent unwanted redirects when users are switching locales.
*
* @param request - The incoming Next.js request object.
* @returns - True if the request is a prefetch request, false otherwise.
*/
const isPrefetchRequest = (request) => {
const purpose = request.headers.get("purpose");
const nextRouterPrefetch = request.headers.get("next-router-prefetch");
const nextUrl = request.headers.get("next-url");
const xNextjsData = request.headers.get("x-nextjs-data");
return purpose === "prefetch" || nextRouterPrefetch === "1" || !!nextUrl || !!xNextjsData;
};
const appendLocaleSearchIfNeeded = (search, locale) => {
if (effectiveMode !== "search-params") return search;
const params = new URLSearchParams(search ?? "");
params.set("locale", locale);
return `?${params.toString()}`;
};
/**
* Proxy that handles the internationalization layer
*
* Usage:
*
* ```ts
* // ./src/proxy.ts
*
* export { intlayerProxy as proxy } from '@intlayer/next/proxy';
*
* // applies this proxy only to files in the app directory
* export const config = {
* matcher: '/((?!api|static|.*\\..*|_next).*)',
* };
* ```
*
* Main proxy function for handling internationalization.
*
* @param request - The incoming Next.js request object.
* @param event - The Next.js fetch event (optional).
* @param response - The Next.js response object (optional).
* @returns - The response to be returned to the client.
*/
const intlayerProxy = (request, _event, _response) => {
const pathname = request.nextUrl.pathname;
const localLocale = getLocalLocale(request);
if (noPrefix) return handleNoPrefix(request, localLocale, pathname);
return handlePrefix(request, localLocale, getPathLocale(pathname), pathname);
};
/**
* Retrieves the locale from the request cookies if available and valid.
*
* @param request - The incoming Next.js request object.
* @returns - The locale found in the cookies, or undefined if not found or invalid.
*/
const getLocalLocale = (request) => (0, __intlayer_core.getLocaleFromStorage)({
getCookie: (name) => request.cookies.get(name)?.value ?? null,
getHeader: (name) => request.headers.get(name) ?? null
});
/**
* Handles the case where URLs do not have locale prefixes.
*
* @param request - The incoming Next.js request object.
* @param localLocale - The locale from the cookie.
* @param pathname - The pathname from the request URL.
* @returns - The rewritten response with the locale applied.
*/
const handleNoPrefix = (request, localLocale, pathname) => {
const pathLocale = getPathLocale(pathname);
if (pathLocale) {
const pathWithoutLocale = pathname.slice(`/${pathLocale}`.length) || "/";
const search$1 = appendLocaleSearchIfNeeded(request.nextUrl.search, pathLocale);
return redirectUrl(request, search$1 ? `${pathWithoutLocale}${search$1}` : `${pathWithoutLocale}${request.nextUrl.search ?? ""}`);
}
const locale = localLocale ?? defaultLocale;
if (effectiveMode === "search-params") {
if (new URLSearchParams(request.nextUrl.search).get("locale") === locale) return rewriteUrl(request, `${`/${locale}${pathname}`}${request.nextUrl.search ?? ""}`, locale);
const search$1 = appendLocaleSearchIfNeeded(request.nextUrl.search, locale);
return redirectUrl(request, search$1 ? `${pathname}${search$1}` : `${pathname}${request.nextUrl.search ?? ""}`);
}
const internalPath = `/${locale}${pathname}`;
const search = appendLocaleSearchIfNeeded(request.nextUrl.search, locale);
return rewriteUrl(request, search ? `${internalPath}${search}` : `${internalPath}${request.nextUrl.search ?? ""}`, locale);
};
/**
* Extracts the locale from the URL pathname if present.
*
* @param pathname - The pathname from the request URL.
* @returns - The locale found in the pathname, or undefined if not found.
*/
const getPathLocale = (pathname) => locales.find((locale) => pathname.startsWith(`/${locale}/`) || pathname === `/${locale}`);
/**
* Handles the case where URLs have locale prefixes.
*
* @param request - The incoming Next.js request object.
* @param localLocale - The locale from the cookie.
* @param pathLocale - The locale extracted from the pathname.
* @param pathname - The pathname from the request URL.
* @param basePathTrailingSlash - Indicates if the basePath ends with a slash.
* @returns - The response to be returned to the client.
*/
const handlePrefix = (request, localLocale, pathLocale, pathname) => {
if (!pathLocale) {
if (isPrefetchRequest(request) && true) return handleMissingPathLocale(request, defaultLocale, pathname);
return handleMissingPathLocale(request, localLocale, pathname);
}
return handleExistingPathLocale(request, localLocale, pathLocale, pathname);
};
/**
* Handles requests where the locale is missing from the URL pathname.
*
* @param request - The incoming Next.js request object.
* @param localLocale - The locale from the cookie.
* @param pathname - The pathname from the request URL.
* @param basePathTrailingSlash - Indicates if the basePath ends with a slash.
* @returns - The response to be returned to the client.
*/
const handleMissingPathLocale = (request, localLocale, pathname) => {
let locale = localLocale ?? require_proxy_localeDetector.localeDetector?.(request) ?? defaultLocale;
if (!locales.includes(locale)) locale = defaultLocale;
const newPath = constructPath(locale, pathname, basePath, appendLocaleSearchIfNeeded(request.nextUrl.search, locale));
return prefixDefault || locale !== defaultLocale ? redirectUrl(request, newPath) : rewriteUrl(request, newPath, locale);
};
/**
* Handles requests where the locale exists in the URL pathname.
*
* @param request - The incoming Next.js request object.
* @param localLocale - The locale from the cookie.
* @param pathLocale - The locale extracted from the pathname.
* @param pathname - The pathname from the request URL.
* @returns - The response to be returned to the client.
*/
const handleExistingPathLocale = (request, localLocale, pathLocale, pathname) => {
if (localLocale && localLocale !== pathLocale) return redirectUrl(request, handleCookieLocaleMismatch(request, pathname, pathLocale, localLocale, basePath));
return handleDefaultLocaleRedirect(request, pathLocale, pathname);
};
/**
* Handles the scenario where the locale in the cookie does not match the locale in the URL pathname.
*
* @param request - The incoming Next.js request object.
* @param pathname - The pathname from the request URL.
* @param pathLocale - The locale extracted from the pathname.
* @param localLocale - The locale from the cookie.
* @param basePath - The base path of the application.
* @returns - The new URL path with the correct locale.
*/
const handleCookieLocaleMismatch = (request, pathname, pathLocale, localLocale, basePath$1) => {
return constructPath(localLocale, pathname.replace(`/${pathLocale}`, `/${localLocale}`), basePath$1, appendLocaleSearchIfNeeded(request.nextUrl.search, localLocale));
};
/**
* Handles redirection when the default locale is used and prefixing is not required.
*
* @param request - The incoming Next.js request object.
* @param pathLocale - The locale extracted from the pathname.
* @param pathname - The pathname from the request URL.
* @returns - The rewritten response without the locale prefix.
*/
const handleDefaultLocaleRedirect = (request, pathLocale, pathname) => {
if (!prefixDefault && pathLocale === defaultLocale) {
let pathWithoutLocale = pathname.slice(`/${pathLocale}`.length) || "/";
if (basePath.endsWith("/")) pathWithoutLocale = pathWithoutLocale.slice(1);
const searchWithLocale$1 = appendLocaleSearchIfNeeded(request.nextUrl.search, pathLocale);
if (searchWithLocale$1) pathWithoutLocale += searchWithLocale$1;
else if (request.nextUrl.search) pathWithoutLocale += request.nextUrl.search;
return redirectUrl(request, `${basePath}${pathWithoutLocale}`);
}
const searchWithLocale = appendLocaleSearchIfNeeded(request.nextUrl.search, pathLocale);
return rewriteUrl(request, searchWithLocale ? `${pathname}${searchWithLocale}` : pathname, pathLocale);
};
/**
* Constructs a new path by combining the locale, path, basePath, and search parameters.
*
* @param locale - The locale to include in the path.
* @param path - The original path from the request.
* @param basePath - The base path of the application.
* @param [search] - The query string from the request URL (optional).
* @returns - The constructed new path.
*/
const constructPath = (locale, path, basePath$1, search) => {
const pathWithoutPrefix = path.startsWith(`/${locale}`) ? path.slice(`/${locale}`.length) || "/" : path;
if (effectiveMode === "no-prefix") {
if (search) return `${pathWithoutPrefix}?${search}`;
return pathWithoutPrefix;
}
if (effectiveMode === "search-params") {
if (search) return `${pathWithoutPrefix}?${search}`;
return pathWithoutPrefix;
}
const pathWithLocalePrefix = path.startsWith(`/${locale}`) ? path : `${locale}${path}`;
return `${basePath$1}${basePath$1.endsWith("/") ? "" : "/"}${pathWithLocalePrefix}`;
};
/**
* Rewrites the URL to the new path and sets the locale header.
*
* @param request - The incoming Next.js request object.
* @param newPath - The new path to rewrite to.
* @param locale - The locale to set in the response header.
* @returns - The rewritten response.
*/
const rewriteUrl = (request, newPath, locale) => {
const search = request.nextUrl.search;
const pathWithSearch = search && !newPath.includes("?") ? `${newPath}${search}` : newPath;
const response = next_server.NextResponse.rewrite(new URL(pathWithSearch, request.url));
(0, __intlayer_core.setLocaleInStorage)(locale, { setHeader: (name, value) => response.headers.set(name, value) });
return response;
};
/**
* Redirects the request to the new path.
*
* @param request - The incoming Next.js request object.
* @param newPath - The new path to redirect to.
* @returns - The redirect response.
*/
const redirectUrl = (request, newPath) => {
const search = request.nextUrl.search;
const pathWithSearch = search && !newPath.includes("?") ? `${newPath}${search}` : newPath;
return next_server.NextResponse.redirect(new URL(pathWithSearch, request.url));
};
//#endregion
exports.intlayerProxy = intlayerProxy;
//# sourceMappingURL=intlayerProxy.cjs.map