UNPKG

next-i18next

Version:

The easiest way to translate your NextJs apps.

225 lines (223 loc) 9.51 kB
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" }); //#region \0rolldown/runtime.js var __create = Object.create; var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __getProtoOf = Object.getPrototypeOf; var __hasOwnProp = Object.prototype.hasOwnProperty; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) { key = keys[i]; if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: ((k) => from[k]).bind(null, key), enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, mod)); //#endregion let i18next = require("i18next"); let i18next_resources_to_backend = require("i18next-resources-to-backend"); i18next_resources_to_backend = __toESM(i18next_resources_to_backend); let react = require("react"); let next_headers = require("next/headers"); //#region src/appRouter/config.ts 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/server.ts let _config = null; let _sharedInstance = null; let _sharedInstancePromise = null; function getConfig() { if (!_config) throw new Error("next-i18next: Server module not initialized. Call initServerI18next(config) in your root layout."); return _config; } /** * Initialize the server-side i18next configuration. * Call this once in your root layout or a shared setup file. */ function initServerI18next(userConfig) { _config = normalizeConfig(userConfig); } function hasCustomBackend(plugins) { return plugins.some((b) => b.type === "backend"); } function createResourceBackend(config) { if (config.resourceLoader) return (0, i18next_resources_to_backend.default)(config.resourceLoader); return (0, i18next_resources_to_backend.default)(async (language, namespace) => { const filePath = `${config.localePath}/${config.localeStructure.replace("{{lng}}", language).replace("{{ns}}", namespace)}.${config.localeExtension}`; if (typeof process !== "undefined" && process.versions?.node) try { const fs = await import("fs/promises"); const resolved = (await import("path")).resolve(process.cwd(), `public${filePath}`); const content = await fs.readFile(resolved, "utf-8"); return JSON.parse(content); } catch { throw new Error(`next-i18next: Could not read locale file "public${filePath}". On serverless platforms (Vercel, AWS Lambda, etc.), files in public/ are served via CDN but are NOT available on the filesystem at runtime. Use the \`resourceLoader\` option with dynamic imports instead: resourceLoader: (language, namespace) => import(\`./public/locales/\${language}/\${namespace}.json\`) `); } throw new Error(`next-i18next: Cannot load locale file "${filePath}" in Edge Runtime. Provide pre-bundled \`resources\`, a custom \`resourceLoader\`, or use a custom backend (e.g. i18next-http-backend) via the \`use\` option.`); }); } /** * Get or create the shared i18next instance. * The instance is created once and reused across all requests. * All languages are preloaded so that getFixedT(lng) works for any supported language. * Additional namespaces are loaded on demand and cached in the instance store. */ async function getSharedInstance(config) { if (_sharedInstance?.isInitialized) return _sharedInstance; if (_sharedInstancePromise) return _sharedInstancePromise; _sharedInstancePromise = (async () => { const i18nInstance = (0, i18next.createInstance)(); const partialBundled = config.i18nextOptions?.partialBundledLanguages; if ((!config.resources || partialBundled) && !hasCustomBackend(config.use)) i18nInstance.use(createResourceBackend(config)); config.use.forEach((plugin) => i18nInstance.use(plugin)); await i18nInstance.init({ lng: config.fallbackLng, ns: config.ns, defaultNS: config.defaultNS, fallbackLng: config.fallbackLng, supportedLngs: config.supportedLngs, nonExplicitSupportedLngs: config.nonExplicitSupportedLngs, fallbackNS: config.defaultNS, preload: config.supportedLngs, interpolation: { escapeValue: false }, ...config.resources ? { resources: config.resources } : {}, ...config.i18nextOptions }); _sharedInstance = i18nInstance; return i18nInstance; })(); return _sharedInstancePromise; } const reloadResourcesForRender = (0, react.cache)(async (i18n, lng) => { const ns = i18n.options.ns ?? []; await i18n.reloadResources([lng], ns); }); const detectLanguage = (0, react.cache)(async (config) => { const fromHeader = (await (0, next_headers.headers)()).get(config.headerName); if (fromHeader) return fromHeader; const cookieValue = (await (0, next_headers.cookies)()).get(config.cookieName)?.value; if (cookieValue) { if (config.supportedLngs.includes(cookieValue)) return cookieValue; if (config.nonExplicitSupportedLngs) { const prefix = cookieValue.toLowerCase().split("-")[0]; const match = config.supportedLngs.find((l) => l.toLowerCase() === prefix || l.toLowerCase().split("-")[0] === prefix); if (match) return match; } } return config.fallbackLng; }); /** * Get a translation function for use in Server Components, layouts, and generateMetadata. * * The underlying i18next instance is a **module-level singleton** that persists across * requests. This means custom backends (i18next-http-backend, i18next-locize-backend, etc.) * only fetch translations once (or according to their own reloadInterval), not on every request. * * @example * ```tsx * import { getT } from 'next-i18next/server' * * export default async function Page() { * const { t, i18n } = await getT('home') * return <h1>{t('heading')}</h1> * } * ``` */ async function getT(ns, options = {}) { const config = getConfig(); const lng = options.lng || await detectLanguage(config); const i18nInstance = await getSharedInstance(config); if (config.reloadOnPrerender && process.env.NODE_ENV !== "production") await reloadResourcesForRender(i18nInstance, lng); const missingNs = (ns ? Array.isArray(ns) ? ns : [ns] : config.ns).filter((n) => !i18nInstance.hasLoadedNamespace(n)); if (missingNs.length > 0) await i18nInstance.loadNamespaces(missingNs); const resolvedNs = ns ? Array.isArray(ns) ? ns[0] : ns : config.defaultNS; return { t: i18nInstance.getFixedT(lng, resolvedNs, options.keyPrefix), i18n: i18nInstance, lng }; } /** * Extract loaded resources from the server i18next instance for passing to I18nProvider. * * @example * ```tsx * const { i18n } = await getT() * const resources = getResources(i18n, ['common', 'footer']) * return <I18nProvider language={i18n.language} resources={resources}>{children}</I18nProvider> * ``` */ function getResources(i18n, namespaces) { const resources = {}; const store = i18n.store?.data || {}; const nsFilter = namespaces ? new Set(namespaces) : null; for (const lng of Object.keys(store)) { resources[lng] = {}; for (const ns of Object.keys(store[lng])) if (!nsFilter || nsFilter.has(ns)) resources[lng][ns] = store[lng][ns]; } return resources; } /** * Helper for generateStaticParams — returns params for all supported languages. * * @example * ```tsx * import { generateI18nStaticParams } from 'next-i18next/server' * * export async function generateStaticParams() { * return generateI18nStaticParams() * } * ``` */ function generateI18nStaticParams() { return getConfig().supportedLngs.map((lng) => ({ lng })); } //#endregion exports.generateI18nStaticParams = generateI18nStaticParams; exports.getResources = getResources; exports.getT = getT; exports.initServerI18next = initServerI18next; //# sourceMappingURL=server.cjs.map