UNPKG

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
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