next-i18next 16.0.2 → 16.0.4
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/README.md +22 -1
- package/dist/appRouter/index.cjs +1 -0
- package/dist/appRouter/index.cjs.map +1 -1
- package/dist/appRouter/index.d.cts +5 -0
- package/dist/appRouter/index.d.cts.map +1 -1
- package/dist/appRouter/index.d.mts +5 -0
- package/dist/appRouter/index.d.mts.map +1 -1
- package/dist/appRouter/index.mjs +1 -0
- package/dist/appRouter/index.mjs.map +1 -1
- package/dist/appRouter/proxy/index.cjs +25 -2
- package/dist/appRouter/proxy/index.cjs.map +1 -1
- package/dist/appRouter/proxy/index.d.cts +5 -0
- package/dist/appRouter/proxy/index.d.cts.map +1 -1
- package/dist/appRouter/proxy/index.d.mts +5 -0
- package/dist/appRouter/proxy/index.d.mts.map +1 -1
- package/dist/appRouter/proxy/index.mjs +25 -2
- package/dist/appRouter/proxy/index.mjs.map +1 -1
- package/dist/appRouter/server.cjs +1 -0
- package/dist/appRouter/server.cjs.map +1 -1
- package/dist/appRouter/server.d.cts +4 -0
- package/dist/appRouter/server.d.cts.map +1 -1
- package/dist/appRouter/server.d.mts +4 -0
- package/dist/appRouter/server.d.mts.map +1 -1
- package/dist/appRouter/server.mjs +1 -0
- package/dist/appRouter/server.mjs.map +1 -1
- package/dist/pagesRouter/config/createConfig.cjs.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -242,9 +242,29 @@ For the no-locale-path mode (cookie-based), see `useChangeLanguage` [below](#no-
|
|
|
242
242
|
|
|
243
243
|
---
|
|
244
244
|
|
|
245
|
+
## Hide Default Locale
|
|
246
|
+
|
|
247
|
+
If you want clean URLs for the default language while keeping locale prefixes for other languages, set `hideDefaultLocale: true`:
|
|
248
|
+
|
|
249
|
+
```ts
|
|
250
|
+
const i18nConfig: I18nConfig = {
|
|
251
|
+
supportedLngs: ['en', 'de'],
|
|
252
|
+
fallbackLng: 'en',
|
|
253
|
+
hideDefaultLocale: true,
|
|
254
|
+
}
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
In this mode:
|
|
258
|
+
- `/about` serves the default language (English) — no prefix needed
|
|
259
|
+
- `/de/about` serves German — non-default locales keep their prefix
|
|
260
|
+
- `/en/about` automatically redirects to `/about` (canonical clean URL)
|
|
261
|
+
- The `[lng]` folder structure stays the same — the proxy rewrites internally
|
|
262
|
+
|
|
263
|
+
---
|
|
264
|
+
|
|
245
265
|
## No-Locale-Path Mode
|
|
246
266
|
|
|
247
|
-
If you prefer clean URLs without a locale prefix (e.g., `/about` instead of `/en/about`), set `localeInPath: false`:
|
|
267
|
+
If you prefer clean URLs without a locale prefix for **all** languages (e.g., `/about` instead of `/en/about`), set `localeInPath: false`:
|
|
248
268
|
|
|
249
269
|
```ts
|
|
250
270
|
const i18nConfig: I18nConfig = {
|
|
@@ -609,6 +629,7 @@ In **serverless environments** (Lambda, Vercel Serverless, etc.), the cache only
|
|
|
609
629
|
| `defaultNS` | `'common'` | Default namespace |
|
|
610
630
|
| `ns` | `[defaultNS]` | All known namespaces |
|
|
611
631
|
| `localeInPath` | `true` | Include locale in URL path |
|
|
632
|
+
| `hideDefaultLocale` | `false` | When `true` (with `localeInPath: true`), the default language has no URL prefix |
|
|
612
633
|
| `localePath` | `'/locales'` | Path to locale files relative to `/public` |
|
|
613
634
|
| `localeStructure` | `'{{lng}}/{{ns}}'` | Locale file directory structure |
|
|
614
635
|
| `localeExtension` | `'json'` | Locale file extension |
|
package/dist/appRouter/index.cjs
CHANGED
|
@@ -15,6 +15,7 @@ function normalizeConfig(userConfig) {
|
|
|
15
15
|
defaultNS,
|
|
16
16
|
ns: userConfig.ns ?? [defaultNS],
|
|
17
17
|
localeInPath: userConfig.localeInPath ?? true,
|
|
18
|
+
hideDefaultLocale: userConfig.hideDefaultLocale ?? false,
|
|
18
19
|
localePath: userConfig.localePath ?? "/locales",
|
|
19
20
|
localeStructure: userConfig.localeStructure ?? "{{lng}}/{{ns}}",
|
|
20
21
|
localeExtension: userConfig.localeExtension ?? "json",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/appRouter/config.ts"],"sourcesContent":["import type { I18nConfig, NormalizedConfig } from './types'\n\nexport function defineConfig(config: I18nConfig): I18nConfig {\n return config\n}\n\nexport function normalizeConfig(userConfig: I18nConfig): NormalizedConfig {\n // Support legacy format: { i18n: { defaultLocale, locales } }\n const supportedLngs = userConfig.supportedLngs ??\n userConfig.i18n?.locales?.filter((l: string) => l !== 'default') ??\n ['en']\n const fallbackLng = userConfig.fallbackLng ??\n userConfig.i18n?.defaultLocale ??\n supportedLngs[0]\n\n if (!fallbackLng) {\n throw new Error('next-i18next: fallbackLng (or i18n.defaultLocale) is required')\n }\n if (supportedLngs.length === 0) {\n throw new Error('next-i18next: supportedLngs (or i18n.locales) must contain at least one language')\n }\n\n const defaultNS = userConfig.defaultNS ?? 'common'\n\n return {\n supportedLngs,\n fallbackLng,\n defaultNS,\n ns: userConfig.ns ?? [defaultNS],\n localeInPath: userConfig.localeInPath ?? true,\n localePath: userConfig.localePath ?? '/locales',\n localeStructure: userConfig.localeStructure ?? '{{lng}}/{{ns}}',\n localeExtension: userConfig.localeExtension ?? 'json',\n cookieName: userConfig.cookieName ?? 'i18next',\n headerName: userConfig.headerName ?? 'x-i18next-current-language',\n cookieMaxAge: userConfig.cookieMaxAge ?? 365 * 24 * 60 * 60,\n ignoredPaths: userConfig.ignoredPaths ?? ['/api', '/_next', '/static'],\n basePath: userConfig.basePath,\n resources: userConfig.resources,\n resourceLoader: userConfig.resourceLoader,\n use: userConfig.use ?? [],\n i18nextOptions: (userConfig.i18nextOptions ?? {}) as Record<string, any>,\n nonExplicitSupportedLngs: userConfig.nonExplicitSupportedLngs ?? false,\n // Preserve legacy fields\n i18n: userConfig.i18n,\n serializeConfig: userConfig.serializeConfig,\n reloadOnPrerender: userConfig.reloadOnPrerender,\n }\n}\n"],"mappings":";;AAEA,SAAgB,aAAa,QAAgC;AAC3D,QAAO;;AAGT,SAAgB,gBAAgB,YAA0C;CAExE,MAAM,gBAAgB,WAAW,iBAC/B,WAAW,MAAM,SAAS,QAAQ,MAAc,MAAM,UAAU,IAChE,CAAC,KAAK;CACR,MAAM,cAAc,WAAW,eAC7B,WAAW,MAAM,iBACjB,cAAc;AAEhB,KAAI,CAAC,YACH,OAAM,IAAI,MAAM,gEAAgE;AAElF,KAAI,cAAc,WAAW,EAC3B,OAAM,IAAI,MAAM,mFAAmF;CAGrG,MAAM,YAAY,WAAW,aAAa;AAE1C,QAAO;EACL;EACA;EACA;EACA,IAAI,WAAW,MAAM,CAAC,UAAU;EAChC,cAAc,WAAW,gBAAgB;EACzC,YAAY,WAAW,cAAc;EACrC,iBAAiB,WAAW,mBAAmB;EAC/C,iBAAiB,WAAW,mBAAmB;EAC/C,YAAY,WAAW,cAAc;EACrC,YAAY,WAAW,cAAc;EACrC,cAAc,WAAW,gBAAgB,MAAM,KAAK,KAAK;EACzD,cAAc,WAAW,gBAAgB;GAAC;GAAQ;GAAU;GAAU;EACtE,UAAU,WAAW;EACrB,WAAW,WAAW;EACtB,gBAAgB,WAAW;EAC3B,KAAK,WAAW,OAAO,EAAE;EACzB,gBAAiB,WAAW,kBAAkB,EAAE;EAChD,0BAA0B,WAAW,4BAA4B;EAEjE,MAAM,WAAW;EACjB,iBAAiB,WAAW;EAC5B,mBAAmB,WAAW;EAC/B"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":[],"sources":["../../src/appRouter/config.ts"],"sourcesContent":["import type { I18nConfig, NormalizedConfig } from './types'\n\nexport function defineConfig(config: I18nConfig): I18nConfig {\n return config\n}\n\nexport function normalizeConfig(userConfig: I18nConfig): NormalizedConfig {\n // Support legacy format: { i18n: { defaultLocale, locales } }\n const supportedLngs = userConfig.supportedLngs ??\n userConfig.i18n?.locales?.filter((l: string) => l !== 'default') ??\n ['en']\n const fallbackLng = userConfig.fallbackLng ??\n userConfig.i18n?.defaultLocale ??\n supportedLngs[0]\n\n if (!fallbackLng) {\n throw new Error('next-i18next: fallbackLng (or i18n.defaultLocale) is required')\n }\n if (supportedLngs.length === 0) {\n throw new Error('next-i18next: supportedLngs (or i18n.locales) must contain at least one language')\n }\n\n const defaultNS = userConfig.defaultNS ?? 'common'\n\n return {\n supportedLngs,\n fallbackLng,\n defaultNS,\n ns: userConfig.ns ?? [defaultNS],\n localeInPath: userConfig.localeInPath ?? true,\n hideDefaultLocale: userConfig.hideDefaultLocale ?? false,\n localePath: userConfig.localePath ?? '/locales',\n localeStructure: userConfig.localeStructure ?? '{{lng}}/{{ns}}',\n localeExtension: userConfig.localeExtension ?? 'json',\n cookieName: userConfig.cookieName ?? 'i18next',\n headerName: userConfig.headerName ?? 'x-i18next-current-language',\n cookieMaxAge: userConfig.cookieMaxAge ?? 365 * 24 * 60 * 60,\n ignoredPaths: userConfig.ignoredPaths ?? ['/api', '/_next', '/static'],\n basePath: userConfig.basePath,\n resources: userConfig.resources,\n resourceLoader: userConfig.resourceLoader,\n use: userConfig.use ?? [],\n i18nextOptions: (userConfig.i18nextOptions ?? {}) as Record<string, any>,\n nonExplicitSupportedLngs: userConfig.nonExplicitSupportedLngs ?? false,\n // Preserve legacy fields\n i18n: userConfig.i18n,\n serializeConfig: userConfig.serializeConfig,\n reloadOnPrerender: userConfig.reloadOnPrerender,\n }\n}\n"],"mappings":";;AAEA,SAAgB,aAAa,QAAgC;AAC3D,QAAO;;AAGT,SAAgB,gBAAgB,YAA0C;CAExE,MAAM,gBAAgB,WAAW,iBAC/B,WAAW,MAAM,SAAS,QAAQ,MAAc,MAAM,UAAU,IAChE,CAAC,KAAK;CACR,MAAM,cAAc,WAAW,eAC7B,WAAW,MAAM,iBACjB,cAAc;AAEhB,KAAI,CAAC,YACH,OAAM,IAAI,MAAM,gEAAgE;AAElF,KAAI,cAAc,WAAW,EAC3B,OAAM,IAAI,MAAM,mFAAmF;CAGrG,MAAM,YAAY,WAAW,aAAa;AAE1C,QAAO;EACL;EACA;EACA;EACA,IAAI,WAAW,MAAM,CAAC,UAAU;EAChC,cAAc,WAAW,gBAAgB;EACzC,mBAAmB,WAAW,qBAAqB;EACnD,YAAY,WAAW,cAAc;EACrC,iBAAiB,WAAW,mBAAmB;EAC/C,iBAAiB,WAAW,mBAAmB;EAC/C,YAAY,WAAW,cAAc;EACrC,YAAY,WAAW,cAAc;EACrC,cAAc,WAAW,gBAAgB,MAAM,KAAK,KAAK;EACzD,cAAc,WAAW,gBAAgB;GAAC;GAAQ;GAAU;GAAU;EACtE,UAAU,WAAW;EACrB,WAAW,WAAW;EACtB,gBAAgB,WAAW;EAC3B,KAAK,WAAW,OAAO,EAAE;EACzB,gBAAiB,WAAW,kBAAkB,EAAE;EAChD,0BAA0B,WAAW,4BAA4B;EAEjE,MAAM,WAAW;EACjB,iBAAiB,WAAW;EAC5B,mBAAmB,WAAW;EAC/B"}
|
|
@@ -23,6 +23,10 @@ interface I18nConfig {
|
|
|
23
23
|
resourceLoader?: ResourceLoader;
|
|
24
24
|
/** Whether to include locale in URL path (defaults to true) */
|
|
25
25
|
localeInPath?: boolean;
|
|
26
|
+
/** When true (and localeInPath is true), the default language has no URL prefix.
|
|
27
|
+
* e.g. `/about` serves the default language, `/de/about` serves German.
|
|
28
|
+
* Requests to the explicit default prefix (`/en/about`) are redirected to `/about`. */
|
|
29
|
+
hideDefaultLocale?: boolean;
|
|
26
30
|
/** Cookie name for storing selected language (defaults to 'i18next') */
|
|
27
31
|
cookieName?: string;
|
|
28
32
|
/** Custom header name for passing language to server components (defaults to 'x-i18next-current-language') */
|
|
@@ -62,6 +66,7 @@ interface NormalizedConfig {
|
|
|
62
66
|
defaultNS: string;
|
|
63
67
|
ns: string[];
|
|
64
68
|
localeInPath: boolean;
|
|
69
|
+
hideDefaultLocale: boolean;
|
|
65
70
|
localePath: string;
|
|
66
71
|
localeStructure: string;
|
|
67
72
|
localeExtension: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/appRouter/types.ts","../../src/appRouter/config.ts"],"mappings":";;;KAEY,cAAA,IAAkB,QAAA,UAAkB,SAAA,aAAsB,OAAA;AAAA,UAErD,UAAA;EAFL;EAIV,aAAA;;EAEA,WAAA;EAN4B;EAQ5B,SAAA;EARoE;EAUpE,EAAA;EAV2E;EAc3E,UAAA;EAZyB;EAczB,eAAA;EAIY;EAFZ,eAAA;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/appRouter/types.ts","../../src/appRouter/config.ts"],"mappings":";;;KAEY,cAAA,IAAkB,QAAA,UAAkB,SAAA,aAAsB,OAAA;AAAA,UAErD,UAAA;EAFL;EAIV,aAAA;;EAEA,WAAA;EAN4B;EAQ5B,SAAA;EARoE;EAUpE,EAAA;EAV2E;EAc3E,UAAA;EAZyB;EAczB,eAAA;EAIY;EAFZ,eAAA;EA8BsB;EA5BtB,SAAA,GAAY,QAAA;EA4BS;EA1BrB,cAAA,GAAiB,cAAA;EAlBjB;EAsBA,YAAA;EAlBA;;;EAsBA,iBAAA;EAZA;EAgBA,UAAA;EAdY;EAgBZ,UAAA;EAdiB;EAgBjB,YAAA;EARA;EAUA,YAAA;EAJA;EAMA,QAAA;EAFA;EAMA,GAAA;EAAA;EAEA,cAAA,GAAiB,IAAA,CAAK,WAAA;EAAL;EAIjB,IAAA;IACE,aAAA;IACA,OAAA;IACA,OAAA;MACE,aAAA;MACA,MAAA;MACA,IAAA;MACA,OAAA;IAAA;IAEF,eAAA;EAAA;EAKF;EAFA,eAAA;EAIwB;EAFxB,iBAAA;EAKe;EAHf,wBAAA;AAAA;AAAA,UAGe,gBAAA;EACf,aAAA;EACA,WAAA;EACA,SAAA;EACA,EAAA;EACA,YAAA;EACA,iBAAA;EACA,UAAA;EACA,eAAA;EACA,eAAA;EACA,UAAA;EACA,UAAA;EACA,YAAA;EACA,YAAA;EACA,QAAA;EACA,SAAA,GAAY,QAAA;EACZ,cAAA,GAAiB,cAAA;EACjB,GAAA;EACA,cAAA,EAAgB,MAAA;EAChB,wBAAA;EAEA,IAAA,GAAO,UAAA;EACP,eAAA;EACA,iBAAA;AAAA;AAAA,KAGU,UAAA,YAAsB,aAAA,GAAgB,aAAA;EAChD,CAAA,EAAG,SAAA,CAAU,EAAA,EAAI,OAAA;EACjB,IAAA,EAAM,IAAA,EATN;EAWA,GAAA;AAAA;;;iBCrGc,YAAA,CAAa,MAAA,EAAQ,UAAA,GAAa,UAAA;AAAA,iBAIlC,eAAA,CAAgB,UAAA,EAAY,UAAA,GAAa,gBAAA"}
|
|
@@ -23,6 +23,10 @@ interface I18nConfig {
|
|
|
23
23
|
resourceLoader?: ResourceLoader;
|
|
24
24
|
/** Whether to include locale in URL path (defaults to true) */
|
|
25
25
|
localeInPath?: boolean;
|
|
26
|
+
/** When true (and localeInPath is true), the default language has no URL prefix.
|
|
27
|
+
* e.g. `/about` serves the default language, `/de/about` serves German.
|
|
28
|
+
* Requests to the explicit default prefix (`/en/about`) are redirected to `/about`. */
|
|
29
|
+
hideDefaultLocale?: boolean;
|
|
26
30
|
/** Cookie name for storing selected language (defaults to 'i18next') */
|
|
27
31
|
cookieName?: string;
|
|
28
32
|
/** Custom header name for passing language to server components (defaults to 'x-i18next-current-language') */
|
|
@@ -62,6 +66,7 @@ interface NormalizedConfig {
|
|
|
62
66
|
defaultNS: string;
|
|
63
67
|
ns: string[];
|
|
64
68
|
localeInPath: boolean;
|
|
69
|
+
hideDefaultLocale: boolean;
|
|
65
70
|
localePath: string;
|
|
66
71
|
localeStructure: string;
|
|
67
72
|
localeExtension: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/appRouter/types.ts","../../src/appRouter/config.ts"],"mappings":";;;KAEY,cAAA,IAAkB,QAAA,UAAkB,SAAA,aAAsB,OAAA;AAAA,UAErD,UAAA;EAFL;EAIV,aAAA;;EAEA,WAAA;EAN4B;EAQ5B,SAAA;EARoE;EAUpE,EAAA;EAV2E;EAc3E,UAAA;EAZyB;EAczB,eAAA;EAIY;EAFZ,eAAA;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/appRouter/types.ts","../../src/appRouter/config.ts"],"mappings":";;;KAEY,cAAA,IAAkB,QAAA,UAAkB,SAAA,aAAsB,OAAA;AAAA,UAErD,UAAA;EAFL;EAIV,aAAA;;EAEA,WAAA;EAN4B;EAQ5B,SAAA;EARoE;EAUpE,EAAA;EAV2E;EAc3E,UAAA;EAZyB;EAczB,eAAA;EAIY;EAFZ,eAAA;EA8BsB;EA5BtB,SAAA,GAAY,QAAA;EA4BS;EA1BrB,cAAA,GAAiB,cAAA;EAlBjB;EAsBA,YAAA;EAlBA;;;EAsBA,iBAAA;EAZA;EAgBA,UAAA;EAdY;EAgBZ,UAAA;EAdiB;EAgBjB,YAAA;EARA;EAUA,YAAA;EAJA;EAMA,QAAA;EAFA;EAMA,GAAA;EAAA;EAEA,cAAA,GAAiB,IAAA,CAAK,WAAA;EAAL;EAIjB,IAAA;IACE,aAAA;IACA,OAAA;IACA,OAAA;MACE,aAAA;MACA,MAAA;MACA,IAAA;MACA,OAAA;IAAA;IAEF,eAAA;EAAA;EAKF;EAFA,eAAA;EAIwB;EAFxB,iBAAA;EAKe;EAHf,wBAAA;AAAA;AAAA,UAGe,gBAAA;EACf,aAAA;EACA,WAAA;EACA,SAAA;EACA,EAAA;EACA,YAAA;EACA,iBAAA;EACA,UAAA;EACA,eAAA;EACA,eAAA;EACA,UAAA;EACA,UAAA;EACA,YAAA;EACA,YAAA;EACA,QAAA;EACA,SAAA,GAAY,QAAA;EACZ,cAAA,GAAiB,cAAA;EACjB,GAAA;EACA,cAAA,EAAgB,MAAA;EAChB,wBAAA;EAEA,IAAA,GAAO,UAAA;EACP,eAAA;EACA,iBAAA;AAAA;AAAA,KAGU,UAAA,YAAsB,aAAA,GAAgB,aAAA;EAChD,CAAA,EAAG,SAAA,CAAU,EAAA,EAAI,OAAA;EACjB,IAAA,EAAM,IAAA,EATN;EAWA,GAAA;AAAA;;;iBCrGc,YAAA,CAAa,MAAA,EAAQ,UAAA,GAAa,UAAA;AAAA,iBAIlC,eAAA,CAAgB,UAAA,EAAY,UAAA,GAAa,gBAAA"}
|
package/dist/appRouter/index.mjs
CHANGED
|
@@ -14,6 +14,7 @@ function normalizeConfig(userConfig) {
|
|
|
14
14
|
defaultNS,
|
|
15
15
|
ns: userConfig.ns ?? [defaultNS],
|
|
16
16
|
localeInPath: userConfig.localeInPath ?? true,
|
|
17
|
+
hideDefaultLocale: userConfig.hideDefaultLocale ?? false,
|
|
17
18
|
localePath: userConfig.localePath ?? "/locales",
|
|
18
19
|
localeStructure: userConfig.localeStructure ?? "{{lng}}/{{ns}}",
|
|
19
20
|
localeExtension: userConfig.localeExtension ?? "json",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/appRouter/config.ts"],"sourcesContent":["import type { I18nConfig, NormalizedConfig } from './types'\n\nexport function defineConfig(config: I18nConfig): I18nConfig {\n return config\n}\n\nexport function normalizeConfig(userConfig: I18nConfig): NormalizedConfig {\n // Support legacy format: { i18n: { defaultLocale, locales } }\n const supportedLngs = userConfig.supportedLngs ??\n userConfig.i18n?.locales?.filter((l: string) => l !== 'default') ??\n ['en']\n const fallbackLng = userConfig.fallbackLng ??\n userConfig.i18n?.defaultLocale ??\n supportedLngs[0]\n\n if (!fallbackLng) {\n throw new Error('next-i18next: fallbackLng (or i18n.defaultLocale) is required')\n }\n if (supportedLngs.length === 0) {\n throw new Error('next-i18next: supportedLngs (or i18n.locales) must contain at least one language')\n }\n\n const defaultNS = userConfig.defaultNS ?? 'common'\n\n return {\n supportedLngs,\n fallbackLng,\n defaultNS,\n ns: userConfig.ns ?? [defaultNS],\n localeInPath: userConfig.localeInPath ?? true,\n localePath: userConfig.localePath ?? '/locales',\n localeStructure: userConfig.localeStructure ?? '{{lng}}/{{ns}}',\n localeExtension: userConfig.localeExtension ?? 'json',\n cookieName: userConfig.cookieName ?? 'i18next',\n headerName: userConfig.headerName ?? 'x-i18next-current-language',\n cookieMaxAge: userConfig.cookieMaxAge ?? 365 * 24 * 60 * 60,\n ignoredPaths: userConfig.ignoredPaths ?? ['/api', '/_next', '/static'],\n basePath: userConfig.basePath,\n resources: userConfig.resources,\n resourceLoader: userConfig.resourceLoader,\n use: userConfig.use ?? [],\n i18nextOptions: (userConfig.i18nextOptions ?? {}) as Record<string, any>,\n nonExplicitSupportedLngs: userConfig.nonExplicitSupportedLngs ?? false,\n // Preserve legacy fields\n i18n: userConfig.i18n,\n serializeConfig: userConfig.serializeConfig,\n reloadOnPrerender: userConfig.reloadOnPrerender,\n }\n}\n"],"mappings":";AAEA,SAAgB,aAAa,QAAgC;AAC3D,QAAO;;AAGT,SAAgB,gBAAgB,YAA0C;CAExE,MAAM,gBAAgB,WAAW,iBAC/B,WAAW,MAAM,SAAS,QAAQ,MAAc,MAAM,UAAU,IAChE,CAAC,KAAK;CACR,MAAM,cAAc,WAAW,eAC7B,WAAW,MAAM,iBACjB,cAAc;AAEhB,KAAI,CAAC,YACH,OAAM,IAAI,MAAM,gEAAgE;AAElF,KAAI,cAAc,WAAW,EAC3B,OAAM,IAAI,MAAM,mFAAmF;CAGrG,MAAM,YAAY,WAAW,aAAa;AAE1C,QAAO;EACL;EACA;EACA;EACA,IAAI,WAAW,MAAM,CAAC,UAAU;EAChC,cAAc,WAAW,gBAAgB;EACzC,YAAY,WAAW,cAAc;EACrC,iBAAiB,WAAW,mBAAmB;EAC/C,iBAAiB,WAAW,mBAAmB;EAC/C,YAAY,WAAW,cAAc;EACrC,YAAY,WAAW,cAAc;EACrC,cAAc,WAAW,gBAAgB,MAAM,KAAK,KAAK;EACzD,cAAc,WAAW,gBAAgB;GAAC;GAAQ;GAAU;GAAU;EACtE,UAAU,WAAW;EACrB,WAAW,WAAW;EACtB,gBAAgB,WAAW;EAC3B,KAAK,WAAW,OAAO,EAAE;EACzB,gBAAiB,WAAW,kBAAkB,EAAE;EAChD,0BAA0B,WAAW,4BAA4B;EAEjE,MAAM,WAAW;EACjB,iBAAiB,WAAW;EAC5B,mBAAmB,WAAW;EAC/B"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/appRouter/config.ts"],"sourcesContent":["import type { I18nConfig, NormalizedConfig } from './types'\n\nexport function defineConfig(config: I18nConfig): I18nConfig {\n return config\n}\n\nexport function normalizeConfig(userConfig: I18nConfig): NormalizedConfig {\n // Support legacy format: { i18n: { defaultLocale, locales } }\n const supportedLngs = userConfig.supportedLngs ??\n userConfig.i18n?.locales?.filter((l: string) => l !== 'default') ??\n ['en']\n const fallbackLng = userConfig.fallbackLng ??\n userConfig.i18n?.defaultLocale ??\n supportedLngs[0]\n\n if (!fallbackLng) {\n throw new Error('next-i18next: fallbackLng (or i18n.defaultLocale) is required')\n }\n if (supportedLngs.length === 0) {\n throw new Error('next-i18next: supportedLngs (or i18n.locales) must contain at least one language')\n }\n\n const defaultNS = userConfig.defaultNS ?? 'common'\n\n return {\n supportedLngs,\n fallbackLng,\n defaultNS,\n ns: userConfig.ns ?? [defaultNS],\n localeInPath: userConfig.localeInPath ?? true,\n hideDefaultLocale: userConfig.hideDefaultLocale ?? false,\n localePath: userConfig.localePath ?? '/locales',\n localeStructure: userConfig.localeStructure ?? '{{lng}}/{{ns}}',\n localeExtension: userConfig.localeExtension ?? 'json',\n cookieName: userConfig.cookieName ?? 'i18next',\n headerName: userConfig.headerName ?? 'x-i18next-current-language',\n cookieMaxAge: userConfig.cookieMaxAge ?? 365 * 24 * 60 * 60,\n ignoredPaths: userConfig.ignoredPaths ?? ['/api', '/_next', '/static'],\n basePath: userConfig.basePath,\n resources: userConfig.resources,\n resourceLoader: userConfig.resourceLoader,\n use: userConfig.use ?? [],\n i18nextOptions: (userConfig.i18nextOptions ?? {}) as Record<string, any>,\n nonExplicitSupportedLngs: userConfig.nonExplicitSupportedLngs ?? false,\n // Preserve legacy fields\n i18n: userConfig.i18n,\n serializeConfig: userConfig.serializeConfig,\n reloadOnPrerender: userConfig.reloadOnPrerender,\n }\n}\n"],"mappings":";AAEA,SAAgB,aAAa,QAAgC;AAC3D,QAAO;;AAGT,SAAgB,gBAAgB,YAA0C;CAExE,MAAM,gBAAgB,WAAW,iBAC/B,WAAW,MAAM,SAAS,QAAQ,MAAc,MAAM,UAAU,IAChE,CAAC,KAAK;CACR,MAAM,cAAc,WAAW,eAC7B,WAAW,MAAM,iBACjB,cAAc;AAEhB,KAAI,CAAC,YACH,OAAM,IAAI,MAAM,gEAAgE;AAElF,KAAI,cAAc,WAAW,EAC3B,OAAM,IAAI,MAAM,mFAAmF;CAGrG,MAAM,YAAY,WAAW,aAAa;AAE1C,QAAO;EACL;EACA;EACA;EACA,IAAI,WAAW,MAAM,CAAC,UAAU;EAChC,cAAc,WAAW,gBAAgB;EACzC,mBAAmB,WAAW,qBAAqB;EACnD,YAAY,WAAW,cAAc;EACrC,iBAAiB,WAAW,mBAAmB;EAC/C,iBAAiB,WAAW,mBAAmB;EAC/C,YAAY,WAAW,cAAc;EACrC,YAAY,WAAW,cAAc;EACrC,cAAc,WAAW,gBAAgB,MAAM,KAAK,KAAK;EACzD,cAAc,WAAW,gBAAgB;GAAC;GAAQ;GAAU;GAAU;EACtE,UAAU,WAAW;EACrB,WAAW,WAAW;EACtB,gBAAgB,WAAW;EAC3B,KAAK,WAAW,OAAO,EAAE;EACzB,gBAAiB,WAAW,kBAAkB,EAAE;EAChD,0BAA0B,WAAW,4BAA4B;EAEjE,MAAM,WAAW;EACjB,iBAAiB,WAAW;EAC5B,mBAAmB,WAAW;EAC/B"}
|
|
@@ -16,6 +16,7 @@ function normalizeConfig(userConfig) {
|
|
|
16
16
|
defaultNS,
|
|
17
17
|
ns: userConfig.ns ?? [defaultNS],
|
|
18
18
|
localeInPath: userConfig.localeInPath ?? true,
|
|
19
|
+
hideDefaultLocale: userConfig.hideDefaultLocale ?? false,
|
|
19
20
|
localePath: userConfig.localePath ?? "/locales",
|
|
20
21
|
localeStructure: userConfig.localeStructure ?? "{{lng}}/{{ns}}",
|
|
21
22
|
localeExtension: userConfig.localeExtension ?? "json",
|
|
@@ -108,11 +109,33 @@ function createProxy(userConfig) {
|
|
|
108
109
|
if (!lng) lng = config.fallbackLng;
|
|
109
110
|
const lngInPath = findLocaleInPath(basePath ? pathname.slice(basePath.length) || "/" : pathname, config.supportedLngs, nonExplicit);
|
|
110
111
|
if (config.localeInPath) {
|
|
112
|
+
const prefix = basePath ?? "";
|
|
113
|
+
const pathAfterBase = basePath ? pathname.slice(basePath.length) : pathname;
|
|
114
|
+
if (config.hideDefaultLocale && lngInPath === config.fallbackLng) {
|
|
115
|
+
const pathWithoutLocale = pathAfterBase.replace(/^\/[^/]+/, "") || "/";
|
|
116
|
+
const redirectUrl = new URL(`${prefix}${pathWithoutLocale}${search}`, req.url);
|
|
117
|
+
const response = next_server.NextResponse.redirect(redirectUrl);
|
|
118
|
+
response.cookies.set(config.cookieName, config.fallbackLng, {
|
|
119
|
+
path: "/",
|
|
120
|
+
maxAge: config.cookieMaxAge,
|
|
121
|
+
sameSite: "lax"
|
|
122
|
+
});
|
|
123
|
+
return response;
|
|
124
|
+
}
|
|
111
125
|
const headers = new Headers(req.headers);
|
|
112
126
|
headers.set(config.headerName, lngInPath || lng);
|
|
113
127
|
if (!lngInPath) {
|
|
114
|
-
|
|
115
|
-
|
|
128
|
+
if (config.hideDefaultLocale) {
|
|
129
|
+
const rewriteUrl = new URL(`${prefix}/${config.fallbackLng}${pathAfterBase}${search}`, req.url);
|
|
130
|
+
headers.set(config.headerName, config.fallbackLng);
|
|
131
|
+
const response = next_server.NextResponse.rewrite(rewriteUrl, { request: { headers } });
|
|
132
|
+
response.cookies.set(config.cookieName, config.fallbackLng, {
|
|
133
|
+
path: "/",
|
|
134
|
+
maxAge: config.cookieMaxAge,
|
|
135
|
+
sameSite: "lax"
|
|
136
|
+
});
|
|
137
|
+
return response;
|
|
138
|
+
}
|
|
116
139
|
const redirectUrl = new URL(`${prefix}/${lng}${pathAfterBase}${search}`, req.url);
|
|
117
140
|
const response = next_server.NextResponse.redirect(redirectUrl);
|
|
118
141
|
response.cookies.set(config.cookieName, lng, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["NextResponse"],"sources":["../../../src/appRouter/config.ts","../../../src/appRouter/proxy/languageDetector.ts","../../../src/appRouter/proxy/index.ts"],"sourcesContent":["import type { I18nConfig, NormalizedConfig } from './types'\n\nexport function defineConfig(config: I18nConfig): I18nConfig {\n return config\n}\n\nexport function normalizeConfig(userConfig: I18nConfig): NormalizedConfig {\n // Support legacy format: { i18n: { defaultLocale, locales } }\n const supportedLngs = userConfig.supportedLngs ??\n userConfig.i18n?.locales?.filter((l: string) => l !== 'default') ??\n ['en']\n const fallbackLng = userConfig.fallbackLng ??\n userConfig.i18n?.defaultLocale ??\n supportedLngs[0]\n\n if (!fallbackLng) {\n throw new Error('next-i18next: fallbackLng (or i18n.defaultLocale) is required')\n }\n if (supportedLngs.length === 0) {\n throw new Error('next-i18next: supportedLngs (or i18n.locales) must contain at least one language')\n }\n\n const defaultNS = userConfig.defaultNS ?? 'common'\n\n return {\n supportedLngs,\n fallbackLng,\n defaultNS,\n ns: userConfig.ns ?? [defaultNS],\n localeInPath: userConfig.localeInPath ?? true,\n localePath: userConfig.localePath ?? '/locales',\n localeStructure: userConfig.localeStructure ?? '{{lng}}/{{ns}}',\n localeExtension: userConfig.localeExtension ?? 'json',\n cookieName: userConfig.cookieName ?? 'i18next',\n headerName: userConfig.headerName ?? 'x-i18next-current-language',\n cookieMaxAge: userConfig.cookieMaxAge ?? 365 * 24 * 60 * 60,\n ignoredPaths: userConfig.ignoredPaths ?? ['/api', '/_next', '/static'],\n basePath: userConfig.basePath,\n resources: userConfig.resources,\n resourceLoader: userConfig.resourceLoader,\n use: userConfig.use ?? [],\n i18nextOptions: (userConfig.i18nextOptions ?? {}) as Record<string, any>,\n nonExplicitSupportedLngs: userConfig.nonExplicitSupportedLngs ?? false,\n // Preserve legacy fields\n i18n: userConfig.i18n,\n serializeConfig: userConfig.serializeConfig,\n reloadOnPrerender: userConfig.reloadOnPrerender,\n }\n}\n","/**\n * Edge-safe Accept-Language parser and language matcher.\n * No external dependencies — runs in Edge Runtime, Node.js, and browser.\n */\n\nexport function parseAcceptLanguage(header: string | null | undefined): string[] {\n if (!header) return []\n return header\n .split(',')\n .map(part => {\n const [lang, qPart] = part.trim().split(';')\n const q = qPart?.trim().startsWith('q=')\n ? parseFloat(qPart.trim().slice(2))\n : 1.0\n return { lang: lang.trim(), q: isNaN(q) ? 0 : q }\n })\n .filter(item => item.lang && item.q > 0)\n .sort((a, b) => b.q - a.q)\n .map(item => item.lang)\n}\n\n/**\n * Find a matching supported language for a given code, respecting nonExplicitSupportedLngs.\n *\n * Matching order (mirrors i18next's LanguageUtils.getBestMatchFromCodes):\n * 1. Exact match (case-insensitive)\n * 2. Preferred prefix → supported base (e.g. preferred 'en-US' matches supported 'en')\n * 3. If nonExplicitSupportedLngs: preferred base → supported region\n * (e.g. preferred 'en' matches supported 'en-US')\n */\nexport function findSupportedMatch(\n code: string,\n supportedLanguages: readonly string[],\n nonExplicitSupportedLngs: boolean,\n): string | undefined {\n const lower = code.toLowerCase()\n\n // 1. Exact match (case-insensitive)\n const exact = supportedLanguages.find(l => l.toLowerCase() === lower)\n if (exact) return exact\n\n // 2. Preferred prefix → supported base (e.g. 'en-US' → 'en')\n const prefix = lower.split('-')[0]\n const partial = supportedLanguages.find(l => l.toLowerCase() === prefix)\n if (partial) return partial\n\n // 3. Reverse match: preferred base → supported region (e.g. 'en' → 'en-US')\n if (nonExplicitSupportedLngs) {\n const reverse = supportedLanguages.find(\n l => l.toLowerCase().split('-')[0] === prefix\n )\n if (reverse) return reverse\n }\n\n return undefined\n}\n\nexport function matchLanguage(\n acceptLanguages: string[],\n supportedLanguages: readonly string[],\n defaultLanguage: string,\n nonExplicitSupportedLngs = false,\n): string {\n for (const preferred of acceptLanguages) {\n const match = findSupportedMatch(preferred, supportedLanguages, nonExplicitSupportedLngs)\n if (match) return match\n }\n return defaultLanguage\n}\n","import { NextRequest, NextResponse } from 'next/server'\nimport type { I18nConfig } from '../types'\nimport { normalizeConfig } from '../config'\nimport { parseAcceptLanguage, matchLanguage, findSupportedMatch } from './languageDetector'\n\n// Re-export config utilities for Edge-safe usage (no react-i18next dependency)\nexport { defineConfig, normalizeConfig } from '../config'\nexport type { I18nConfig, NormalizedConfig, ResourceLoader } from '../types'\n\nfunction findLocaleInPath(\n pathname: string,\n supportedLngs: readonly string[],\n nonExplicitSupportedLngs: boolean,\n): string | undefined {\n // Extract the first path segment\n const match = pathname.match(/^\\/([^/]+)/)\n if (!match) return undefined\n return findSupportedMatch(match[1], supportedLngs, nonExplicitSupportedLngs)\n}\n\nexport function createProxy(userConfig: I18nConfig) {\n const config = normalizeConfig(userConfig)\n const nonExplicit = config.nonExplicitSupportedLngs\n // Normalize basePath: ensure leading slash, strip trailing slash\n const basePath = config.basePath\n ? ('/' + config.basePath.replace(/^\\/+/, '').replace(/\\/+$/, ''))\n : undefined\n\n return function middleware(req: NextRequest): NextResponse {\n const { pathname, search } = req.nextUrl\n\n // When basePath is set, only handle requests under that prefix\n if (basePath) {\n if (pathname !== basePath && !pathname.startsWith(basePath + '/')) {\n return NextResponse.next()\n }\n }\n\n // Skip ignored paths\n for (const ignored of config.ignoredPaths) {\n if (pathname.startsWith(ignored)) {\n return NextResponse.next()\n }\n }\n\n // Skip common static file extensions\n if (/\\.(ico|png|jpg|jpeg|svg|gif|webp|css|js|map|woff2?|ttf|eot)$/.test(pathname)) {\n return NextResponse.next()\n }\n\n // Detect language from cookie, then Accept-Language header, then default\n let lng: string | undefined\n const cookieValue = req.cookies.get(config.cookieName)?.value\n if (cookieValue) {\n lng = matchLanguage([cookieValue], config.supportedLngs, config.fallbackLng, nonExplicit)\n }\n if (!lng) {\n lng = matchLanguage(\n parseAcceptLanguage(req.headers.get('Accept-Language')),\n config.supportedLngs,\n config.fallbackLng,\n nonExplicit,\n )\n }\n if (!lng) {\n lng = config.fallbackLng\n }\n\n // For locale-in-path detection, strip basePath prefix so we look at the right segment\n const pathForLocale = basePath ? pathname.slice(basePath.length) || '/' : pathname\n const lngInPath = findLocaleInPath(pathForLocale, config.supportedLngs, nonExplicit)\n\n if (config.localeInPath) {\n // Set custom header for server components to read\n const headers = new Headers(req.headers)\n headers.set(config.headerName, lngInPath || lng)\n\n // Redirect if no locale in path\n if (!lngInPath) {\n const prefix = basePath ?? ''\n const pathAfterBase = basePath ? pathname.slice(basePath.length) : pathname\n const redirectUrl = new URL(`${prefix}/${lng}${pathAfterBase}${search}`, req.url)\n const response = NextResponse.redirect(redirectUrl)\n response.cookies.set(config.cookieName, lng, {\n path: '/',\n maxAge: config.cookieMaxAge,\n sameSite: 'lax',\n })\n return response\n }\n\n // Persist language from referer URL into cookie\n const response = NextResponse.next({ request: { headers } })\n if (req.headers.has('referer')) {\n const refererUrl = new URL(req.headers.get('referer')!)\n const refererPathForLocale = basePath\n ? refererUrl.pathname.slice(basePath.length) || '/'\n : refererUrl.pathname\n const lngInReferer = findLocaleInPath(refererPathForLocale, config.supportedLngs, nonExplicit)\n if (lngInReferer) {\n response.cookies.set(config.cookieName, lngInReferer, {\n path: '/',\n maxAge: config.cookieMaxAge,\n sameSite: 'lax',\n })\n }\n }\n\n return response\n } else {\n // No-locale-path mode: don't redirect, just set the header\n const headers = new Headers(req.headers)\n headers.set(config.headerName, lng)\n\n const response = NextResponse.next({ request: { headers } })\n return response\n }\n }\n}\n\n/**\n * Backwards-compatible alias for createProxy.\n * Use `createProxy` for new projects with Next.js 16+ `proxy.ts`.\n */\nexport const createMiddleware = createProxy\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["NextResponse"],"sources":["../../../src/appRouter/config.ts","../../../src/appRouter/proxy/languageDetector.ts","../../../src/appRouter/proxy/index.ts"],"sourcesContent":["import type { I18nConfig, NormalizedConfig } from './types'\n\nexport function defineConfig(config: I18nConfig): I18nConfig {\n return config\n}\n\nexport function normalizeConfig(userConfig: I18nConfig): NormalizedConfig {\n // Support legacy format: { i18n: { defaultLocale, locales } }\n const supportedLngs = userConfig.supportedLngs ??\n userConfig.i18n?.locales?.filter((l: string) => l !== 'default') ??\n ['en']\n const fallbackLng = userConfig.fallbackLng ??\n userConfig.i18n?.defaultLocale ??\n supportedLngs[0]\n\n if (!fallbackLng) {\n throw new Error('next-i18next: fallbackLng (or i18n.defaultLocale) is required')\n }\n if (supportedLngs.length === 0) {\n throw new Error('next-i18next: supportedLngs (or i18n.locales) must contain at least one language')\n }\n\n const defaultNS = userConfig.defaultNS ?? 'common'\n\n return {\n supportedLngs,\n fallbackLng,\n defaultNS,\n ns: userConfig.ns ?? [defaultNS],\n localeInPath: userConfig.localeInPath ?? true,\n hideDefaultLocale: userConfig.hideDefaultLocale ?? false,\n localePath: userConfig.localePath ?? '/locales',\n localeStructure: userConfig.localeStructure ?? '{{lng}}/{{ns}}',\n localeExtension: userConfig.localeExtension ?? 'json',\n cookieName: userConfig.cookieName ?? 'i18next',\n headerName: userConfig.headerName ?? 'x-i18next-current-language',\n cookieMaxAge: userConfig.cookieMaxAge ?? 365 * 24 * 60 * 60,\n ignoredPaths: userConfig.ignoredPaths ?? ['/api', '/_next', '/static'],\n basePath: userConfig.basePath,\n resources: userConfig.resources,\n resourceLoader: userConfig.resourceLoader,\n use: userConfig.use ?? [],\n i18nextOptions: (userConfig.i18nextOptions ?? {}) as Record<string, any>,\n nonExplicitSupportedLngs: userConfig.nonExplicitSupportedLngs ?? false,\n // Preserve legacy fields\n i18n: userConfig.i18n,\n serializeConfig: userConfig.serializeConfig,\n reloadOnPrerender: userConfig.reloadOnPrerender,\n }\n}\n","/**\n * Edge-safe Accept-Language parser and language matcher.\n * No external dependencies — runs in Edge Runtime, Node.js, and browser.\n */\n\nexport function parseAcceptLanguage(header: string | null | undefined): string[] {\n if (!header) return []\n return header\n .split(',')\n .map(part => {\n const [lang, qPart] = part.trim().split(';')\n const q = qPart?.trim().startsWith('q=')\n ? parseFloat(qPart.trim().slice(2))\n : 1.0\n return { lang: lang.trim(), q: isNaN(q) ? 0 : q }\n })\n .filter(item => item.lang && item.q > 0)\n .sort((a, b) => b.q - a.q)\n .map(item => item.lang)\n}\n\n/**\n * Find a matching supported language for a given code, respecting nonExplicitSupportedLngs.\n *\n * Matching order (mirrors i18next's LanguageUtils.getBestMatchFromCodes):\n * 1. Exact match (case-insensitive)\n * 2. Preferred prefix → supported base (e.g. preferred 'en-US' matches supported 'en')\n * 3. If nonExplicitSupportedLngs: preferred base → supported region\n * (e.g. preferred 'en' matches supported 'en-US')\n */\nexport function findSupportedMatch(\n code: string,\n supportedLanguages: readonly string[],\n nonExplicitSupportedLngs: boolean,\n): string | undefined {\n const lower = code.toLowerCase()\n\n // 1. Exact match (case-insensitive)\n const exact = supportedLanguages.find(l => l.toLowerCase() === lower)\n if (exact) return exact\n\n // 2. Preferred prefix → supported base (e.g. 'en-US' → 'en')\n const prefix = lower.split('-')[0]\n const partial = supportedLanguages.find(l => l.toLowerCase() === prefix)\n if (partial) return partial\n\n // 3. Reverse match: preferred base → supported region (e.g. 'en' → 'en-US')\n if (nonExplicitSupportedLngs) {\n const reverse = supportedLanguages.find(\n l => l.toLowerCase().split('-')[0] === prefix\n )\n if (reverse) return reverse\n }\n\n return undefined\n}\n\nexport function matchLanguage(\n acceptLanguages: string[],\n supportedLanguages: readonly string[],\n defaultLanguage: string,\n nonExplicitSupportedLngs = false,\n): string {\n for (const preferred of acceptLanguages) {\n const match = findSupportedMatch(preferred, supportedLanguages, nonExplicitSupportedLngs)\n if (match) return match\n }\n return defaultLanguage\n}\n","import { NextRequest, NextResponse } from 'next/server'\nimport type { I18nConfig } from '../types'\nimport { normalizeConfig } from '../config'\nimport { parseAcceptLanguage, matchLanguage, findSupportedMatch } from './languageDetector'\n\n// Re-export config utilities for Edge-safe usage (no react-i18next dependency)\nexport { defineConfig, normalizeConfig } from '../config'\nexport type { I18nConfig, NormalizedConfig, ResourceLoader } from '../types'\n\nfunction findLocaleInPath(\n pathname: string,\n supportedLngs: readonly string[],\n nonExplicitSupportedLngs: boolean,\n): string | undefined {\n // Extract the first path segment\n const match = pathname.match(/^\\/([^/]+)/)\n if (!match) return undefined\n return findSupportedMatch(match[1], supportedLngs, nonExplicitSupportedLngs)\n}\n\nexport function createProxy(userConfig: I18nConfig) {\n const config = normalizeConfig(userConfig)\n const nonExplicit = config.nonExplicitSupportedLngs\n // Normalize basePath: ensure leading slash, strip trailing slash\n const basePath = config.basePath\n ? ('/' + config.basePath.replace(/^\\/+/, '').replace(/\\/+$/, ''))\n : undefined\n\n return function middleware(req: NextRequest): NextResponse {\n const { pathname, search } = req.nextUrl\n\n // When basePath is set, only handle requests under that prefix\n if (basePath) {\n if (pathname !== basePath && !pathname.startsWith(basePath + '/')) {\n return NextResponse.next()\n }\n }\n\n // Skip ignored paths\n for (const ignored of config.ignoredPaths) {\n if (pathname.startsWith(ignored)) {\n return NextResponse.next()\n }\n }\n\n // Skip common static file extensions\n if (/\\.(ico|png|jpg|jpeg|svg|gif|webp|css|js|map|woff2?|ttf|eot)$/.test(pathname)) {\n return NextResponse.next()\n }\n\n // Detect language from cookie, then Accept-Language header, then default\n let lng: string | undefined\n const cookieValue = req.cookies.get(config.cookieName)?.value\n if (cookieValue) {\n lng = matchLanguage([cookieValue], config.supportedLngs, config.fallbackLng, nonExplicit)\n }\n if (!lng) {\n lng = matchLanguage(\n parseAcceptLanguage(req.headers.get('Accept-Language')),\n config.supportedLngs,\n config.fallbackLng,\n nonExplicit,\n )\n }\n if (!lng) {\n lng = config.fallbackLng\n }\n\n // For locale-in-path detection, strip basePath prefix so we look at the right segment\n const pathForLocale = basePath ? pathname.slice(basePath.length) || '/' : pathname\n const lngInPath = findLocaleInPath(pathForLocale, config.supportedLngs, nonExplicit)\n\n if (config.localeInPath) {\n const prefix = basePath ?? ''\n const pathAfterBase = basePath ? pathname.slice(basePath.length) : pathname\n\n // hideDefaultLocale: redirect explicit default-locale paths to the clean URL\n if (config.hideDefaultLocale && lngInPath === config.fallbackLng) {\n const pathWithoutLocale = pathAfterBase.replace(/^\\/[^/]+/, '') || '/'\n const redirectUrl = new URL(`${prefix}${pathWithoutLocale}${search}`, req.url)\n const response = NextResponse.redirect(redirectUrl)\n response.cookies.set(config.cookieName, config.fallbackLng, {\n path: '/',\n maxAge: config.cookieMaxAge,\n sameSite: 'lax',\n })\n return response\n }\n\n // Set custom header for server components to read\n const headers = new Headers(req.headers)\n headers.set(config.headerName, lngInPath || lng)\n\n // Redirect if no locale in path\n if (!lngInPath) {\n if (config.hideDefaultLocale) {\n // Rewrite internally to the default-locale path, keeping the clean URL\n const rewriteUrl = new URL(`${prefix}/${config.fallbackLng}${pathAfterBase}${search}`, req.url)\n headers.set(config.headerName, config.fallbackLng)\n const response = NextResponse.rewrite(rewriteUrl, { request: { headers } })\n response.cookies.set(config.cookieName, config.fallbackLng, {\n path: '/',\n maxAge: config.cookieMaxAge,\n sameSite: 'lax',\n })\n return response\n }\n\n const redirectUrl = new URL(`${prefix}/${lng}${pathAfterBase}${search}`, req.url)\n const response = NextResponse.redirect(redirectUrl)\n response.cookies.set(config.cookieName, lng, {\n path: '/',\n maxAge: config.cookieMaxAge,\n sameSite: 'lax',\n })\n return response\n }\n\n // Persist language from referer URL into cookie\n const response = NextResponse.next({ request: { headers } })\n if (req.headers.has('referer')) {\n const refererUrl = new URL(req.headers.get('referer')!)\n const refererPathForLocale = basePath\n ? refererUrl.pathname.slice(basePath.length) || '/'\n : refererUrl.pathname\n const lngInReferer = findLocaleInPath(refererPathForLocale, config.supportedLngs, nonExplicit)\n if (lngInReferer) {\n response.cookies.set(config.cookieName, lngInReferer, {\n path: '/',\n maxAge: config.cookieMaxAge,\n sameSite: 'lax',\n })\n }\n }\n\n return response\n } else {\n // No-locale-path mode: don't redirect, just set the header\n const headers = new Headers(req.headers)\n headers.set(config.headerName, lng)\n\n const response = NextResponse.next({ request: { headers } })\n return response\n }\n }\n}\n\n/**\n * Backwards-compatible alias for createProxy.\n * Use `createProxy` for new projects with Next.js 16+ `proxy.ts`.\n */\nexport const createMiddleware = createProxy\n"],"mappings":"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"}
|
|
@@ -24,6 +24,10 @@ interface I18nConfig {
|
|
|
24
24
|
resourceLoader?: ResourceLoader;
|
|
25
25
|
/** Whether to include locale in URL path (defaults to true) */
|
|
26
26
|
localeInPath?: boolean;
|
|
27
|
+
/** When true (and localeInPath is true), the default language has no URL prefix.
|
|
28
|
+
* e.g. `/about` serves the default language, `/de/about` serves German.
|
|
29
|
+
* Requests to the explicit default prefix (`/en/about`) are redirected to `/about`. */
|
|
30
|
+
hideDefaultLocale?: boolean;
|
|
27
31
|
/** Cookie name for storing selected language (defaults to 'i18next') */
|
|
28
32
|
cookieName?: string;
|
|
29
33
|
/** Custom header name for passing language to server components (defaults to 'x-i18next-current-language') */
|
|
@@ -63,6 +67,7 @@ interface NormalizedConfig {
|
|
|
63
67
|
defaultNS: string;
|
|
64
68
|
ns: string[];
|
|
65
69
|
localeInPath: boolean;
|
|
70
|
+
hideDefaultLocale: boolean;
|
|
66
71
|
localePath: string;
|
|
67
72
|
localeStructure: string;
|
|
68
73
|
localeExtension: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../../../src/appRouter/types.ts","../../../src/appRouter/config.ts","../../../src/appRouter/proxy/index.ts"],"mappings":";;;;KAEY,cAAA,IAAkB,QAAA,UAAkB,SAAA,aAAsB,OAAA;AAAA,UAErD,UAAA;;EAEf,aAAA;EAJwB;EAMxB,WAAA;EAN2E;EAQ3E,SAAA;EAR8C;EAU9C,EAAA;EAV2E;EAc3E,UAAA;EAZe;EAcf,eAAA;;EAEA,eAAA;EAIiB;EAFjB,SAAA,GAAY,QAAA;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../../../src/appRouter/types.ts","../../../src/appRouter/config.ts","../../../src/appRouter/proxy/index.ts"],"mappings":";;;;KAEY,cAAA,IAAkB,QAAA,UAAkB,SAAA,aAAsB,OAAA;AAAA,UAErD,UAAA;;EAEf,aAAA;EAJwB;EAMxB,WAAA;EAN2E;EAQ3E,SAAA;EAR8C;EAU9C,EAAA;EAV2E;EAc3E,UAAA;EAZe;EAcf,eAAA;;EAEA,eAAA;EAIiB;EAFjB,SAAA,GAAY,QAAA;EA4BK;EA1BjB,cAAA,GAAiB,cAAA;EA0BI;EAtBrB,YAAA;EApBA;;;EAwBA,iBAAA;EAdA;EAkBA,UAAA;EAdA;EAgBA,UAAA;EAdA;EAgBA,YAAA;EAZA;EAcA,YAAA;EANA;EAQA,QAAA;EAJA;EAQA,GAAA;EAJA;EAMA,cAAA,GAAiB,IAAA,CAAK,WAAA;EAAtB;EAIA,IAAA;IACE,aAAA;IACA,OAAA;IACA,OAAA;MACE,aAAA;MACA,MAAA;MACA,IAAA;MACA,OAAA;IAAA;IAEF,eAAA;EAAA;EAGF;EAAA,eAAA;EAIA;EAFA,iBAAA;EAEwB;EAAxB,wBAAA;AAAA;AAAA,UAGe,gBAAA;EACf,aAAA;EACA,WAAA;EACA,SAAA;EACA,EAAA;EACA,YAAA;EACA,iBAAA;EACA,UAAA;EACA,eAAA;EACA,eAAA;EACA,UAAA;EACA,UAAA;EACA,YAAA;EACA,YAAA;EACA,QAAA;EACA,SAAA,GAAY,QAAA;EACZ,cAAA,GAAiB,cAAA;EACjB,GAAA;EACA,cAAA,EAAgB,MAAA;EAChB,wBAAA;EAEA,IAAA,GAAO,UAAA;EACP,eAAA;EACA,iBAAA;AAAA;;;iBC9Fc,YAAA,CAAa,MAAA,EAAQ,UAAA,GAAa,UAAA;AAAA,iBAIlC,eAAA,CAAgB,UAAA,EAAY,UAAA,GAAa,gBAAA;;;iBCczC,WAAA,CAAY,UAAA,EAAY,UAAA,IAQX,GAAA,EAAK,WAAA,KAAc,YAAA;;;;;cA2HnC,gBAAA,SAAgB,WAAA"}
|
|
@@ -24,6 +24,10 @@ interface I18nConfig {
|
|
|
24
24
|
resourceLoader?: ResourceLoader;
|
|
25
25
|
/** Whether to include locale in URL path (defaults to true) */
|
|
26
26
|
localeInPath?: boolean;
|
|
27
|
+
/** When true (and localeInPath is true), the default language has no URL prefix.
|
|
28
|
+
* e.g. `/about` serves the default language, `/de/about` serves German.
|
|
29
|
+
* Requests to the explicit default prefix (`/en/about`) are redirected to `/about`. */
|
|
30
|
+
hideDefaultLocale?: boolean;
|
|
27
31
|
/** Cookie name for storing selected language (defaults to 'i18next') */
|
|
28
32
|
cookieName?: string;
|
|
29
33
|
/** Custom header name for passing language to server components (defaults to 'x-i18next-current-language') */
|
|
@@ -63,6 +67,7 @@ interface NormalizedConfig {
|
|
|
63
67
|
defaultNS: string;
|
|
64
68
|
ns: string[];
|
|
65
69
|
localeInPath: boolean;
|
|
70
|
+
hideDefaultLocale: boolean;
|
|
66
71
|
localePath: string;
|
|
67
72
|
localeStructure: string;
|
|
68
73
|
localeExtension: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/appRouter/types.ts","../../../src/appRouter/config.ts","../../../src/appRouter/proxy/index.ts"],"mappings":";;;;KAEY,cAAA,IAAkB,QAAA,UAAkB,SAAA,aAAsB,OAAA;AAAA,UAErD,UAAA;;EAEf,aAAA;EAJwB;EAMxB,WAAA;EAN2E;EAQ3E,SAAA;EAR8C;EAU9C,EAAA;EAV2E;EAc3E,UAAA;EAZe;EAcf,eAAA;;EAEA,eAAA;EAIiB;EAFjB,SAAA,GAAY,QAAA;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../../../src/appRouter/types.ts","../../../src/appRouter/config.ts","../../../src/appRouter/proxy/index.ts"],"mappings":";;;;KAEY,cAAA,IAAkB,QAAA,UAAkB,SAAA,aAAsB,OAAA;AAAA,UAErD,UAAA;;EAEf,aAAA;EAJwB;EAMxB,WAAA;EAN2E;EAQ3E,SAAA;EAR8C;EAU9C,EAAA;EAV2E;EAc3E,UAAA;EAZe;EAcf,eAAA;;EAEA,eAAA;EAIiB;EAFjB,SAAA,GAAY,QAAA;EA4BK;EA1BjB,cAAA,GAAiB,cAAA;EA0BI;EAtBrB,YAAA;EApBA;;;EAwBA,iBAAA;EAdA;EAkBA,UAAA;EAdA;EAgBA,UAAA;EAdA;EAgBA,YAAA;EAZA;EAcA,YAAA;EANA;EAQA,QAAA;EAJA;EAQA,GAAA;EAJA;EAMA,cAAA,GAAiB,IAAA,CAAK,WAAA;EAAtB;EAIA,IAAA;IACE,aAAA;IACA,OAAA;IACA,OAAA;MACE,aAAA;MACA,MAAA;MACA,IAAA;MACA,OAAA;IAAA;IAEF,eAAA;EAAA;EAGF;EAAA,eAAA;EAIA;EAFA,iBAAA;EAEwB;EAAxB,wBAAA;AAAA;AAAA,UAGe,gBAAA;EACf,aAAA;EACA,WAAA;EACA,SAAA;EACA,EAAA;EACA,YAAA;EACA,iBAAA;EACA,UAAA;EACA,eAAA;EACA,eAAA;EACA,UAAA;EACA,UAAA;EACA,YAAA;EACA,YAAA;EACA,QAAA;EACA,SAAA,GAAY,QAAA;EACZ,cAAA,GAAiB,cAAA;EACjB,GAAA;EACA,cAAA,EAAgB,MAAA;EAChB,wBAAA;EAEA,IAAA,GAAO,UAAA;EACP,eAAA;EACA,iBAAA;AAAA;;;iBC9Fc,YAAA,CAAa,MAAA,EAAQ,UAAA,GAAa,UAAA;AAAA,iBAIlC,eAAA,CAAgB,UAAA,EAAY,UAAA,GAAa,gBAAA;;;iBCczC,WAAA,CAAY,UAAA,EAAY,UAAA,IAQX,GAAA,EAAK,WAAA,KAAc,YAAA;;;;;cA2HnC,gBAAA,SAAgB,WAAA"}
|
|
@@ -15,6 +15,7 @@ function normalizeConfig(userConfig) {
|
|
|
15
15
|
defaultNS,
|
|
16
16
|
ns: userConfig.ns ?? [defaultNS],
|
|
17
17
|
localeInPath: userConfig.localeInPath ?? true,
|
|
18
|
+
hideDefaultLocale: userConfig.hideDefaultLocale ?? false,
|
|
18
19
|
localePath: userConfig.localePath ?? "/locales",
|
|
19
20
|
localeStructure: userConfig.localeStructure ?? "{{lng}}/{{ns}}",
|
|
20
21
|
localeExtension: userConfig.localeExtension ?? "json",
|
|
@@ -107,11 +108,33 @@ function createProxy(userConfig) {
|
|
|
107
108
|
if (!lng) lng = config.fallbackLng;
|
|
108
109
|
const lngInPath = findLocaleInPath(basePath ? pathname.slice(basePath.length) || "/" : pathname, config.supportedLngs, nonExplicit);
|
|
109
110
|
if (config.localeInPath) {
|
|
111
|
+
const prefix = basePath ?? "";
|
|
112
|
+
const pathAfterBase = basePath ? pathname.slice(basePath.length) : pathname;
|
|
113
|
+
if (config.hideDefaultLocale && lngInPath === config.fallbackLng) {
|
|
114
|
+
const pathWithoutLocale = pathAfterBase.replace(/^\/[^/]+/, "") || "/";
|
|
115
|
+
const redirectUrl = new URL(`${prefix}${pathWithoutLocale}${search}`, req.url);
|
|
116
|
+
const response = NextResponse.redirect(redirectUrl);
|
|
117
|
+
response.cookies.set(config.cookieName, config.fallbackLng, {
|
|
118
|
+
path: "/",
|
|
119
|
+
maxAge: config.cookieMaxAge,
|
|
120
|
+
sameSite: "lax"
|
|
121
|
+
});
|
|
122
|
+
return response;
|
|
123
|
+
}
|
|
110
124
|
const headers = new Headers(req.headers);
|
|
111
125
|
headers.set(config.headerName, lngInPath || lng);
|
|
112
126
|
if (!lngInPath) {
|
|
113
|
-
|
|
114
|
-
|
|
127
|
+
if (config.hideDefaultLocale) {
|
|
128
|
+
const rewriteUrl = new URL(`${prefix}/${config.fallbackLng}${pathAfterBase}${search}`, req.url);
|
|
129
|
+
headers.set(config.headerName, config.fallbackLng);
|
|
130
|
+
const response = NextResponse.rewrite(rewriteUrl, { request: { headers } });
|
|
131
|
+
response.cookies.set(config.cookieName, config.fallbackLng, {
|
|
132
|
+
path: "/",
|
|
133
|
+
maxAge: config.cookieMaxAge,
|
|
134
|
+
sameSite: "lax"
|
|
135
|
+
});
|
|
136
|
+
return response;
|
|
137
|
+
}
|
|
115
138
|
const redirectUrl = new URL(`${prefix}/${lng}${pathAfterBase}${search}`, req.url);
|
|
116
139
|
const response = NextResponse.redirect(redirectUrl);
|
|
117
140
|
response.cookies.set(config.cookieName, lng, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/appRouter/config.ts","../../../src/appRouter/proxy/languageDetector.ts","../../../src/appRouter/proxy/index.ts"],"sourcesContent":["import type { I18nConfig, NormalizedConfig } from './types'\n\nexport function defineConfig(config: I18nConfig): I18nConfig {\n return config\n}\n\nexport function normalizeConfig(userConfig: I18nConfig): NormalizedConfig {\n // Support legacy format: { i18n: { defaultLocale, locales } }\n const supportedLngs = userConfig.supportedLngs ??\n userConfig.i18n?.locales?.filter((l: string) => l !== 'default') ??\n ['en']\n const fallbackLng = userConfig.fallbackLng ??\n userConfig.i18n?.defaultLocale ??\n supportedLngs[0]\n\n if (!fallbackLng) {\n throw new Error('next-i18next: fallbackLng (or i18n.defaultLocale) is required')\n }\n if (supportedLngs.length === 0) {\n throw new Error('next-i18next: supportedLngs (or i18n.locales) must contain at least one language')\n }\n\n const defaultNS = userConfig.defaultNS ?? 'common'\n\n return {\n supportedLngs,\n fallbackLng,\n defaultNS,\n ns: userConfig.ns ?? [defaultNS],\n localeInPath: userConfig.localeInPath ?? true,\n localePath: userConfig.localePath ?? '/locales',\n localeStructure: userConfig.localeStructure ?? '{{lng}}/{{ns}}',\n localeExtension: userConfig.localeExtension ?? 'json',\n cookieName: userConfig.cookieName ?? 'i18next',\n headerName: userConfig.headerName ?? 'x-i18next-current-language',\n cookieMaxAge: userConfig.cookieMaxAge ?? 365 * 24 * 60 * 60,\n ignoredPaths: userConfig.ignoredPaths ?? ['/api', '/_next', '/static'],\n basePath: userConfig.basePath,\n resources: userConfig.resources,\n resourceLoader: userConfig.resourceLoader,\n use: userConfig.use ?? [],\n i18nextOptions: (userConfig.i18nextOptions ?? {}) as Record<string, any>,\n nonExplicitSupportedLngs: userConfig.nonExplicitSupportedLngs ?? false,\n // Preserve legacy fields\n i18n: userConfig.i18n,\n serializeConfig: userConfig.serializeConfig,\n reloadOnPrerender: userConfig.reloadOnPrerender,\n }\n}\n","/**\n * Edge-safe Accept-Language parser and language matcher.\n * No external dependencies — runs in Edge Runtime, Node.js, and browser.\n */\n\nexport function parseAcceptLanguage(header: string | null | undefined): string[] {\n if (!header) return []\n return header\n .split(',')\n .map(part => {\n const [lang, qPart] = part.trim().split(';')\n const q = qPart?.trim().startsWith('q=')\n ? parseFloat(qPart.trim().slice(2))\n : 1.0\n return { lang: lang.trim(), q: isNaN(q) ? 0 : q }\n })\n .filter(item => item.lang && item.q > 0)\n .sort((a, b) => b.q - a.q)\n .map(item => item.lang)\n}\n\n/**\n * Find a matching supported language for a given code, respecting nonExplicitSupportedLngs.\n *\n * Matching order (mirrors i18next's LanguageUtils.getBestMatchFromCodes):\n * 1. Exact match (case-insensitive)\n * 2. Preferred prefix → supported base (e.g. preferred 'en-US' matches supported 'en')\n * 3. If nonExplicitSupportedLngs: preferred base → supported region\n * (e.g. preferred 'en' matches supported 'en-US')\n */\nexport function findSupportedMatch(\n code: string,\n supportedLanguages: readonly string[],\n nonExplicitSupportedLngs: boolean,\n): string | undefined {\n const lower = code.toLowerCase()\n\n // 1. Exact match (case-insensitive)\n const exact = supportedLanguages.find(l => l.toLowerCase() === lower)\n if (exact) return exact\n\n // 2. Preferred prefix → supported base (e.g. 'en-US' → 'en')\n const prefix = lower.split('-')[0]\n const partial = supportedLanguages.find(l => l.toLowerCase() === prefix)\n if (partial) return partial\n\n // 3. Reverse match: preferred base → supported region (e.g. 'en' → 'en-US')\n if (nonExplicitSupportedLngs) {\n const reverse = supportedLanguages.find(\n l => l.toLowerCase().split('-')[0] === prefix\n )\n if (reverse) return reverse\n }\n\n return undefined\n}\n\nexport function matchLanguage(\n acceptLanguages: string[],\n supportedLanguages: readonly string[],\n defaultLanguage: string,\n nonExplicitSupportedLngs = false,\n): string {\n for (const preferred of acceptLanguages) {\n const match = findSupportedMatch(preferred, supportedLanguages, nonExplicitSupportedLngs)\n if (match) return match\n }\n return defaultLanguage\n}\n","import { NextRequest, NextResponse } from 'next/server'\nimport type { I18nConfig } from '../types'\nimport { normalizeConfig } from '../config'\nimport { parseAcceptLanguage, matchLanguage, findSupportedMatch } from './languageDetector'\n\n// Re-export config utilities for Edge-safe usage (no react-i18next dependency)\nexport { defineConfig, normalizeConfig } from '../config'\nexport type { I18nConfig, NormalizedConfig, ResourceLoader } from '../types'\n\nfunction findLocaleInPath(\n pathname: string,\n supportedLngs: readonly string[],\n nonExplicitSupportedLngs: boolean,\n): string | undefined {\n // Extract the first path segment\n const match = pathname.match(/^\\/([^/]+)/)\n if (!match) return undefined\n return findSupportedMatch(match[1], supportedLngs, nonExplicitSupportedLngs)\n}\n\nexport function createProxy(userConfig: I18nConfig) {\n const config = normalizeConfig(userConfig)\n const nonExplicit = config.nonExplicitSupportedLngs\n // Normalize basePath: ensure leading slash, strip trailing slash\n const basePath = config.basePath\n ? ('/' + config.basePath.replace(/^\\/+/, '').replace(/\\/+$/, ''))\n : undefined\n\n return function middleware(req: NextRequest): NextResponse {\n const { pathname, search } = req.nextUrl\n\n // When basePath is set, only handle requests under that prefix\n if (basePath) {\n if (pathname !== basePath && !pathname.startsWith(basePath + '/')) {\n return NextResponse.next()\n }\n }\n\n // Skip ignored paths\n for (const ignored of config.ignoredPaths) {\n if (pathname.startsWith(ignored)) {\n return NextResponse.next()\n }\n }\n\n // Skip common static file extensions\n if (/\\.(ico|png|jpg|jpeg|svg|gif|webp|css|js|map|woff2?|ttf|eot)$/.test(pathname)) {\n return NextResponse.next()\n }\n\n // Detect language from cookie, then Accept-Language header, then default\n let lng: string | undefined\n const cookieValue = req.cookies.get(config.cookieName)?.value\n if (cookieValue) {\n lng = matchLanguage([cookieValue], config.supportedLngs, config.fallbackLng, nonExplicit)\n }\n if (!lng) {\n lng = matchLanguage(\n parseAcceptLanguage(req.headers.get('Accept-Language')),\n config.supportedLngs,\n config.fallbackLng,\n nonExplicit,\n )\n }\n if (!lng) {\n lng = config.fallbackLng\n }\n\n // For locale-in-path detection, strip basePath prefix so we look at the right segment\n const pathForLocale = basePath ? pathname.slice(basePath.length) || '/' : pathname\n const lngInPath = findLocaleInPath(pathForLocale, config.supportedLngs, nonExplicit)\n\n if (config.localeInPath) {\n // Set custom header for server components to read\n const headers = new Headers(req.headers)\n headers.set(config.headerName, lngInPath || lng)\n\n // Redirect if no locale in path\n if (!lngInPath) {\n const prefix = basePath ?? ''\n const pathAfterBase = basePath ? pathname.slice(basePath.length) : pathname\n const redirectUrl = new URL(`${prefix}/${lng}${pathAfterBase}${search}`, req.url)\n const response = NextResponse.redirect(redirectUrl)\n response.cookies.set(config.cookieName, lng, {\n path: '/',\n maxAge: config.cookieMaxAge,\n sameSite: 'lax',\n })\n return response\n }\n\n // Persist language from referer URL into cookie\n const response = NextResponse.next({ request: { headers } })\n if (req.headers.has('referer')) {\n const refererUrl = new URL(req.headers.get('referer')!)\n const refererPathForLocale = basePath\n ? refererUrl.pathname.slice(basePath.length) || '/'\n : refererUrl.pathname\n const lngInReferer = findLocaleInPath(refererPathForLocale, config.supportedLngs, nonExplicit)\n if (lngInReferer) {\n response.cookies.set(config.cookieName, lngInReferer, {\n path: '/',\n maxAge: config.cookieMaxAge,\n sameSite: 'lax',\n })\n }\n }\n\n return response\n } else {\n // No-locale-path mode: don't redirect, just set the header\n const headers = new Headers(req.headers)\n headers.set(config.headerName, lng)\n\n const response = NextResponse.next({ request: { headers } })\n return response\n }\n }\n}\n\n/**\n * Backwards-compatible alias for createProxy.\n * Use `createProxy` for new projects with Next.js 16+ `proxy.ts`.\n */\nexport const createMiddleware = createProxy\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../../../src/appRouter/config.ts","../../../src/appRouter/proxy/languageDetector.ts","../../../src/appRouter/proxy/index.ts"],"sourcesContent":["import type { I18nConfig, NormalizedConfig } from './types'\n\nexport function defineConfig(config: I18nConfig): I18nConfig {\n return config\n}\n\nexport function normalizeConfig(userConfig: I18nConfig): NormalizedConfig {\n // Support legacy format: { i18n: { defaultLocale, locales } }\n const supportedLngs = userConfig.supportedLngs ??\n userConfig.i18n?.locales?.filter((l: string) => l !== 'default') ??\n ['en']\n const fallbackLng = userConfig.fallbackLng ??\n userConfig.i18n?.defaultLocale ??\n supportedLngs[0]\n\n if (!fallbackLng) {\n throw new Error('next-i18next: fallbackLng (or i18n.defaultLocale) is required')\n }\n if (supportedLngs.length === 0) {\n throw new Error('next-i18next: supportedLngs (or i18n.locales) must contain at least one language')\n }\n\n const defaultNS = userConfig.defaultNS ?? 'common'\n\n return {\n supportedLngs,\n fallbackLng,\n defaultNS,\n ns: userConfig.ns ?? [defaultNS],\n localeInPath: userConfig.localeInPath ?? true,\n hideDefaultLocale: userConfig.hideDefaultLocale ?? false,\n localePath: userConfig.localePath ?? '/locales',\n localeStructure: userConfig.localeStructure ?? '{{lng}}/{{ns}}',\n localeExtension: userConfig.localeExtension ?? 'json',\n cookieName: userConfig.cookieName ?? 'i18next',\n headerName: userConfig.headerName ?? 'x-i18next-current-language',\n cookieMaxAge: userConfig.cookieMaxAge ?? 365 * 24 * 60 * 60,\n ignoredPaths: userConfig.ignoredPaths ?? ['/api', '/_next', '/static'],\n basePath: userConfig.basePath,\n resources: userConfig.resources,\n resourceLoader: userConfig.resourceLoader,\n use: userConfig.use ?? [],\n i18nextOptions: (userConfig.i18nextOptions ?? {}) as Record<string, any>,\n nonExplicitSupportedLngs: userConfig.nonExplicitSupportedLngs ?? false,\n // Preserve legacy fields\n i18n: userConfig.i18n,\n serializeConfig: userConfig.serializeConfig,\n reloadOnPrerender: userConfig.reloadOnPrerender,\n }\n}\n","/**\n * Edge-safe Accept-Language parser and language matcher.\n * No external dependencies — runs in Edge Runtime, Node.js, and browser.\n */\n\nexport function parseAcceptLanguage(header: string | null | undefined): string[] {\n if (!header) return []\n return header\n .split(',')\n .map(part => {\n const [lang, qPart] = part.trim().split(';')\n const q = qPart?.trim().startsWith('q=')\n ? parseFloat(qPart.trim().slice(2))\n : 1.0\n return { lang: lang.trim(), q: isNaN(q) ? 0 : q }\n })\n .filter(item => item.lang && item.q > 0)\n .sort((a, b) => b.q - a.q)\n .map(item => item.lang)\n}\n\n/**\n * Find a matching supported language for a given code, respecting nonExplicitSupportedLngs.\n *\n * Matching order (mirrors i18next's LanguageUtils.getBestMatchFromCodes):\n * 1. Exact match (case-insensitive)\n * 2. Preferred prefix → supported base (e.g. preferred 'en-US' matches supported 'en')\n * 3. If nonExplicitSupportedLngs: preferred base → supported region\n * (e.g. preferred 'en' matches supported 'en-US')\n */\nexport function findSupportedMatch(\n code: string,\n supportedLanguages: readonly string[],\n nonExplicitSupportedLngs: boolean,\n): string | undefined {\n const lower = code.toLowerCase()\n\n // 1. Exact match (case-insensitive)\n const exact = supportedLanguages.find(l => l.toLowerCase() === lower)\n if (exact) return exact\n\n // 2. Preferred prefix → supported base (e.g. 'en-US' → 'en')\n const prefix = lower.split('-')[0]\n const partial = supportedLanguages.find(l => l.toLowerCase() === prefix)\n if (partial) return partial\n\n // 3. Reverse match: preferred base → supported region (e.g. 'en' → 'en-US')\n if (nonExplicitSupportedLngs) {\n const reverse = supportedLanguages.find(\n l => l.toLowerCase().split('-')[0] === prefix\n )\n if (reverse) return reverse\n }\n\n return undefined\n}\n\nexport function matchLanguage(\n acceptLanguages: string[],\n supportedLanguages: readonly string[],\n defaultLanguage: string,\n nonExplicitSupportedLngs = false,\n): string {\n for (const preferred of acceptLanguages) {\n const match = findSupportedMatch(preferred, supportedLanguages, nonExplicitSupportedLngs)\n if (match) return match\n }\n return defaultLanguage\n}\n","import { NextRequest, NextResponse } from 'next/server'\nimport type { I18nConfig } from '../types'\nimport { normalizeConfig } from '../config'\nimport { parseAcceptLanguage, matchLanguage, findSupportedMatch } from './languageDetector'\n\n// Re-export config utilities for Edge-safe usage (no react-i18next dependency)\nexport { defineConfig, normalizeConfig } from '../config'\nexport type { I18nConfig, NormalizedConfig, ResourceLoader } from '../types'\n\nfunction findLocaleInPath(\n pathname: string,\n supportedLngs: readonly string[],\n nonExplicitSupportedLngs: boolean,\n): string | undefined {\n // Extract the first path segment\n const match = pathname.match(/^\\/([^/]+)/)\n if (!match) return undefined\n return findSupportedMatch(match[1], supportedLngs, nonExplicitSupportedLngs)\n}\n\nexport function createProxy(userConfig: I18nConfig) {\n const config = normalizeConfig(userConfig)\n const nonExplicit = config.nonExplicitSupportedLngs\n // Normalize basePath: ensure leading slash, strip trailing slash\n const basePath = config.basePath\n ? ('/' + config.basePath.replace(/^\\/+/, '').replace(/\\/+$/, ''))\n : undefined\n\n return function middleware(req: NextRequest): NextResponse {\n const { pathname, search } = req.nextUrl\n\n // When basePath is set, only handle requests under that prefix\n if (basePath) {\n if (pathname !== basePath && !pathname.startsWith(basePath + '/')) {\n return NextResponse.next()\n }\n }\n\n // Skip ignored paths\n for (const ignored of config.ignoredPaths) {\n if (pathname.startsWith(ignored)) {\n return NextResponse.next()\n }\n }\n\n // Skip common static file extensions\n if (/\\.(ico|png|jpg|jpeg|svg|gif|webp|css|js|map|woff2?|ttf|eot)$/.test(pathname)) {\n return NextResponse.next()\n }\n\n // Detect language from cookie, then Accept-Language header, then default\n let lng: string | undefined\n const cookieValue = req.cookies.get(config.cookieName)?.value\n if (cookieValue) {\n lng = matchLanguage([cookieValue], config.supportedLngs, config.fallbackLng, nonExplicit)\n }\n if (!lng) {\n lng = matchLanguage(\n parseAcceptLanguage(req.headers.get('Accept-Language')),\n config.supportedLngs,\n config.fallbackLng,\n nonExplicit,\n )\n }\n if (!lng) {\n lng = config.fallbackLng\n }\n\n // For locale-in-path detection, strip basePath prefix so we look at the right segment\n const pathForLocale = basePath ? pathname.slice(basePath.length) || '/' : pathname\n const lngInPath = findLocaleInPath(pathForLocale, config.supportedLngs, nonExplicit)\n\n if (config.localeInPath) {\n const prefix = basePath ?? ''\n const pathAfterBase = basePath ? pathname.slice(basePath.length) : pathname\n\n // hideDefaultLocale: redirect explicit default-locale paths to the clean URL\n if (config.hideDefaultLocale && lngInPath === config.fallbackLng) {\n const pathWithoutLocale = pathAfterBase.replace(/^\\/[^/]+/, '') || '/'\n const redirectUrl = new URL(`${prefix}${pathWithoutLocale}${search}`, req.url)\n const response = NextResponse.redirect(redirectUrl)\n response.cookies.set(config.cookieName, config.fallbackLng, {\n path: '/',\n maxAge: config.cookieMaxAge,\n sameSite: 'lax',\n })\n return response\n }\n\n // Set custom header for server components to read\n const headers = new Headers(req.headers)\n headers.set(config.headerName, lngInPath || lng)\n\n // Redirect if no locale in path\n if (!lngInPath) {\n if (config.hideDefaultLocale) {\n // Rewrite internally to the default-locale path, keeping the clean URL\n const rewriteUrl = new URL(`${prefix}/${config.fallbackLng}${pathAfterBase}${search}`, req.url)\n headers.set(config.headerName, config.fallbackLng)\n const response = NextResponse.rewrite(rewriteUrl, { request: { headers } })\n response.cookies.set(config.cookieName, config.fallbackLng, {\n path: '/',\n maxAge: config.cookieMaxAge,\n sameSite: 'lax',\n })\n return response\n }\n\n const redirectUrl = new URL(`${prefix}/${lng}${pathAfterBase}${search}`, req.url)\n const response = NextResponse.redirect(redirectUrl)\n response.cookies.set(config.cookieName, lng, {\n path: '/',\n maxAge: config.cookieMaxAge,\n sameSite: 'lax',\n })\n return response\n }\n\n // Persist language from referer URL into cookie\n const response = NextResponse.next({ request: { headers } })\n if (req.headers.has('referer')) {\n const refererUrl = new URL(req.headers.get('referer')!)\n const refererPathForLocale = basePath\n ? refererUrl.pathname.slice(basePath.length) || '/'\n : refererUrl.pathname\n const lngInReferer = findLocaleInPath(refererPathForLocale, config.supportedLngs, nonExplicit)\n if (lngInReferer) {\n response.cookies.set(config.cookieName, lngInReferer, {\n path: '/',\n maxAge: config.cookieMaxAge,\n sameSite: 'lax',\n })\n }\n }\n\n return response\n } else {\n // No-locale-path mode: don't redirect, just set the header\n const headers = new Headers(req.headers)\n headers.set(config.headerName, lng)\n\n const response = NextResponse.next({ request: { headers } })\n return response\n }\n }\n}\n\n/**\n * Backwards-compatible alias for createProxy.\n * Use `createProxy` for new projects with Next.js 16+ `proxy.ts`.\n */\nexport const createMiddleware = createProxy\n"],"mappings":"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"}
|
|
@@ -39,6 +39,7 @@ function normalizeConfig(userConfig) {
|
|
|
39
39
|
defaultNS,
|
|
40
40
|
ns: userConfig.ns ?? [defaultNS],
|
|
41
41
|
localeInPath: userConfig.localeInPath ?? true,
|
|
42
|
+
hideDefaultLocale: userConfig.hideDefaultLocale ?? false,
|
|
42
43
|
localePath: userConfig.localePath ?? "/locales",
|
|
43
44
|
localeStructure: userConfig.localeStructure ?? "{{lng}}/{{ns}}",
|
|
44
45
|
localeExtension: userConfig.localeExtension ?? "json",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.cjs","names":[],"sources":["../../src/appRouter/config.ts","../../src/appRouter/server.ts"],"sourcesContent":["import type { I18nConfig, NormalizedConfig } from './types'\n\nexport function defineConfig(config: I18nConfig): I18nConfig {\n return config\n}\n\nexport function normalizeConfig(userConfig: I18nConfig): NormalizedConfig {\n // Support legacy format: { i18n: { defaultLocale, locales } }\n const supportedLngs = userConfig.supportedLngs ??\n userConfig.i18n?.locales?.filter((l: string) => l !== 'default') ??\n ['en']\n const fallbackLng = userConfig.fallbackLng ??\n userConfig.i18n?.defaultLocale ??\n supportedLngs[0]\n\n if (!fallbackLng) {\n throw new Error('next-i18next: fallbackLng (or i18n.defaultLocale) is required')\n }\n if (supportedLngs.length === 0) {\n throw new Error('next-i18next: supportedLngs (or i18n.locales) must contain at least one language')\n }\n\n const defaultNS = userConfig.defaultNS ?? 'common'\n\n return {\n supportedLngs,\n fallbackLng,\n defaultNS,\n ns: userConfig.ns ?? [defaultNS],\n localeInPath: userConfig.localeInPath ?? true,\n localePath: userConfig.localePath ?? '/locales',\n localeStructure: userConfig.localeStructure ?? '{{lng}}/{{ns}}',\n localeExtension: userConfig.localeExtension ?? 'json',\n cookieName: userConfig.cookieName ?? 'i18next',\n headerName: userConfig.headerName ?? 'x-i18next-current-language',\n cookieMaxAge: userConfig.cookieMaxAge ?? 365 * 24 * 60 * 60,\n ignoredPaths: userConfig.ignoredPaths ?? ['/api', '/_next', '/static'],\n basePath: userConfig.basePath,\n resources: userConfig.resources,\n resourceLoader: userConfig.resourceLoader,\n use: userConfig.use ?? [],\n i18nextOptions: (userConfig.i18nextOptions ?? {}) as Record<string, any>,\n nonExplicitSupportedLngs: userConfig.nonExplicitSupportedLngs ?? false,\n // Preserve legacy fields\n i18n: userConfig.i18n,\n serializeConfig: userConfig.serializeConfig,\n reloadOnPrerender: userConfig.reloadOnPrerender,\n }\n}\n","import { createInstance } from 'i18next'\nimport type { i18n as I18NextClient, Resource, Module, FlatNamespace, KeyPrefix } from 'i18next'\nimport resourcesToBackend from 'i18next-resources-to-backend'\nimport { cache } from 'react'\nimport { headers, cookies } from 'next/headers'\n\nimport type { I18nConfig, NormalizedConfig, GetTResult } from './types'\nimport { normalizeConfig } from './config'\n\nlet _config: NormalizedConfig | null = null\n\n// Module-level singleton: persists across requests within the same server process.\n// This is critical for custom backends (i18next-http-backend, i18next-locize-backend)\n// to avoid re-fetching translations on every request.\n// In serverless environments (Lambda, Cloud Functions, etc.), this lives as long as\n// the warm function instance — backends with reloadInterval will refresh automatically.\nlet _sharedInstance: I18NextClient | null = null\nlet _sharedInstancePromise: Promise<I18NextClient> | null = null\n\nfunction getConfig(): NormalizedConfig {\n if (!_config) {\n throw new Error(\n 'next-i18next: Server module not initialized. Call initServerI18next(config) in your root layout.'\n )\n }\n return _config\n}\n\n/**\n * Initialize the server-side i18next configuration.\n * Call this once in your root layout or a shared setup file.\n */\nexport function initServerI18next(userConfig: I18nConfig): void {\n _config = normalizeConfig(userConfig)\n}\n\nfunction hasCustomBackend(plugins: any[]): boolean {\n return plugins.some((b: Module) => b.type === 'backend')\n}\n\nfunction createResourceBackend(config: NormalizedConfig) {\n if (config.resourceLoader) {\n return resourcesToBackend(config.resourceLoader)\n }\n return resourcesToBackend(async (language: string, namespace: string) => {\n const filePath = `${config.localePath}/${config.localeStructure\n .replace('{{lng}}', language)\n .replace('{{ns}}', namespace)}.${config.localeExtension}`\n\n // Node.js runtime: read from filesystem\n if (typeof process !== 'undefined' && process.versions?.node) {\n try {\n const fs = await import('fs/promises')\n const pathMod = await import('path')\n const resolved = pathMod.resolve(process.cwd(), `public${filePath}`)\n const content = await fs.readFile(resolved, 'utf-8')\n return JSON.parse(content)\n } catch {\n throw new Error(\n `next-i18next: Could not read locale file \"public${filePath}\". ` +\n 'On serverless platforms (Vercel, AWS Lambda, etc.), files in public/ are served via CDN ' +\n 'but are NOT available on the filesystem at runtime. Use the `resourceLoader` option with ' +\n 'dynamic imports instead:\\n\\n' +\n ' resourceLoader: (language, namespace) =>\\n' +\n // eslint-disable-next-line no-template-curly-in-string\n ' import(`./public/locales/${language}/${namespace}.json`)\\n'\n )\n }\n }\n\n // Edge runtime: filesystem not available\n throw new Error(\n `next-i18next: Cannot load locale file \"${filePath}\" in Edge Runtime. ` +\n 'Provide pre-bundled `resources`, a custom `resourceLoader`, or use a custom backend (e.g. i18next-http-backend) via the `use` option.'\n )\n })\n}\n\n/**\n * Get or create the shared i18next instance.\n * The instance is created once and reused across all requests.\n * All languages are preloaded so that getFixedT(lng) works for any supported language.\n * Additional namespaces are loaded on demand and cached in the instance store.\n */\nasync function getSharedInstance(config: NormalizedConfig): Promise<I18NextClient> {\n if (_sharedInstance?.isInitialized) return _sharedInstance\n\n // Deduplicate concurrent init calls (multiple requests arriving while first init is in flight)\n if (_sharedInstancePromise) return _sharedInstancePromise\n\n _sharedInstancePromise = (async () => {\n const i18nInstance = createInstance()\n\n // Add a backend when needed:\n // - No resources provided → backend loads everything\n // - Resources provided with partialBundledLanguages → backend loads the rest\n // - Custom backend in config.use → user handles it, skip default backend\n const partialBundled = config.i18nextOptions?.partialBundledLanguages\n if ((!config.resources || partialBundled) && !hasCustomBackend(config.use)) {\n i18nInstance.use(createResourceBackend(config))\n }\n\n config.use.forEach((plugin: any) => i18nInstance.use(plugin))\n\n await i18nInstance.init({\n // No `lng` — the shared instance is language-neutral.\n // We use getFixedT(lng, ns) to get language-specific translators.\n lng: config.fallbackLng,\n ns: config.ns,\n defaultNS: config.defaultNS,\n fallbackLng: config.fallbackLng,\n supportedLngs: config.supportedLngs,\n nonExplicitSupportedLngs: config.nonExplicitSupportedLngs,\n fallbackNS: config.defaultNS,\n preload: config.supportedLngs, // preload ALL languages upfront\n interpolation: { escapeValue: false },\n ...(config.resources ? { resources: config.resources } : {}),\n ...config.i18nextOptions,\n })\n\n _sharedInstance = i18nInstance\n return i18nInstance\n })()\n\n return _sharedInstancePromise\n}\n\n// Per-request language detection, deduplicated within a single React render\nconst detectLanguage = cache(async (config: NormalizedConfig): Promise<string> => {\n const headerList = await headers()\n const fromHeader = headerList.get(config.headerName)\n if (fromHeader) return fromHeader\n\n const cookieStore = await cookies()\n const cookieValue = cookieStore.get(config.cookieName)?.value\n if (cookieValue) {\n if (config.supportedLngs.includes(cookieValue)) {\n return cookieValue\n }\n // nonExplicitSupportedLngs: e.g. cookie 'en' matches supported 'en-US'\n if (config.nonExplicitSupportedLngs) {\n const prefix = cookieValue.toLowerCase().split('-')[0]\n const match = config.supportedLngs.find(\n l => l.toLowerCase() === prefix || l.toLowerCase().split('-')[0] === prefix\n )\n if (match) return match\n }\n }\n\n return config.fallbackLng\n})\n\n/**\n * Get a translation function for use in Server Components, layouts, and generateMetadata.\n *\n * The underlying i18next instance is a **module-level singleton** that persists across\n * requests. This means custom backends (i18next-http-backend, i18next-locize-backend, etc.)\n * only fetch translations once (or according to their own reloadInterval), not on every request.\n *\n * @example\n * ```tsx\n * import { getT } from 'next-i18next/server'\n *\n * export default async function Page() {\n * const { t, i18n } = await getT('home')\n * return <h1>{t('heading')}</h1>\n * }\n * ```\n */\nexport async function getT<\n Ns extends FlatNamespace = FlatNamespace,\n KPrefix extends KeyPrefix<Ns> = undefined,\n>(\n ns?: Ns | Ns[],\n options: { keyPrefix?: KPrefix; lng?: string } = {},\n): Promise<GetTResult<Ns, KPrefix>> {\n const config = getConfig()\n\n const lng = options.lng || await detectLanguage(config)\n const i18nInstance = await getSharedInstance(config)\n\n // Load additional namespaces on demand if not already loaded\n const nsArray: string[] = ns\n ? (Array.isArray(ns) ? ns as string[] : [ns as string])\n : config.ns\n const missingNs = nsArray.filter(n => !i18nInstance.hasLoadedNamespace(n))\n if (missingNs.length > 0) {\n await i18nInstance.loadNamespaces(missingNs)\n }\n\n const resolvedNs = ns\n ? (Array.isArray(ns) ? ns[0] : ns) as string\n : config.defaultNS\n\n return {\n t: i18nInstance.getFixedT(lng, resolvedNs, options.keyPrefix as string | undefined),\n i18n: i18nInstance,\n lng,\n } as any\n}\n\n/**\n * Extract loaded resources from the server i18next instance for passing to I18nProvider.\n *\n * @example\n * ```tsx\n * const { i18n } = await getT()\n * const resources = getResources(i18n, ['common', 'footer'])\n * return <I18nProvider language={i18n.language} resources={resources}>{children}</I18nProvider>\n * ```\n */\nexport function getResources(\n i18n: I18NextClient,\n namespaces?: string[],\n): Resource {\n const resources: Resource = {}\n const store = i18n.store?.data || {}\n const nsFilter = namespaces ? new Set(namespaces) : null\n\n for (const lng of Object.keys(store)) {\n resources[lng] = {}\n for (const ns of Object.keys(store[lng])) {\n if (!nsFilter || nsFilter.has(ns)) {\n resources[lng][ns] = store[lng][ns]\n }\n }\n }\n\n return resources\n}\n\n/**\n * Helper for generateStaticParams — returns params for all supported languages.\n *\n * @example\n * ```tsx\n * import { generateI18nStaticParams } from 'next-i18next/server'\n *\n * export async function generateStaticParams() {\n * return generateI18nStaticParams()\n * }\n * ```\n */\nexport function generateI18nStaticParams(): { lng: string }[] {\n const config = getConfig()\n return config.supportedLngs.map(lng => ({ lng }))\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"server.cjs","names":[],"sources":["../../src/appRouter/config.ts","../../src/appRouter/server.ts"],"sourcesContent":["import type { I18nConfig, NormalizedConfig } from './types'\n\nexport function defineConfig(config: I18nConfig): I18nConfig {\n return config\n}\n\nexport function normalizeConfig(userConfig: I18nConfig): NormalizedConfig {\n // Support legacy format: { i18n: { defaultLocale, locales } }\n const supportedLngs = userConfig.supportedLngs ??\n userConfig.i18n?.locales?.filter((l: string) => l !== 'default') ??\n ['en']\n const fallbackLng = userConfig.fallbackLng ??\n userConfig.i18n?.defaultLocale ??\n supportedLngs[0]\n\n if (!fallbackLng) {\n throw new Error('next-i18next: fallbackLng (or i18n.defaultLocale) is required')\n }\n if (supportedLngs.length === 0) {\n throw new Error('next-i18next: supportedLngs (or i18n.locales) must contain at least one language')\n }\n\n const defaultNS = userConfig.defaultNS ?? 'common'\n\n return {\n supportedLngs,\n fallbackLng,\n defaultNS,\n ns: userConfig.ns ?? [defaultNS],\n localeInPath: userConfig.localeInPath ?? true,\n hideDefaultLocale: userConfig.hideDefaultLocale ?? false,\n localePath: userConfig.localePath ?? '/locales',\n localeStructure: userConfig.localeStructure ?? '{{lng}}/{{ns}}',\n localeExtension: userConfig.localeExtension ?? 'json',\n cookieName: userConfig.cookieName ?? 'i18next',\n headerName: userConfig.headerName ?? 'x-i18next-current-language',\n cookieMaxAge: userConfig.cookieMaxAge ?? 365 * 24 * 60 * 60,\n ignoredPaths: userConfig.ignoredPaths ?? ['/api', '/_next', '/static'],\n basePath: userConfig.basePath,\n resources: userConfig.resources,\n resourceLoader: userConfig.resourceLoader,\n use: userConfig.use ?? [],\n i18nextOptions: (userConfig.i18nextOptions ?? {}) as Record<string, any>,\n nonExplicitSupportedLngs: userConfig.nonExplicitSupportedLngs ?? false,\n // Preserve legacy fields\n i18n: userConfig.i18n,\n serializeConfig: userConfig.serializeConfig,\n reloadOnPrerender: userConfig.reloadOnPrerender,\n }\n}\n","import { createInstance } from 'i18next'\nimport type { i18n as I18NextClient, Resource, Module, FlatNamespace, KeyPrefix } from 'i18next'\nimport resourcesToBackend from 'i18next-resources-to-backend'\nimport { cache } from 'react'\nimport { headers, cookies } from 'next/headers'\n\nimport type { I18nConfig, NormalizedConfig, GetTResult } from './types'\nimport { normalizeConfig } from './config'\n\nlet _config: NormalizedConfig | null = null\n\n// Module-level singleton: persists across requests within the same server process.\n// This is critical for custom backends (i18next-http-backend, i18next-locize-backend)\n// to avoid re-fetching translations on every request.\n// In serverless environments (Lambda, Cloud Functions, etc.), this lives as long as\n// the warm function instance — backends with reloadInterval will refresh automatically.\nlet _sharedInstance: I18NextClient | null = null\nlet _sharedInstancePromise: Promise<I18NextClient> | null = null\n\nfunction getConfig(): NormalizedConfig {\n if (!_config) {\n throw new Error(\n 'next-i18next: Server module not initialized. Call initServerI18next(config) in your root layout.'\n )\n }\n return _config\n}\n\n/**\n * Initialize the server-side i18next configuration.\n * Call this once in your root layout or a shared setup file.\n */\nexport function initServerI18next(userConfig: I18nConfig): void {\n _config = normalizeConfig(userConfig)\n}\n\nfunction hasCustomBackend(plugins: any[]): boolean {\n return plugins.some((b: Module) => b.type === 'backend')\n}\n\nfunction createResourceBackend(config: NormalizedConfig) {\n if (config.resourceLoader) {\n return resourcesToBackend(config.resourceLoader)\n }\n return resourcesToBackend(async (language: string, namespace: string) => {\n const filePath = `${config.localePath}/${config.localeStructure\n .replace('{{lng}}', language)\n .replace('{{ns}}', namespace)}.${config.localeExtension}`\n\n // Node.js runtime: read from filesystem\n if (typeof process !== 'undefined' && process.versions?.node) {\n try {\n const fs = await import('fs/promises')\n const pathMod = await import('path')\n const resolved = pathMod.resolve(process.cwd(), `public${filePath}`)\n const content = await fs.readFile(resolved, 'utf-8')\n return JSON.parse(content)\n } catch {\n throw new Error(\n `next-i18next: Could not read locale file \"public${filePath}\". ` +\n 'On serverless platforms (Vercel, AWS Lambda, etc.), files in public/ are served via CDN ' +\n 'but are NOT available on the filesystem at runtime. Use the `resourceLoader` option with ' +\n 'dynamic imports instead:\\n\\n' +\n ' resourceLoader: (language, namespace) =>\\n' +\n // eslint-disable-next-line no-template-curly-in-string\n ' import(`./public/locales/${language}/${namespace}.json`)\\n'\n )\n }\n }\n\n // Edge runtime: filesystem not available\n throw new Error(\n `next-i18next: Cannot load locale file \"${filePath}\" in Edge Runtime. ` +\n 'Provide pre-bundled `resources`, a custom `resourceLoader`, or use a custom backend (e.g. i18next-http-backend) via the `use` option.'\n )\n })\n}\n\n/**\n * Get or create the shared i18next instance.\n * The instance is created once and reused across all requests.\n * All languages are preloaded so that getFixedT(lng) works for any supported language.\n * Additional namespaces are loaded on demand and cached in the instance store.\n */\nasync function getSharedInstance(config: NormalizedConfig): Promise<I18NextClient> {\n if (_sharedInstance?.isInitialized) return _sharedInstance\n\n // Deduplicate concurrent init calls (multiple requests arriving while first init is in flight)\n if (_sharedInstancePromise) return _sharedInstancePromise\n\n _sharedInstancePromise = (async () => {\n const i18nInstance = createInstance()\n\n // Add a backend when needed:\n // - No resources provided → backend loads everything\n // - Resources provided with partialBundledLanguages → backend loads the rest\n // - Custom backend in config.use → user handles it, skip default backend\n const partialBundled = config.i18nextOptions?.partialBundledLanguages\n if ((!config.resources || partialBundled) && !hasCustomBackend(config.use)) {\n i18nInstance.use(createResourceBackend(config))\n }\n\n config.use.forEach((plugin: any) => i18nInstance.use(plugin))\n\n await i18nInstance.init({\n // No `lng` — the shared instance is language-neutral.\n // We use getFixedT(lng, ns) to get language-specific translators.\n lng: config.fallbackLng,\n ns: config.ns,\n defaultNS: config.defaultNS,\n fallbackLng: config.fallbackLng,\n supportedLngs: config.supportedLngs,\n nonExplicitSupportedLngs: config.nonExplicitSupportedLngs,\n fallbackNS: config.defaultNS,\n preload: config.supportedLngs, // preload ALL languages upfront\n interpolation: { escapeValue: false },\n ...(config.resources ? { resources: config.resources } : {}),\n ...config.i18nextOptions,\n })\n\n _sharedInstance = i18nInstance\n return i18nInstance\n })()\n\n return _sharedInstancePromise\n}\n\n// Per-request language detection, deduplicated within a single React render\nconst detectLanguage = cache(async (config: NormalizedConfig): Promise<string> => {\n const headerList = await headers()\n const fromHeader = headerList.get(config.headerName)\n if (fromHeader) return fromHeader\n\n const cookieStore = await cookies()\n const cookieValue = cookieStore.get(config.cookieName)?.value\n if (cookieValue) {\n if (config.supportedLngs.includes(cookieValue)) {\n return cookieValue\n }\n // nonExplicitSupportedLngs: e.g. cookie 'en' matches supported 'en-US'\n if (config.nonExplicitSupportedLngs) {\n const prefix = cookieValue.toLowerCase().split('-')[0]\n const match = config.supportedLngs.find(\n l => l.toLowerCase() === prefix || l.toLowerCase().split('-')[0] === prefix\n )\n if (match) return match\n }\n }\n\n return config.fallbackLng\n})\n\n/**\n * Get a translation function for use in Server Components, layouts, and generateMetadata.\n *\n * The underlying i18next instance is a **module-level singleton** that persists across\n * requests. This means custom backends (i18next-http-backend, i18next-locize-backend, etc.)\n * only fetch translations once (or according to their own reloadInterval), not on every request.\n *\n * @example\n * ```tsx\n * import { getT } from 'next-i18next/server'\n *\n * export default async function Page() {\n * const { t, i18n } = await getT('home')\n * return <h1>{t('heading')}</h1>\n * }\n * ```\n */\nexport async function getT<\n Ns extends FlatNamespace = FlatNamespace,\n KPrefix extends KeyPrefix<Ns> = undefined,\n>(\n ns?: Ns | Ns[],\n options: { keyPrefix?: KPrefix; lng?: string } = {},\n): Promise<GetTResult<Ns, KPrefix>> {\n const config = getConfig()\n\n const lng = options.lng || await detectLanguage(config)\n const i18nInstance = await getSharedInstance(config)\n\n // Load additional namespaces on demand if not already loaded\n const nsArray: string[] = ns\n ? (Array.isArray(ns) ? ns as string[] : [ns as string])\n : config.ns\n const missingNs = nsArray.filter(n => !i18nInstance.hasLoadedNamespace(n))\n if (missingNs.length > 0) {\n await i18nInstance.loadNamespaces(missingNs)\n }\n\n const resolvedNs = ns\n ? (Array.isArray(ns) ? ns[0] : ns) as string\n : config.defaultNS\n\n return {\n t: i18nInstance.getFixedT(lng, resolvedNs, options.keyPrefix as string | undefined),\n i18n: i18nInstance,\n lng,\n } as any\n}\n\n/**\n * Extract loaded resources from the server i18next instance for passing to I18nProvider.\n *\n * @example\n * ```tsx\n * const { i18n } = await getT()\n * const resources = getResources(i18n, ['common', 'footer'])\n * return <I18nProvider language={i18n.language} resources={resources}>{children}</I18nProvider>\n * ```\n */\nexport function getResources(\n i18n: I18NextClient,\n namespaces?: string[],\n): Resource {\n const resources: Resource = {}\n const store = i18n.store?.data || {}\n const nsFilter = namespaces ? new Set(namespaces) : null\n\n for (const lng of Object.keys(store)) {\n resources[lng] = {}\n for (const ns of Object.keys(store[lng])) {\n if (!nsFilter || nsFilter.has(ns)) {\n resources[lng][ns] = store[lng][ns]\n }\n }\n }\n\n return resources\n}\n\n/**\n * Helper for generateStaticParams — returns params for all supported languages.\n *\n * @example\n * ```tsx\n * import { generateI18nStaticParams } from 'next-i18next/server'\n *\n * export async function generateStaticParams() {\n * return generateI18nStaticParams()\n * }\n * ```\n */\nexport function generateI18nStaticParams(): { lng: string }[] {\n const config = getConfig()\n return config.supportedLngs.map(lng => ({ lng }))\n}\n"],"mappings":"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"}
|
|
@@ -23,6 +23,10 @@ interface I18nConfig {
|
|
|
23
23
|
resourceLoader?: ResourceLoader;
|
|
24
24
|
/** Whether to include locale in URL path (defaults to true) */
|
|
25
25
|
localeInPath?: boolean;
|
|
26
|
+
/** When true (and localeInPath is true), the default language has no URL prefix.
|
|
27
|
+
* e.g. `/about` serves the default language, `/de/about` serves German.
|
|
28
|
+
* Requests to the explicit default prefix (`/en/about`) are redirected to `/about`. */
|
|
29
|
+
hideDefaultLocale?: boolean;
|
|
26
30
|
/** Cookie name for storing selected language (defaults to 'i18next') */
|
|
27
31
|
cookieName?: string;
|
|
28
32
|
/** Custom header name for passing language to server components (defaults to 'x-i18next-current-language') */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.cts","names":[],"sources":["../../src/appRouter/types.ts","../../src/appRouter/server.ts"],"mappings":";;;KAEY,cAAA,IAAkB,QAAA,UAAkB,SAAA,aAAsB,OAAA;AAAA,UAErD,UAAA;EAFL;EAIV,aAAA;;EAEA,WAAA;EAN4B;EAQ5B,SAAA;EARoE;EAUpE,EAAA;EAV2E;EAc3E,UAAA;EAZyB;EAczB,eAAA;EAIY;EAFZ,eAAA;
|
|
1
|
+
{"version":3,"file":"server.d.cts","names":[],"sources":["../../src/appRouter/types.ts","../../src/appRouter/server.ts"],"mappings":";;;KAEY,cAAA,IAAkB,QAAA,UAAkB,SAAA,aAAsB,OAAA;AAAA,UAErD,UAAA;EAFL;EAIV,aAAA;;EAEA,WAAA;EAN4B;EAQ5B,SAAA;EARoE;EAUpE,EAAA;EAV2E;EAc3E,UAAA;EAZyB;EAczB,eAAA;EAIY;EAFZ,eAAA;EA8BsB;EA5BtB,SAAA,GAAY,QAAA;EA4BS;EA1BrB,cAAA,GAAiB,cAAA;EAlBjB;EAsBA,YAAA;EAlBA;;;EAsBA,iBAAA;EAZA;EAgBA,UAAA;EAdY;EAgBZ,UAAA;EAdiB;EAgBjB,YAAA;EARA;EAUA,YAAA;EAJA;EAMA,QAAA;EAFA;EAMA,GAAA;EAAA;EAEA,cAAA,GAAiB,IAAA,CAAK,WAAA;EAAL;EAIjB,IAAA;IACE,aAAA;IACA,OAAA;IACA,OAAA;MACE,aAAA;MACA,MAAA;MACA,IAAA;MACA,OAAA;IAAA;IAEF,eAAA;EAAA;EAKF;EAFA,eAAA;EAIwB;EAFxB,iBAAA;EA+BU;EA7BV,wBAAA;AAAA;AAAA,KA6BU,UAAA,YAAsB,aAAA,GAAgB,aAAA;EAChD,CAAA,EAAG,SAAA,CAAU,EAAA,EAAI,OAAA;EACjB,IAAA,EAAM,IAAA,ECrEsC;EDuE5C,GAAA;AAAA;;;;AArGF;;;iBC8BgB,iBAAA,CAAkB,UAAA,EAAY,UAAA;;;;;;AD5B9C;;;;;;;;;;;;iBCqKsB,IAAA,YACT,aAAA,GAAgB,aAAA,kBACX,SAAA,CAAU,EAAA,cAAA,CAE1B,EAAA,GAAK,EAAA,GAAK,EAAA,IACV,OAAA;EAAW,SAAA,GAAY,OAAA;EAAS,GAAA;AAAA,IAC/B,OAAA,CAAQ,UAAA,CAAW,EAAA,EAAI,OAAA;;;;;;;;;;;iBAoCV,YAAA,CACd,IAAA,EAAM,IAAA,EACN,UAAA,cACC,QAAA;;;;;;;;;;;;;iBA6Ba,wBAAA,CAAA;EAA8B,GAAA;AAAA"}
|
|
@@ -23,6 +23,10 @@ interface I18nConfig {
|
|
|
23
23
|
resourceLoader?: ResourceLoader;
|
|
24
24
|
/** Whether to include locale in URL path (defaults to true) */
|
|
25
25
|
localeInPath?: boolean;
|
|
26
|
+
/** When true (and localeInPath is true), the default language has no URL prefix.
|
|
27
|
+
* e.g. `/about` serves the default language, `/de/about` serves German.
|
|
28
|
+
* Requests to the explicit default prefix (`/en/about`) are redirected to `/about`. */
|
|
29
|
+
hideDefaultLocale?: boolean;
|
|
26
30
|
/** Cookie name for storing selected language (defaults to 'i18next') */
|
|
27
31
|
cookieName?: string;
|
|
28
32
|
/** Custom header name for passing language to server components (defaults to 'x-i18next-current-language') */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.mts","names":[],"sources":["../../src/appRouter/types.ts","../../src/appRouter/server.ts"],"mappings":";;;KAEY,cAAA,IAAkB,QAAA,UAAkB,SAAA,aAAsB,OAAA;AAAA,UAErD,UAAA;EAFL;EAIV,aAAA;;EAEA,WAAA;EAN4B;EAQ5B,SAAA;EARoE;EAUpE,EAAA;EAV2E;EAc3E,UAAA;EAZyB;EAczB,eAAA;EAIY;EAFZ,eAAA;
|
|
1
|
+
{"version":3,"file":"server.d.mts","names":[],"sources":["../../src/appRouter/types.ts","../../src/appRouter/server.ts"],"mappings":";;;KAEY,cAAA,IAAkB,QAAA,UAAkB,SAAA,aAAsB,OAAA;AAAA,UAErD,UAAA;EAFL;EAIV,aAAA;;EAEA,WAAA;EAN4B;EAQ5B,SAAA;EARoE;EAUpE,EAAA;EAV2E;EAc3E,UAAA;EAZyB;EAczB,eAAA;EAIY;EAFZ,eAAA;EA8BsB;EA5BtB,SAAA,GAAY,QAAA;EA4BS;EA1BrB,cAAA,GAAiB,cAAA;EAlBjB;EAsBA,YAAA;EAlBA;;;EAsBA,iBAAA;EAZA;EAgBA,UAAA;EAdY;EAgBZ,UAAA;EAdiB;EAgBjB,YAAA;EARA;EAUA,YAAA;EAJA;EAMA,QAAA;EAFA;EAMA,GAAA;EAAA;EAEA,cAAA,GAAiB,IAAA,CAAK,WAAA;EAAL;EAIjB,IAAA;IACE,aAAA;IACA,OAAA;IACA,OAAA;MACE,aAAA;MACA,MAAA;MACA,IAAA;MACA,OAAA;IAAA;IAEF,eAAA;EAAA;EAKF;EAFA,eAAA;EAIwB;EAFxB,iBAAA;EA+BU;EA7BV,wBAAA;AAAA;AAAA,KA6BU,UAAA,YAAsB,aAAA,GAAgB,aAAA;EAChD,CAAA,EAAG,SAAA,CAAU,EAAA,EAAI,OAAA;EACjB,IAAA,EAAM,IAAA,ECrEsC;EDuE5C,GAAA;AAAA;;;;AArGF;;;iBC8BgB,iBAAA,CAAkB,UAAA,EAAY,UAAA;;;;;;AD5B9C;;;;;;;;;;;;iBCqKsB,IAAA,YACT,aAAA,GAAgB,aAAA,kBACX,SAAA,CAAU,EAAA,cAAA,CAE1B,EAAA,GAAK,EAAA,GAAK,EAAA,IACV,OAAA;EAAW,SAAA,GAAY,OAAA;EAAS,GAAA;AAAA,IAC/B,OAAA,CAAQ,UAAA,CAAW,EAAA,EAAI,OAAA;;;;;;;;;;;iBAoCV,YAAA,CACd,IAAA,EAAM,IAAA,EACN,UAAA,cACC,QAAA;;;;;;;;;;;;;iBA6Ba,wBAAA,CAAA;EAA8B,GAAA;AAAA"}
|
|
@@ -15,6 +15,7 @@ function normalizeConfig(userConfig) {
|
|
|
15
15
|
defaultNS,
|
|
16
16
|
ns: userConfig.ns ?? [defaultNS],
|
|
17
17
|
localeInPath: userConfig.localeInPath ?? true,
|
|
18
|
+
hideDefaultLocale: userConfig.hideDefaultLocale ?? false,
|
|
18
19
|
localePath: userConfig.localePath ?? "/locales",
|
|
19
20
|
localeStructure: userConfig.localeStructure ?? "{{lng}}/{{ns}}",
|
|
20
21
|
localeExtension: userConfig.localeExtension ?? "json",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.mjs","names":[],"sources":["../../src/appRouter/config.ts","../../src/appRouter/server.ts"],"sourcesContent":["import type { I18nConfig, NormalizedConfig } from './types'\n\nexport function defineConfig(config: I18nConfig): I18nConfig {\n return config\n}\n\nexport function normalizeConfig(userConfig: I18nConfig): NormalizedConfig {\n // Support legacy format: { i18n: { defaultLocale, locales } }\n const supportedLngs = userConfig.supportedLngs ??\n userConfig.i18n?.locales?.filter((l: string) => l !== 'default') ??\n ['en']\n const fallbackLng = userConfig.fallbackLng ??\n userConfig.i18n?.defaultLocale ??\n supportedLngs[0]\n\n if (!fallbackLng) {\n throw new Error('next-i18next: fallbackLng (or i18n.defaultLocale) is required')\n }\n if (supportedLngs.length === 0) {\n throw new Error('next-i18next: supportedLngs (or i18n.locales) must contain at least one language')\n }\n\n const defaultNS = userConfig.defaultNS ?? 'common'\n\n return {\n supportedLngs,\n fallbackLng,\n defaultNS,\n ns: userConfig.ns ?? [defaultNS],\n localeInPath: userConfig.localeInPath ?? true,\n localePath: userConfig.localePath ?? '/locales',\n localeStructure: userConfig.localeStructure ?? '{{lng}}/{{ns}}',\n localeExtension: userConfig.localeExtension ?? 'json',\n cookieName: userConfig.cookieName ?? 'i18next',\n headerName: userConfig.headerName ?? 'x-i18next-current-language',\n cookieMaxAge: userConfig.cookieMaxAge ?? 365 * 24 * 60 * 60,\n ignoredPaths: userConfig.ignoredPaths ?? ['/api', '/_next', '/static'],\n basePath: userConfig.basePath,\n resources: userConfig.resources,\n resourceLoader: userConfig.resourceLoader,\n use: userConfig.use ?? [],\n i18nextOptions: (userConfig.i18nextOptions ?? {}) as Record<string, any>,\n nonExplicitSupportedLngs: userConfig.nonExplicitSupportedLngs ?? false,\n // Preserve legacy fields\n i18n: userConfig.i18n,\n serializeConfig: userConfig.serializeConfig,\n reloadOnPrerender: userConfig.reloadOnPrerender,\n }\n}\n","import { createInstance } from 'i18next'\nimport type { i18n as I18NextClient, Resource, Module, FlatNamespace, KeyPrefix } from 'i18next'\nimport resourcesToBackend from 'i18next-resources-to-backend'\nimport { cache } from 'react'\nimport { headers, cookies } from 'next/headers'\n\nimport type { I18nConfig, NormalizedConfig, GetTResult } from './types'\nimport { normalizeConfig } from './config'\n\nlet _config: NormalizedConfig | null = null\n\n// Module-level singleton: persists across requests within the same server process.\n// This is critical for custom backends (i18next-http-backend, i18next-locize-backend)\n// to avoid re-fetching translations on every request.\n// In serverless environments (Lambda, Cloud Functions, etc.), this lives as long as\n// the warm function instance — backends with reloadInterval will refresh automatically.\nlet _sharedInstance: I18NextClient | null = null\nlet _sharedInstancePromise: Promise<I18NextClient> | null = null\n\nfunction getConfig(): NormalizedConfig {\n if (!_config) {\n throw new Error(\n 'next-i18next: Server module not initialized. Call initServerI18next(config) in your root layout.'\n )\n }\n return _config\n}\n\n/**\n * Initialize the server-side i18next configuration.\n * Call this once in your root layout or a shared setup file.\n */\nexport function initServerI18next(userConfig: I18nConfig): void {\n _config = normalizeConfig(userConfig)\n}\n\nfunction hasCustomBackend(plugins: any[]): boolean {\n return plugins.some((b: Module) => b.type === 'backend')\n}\n\nfunction createResourceBackend(config: NormalizedConfig) {\n if (config.resourceLoader) {\n return resourcesToBackend(config.resourceLoader)\n }\n return resourcesToBackend(async (language: string, namespace: string) => {\n const filePath = `${config.localePath}/${config.localeStructure\n .replace('{{lng}}', language)\n .replace('{{ns}}', namespace)}.${config.localeExtension}`\n\n // Node.js runtime: read from filesystem\n if (typeof process !== 'undefined' && process.versions?.node) {\n try {\n const fs = await import('fs/promises')\n const pathMod = await import('path')\n const resolved = pathMod.resolve(process.cwd(), `public${filePath}`)\n const content = await fs.readFile(resolved, 'utf-8')\n return JSON.parse(content)\n } catch {\n throw new Error(\n `next-i18next: Could not read locale file \"public${filePath}\". ` +\n 'On serverless platforms (Vercel, AWS Lambda, etc.), files in public/ are served via CDN ' +\n 'but are NOT available on the filesystem at runtime. Use the `resourceLoader` option with ' +\n 'dynamic imports instead:\\n\\n' +\n ' resourceLoader: (language, namespace) =>\\n' +\n // eslint-disable-next-line no-template-curly-in-string\n ' import(`./public/locales/${language}/${namespace}.json`)\\n'\n )\n }\n }\n\n // Edge runtime: filesystem not available\n throw new Error(\n `next-i18next: Cannot load locale file \"${filePath}\" in Edge Runtime. ` +\n 'Provide pre-bundled `resources`, a custom `resourceLoader`, or use a custom backend (e.g. i18next-http-backend) via the `use` option.'\n )\n })\n}\n\n/**\n * Get or create the shared i18next instance.\n * The instance is created once and reused across all requests.\n * All languages are preloaded so that getFixedT(lng) works for any supported language.\n * Additional namespaces are loaded on demand and cached in the instance store.\n */\nasync function getSharedInstance(config: NormalizedConfig): Promise<I18NextClient> {\n if (_sharedInstance?.isInitialized) return _sharedInstance\n\n // Deduplicate concurrent init calls (multiple requests arriving while first init is in flight)\n if (_sharedInstancePromise) return _sharedInstancePromise\n\n _sharedInstancePromise = (async () => {\n const i18nInstance = createInstance()\n\n // Add a backend when needed:\n // - No resources provided → backend loads everything\n // - Resources provided with partialBundledLanguages → backend loads the rest\n // - Custom backend in config.use → user handles it, skip default backend\n const partialBundled = config.i18nextOptions?.partialBundledLanguages\n if ((!config.resources || partialBundled) && !hasCustomBackend(config.use)) {\n i18nInstance.use(createResourceBackend(config))\n }\n\n config.use.forEach((plugin: any) => i18nInstance.use(plugin))\n\n await i18nInstance.init({\n // No `lng` — the shared instance is language-neutral.\n // We use getFixedT(lng, ns) to get language-specific translators.\n lng: config.fallbackLng,\n ns: config.ns,\n defaultNS: config.defaultNS,\n fallbackLng: config.fallbackLng,\n supportedLngs: config.supportedLngs,\n nonExplicitSupportedLngs: config.nonExplicitSupportedLngs,\n fallbackNS: config.defaultNS,\n preload: config.supportedLngs, // preload ALL languages upfront\n interpolation: { escapeValue: false },\n ...(config.resources ? { resources: config.resources } : {}),\n ...config.i18nextOptions,\n })\n\n _sharedInstance = i18nInstance\n return i18nInstance\n })()\n\n return _sharedInstancePromise\n}\n\n// Per-request language detection, deduplicated within a single React render\nconst detectLanguage = cache(async (config: NormalizedConfig): Promise<string> => {\n const headerList = await headers()\n const fromHeader = headerList.get(config.headerName)\n if (fromHeader) return fromHeader\n\n const cookieStore = await cookies()\n const cookieValue = cookieStore.get(config.cookieName)?.value\n if (cookieValue) {\n if (config.supportedLngs.includes(cookieValue)) {\n return cookieValue\n }\n // nonExplicitSupportedLngs: e.g. cookie 'en' matches supported 'en-US'\n if (config.nonExplicitSupportedLngs) {\n const prefix = cookieValue.toLowerCase().split('-')[0]\n const match = config.supportedLngs.find(\n l => l.toLowerCase() === prefix || l.toLowerCase().split('-')[0] === prefix\n )\n if (match) return match\n }\n }\n\n return config.fallbackLng\n})\n\n/**\n * Get a translation function for use in Server Components, layouts, and generateMetadata.\n *\n * The underlying i18next instance is a **module-level singleton** that persists across\n * requests. This means custom backends (i18next-http-backend, i18next-locize-backend, etc.)\n * only fetch translations once (or according to their own reloadInterval), not on every request.\n *\n * @example\n * ```tsx\n * import { getT } from 'next-i18next/server'\n *\n * export default async function Page() {\n * const { t, i18n } = await getT('home')\n * return <h1>{t('heading')}</h1>\n * }\n * ```\n */\nexport async function getT<\n Ns extends FlatNamespace = FlatNamespace,\n KPrefix extends KeyPrefix<Ns> = undefined,\n>(\n ns?: Ns | Ns[],\n options: { keyPrefix?: KPrefix; lng?: string } = {},\n): Promise<GetTResult<Ns, KPrefix>> {\n const config = getConfig()\n\n const lng = options.lng || await detectLanguage(config)\n const i18nInstance = await getSharedInstance(config)\n\n // Load additional namespaces on demand if not already loaded\n const nsArray: string[] = ns\n ? (Array.isArray(ns) ? ns as string[] : [ns as string])\n : config.ns\n const missingNs = nsArray.filter(n => !i18nInstance.hasLoadedNamespace(n))\n if (missingNs.length > 0) {\n await i18nInstance.loadNamespaces(missingNs)\n }\n\n const resolvedNs = ns\n ? (Array.isArray(ns) ? ns[0] : ns) as string\n : config.defaultNS\n\n return {\n t: i18nInstance.getFixedT(lng, resolvedNs, options.keyPrefix as string | undefined),\n i18n: i18nInstance,\n lng,\n } as any\n}\n\n/**\n * Extract loaded resources from the server i18next instance for passing to I18nProvider.\n *\n * @example\n * ```tsx\n * const { i18n } = await getT()\n * const resources = getResources(i18n, ['common', 'footer'])\n * return <I18nProvider language={i18n.language} resources={resources}>{children}</I18nProvider>\n * ```\n */\nexport function getResources(\n i18n: I18NextClient,\n namespaces?: string[],\n): Resource {\n const resources: Resource = {}\n const store = i18n.store?.data || {}\n const nsFilter = namespaces ? new Set(namespaces) : null\n\n for (const lng of Object.keys(store)) {\n resources[lng] = {}\n for (const ns of Object.keys(store[lng])) {\n if (!nsFilter || nsFilter.has(ns)) {\n resources[lng][ns] = store[lng][ns]\n }\n }\n }\n\n return resources\n}\n\n/**\n * Helper for generateStaticParams — returns params for all supported languages.\n *\n * @example\n * ```tsx\n * import { generateI18nStaticParams } from 'next-i18next/server'\n *\n * export async function generateStaticParams() {\n * return generateI18nStaticParams()\n * }\n * ```\n */\nexport function generateI18nStaticParams(): { lng: string }[] {\n const config = getConfig()\n return config.supportedLngs.map(lng => ({ lng }))\n}\n"],"mappings":";;;;;AAMA,SAAgB,gBAAgB,YAA0C;CAExE,MAAM,gBAAgB,WAAW,iBAC/B,WAAW,MAAM,SAAS,QAAQ,MAAc,MAAM,UAAU,IAChE,CAAC,KAAK;CACR,MAAM,cAAc,WAAW,eAC7B,WAAW,MAAM,iBACjB,cAAc;AAEhB,KAAI,CAAC,YACH,OAAM,IAAI,MAAM,gEAAgE;AAElF,KAAI,cAAc,WAAW,EAC3B,OAAM,IAAI,MAAM,mFAAmF;CAGrG,MAAM,YAAY,WAAW,aAAa;AAE1C,QAAO;EACL;EACA;EACA;EACA,IAAI,WAAW,MAAM,CAAC,UAAU;EAChC,cAAc,WAAW,gBAAgB;EACzC,YAAY,WAAW,cAAc;EACrC,iBAAiB,WAAW,mBAAmB;EAC/C,iBAAiB,WAAW,mBAAmB;EAC/C,YAAY,WAAW,cAAc;EACrC,YAAY,WAAW,cAAc;EACrC,cAAc,WAAW,gBAAgB,MAAM,KAAK,KAAK;EACzD,cAAc,WAAW,gBAAgB;GAAC;GAAQ;GAAU;GAAU;EACtE,UAAU,WAAW;EACrB,WAAW,WAAW;EACtB,gBAAgB,WAAW;EAC3B,KAAK,WAAW,OAAO,EAAE;EACzB,gBAAiB,WAAW,kBAAkB,EAAE;EAChD,0BAA0B,WAAW,4BAA4B;EAEjE,MAAM,WAAW;EACjB,iBAAiB,WAAW;EAC5B,mBAAmB,WAAW;EAC/B;;;;ACtCH,IAAI,UAAmC;AAOvC,IAAI,kBAAwC;AAC5C,IAAI,yBAAwD;AAE5D,SAAS,YAA8B;AACrC,KAAI,CAAC,QACH,OAAM,IAAI,MACR,mGACD;AAEH,QAAO;;;;;;AAOT,SAAgB,kBAAkB,YAA8B;AAC9D,WAAU,gBAAgB,WAAW;;AAGvC,SAAS,iBAAiB,SAAyB;AACjD,QAAO,QAAQ,MAAM,MAAc,EAAE,SAAS,UAAU;;AAG1D,SAAS,sBAAsB,QAA0B;AACvD,KAAI,OAAO,eACT,QAAO,mBAAmB,OAAO,eAAe;AAElD,QAAO,mBAAmB,OAAO,UAAkB,cAAsB;EACvE,MAAM,WAAW,GAAG,OAAO,WAAW,GAAG,OAAO,gBAC7C,QAAQ,WAAW,SAAS,CAC5B,QAAQ,UAAU,UAAU,CAAC,GAAG,OAAO;AAG1C,MAAI,OAAO,YAAY,eAAe,QAAQ,UAAU,KACtD,KAAI;GACF,MAAM,KAAK,MAAM,OAAO;GAExB,MAAM,YADU,MAAM,OAAO,SACJ,QAAQ,QAAQ,KAAK,EAAE,SAAS,WAAW;GACpE,MAAM,UAAU,MAAM,GAAG,SAAS,UAAU,QAAQ;AACpD,UAAO,KAAK,MAAM,QAAQ;UACpB;AACN,SAAM,IAAI,MACR,mDAAmD,SAAS;;;;EAO7D;;AAKL,QAAM,IAAI,MACR,0CAA0C,SAAS,gKAEpD;GACD;;;;;;;;AASJ,eAAe,kBAAkB,QAAkD;AACjF,KAAI,iBAAiB,cAAe,QAAO;AAG3C,KAAI,uBAAwB,QAAO;AAEnC,2BAA0B,YAAY;EACpC,MAAM,eAAe,gBAAgB;EAMrC,MAAM,iBAAiB,OAAO,gBAAgB;AAC9C,OAAK,CAAC,OAAO,aAAa,mBAAmB,CAAC,iBAAiB,OAAO,IAAI,CACxE,cAAa,IAAI,sBAAsB,OAAO,CAAC;AAGjD,SAAO,IAAI,SAAS,WAAgB,aAAa,IAAI,OAAO,CAAC;AAE7D,QAAM,aAAa,KAAK;GAGtB,KAAK,OAAO;GACZ,IAAI,OAAO;GACX,WAAW,OAAO;GAClB,aAAa,OAAO;GACpB,eAAe,OAAO;GACtB,0BAA0B,OAAO;GACjC,YAAY,OAAO;GACnB,SAAS,OAAO;GAChB,eAAe,EAAE,aAAa,OAAO;GACrC,GAAI,OAAO,YAAY,EAAE,WAAW,OAAO,WAAW,GAAG,EAAE;GAC3D,GAAG,OAAO;GACX,CAAC;AAEF,oBAAkB;AAClB,SAAO;KACL;AAEJ,QAAO;;AAIT,MAAM,iBAAiB,MAAM,OAAO,WAA8C;CAEhF,MAAM,cADa,MAAM,SAAS,EACJ,IAAI,OAAO,WAAW;AACpD,KAAI,WAAY,QAAO;CAGvB,MAAM,eADc,MAAM,SAAS,EACH,IAAI,OAAO,WAAW,EAAE;AACxD,KAAI,aAAa;AACf,MAAI,OAAO,cAAc,SAAS,YAAY,CAC5C,QAAO;AAGT,MAAI,OAAO,0BAA0B;GACnC,MAAM,SAAS,YAAY,aAAa,CAAC,MAAM,IAAI,CAAC;GACpD,MAAM,QAAQ,OAAO,cAAc,MACjC,MAAK,EAAE,aAAa,KAAK,UAAU,EAAE,aAAa,CAAC,MAAM,IAAI,CAAC,OAAO,OACtE;AACD,OAAI,MAAO,QAAO;;;AAItB,QAAO,OAAO;EACd;;;;;;;;;;;;;;;;;;AAmBF,eAAsB,KAIpB,IACA,UAAiD,EAAE,EACjB;CAClC,MAAM,SAAS,WAAW;CAE1B,MAAM,MAAM,QAAQ,OAAO,MAAM,eAAe,OAAO;CACvD,MAAM,eAAe,MAAM,kBAAkB,OAAO;CAMpD,MAAM,aAHoB,KACrB,MAAM,QAAQ,GAAG,GAAG,KAAiB,CAAC,GAAa,GACpD,OAAO,IACe,QAAO,MAAK,CAAC,aAAa,mBAAmB,EAAE,CAAC;AAC1E,KAAI,UAAU,SAAS,EACrB,OAAM,aAAa,eAAe,UAAU;CAG9C,MAAM,aAAa,KACd,MAAM,QAAQ,GAAG,GAAG,GAAG,KAAK,KAC7B,OAAO;AAEX,QAAO;EACL,GAAG,aAAa,UAAU,KAAK,YAAY,QAAQ,UAAgC;EACnF,MAAM;EACN;EACD;;;;;;;;;;;;AAaH,SAAgB,aACd,MACA,YACU;CACV,MAAM,YAAsB,EAAE;CAC9B,MAAM,QAAQ,KAAK,OAAO,QAAQ,EAAE;CACpC,MAAM,WAAW,aAAa,IAAI,IAAI,WAAW,GAAG;AAEpD,MAAK,MAAM,OAAO,OAAO,KAAK,MAAM,EAAE;AACpC,YAAU,OAAO,EAAE;AACnB,OAAK,MAAM,MAAM,OAAO,KAAK,MAAM,KAAK,CACtC,KAAI,CAAC,YAAY,SAAS,IAAI,GAAG,CAC/B,WAAU,KAAK,MAAM,MAAM,KAAK;;AAKtC,QAAO;;;;;;;;;;;;;;AAeT,SAAgB,2BAA8C;AAE5D,QADe,WAAW,CACZ,cAAc,KAAI,SAAQ,EAAE,KAAK,EAAE"}
|
|
1
|
+
{"version":3,"file":"server.mjs","names":[],"sources":["../../src/appRouter/config.ts","../../src/appRouter/server.ts"],"sourcesContent":["import type { I18nConfig, NormalizedConfig } from './types'\n\nexport function defineConfig(config: I18nConfig): I18nConfig {\n return config\n}\n\nexport function normalizeConfig(userConfig: I18nConfig): NormalizedConfig {\n // Support legacy format: { i18n: { defaultLocale, locales } }\n const supportedLngs = userConfig.supportedLngs ??\n userConfig.i18n?.locales?.filter((l: string) => l !== 'default') ??\n ['en']\n const fallbackLng = userConfig.fallbackLng ??\n userConfig.i18n?.defaultLocale ??\n supportedLngs[0]\n\n if (!fallbackLng) {\n throw new Error('next-i18next: fallbackLng (or i18n.defaultLocale) is required')\n }\n if (supportedLngs.length === 0) {\n throw new Error('next-i18next: supportedLngs (or i18n.locales) must contain at least one language')\n }\n\n const defaultNS = userConfig.defaultNS ?? 'common'\n\n return {\n supportedLngs,\n fallbackLng,\n defaultNS,\n ns: userConfig.ns ?? [defaultNS],\n localeInPath: userConfig.localeInPath ?? true,\n hideDefaultLocale: userConfig.hideDefaultLocale ?? false,\n localePath: userConfig.localePath ?? '/locales',\n localeStructure: userConfig.localeStructure ?? '{{lng}}/{{ns}}',\n localeExtension: userConfig.localeExtension ?? 'json',\n cookieName: userConfig.cookieName ?? 'i18next',\n headerName: userConfig.headerName ?? 'x-i18next-current-language',\n cookieMaxAge: userConfig.cookieMaxAge ?? 365 * 24 * 60 * 60,\n ignoredPaths: userConfig.ignoredPaths ?? ['/api', '/_next', '/static'],\n basePath: userConfig.basePath,\n resources: userConfig.resources,\n resourceLoader: userConfig.resourceLoader,\n use: userConfig.use ?? [],\n i18nextOptions: (userConfig.i18nextOptions ?? {}) as Record<string, any>,\n nonExplicitSupportedLngs: userConfig.nonExplicitSupportedLngs ?? false,\n // Preserve legacy fields\n i18n: userConfig.i18n,\n serializeConfig: userConfig.serializeConfig,\n reloadOnPrerender: userConfig.reloadOnPrerender,\n }\n}\n","import { createInstance } from 'i18next'\nimport type { i18n as I18NextClient, Resource, Module, FlatNamespace, KeyPrefix } from 'i18next'\nimport resourcesToBackend from 'i18next-resources-to-backend'\nimport { cache } from 'react'\nimport { headers, cookies } from 'next/headers'\n\nimport type { I18nConfig, NormalizedConfig, GetTResult } from './types'\nimport { normalizeConfig } from './config'\n\nlet _config: NormalizedConfig | null = null\n\n// Module-level singleton: persists across requests within the same server process.\n// This is critical for custom backends (i18next-http-backend, i18next-locize-backend)\n// to avoid re-fetching translations on every request.\n// In serverless environments (Lambda, Cloud Functions, etc.), this lives as long as\n// the warm function instance — backends with reloadInterval will refresh automatically.\nlet _sharedInstance: I18NextClient | null = null\nlet _sharedInstancePromise: Promise<I18NextClient> | null = null\n\nfunction getConfig(): NormalizedConfig {\n if (!_config) {\n throw new Error(\n 'next-i18next: Server module not initialized. Call initServerI18next(config) in your root layout.'\n )\n }\n return _config\n}\n\n/**\n * Initialize the server-side i18next configuration.\n * Call this once in your root layout or a shared setup file.\n */\nexport function initServerI18next(userConfig: I18nConfig): void {\n _config = normalizeConfig(userConfig)\n}\n\nfunction hasCustomBackend(plugins: any[]): boolean {\n return plugins.some((b: Module) => b.type === 'backend')\n}\n\nfunction createResourceBackend(config: NormalizedConfig) {\n if (config.resourceLoader) {\n return resourcesToBackend(config.resourceLoader)\n }\n return resourcesToBackend(async (language: string, namespace: string) => {\n const filePath = `${config.localePath}/${config.localeStructure\n .replace('{{lng}}', language)\n .replace('{{ns}}', namespace)}.${config.localeExtension}`\n\n // Node.js runtime: read from filesystem\n if (typeof process !== 'undefined' && process.versions?.node) {\n try {\n const fs = await import('fs/promises')\n const pathMod = await import('path')\n const resolved = pathMod.resolve(process.cwd(), `public${filePath}`)\n const content = await fs.readFile(resolved, 'utf-8')\n return JSON.parse(content)\n } catch {\n throw new Error(\n `next-i18next: Could not read locale file \"public${filePath}\". ` +\n 'On serverless platforms (Vercel, AWS Lambda, etc.), files in public/ are served via CDN ' +\n 'but are NOT available on the filesystem at runtime. Use the `resourceLoader` option with ' +\n 'dynamic imports instead:\\n\\n' +\n ' resourceLoader: (language, namespace) =>\\n' +\n // eslint-disable-next-line no-template-curly-in-string\n ' import(`./public/locales/${language}/${namespace}.json`)\\n'\n )\n }\n }\n\n // Edge runtime: filesystem not available\n throw new Error(\n `next-i18next: Cannot load locale file \"${filePath}\" in Edge Runtime. ` +\n 'Provide pre-bundled `resources`, a custom `resourceLoader`, or use a custom backend (e.g. i18next-http-backend) via the `use` option.'\n )\n })\n}\n\n/**\n * Get or create the shared i18next instance.\n * The instance is created once and reused across all requests.\n * All languages are preloaded so that getFixedT(lng) works for any supported language.\n * Additional namespaces are loaded on demand and cached in the instance store.\n */\nasync function getSharedInstance(config: NormalizedConfig): Promise<I18NextClient> {\n if (_sharedInstance?.isInitialized) return _sharedInstance\n\n // Deduplicate concurrent init calls (multiple requests arriving while first init is in flight)\n if (_sharedInstancePromise) return _sharedInstancePromise\n\n _sharedInstancePromise = (async () => {\n const i18nInstance = createInstance()\n\n // Add a backend when needed:\n // - No resources provided → backend loads everything\n // - Resources provided with partialBundledLanguages → backend loads the rest\n // - Custom backend in config.use → user handles it, skip default backend\n const partialBundled = config.i18nextOptions?.partialBundledLanguages\n if ((!config.resources || partialBundled) && !hasCustomBackend(config.use)) {\n i18nInstance.use(createResourceBackend(config))\n }\n\n config.use.forEach((plugin: any) => i18nInstance.use(plugin))\n\n await i18nInstance.init({\n // No `lng` — the shared instance is language-neutral.\n // We use getFixedT(lng, ns) to get language-specific translators.\n lng: config.fallbackLng,\n ns: config.ns,\n defaultNS: config.defaultNS,\n fallbackLng: config.fallbackLng,\n supportedLngs: config.supportedLngs,\n nonExplicitSupportedLngs: config.nonExplicitSupportedLngs,\n fallbackNS: config.defaultNS,\n preload: config.supportedLngs, // preload ALL languages upfront\n interpolation: { escapeValue: false },\n ...(config.resources ? { resources: config.resources } : {}),\n ...config.i18nextOptions,\n })\n\n _sharedInstance = i18nInstance\n return i18nInstance\n })()\n\n return _sharedInstancePromise\n}\n\n// Per-request language detection, deduplicated within a single React render\nconst detectLanguage = cache(async (config: NormalizedConfig): Promise<string> => {\n const headerList = await headers()\n const fromHeader = headerList.get(config.headerName)\n if (fromHeader) return fromHeader\n\n const cookieStore = await cookies()\n const cookieValue = cookieStore.get(config.cookieName)?.value\n if (cookieValue) {\n if (config.supportedLngs.includes(cookieValue)) {\n return cookieValue\n }\n // nonExplicitSupportedLngs: e.g. cookie 'en' matches supported 'en-US'\n if (config.nonExplicitSupportedLngs) {\n const prefix = cookieValue.toLowerCase().split('-')[0]\n const match = config.supportedLngs.find(\n l => l.toLowerCase() === prefix || l.toLowerCase().split('-')[0] === prefix\n )\n if (match) return match\n }\n }\n\n return config.fallbackLng\n})\n\n/**\n * Get a translation function for use in Server Components, layouts, and generateMetadata.\n *\n * The underlying i18next instance is a **module-level singleton** that persists across\n * requests. This means custom backends (i18next-http-backend, i18next-locize-backend, etc.)\n * only fetch translations once (or according to their own reloadInterval), not on every request.\n *\n * @example\n * ```tsx\n * import { getT } from 'next-i18next/server'\n *\n * export default async function Page() {\n * const { t, i18n } = await getT('home')\n * return <h1>{t('heading')}</h1>\n * }\n * ```\n */\nexport async function getT<\n Ns extends FlatNamespace = FlatNamespace,\n KPrefix extends KeyPrefix<Ns> = undefined,\n>(\n ns?: Ns | Ns[],\n options: { keyPrefix?: KPrefix; lng?: string } = {},\n): Promise<GetTResult<Ns, KPrefix>> {\n const config = getConfig()\n\n const lng = options.lng || await detectLanguage(config)\n const i18nInstance = await getSharedInstance(config)\n\n // Load additional namespaces on demand if not already loaded\n const nsArray: string[] = ns\n ? (Array.isArray(ns) ? ns as string[] : [ns as string])\n : config.ns\n const missingNs = nsArray.filter(n => !i18nInstance.hasLoadedNamespace(n))\n if (missingNs.length > 0) {\n await i18nInstance.loadNamespaces(missingNs)\n }\n\n const resolvedNs = ns\n ? (Array.isArray(ns) ? ns[0] : ns) as string\n : config.defaultNS\n\n return {\n t: i18nInstance.getFixedT(lng, resolvedNs, options.keyPrefix as string | undefined),\n i18n: i18nInstance,\n lng,\n } as any\n}\n\n/**\n * Extract loaded resources from the server i18next instance for passing to I18nProvider.\n *\n * @example\n * ```tsx\n * const { i18n } = await getT()\n * const resources = getResources(i18n, ['common', 'footer'])\n * return <I18nProvider language={i18n.language} resources={resources}>{children}</I18nProvider>\n * ```\n */\nexport function getResources(\n i18n: I18NextClient,\n namespaces?: string[],\n): Resource {\n const resources: Resource = {}\n const store = i18n.store?.data || {}\n const nsFilter = namespaces ? new Set(namespaces) : null\n\n for (const lng of Object.keys(store)) {\n resources[lng] = {}\n for (const ns of Object.keys(store[lng])) {\n if (!nsFilter || nsFilter.has(ns)) {\n resources[lng][ns] = store[lng][ns]\n }\n }\n }\n\n return resources\n}\n\n/**\n * Helper for generateStaticParams — returns params for all supported languages.\n *\n * @example\n * ```tsx\n * import { generateI18nStaticParams } from 'next-i18next/server'\n *\n * export async function generateStaticParams() {\n * return generateI18nStaticParams()\n * }\n * ```\n */\nexport function generateI18nStaticParams(): { lng: string }[] {\n const config = getConfig()\n return config.supportedLngs.map(lng => ({ lng }))\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createConfig.cjs","names":["defaultConfig","unique","getFallbackForLng"],"sources":["../../../src/pagesRouter/config/createConfig.ts"],"sourcesContent":["import { defaultConfig } from './defaultConfig'\nimport { InternalConfig, UserConfig } from '../types'\nimport { getFallbackForLng, unique } from '../utils'\nimport { FallbackLngObjList, Module } from 'i18next'\n\nconst deepMergeObjects = ['backend', 'detection'] as (keyof Pick<\n UserConfig,\n 'backend' | 'detection'\n>)[]\n\nexport const createConfig = (\n userConfig: UserConfig\n): InternalConfig => {\n if (typeof userConfig?.lng !== 'string') {\n throw new Error('config.lng was not passed into createConfig')\n }\n\n //\n // Initial merge of default and user-provided config\n //\n const { i18n: userI18n, ...userConfigStripped } = userConfig\n const { i18n: defaultI18n, ...defaultConfigStripped } =\n defaultConfig\n const combinedConfig = {\n ...defaultConfigStripped,\n ...userConfigStripped,\n ...defaultI18n,\n ...userI18n,\n }\n\n const {\n defaultNS,\n lng,\n localeExtension,\n localePath,\n nonExplicitSupportedLngs,\n } = combinedConfig\n\n const locales = combinedConfig.locales.filter((l: string) => l !== 'default')\n\n /**\n * Skips translation file resolution while in cimode\n * https://github.com/i18next/next-i18next/pull/851#discussion_r503113620\n */\n if (lng === 'cimode') {\n return combinedConfig as InternalConfig\n }\n\n if (typeof combinedConfig.fallbackLng === 'undefined') {\n combinedConfig.fallbackLng = combinedConfig.defaultLocale\n if (combinedConfig.fallbackLng === 'default') { [combinedConfig.fallbackLng] = locales }\n }\n\n const userPrefix = userConfig?.interpolation?.prefix\n const userSuffix = userConfig?.interpolation?.suffix\n const prefix = userPrefix ?? '{{'\n const suffix = userSuffix ?? '}}'\n if (\n typeof userConfig?.localeStructure !== 'string' &&\n (userPrefix || userSuffix)\n ) {\n combinedConfig.localeStructure = `${prefix}lng${suffix}/${prefix}ns${suffix}`\n }\n\n const { fallbackLng, localeStructure } = combinedConfig\n\n if (nonExplicitSupportedLngs) {\n const createFallbackObject = (\n acc: FallbackLngObjList,\n l: string\n ) => {\n const [locale] = l.split('-')\n acc[l] = [locale]\n return acc\n }\n\n if (typeof fallbackLng === 'string') {\n combinedConfig.fallbackLng = combinedConfig.locales\n .filter((l: string) => l.includes('-'))\n .reduce(createFallbackObject, { default: [fallbackLng] })\n } else if (Array.isArray(fallbackLng)) {\n combinedConfig.fallbackLng = combinedConfig.locales\n .filter((l: string) => l.includes('-'))\n .reduce(createFallbackObject, { default: fallbackLng })\n } else if (typeof fallbackLng === 'object') {\n combinedConfig.fallbackLng = Object.entries(\n combinedConfig.fallbackLng\n ).reduce<FallbackLngObjList>((acc, [l, f]: [string, any]) => {\n acc[l] = l.includes('-')\n ? unique([l.split('-')[0], ...f])\n : f\n return acc\n }, fallbackLng as FallbackLngObjList)\n } else if (typeof fallbackLng === 'function') {\n throw new Error(\n 'If nonExplicitSupportedLngs is true, no functions are allowed for fallbackLng'\n )\n }\n }\n\n const hasCustomBackend = userConfig?.use?.filter(Boolean).some(\n (b: Module) => b.type === 'backend'\n )\n if (!process.browser && typeof window === 'undefined') {\n combinedConfig.preload = locales\n\n if (!hasCustomBackend) {\n const fs = require('fs')\n const path = require('path')\n\n //\n // Validate defaultNS\n // https://github.com/i18next/next-i18next/issues/358\n //\n if (\n typeof defaultNS === 'string' &&\n typeof lng !== 'undefined'\n ) {\n if (typeof localePath === 'string') {\n const defaultLocaleStructure = localeStructure\n .replace(`${prefix}lng${suffix}`, lng)\n .replace(`${prefix}ns${suffix}`, defaultNS)\n const defaultFile = `/${defaultLocaleStructure}.${localeExtension}`\n const defaultNSPath = path.join(localePath, defaultFile)\n const defaultNSExists = fs.existsSync(defaultNSPath)\n const fallback = getFallbackForLng(\n lng,\n combinedConfig.fallbackLng\n )\n const defaultFallbackNSExists = fallback.some(f => {\n const fallbackFile = defaultFile.replace(lng, f)\n const defaultNSPath = path.join(localePath, fallbackFile)\n return fs.existsSync(defaultNSPath)\n })\n if (\n !defaultNSExists &&\n !defaultFallbackNSExists &&\n process.env.NODE_ENV !== 'production'\n ) {\n throw new Error(\n `Default namespace not found at ${defaultNSPath}`\n )\n }\n } else if (typeof localePath === 'function') {\n const defaultNSPath = localePath(lng, defaultNS, false)\n const defaultNSExists = fs.existsSync(defaultNSPath)\n const fallback = getFallbackForLng(\n lng,\n combinedConfig.fallbackLng\n )\n const defaultFallbackNSExists = fallback.some(f => {\n const defaultNSPath = localePath(f, defaultNS, false)\n return fs.existsSync(defaultNSPath)\n })\n if (\n !defaultNSExists &&\n !defaultFallbackNSExists &&\n process.env.NODE_ENV !== 'production'\n ) {\n throw new Error(\n `Default namespace not found at ${defaultNSPath}`\n )\n }\n }\n }\n\n //\n // Set server side backend\n //\n if (typeof localePath === 'string') {\n combinedConfig.backend = {\n addPath: path.resolve(\n process.cwd(),\n `${localePath}/${localeStructure}.missing.${localeExtension}`\n ),\n loadPath: path.resolve(\n process.cwd(),\n `${localePath}/${localeStructure}.${localeExtension}`\n ),\n }\n } else if (typeof localePath === 'function') {\n combinedConfig.backend = {\n addPath: (locale: string, namespace: string) =>\n localePath(locale, namespace, true),\n loadPath: (locale: string, namespace: string) =>\n localePath(locale, namespace, false),\n }\n } else if (localePath) {\n throw new Error(\n `Unsupported localePath type: ${typeof localePath}`\n )\n }\n\n //\n // Set server side preload (namespaces)\n //\n if (!combinedConfig.ns && typeof lng !== 'undefined') {\n if (typeof localePath === 'function') {\n throw new Error(\n 'Must provide all namespaces in ns option if using a function as localePath'\n )\n }\n\n const getNamespaces = (locales: string[]): string[] => {\n const getLocaleNamespaces = (p: string) => {\n let ret: string[] = []\n\n if (!fs.existsSync(p)) return ret\n\n fs.readdirSync(p).forEach((file: string) => {\n const joinedP = path.join(p, file)\n if (fs.statSync(joinedP).isDirectory()) {\n const subRet = getLocaleNamespaces(joinedP).map(\n n => `${file}/${n}`\n )\n ret = ret.concat(subRet)\n return\n }\n ret.push(file.replace(`.${localeExtension}`, ''))\n })\n return ret\n }\n\n let namespacesByLocale\n const r = combinedConfig.resources\n if (!localePath && r) {\n namespacesByLocale = locales.map(locale => Object.keys(r[locale]))\n } else {\n namespacesByLocale = locales.map(locale =>\n getLocaleNamespaces(\n path.resolve(process.cwd(), `${localePath}/${locale}`)\n )\n )\n }\n\n const allNamespaces = []\n for (const localNamespaces of namespacesByLocale) {\n allNamespaces.push(...localNamespaces)\n }\n\n return unique(allNamespaces)\n }\n\n if (\n localeStructure.indexOf(`${prefix}lng${suffix}`) >\n localeStructure.indexOf(`${prefix}ns${suffix}`)\n ) {\n throw new Error(\n 'Must provide all namespaces in ns option if using a localeStructure that is not namespace-listable like lng/ns'\n )\n }\n\n combinedConfig.ns = getNamespaces(\n unique([\n lng,\n ...getFallbackForLng(lng, combinedConfig.fallbackLng),\n ])\n )\n }\n }\n } else {\n //\n // Set client side backend, if there is no custom backend\n //\n if (!hasCustomBackend) {\n if (typeof localePath === 'string') {\n combinedConfig.backend = {\n addPath: `${localePath}/${localeStructure}.missing.${localeExtension}`,\n loadPath: `${localePath}/${localeStructure}.${localeExtension}`,\n }\n } else if (typeof localePath === 'function') {\n combinedConfig.backend = {\n addPath: (locale: string, namespace: string) =>\n localePath(locale, namespace, true),\n loadPath: (locale: string, namespace: string) =>\n localePath(locale, namespace, false),\n }\n }\n }\n\n if (\n typeof combinedConfig.ns !== 'string' &&\n !Array.isArray(combinedConfig.ns)\n ) {\n combinedConfig.ns = [defaultNS as string]\n }\n }\n\n //\n // Deep merge with overwrite - goes last\n //\n deepMergeObjects.forEach(obj => {\n if (userConfig[obj]) {\n combinedConfig[obj] = {\n ...combinedConfig[obj],\n ...userConfig[obj],\n }\n }\n })\n\n return combinedConfig as InternalConfig\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"createConfig.cjs","names":["defaultConfig","unique","getFallbackForLng"],"sources":["../../../src/pagesRouter/config/createConfig.ts"],"sourcesContent":["import { defaultConfig } from './defaultConfig'\nimport { InternalConfig, UserConfig } from '../types'\nimport { getFallbackForLng, unique } from '../utils'\nimport { FallbackLngObjList, Module } from 'i18next'\n\nconst deepMergeObjects = ['backend', 'detection'] as (keyof Pick<\n UserConfig,\n 'backend' | 'detection'\n>)[]\n\nexport const createConfig = (\n userConfig: UserConfig\n): InternalConfig => {\n if (typeof userConfig?.lng !== 'string') {\n throw new Error('config.lng was not passed into createConfig')\n }\n\n //\n // Initial merge of default and user-provided config\n //\n const { i18n: userI18n, ...userConfigStripped } = userConfig\n const { i18n: defaultI18n, ...defaultConfigStripped } =\n defaultConfig\n const combinedConfig = {\n ...defaultConfigStripped,\n ...userConfigStripped,\n ...defaultI18n,\n ...userI18n,\n }\n\n const {\n defaultNS,\n lng,\n localeExtension,\n localePath,\n nonExplicitSupportedLngs,\n } = combinedConfig\n\n const locales = combinedConfig.locales.filter((l: string) => l !== 'default')\n\n /**\n * Skips translation file resolution while in cimode\n * https://github.com/i18next/next-i18next/pull/851#discussion_r503113620\n */\n if (lng === 'cimode') {\n return combinedConfig as unknown as InternalConfig\n }\n\n if (typeof combinedConfig.fallbackLng === 'undefined') {\n combinedConfig.fallbackLng = combinedConfig.defaultLocale\n if (combinedConfig.fallbackLng === 'default') { [combinedConfig.fallbackLng] = locales }\n }\n\n const userPrefix = userConfig?.interpolation?.prefix\n const userSuffix = userConfig?.interpolation?.suffix\n const prefix = userPrefix ?? '{{'\n const suffix = userSuffix ?? '}}'\n if (\n typeof userConfig?.localeStructure !== 'string' &&\n (userPrefix || userSuffix)\n ) {\n combinedConfig.localeStructure = `${prefix}lng${suffix}/${prefix}ns${suffix}`\n }\n\n const { fallbackLng, localeStructure } = combinedConfig\n\n if (nonExplicitSupportedLngs) {\n const createFallbackObject = (\n acc: FallbackLngObjList,\n l: string\n ) => {\n const [locale] = l.split('-')\n acc[l] = [locale]\n return acc\n }\n\n if (typeof fallbackLng === 'string') {\n combinedConfig.fallbackLng = combinedConfig.locales\n .filter((l: string) => l.includes('-'))\n .reduce(createFallbackObject, { default: [fallbackLng] })\n } else if (Array.isArray(fallbackLng)) {\n combinedConfig.fallbackLng = combinedConfig.locales\n .filter((l: string) => l.includes('-'))\n .reduce(createFallbackObject, { default: fallbackLng })\n } else if (typeof fallbackLng === 'object') {\n combinedConfig.fallbackLng = Object.entries(\n combinedConfig.fallbackLng\n ).reduce<FallbackLngObjList>((acc, [l, f]: [string, any]) => {\n acc[l] = l.includes('-')\n ? unique([l.split('-')[0], ...f])\n : f\n return acc\n }, fallbackLng as FallbackLngObjList)\n } else if (typeof fallbackLng === 'function') {\n throw new Error(\n 'If nonExplicitSupportedLngs is true, no functions are allowed for fallbackLng'\n )\n }\n }\n\n const hasCustomBackend = userConfig?.use?.filter(Boolean).some(\n (b: Module) => b.type === 'backend'\n )\n if (!process.browser && typeof window === 'undefined') {\n combinedConfig.preload = locales\n\n if (!hasCustomBackend) {\n const fs = require('fs')\n const path = require('path')\n\n //\n // Validate defaultNS\n // https://github.com/i18next/next-i18next/issues/358\n //\n if (\n typeof defaultNS === 'string' &&\n typeof lng !== 'undefined'\n ) {\n if (typeof localePath === 'string') {\n const defaultLocaleStructure = localeStructure\n .replace(`${prefix}lng${suffix}`, lng)\n .replace(`${prefix}ns${suffix}`, defaultNS)\n const defaultFile = `/${defaultLocaleStructure}.${localeExtension}`\n const defaultNSPath = path.join(localePath, defaultFile)\n const defaultNSExists = fs.existsSync(defaultNSPath)\n const fallback = getFallbackForLng(\n lng,\n combinedConfig.fallbackLng\n )\n const defaultFallbackNSExists = fallback.some(f => {\n const fallbackFile = defaultFile.replace(lng, f)\n const defaultNSPath = path.join(localePath, fallbackFile)\n return fs.existsSync(defaultNSPath)\n })\n if (\n !defaultNSExists &&\n !defaultFallbackNSExists &&\n process.env.NODE_ENV !== 'production'\n ) {\n throw new Error(\n `Default namespace not found at ${defaultNSPath}`\n )\n }\n } else if (typeof localePath === 'function') {\n const defaultNSPath = localePath(lng, defaultNS, false)\n const defaultNSExists = fs.existsSync(defaultNSPath)\n const fallback = getFallbackForLng(\n lng,\n combinedConfig.fallbackLng\n )\n const defaultFallbackNSExists = fallback.some(f => {\n const defaultNSPath = localePath(f, defaultNS, false)\n return fs.existsSync(defaultNSPath)\n })\n if (\n !defaultNSExists &&\n !defaultFallbackNSExists &&\n process.env.NODE_ENV !== 'production'\n ) {\n throw new Error(\n `Default namespace not found at ${defaultNSPath}`\n )\n }\n }\n }\n\n //\n // Set server side backend\n //\n if (typeof localePath === 'string') {\n combinedConfig.backend = {\n addPath: path.resolve(\n process.cwd(),\n `${localePath}/${localeStructure}.missing.${localeExtension}`\n ),\n loadPath: path.resolve(\n process.cwd(),\n `${localePath}/${localeStructure}.${localeExtension}`\n ),\n }\n } else if (typeof localePath === 'function') {\n combinedConfig.backend = {\n addPath: (locale: string, namespace: string) =>\n localePath(locale, namespace, true),\n loadPath: (locale: string, namespace: string) =>\n localePath(locale, namespace, false),\n }\n } else if (localePath) {\n throw new Error(\n `Unsupported localePath type: ${typeof localePath}`\n )\n }\n\n //\n // Set server side preload (namespaces)\n //\n if (!combinedConfig.ns && typeof lng !== 'undefined') {\n if (typeof localePath === 'function') {\n throw new Error(\n 'Must provide all namespaces in ns option if using a function as localePath'\n )\n }\n\n const getNamespaces = (locales: string[]): string[] => {\n const getLocaleNamespaces = (p: string) => {\n let ret: string[] = []\n\n if (!fs.existsSync(p)) return ret\n\n fs.readdirSync(p).forEach((file: string) => {\n const joinedP = path.join(p, file)\n if (fs.statSync(joinedP).isDirectory()) {\n const subRet = getLocaleNamespaces(joinedP).map(\n n => `${file}/${n}`\n )\n ret = ret.concat(subRet)\n return\n }\n ret.push(file.replace(`.${localeExtension}`, ''))\n })\n return ret\n }\n\n let namespacesByLocale\n const r = combinedConfig.resources\n if (!localePath && r) {\n namespacesByLocale = locales.map(locale => Object.keys(r[locale]))\n } else {\n namespacesByLocale = locales.map(locale =>\n getLocaleNamespaces(\n path.resolve(process.cwd(), `${localePath}/${locale}`)\n )\n )\n }\n\n const allNamespaces = []\n for (const localNamespaces of namespacesByLocale) {\n allNamespaces.push(...localNamespaces)\n }\n\n return unique(allNamespaces)\n }\n\n if (\n localeStructure.indexOf(`${prefix}lng${suffix}`) >\n localeStructure.indexOf(`${prefix}ns${suffix}`)\n ) {\n throw new Error(\n 'Must provide all namespaces in ns option if using a localeStructure that is not namespace-listable like lng/ns'\n )\n }\n\n combinedConfig.ns = getNamespaces(\n unique([\n lng,\n ...getFallbackForLng(lng, combinedConfig.fallbackLng),\n ])\n )\n }\n }\n } else {\n //\n // Set client side backend, if there is no custom backend\n //\n if (!hasCustomBackend) {\n if (typeof localePath === 'string') {\n combinedConfig.backend = {\n addPath: `${localePath}/${localeStructure}.missing.${localeExtension}`,\n loadPath: `${localePath}/${localeStructure}.${localeExtension}`,\n }\n } else if (typeof localePath === 'function') {\n combinedConfig.backend = {\n addPath: (locale: string, namespace: string) =>\n localePath(locale, namespace, true),\n loadPath: (locale: string, namespace: string) =>\n localePath(locale, namespace, false),\n }\n }\n }\n\n if (\n typeof combinedConfig.ns !== 'string' &&\n !Array.isArray(combinedConfig.ns)\n ) {\n combinedConfig.ns = [defaultNS as string]\n }\n }\n\n //\n // Deep merge with overwrite - goes last\n //\n deepMergeObjects.forEach(obj => {\n if (userConfig[obj]) {\n combinedConfig[obj] = {\n ...combinedConfig[obj],\n ...userConfig[obj],\n }\n }\n })\n\n return combinedConfig as unknown as InternalConfig\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-i18next",
|
|
3
|
-
"version": "16.0.
|
|
3
|
+
"version": "16.0.4",
|
|
4
4
|
"repository": "git@github.com:i18next/next-i18next.git",
|
|
5
5
|
"author": "i18next",
|
|
6
6
|
"funding": [
|
|
@@ -171,7 +171,7 @@
|
|
|
171
171
|
"gh-release": "7.0.2",
|
|
172
172
|
"globals": "17.4.0",
|
|
173
173
|
"husky": "^9.0.0",
|
|
174
|
-
"i18next": "^
|
|
174
|
+
"i18next": "^26.0.1",
|
|
175
175
|
"jest": "^30.0.0",
|
|
176
176
|
"jest-environment-jsdom": "^30.0.0",
|
|
177
177
|
"neostandard": "^0.13.0",
|
|
@@ -180,7 +180,7 @@
|
|
|
180
180
|
"prettier": "3.8.1",
|
|
181
181
|
"react": "^19.2.4",
|
|
182
182
|
"react-dom": "^19.2.4",
|
|
183
|
-
"react-i18next": "^
|
|
183
|
+
"react-i18next": "^17.0.1",
|
|
184
184
|
"rimraf": "^6.1.3",
|
|
185
185
|
"start-server-and-test": "^2.1.5",
|
|
186
186
|
"tsdown": "0.21.4",
|