UNPKG

next-i18next

Version:

The easiest way to translate your NextJs apps.

177 lines (176 loc) 7.69 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); let next_server = require("next/server"); //#region src/appRouter/config.ts function defineConfig(config) { return config; } function normalizeConfig(userConfig) { const supportedLngs = userConfig.supportedLngs ?? userConfig.i18n?.locales?.filter((l) => l !== "default") ?? ["en"]; const fallbackLng = userConfig.fallbackLng ?? userConfig.i18n?.defaultLocale ?? supportedLngs[0]; if (!fallbackLng) throw new Error("next-i18next: fallbackLng (or i18n.defaultLocale) is required"); if (supportedLngs.length === 0) throw new Error("next-i18next: supportedLngs (or i18n.locales) must contain at least one language"); const defaultNS = userConfig.defaultNS ?? "common"; return { supportedLngs, fallbackLng, defaultNS, ns: userConfig.ns ?? [defaultNS], localeInPath: userConfig.localeInPath ?? true, hideDefaultLocale: userConfig.hideDefaultLocale ?? false, localePath: userConfig.localePath ?? "/locales", localeStructure: userConfig.localeStructure ?? "{{lng}}/{{ns}}", localeExtension: userConfig.localeExtension ?? "json", cookieName: userConfig.cookieName ?? "i18next", headerName: userConfig.headerName ?? "x-i18next-current-language", cookieMaxAge: userConfig.cookieMaxAge ?? 365 * 24 * 60 * 60, ignoredPaths: userConfig.ignoredPaths ?? [ "/api", "/_next", "/static" ], basePath: userConfig.basePath, resources: userConfig.resources, resourceLoader: userConfig.resourceLoader, use: userConfig.use ?? [], i18nextOptions: userConfig.i18nextOptions ?? {}, nonExplicitSupportedLngs: userConfig.nonExplicitSupportedLngs ?? false, i18n: userConfig.i18n, serializeConfig: userConfig.serializeConfig, reloadOnPrerender: userConfig.reloadOnPrerender }; } //#endregion //#region src/appRouter/proxy/languageDetector.ts /** * Edge-safe Accept-Language parser and language matcher. * No external dependencies — runs in Edge Runtime, Node.js, and browser. */ function parseAcceptLanguage(header) { if (!header) return []; return header.split(",").map((part) => { const [lang, qPart] = part.trim().split(";"); const q = qPart?.trim().startsWith("q=") ? parseFloat(qPart.trim().slice(2)) : 1; return { lang: lang.trim(), q: isNaN(q) ? 0 : q }; }).filter((item) => item.lang && item.q > 0).sort((a, b) => b.q - a.q).map((item) => item.lang); } /** * Find a matching supported language for a given code, respecting nonExplicitSupportedLngs. * * Matching order (mirrors i18next's LanguageUtils.getBestMatchFromCodes): * 1. Exact match (case-insensitive) * 2. Preferred prefix → supported base (e.g. preferred 'en-US' matches supported 'en') * 3. If nonExplicitSupportedLngs: preferred base → supported region * (e.g. preferred 'en' matches supported 'en-US') */ function findSupportedMatch(code, supportedLanguages, nonExplicitSupportedLngs) { const lower = code.toLowerCase(); const exact = supportedLanguages.find((l) => l.toLowerCase() === lower); if (exact) return exact; const prefix = lower.split("-")[0]; const partial = supportedLanguages.find((l) => l.toLowerCase() === prefix); if (partial) return partial; if (nonExplicitSupportedLngs) { const reverse = supportedLanguages.find((l) => l.toLowerCase().split("-")[0] === prefix); if (reverse) return reverse; } } function matchLanguage(acceptLanguages, supportedLanguages, defaultLanguage, nonExplicitSupportedLngs = false) { for (const preferred of acceptLanguages) { const match = findSupportedMatch(preferred, supportedLanguages, nonExplicitSupportedLngs); if (match) return match; } return defaultLanguage; } //#endregion //#region src/appRouter/proxy/index.ts function findLocaleInPath(pathname, supportedLngs, nonExplicitSupportedLngs) { const match = pathname.match(/^\/([^/]+)/); if (!match) return void 0; return findSupportedMatch(match[1], supportedLngs, nonExplicitSupportedLngs); } function createProxy(userConfig) { const config = normalizeConfig(userConfig); const nonExplicit = config.nonExplicitSupportedLngs; const basePath = config.basePath ? "/" + config.basePath.replace(/^\/+/, "").replace(/\/+$/, "") : void 0; return function middleware(req) { const { pathname, search } = req.nextUrl; if (basePath) { if (pathname !== basePath && !pathname.startsWith(basePath + "/")) return next_server.NextResponse.next(); } for (const ignored of config.ignoredPaths) if (pathname.startsWith(ignored)) return next_server.NextResponse.next(); if (/\.(ico|png|jpg|jpeg|svg|gif|webp|css|js|map|woff2?|ttf|eot)$/.test(pathname)) return next_server.NextResponse.next(); let lng; const cookieValue = req.cookies.get(config.cookieName)?.value; if (cookieValue) lng = matchLanguage([cookieValue], config.supportedLngs, config.fallbackLng, nonExplicit); if (!lng) lng = matchLanguage(parseAcceptLanguage(req.headers.get("Accept-Language")), config.supportedLngs, config.fallbackLng, nonExplicit); if (!lng) lng = config.fallbackLng; const lngInPath = findLocaleInPath(basePath ? pathname.slice(basePath.length) || "/" : pathname, config.supportedLngs, nonExplicit); if (config.localeInPath) { const prefix = basePath ?? ""; const pathAfterBase = basePath ? pathname.slice(basePath.length) : pathname; if (config.hideDefaultLocale && lngInPath === config.fallbackLng) { const pathWithoutLocale = pathAfterBase.replace(/^\/[^/]+/, "") || "/"; const redirectUrl = new URL(`${prefix}${pathWithoutLocale}${search}`, req.url); const response = next_server.NextResponse.redirect(redirectUrl); response.cookies.set(config.cookieName, config.fallbackLng, { path: "/", maxAge: config.cookieMaxAge, sameSite: "lax" }); return response; } const headers = new Headers(req.headers); headers.set(config.headerName, lngInPath || lng); if (!lngInPath) { if (config.hideDefaultLocale) { const rewriteUrl = new URL(`${prefix}/${config.fallbackLng}${pathAfterBase}${search}`, req.url); headers.set(config.headerName, config.fallbackLng); const response = next_server.NextResponse.rewrite(rewriteUrl, { request: { headers } }); response.cookies.set(config.cookieName, config.fallbackLng, { path: "/", maxAge: config.cookieMaxAge, sameSite: "lax" }); return response; } const redirectUrl = new URL(`${prefix}/${lng}${pathAfterBase}${search}`, req.url); const response = next_server.NextResponse.redirect(redirectUrl); response.cookies.set(config.cookieName, lng, { path: "/", maxAge: config.cookieMaxAge, sameSite: "lax" }); return response; } const response = next_server.NextResponse.next({ request: { headers } }); if (req.headers.has("referer")) { const refererUrl = new URL(req.headers.get("referer")); const lngInReferer = findLocaleInPath(basePath ? refererUrl.pathname.slice(basePath.length) || "/" : refererUrl.pathname, config.supportedLngs, nonExplicit); if (lngInReferer) response.cookies.set(config.cookieName, lngInReferer, { path: "/", maxAge: config.cookieMaxAge, sameSite: "lax" }); } return response; } else { const headers = new Headers(req.headers); headers.set(config.headerName, lng); return next_server.NextResponse.next({ request: { headers } }); } }; } /** * Backwards-compatible alias for createProxy. * Use `createProxy` for new projects with Next.js 16+ `proxy.ts`. */ const createMiddleware = createProxy; //#endregion exports.createMiddleware = createMiddleware; exports.createProxy = createProxy; exports.defineConfig = defineConfig; exports.normalizeConfig = normalizeConfig; //# sourceMappingURL=index.cjs.map