next-i18next 16.0.4 → 16.0.6
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 +29 -10
- package/dist/appRouter/index.d.cts +5 -1
- package/dist/appRouter/index.d.cts.map +1 -1
- package/dist/appRouter/index.d.mts +5 -1
- package/dist/appRouter/index.d.mts.map +1 -1
- package/dist/appRouter/proxy/index.d.cts +5 -1
- package/dist/appRouter/proxy/index.d.cts.map +1 -1
- package/dist/appRouter/proxy/index.d.mts +5 -1
- package/dist/appRouter/proxy/index.d.mts.map +1 -1
- package/dist/appRouter/server.cjs +5 -0
- package/dist/appRouter/server.cjs.map +1 -1
- package/dist/appRouter/server.d.cts +5 -1
- package/dist/appRouter/server.d.cts.map +1 -1
- package/dist/appRouter/server.d.mts +5 -1
- package/dist/appRouter/server.d.mts.map +1 -1
- package/dist/appRouter/server.mjs +5 -0
- package/dist/appRouter/server.mjs.map +1 -1
- package/dist/pagesRouter/index.d.cts +2 -2
- package/dist/pagesRouter/serverSideTranslations.cjs +1 -2
- package/dist/pagesRouter/serverSideTranslations.cjs.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -87,6 +87,25 @@ The `resourceLoader` uses dynamic `import()` which the bundler can trace, ensuri
|
|
|
87
87
|
|
|
88
88
|
> **Tip**: Import `I18nConfig` from `next-i18next/proxy` (not from `next-i18next`) to keep the config file Edge-safe.
|
|
89
89
|
|
|
90
|
+
> **Dev tip — hot-reloading translations**: set `reloadOnPrerender: process.env.NODE_ENV === 'development'` in your config to refetch translations on every render in dev so edits to locale files appear without restarting `next dev`. The flag is automatically a no-op in production, so it is safe to keep in your committed config — custom backends (HTTP, locize, chained) won't be hit per-request in production builds.
|
|
91
|
+
>
|
|
92
|
+
> **Caveat with `import()`-based `resourceLoader`**: dynamic `import()` of JSON is cached at the bundler level and is not reliably re-invalidated by Turbopack/Webpack HMR after the first edit, so hot-reload can stall after one change. For full hot-reload during development, gate your loader so dev uses `fs.readFile` and production keeps bundler-traceable `import()`:
|
|
93
|
+
> ```ts
|
|
94
|
+
> const resourceLoader: I18nConfig['resourceLoader'] =
|
|
95
|
+
> process.env.NODE_ENV === 'development'
|
|
96
|
+
> ? async (lng, ns) => {
|
|
97
|
+
> const fs = await import('fs/promises')
|
|
98
|
+
> const path = await import('path')
|
|
99
|
+
> const content = await fs.readFile(
|
|
100
|
+
> path.resolve(process.cwd(), `app/i18n/locales/${lng}/${ns}.json`),
|
|
101
|
+
> 'utf-8'
|
|
102
|
+
> )
|
|
103
|
+
> return JSON.parse(content)
|
|
104
|
+
> }
|
|
105
|
+
> : (lng, ns) => import(`./app/i18n/locales/${lng}/${ns}.json`)
|
|
106
|
+
> ```
|
|
107
|
+
> Pages Router and the App Router default backend already use `fs` and are unaffected.
|
|
108
|
+
|
|
90
109
|
### 4. Proxy
|
|
91
110
|
|
|
92
111
|
Create `proxy.ts` at your project root (Next.js 16+ replaces `middleware.ts` with `proxy.ts`):
|
|
@@ -298,13 +317,12 @@ export function LanguageSwitcher() {
|
|
|
298
317
|
}
|
|
299
318
|
```
|
|
300
319
|
|
|
301
|
-
The root layout reads the language from `
|
|
320
|
+
The root layout reads the language from `getT()` instead of URL params:
|
|
302
321
|
|
|
303
322
|
```tsx
|
|
304
323
|
// app/layout.tsx (no [lng] segment)
|
|
305
324
|
export default async function RootLayout({ children }) {
|
|
306
|
-
const { i18n } = await getT()
|
|
307
|
-
const lng = i18n.resolvedLanguage
|
|
325
|
+
const { i18n, lng } = await getT()
|
|
308
326
|
const resources = getResources(i18n)
|
|
309
327
|
|
|
310
328
|
return (
|
|
@@ -474,7 +492,7 @@ See [`examples/pages-router-simple`](examples/pages-router-simple), [`examples/p
|
|
|
474
492
|
|
|
475
493
|
## Custom i18next Backends
|
|
476
494
|
|
|
477
|
-
next-i18next supports any i18next backend plugin for loading translations from an API, CDN, or services like [Locize](https://www.locize.com).
|
|
495
|
+
next-i18next supports any i18next backend plugin for loading translations from an API, CDN, or services like [Locize](https://www.locize.com?utm_source=next_i18next_readme&utm_medium=github&utm_campaign=readme).
|
|
478
496
|
|
|
479
497
|
When a custom backend is provided via `use`, next-i18next will **not** add its default resource loader, giving you full control.
|
|
480
498
|
|
|
@@ -676,7 +694,8 @@ If you were using i18next and react-i18next directly (as recommended in v15):
|
|
|
676
694
|
|
|
677
695
|
1. Update imports from `next-i18next` to `next-i18next/pages`
|
|
678
696
|
2. Update `serverSideTranslations` import to `next-i18next/pages/serverSideTranslations`
|
|
679
|
-
3.
|
|
697
|
+
3. Update type imports: `import type { TFunction, WithTranslation, I18n } from 'next-i18next/pages'`
|
|
698
|
+
4. Everything else works the same
|
|
680
699
|
|
|
681
700
|
---
|
|
682
701
|
|
|
@@ -699,7 +718,7 @@ Thanks goes to these wonderful people ([emoji key](https://github.com/kentcdodds
|
|
|
699
718
|
<td align="center" valign="top" width="14.28%"><a href="https://isaachinman.com/"><img src="https://avatars.githubusercontent.com/u/10575782?v=4?s=100" width="100px;" alt="Isaac Hinman"/><br /><sub><b>Isaac Hinman</b></sub></a><br /><a href="https://github.com/i18next/next-i18next/commits?author=isaachinman" title="Code">💻</a> <a href="https://github.com/i18next/next-i18next/commits?author=isaachinman" title="Documentation">📖</a> <a href="#ideas-isaachinman" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-isaachinman" title="Maintenance">🚧</a></td>
|
|
700
719
|
</tr>
|
|
701
720
|
<tr>
|
|
702
|
-
<td align="center" valign="top" width="14.28%"><a href="https://locize.com
|
|
721
|
+
<td align="center" valign="top" width="14.28%"><a href="https://www.locize.com/?utm_source=next_i18next_readme&utm_medium=github&utm_campaign=readme"><img src="https://avatars.githubusercontent.com/u/1086194?v=4?s=100" width="100px;" alt="Adriano Raiano"/><br /><sub><b>Adriano Raiano</b></sub></a><br /><a href="https://github.com/i18next/next-i18next/commits?author=adrai" title="Code">💻</a> <a href="https://github.com/i18next/next-i18next/commits?author=adrai" title="Documentation">📖</a> <a href="#ideas-adrai" title="Ideas, Planning, & Feedback">🤔</a> <a href="#maintenance-adrai" title="Maintenance">🚧</a></td>
|
|
703
722
|
<td align="center" valign="top" width="14.28%"><a href="https://github.com/felixmosh"><img src="https://avatars.githubusercontent.com/u/9304194?v=4?s=100" width="100px;" alt="Felix Mosheev"/><br /><sub><b>Felix Mosheev</b></sub></a><br /><a href="#question-felixmosh" title="Answering Questions">💬</a> <a href="https://github.com/i18next/next-i18next/commits?author=felixmosh" title="Code">💻</a> <a href="https://github.com/i18next/next-i18next/commits?author=felixmosh" title="Tests">⚠️</a></td>
|
|
704
723
|
<td align="center" valign="top" width="14.28%"><a href="https://soluble.io/pro"><img src="https://avatars.githubusercontent.com/u/259798?v=4?s=100" width="100px;" alt="Sébastien Vanvelthem"/><br /><sub><b>Sébastien Vanvelthem</b></sub></a><br /><a href="https://github.com/i18next/next-i18next/commits?author=belgattitude" title="Code">💻</a> <a href="https://github.com/i18next/next-i18next/commits?author=belgattitude" title="Documentation">📖</a></td>
|
|
705
724
|
</tr>
|
|
@@ -718,21 +737,21 @@ This project follows the [all-contributors](https://github.com/kentcdodds/all-co
|
|
|
718
737
|
<h3 align="center">Gold Sponsors</h3>
|
|
719
738
|
|
|
720
739
|
<p align="center">
|
|
721
|
-
<a href="https://locize.com
|
|
740
|
+
<a href="https://www.locize.com/?utm_source=next_i18next_readme&utm_medium=github&utm_campaign=readme" target="_blank">
|
|
722
741
|
<img src="https://raw.githubusercontent.com/i18next/i18next/master/assets/locize_sponsor_240.gif" width="240px">
|
|
723
742
|
</a>
|
|
724
743
|
</p>
|
|
725
744
|
|
|
726
745
|
---
|
|
727
746
|
|
|
728
|
-
**localization as a service - [Locize](https://www.locize.com)**
|
|
747
|
+
**localization as a service - [Locize](https://www.locize.com?utm_source=next_i18next_readme&utm_medium=github&utm_campaign=readme)**
|
|
729
748
|
|
|
730
749
|
Needing a translation management? Want to edit your translations with an InContext Editor? Use the original provided to you by the maintainers of i18next!
|
|
731
750
|
|
|
732
|
-
**Now with a [Free plan](https://www.locize.com/pricing) for small projects!** Perfect for hobbyists or getting started.
|
|
751
|
+
**Now with a [Free plan](https://www.locize.com/pricing?utm_source=next_i18next_readme&utm_medium=github&utm_campaign=readme) for small projects!** Perfect for hobbyists or getting started.
|
|
733
752
|
|
|
734
753
|

|
|
735
754
|
|
|
736
|
-
By using [Locize](
|
|
755
|
+
By using [Locize](https://www.locize.com/?utm_source=next_i18next_readme&utm_medium=github&utm_campaign=readme) you directly support the future of i18next and next-i18next.
|
|
737
756
|
|
|
738
757
|
---
|
|
@@ -55,7 +55,11 @@ interface I18nConfig {
|
|
|
55
55
|
};
|
|
56
56
|
/** @deprecated Use i18nextOptions instead */
|
|
57
57
|
serializeConfig?: boolean;
|
|
58
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Dev-only: when true (and `NODE_ENV !== 'production'`), reload translation
|
|
60
|
+
* resources from the backend before each render so edits to locale files
|
|
61
|
+
* appear without restarting `next dev`. No effect in production builds.
|
|
62
|
+
*/
|
|
59
63
|
reloadOnPrerender?: boolean;
|
|
60
64
|
/** Support non-explicit language codes like 'en' matching 'en-US' */
|
|
61
65
|
nonExplicitSupportedLngs?: boolean;
|
|
@@ -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;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;
|
|
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;EASF;EANA,eAAA;EAQwB;;AAG1B;;;EALE,iBAAA;EAqBiB;EAnBjB,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,EALN;EAOA,GAAA;AAAA;;;iBCzGc,YAAA,CAAa,MAAA,EAAQ,UAAA,GAAa,UAAA;AAAA,iBAIlC,eAAA,CAAgB,UAAA,EAAY,UAAA,GAAa,gBAAA"}
|
|
@@ -55,7 +55,11 @@ interface I18nConfig {
|
|
|
55
55
|
};
|
|
56
56
|
/** @deprecated Use i18nextOptions instead */
|
|
57
57
|
serializeConfig?: boolean;
|
|
58
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Dev-only: when true (and `NODE_ENV !== 'production'`), reload translation
|
|
60
|
+
* resources from the backend before each render so edits to locale files
|
|
61
|
+
* appear without restarting `next dev`. No effect in production builds.
|
|
62
|
+
*/
|
|
59
63
|
reloadOnPrerender?: boolean;
|
|
60
64
|
/** Support non-explicit language codes like 'en' matching 'en-US' */
|
|
61
65
|
nonExplicitSupportedLngs?: boolean;
|
|
@@ -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;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;
|
|
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;EASF;EANA,eAAA;EAQwB;;AAG1B;;;EALE,iBAAA;EAqBiB;EAnBjB,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,EALN;EAOA,GAAA;AAAA;;;iBCzGc,YAAA,CAAa,MAAA,EAAQ,UAAA,GAAa,UAAA;AAAA,iBAIlC,eAAA,CAAgB,UAAA,EAAY,UAAA,GAAa,gBAAA"}
|
|
@@ -56,7 +56,11 @@ interface I18nConfig {
|
|
|
56
56
|
};
|
|
57
57
|
/** @deprecated Use i18nextOptions instead */
|
|
58
58
|
serializeConfig?: boolean;
|
|
59
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Dev-only: when true (and `NODE_ENV !== 'production'`), reload translation
|
|
61
|
+
* resources from the backend before each render so edits to locale files
|
|
62
|
+
* appear without restarting `next dev`. No effect in production builds.
|
|
63
|
+
*/
|
|
60
64
|
reloadOnPrerender?: boolean;
|
|
61
65
|
/** Support non-explicit language codes like 'en' matching 'en-US' */
|
|
62
66
|
nonExplicitSupportedLngs?: boolean;
|
|
@@ -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;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;
|
|
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;EAQA;;;AAGF;;EALE,iBAAA;EAoBY;EAlBZ,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;;;iBClGc,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"}
|
|
@@ -56,7 +56,11 @@ interface I18nConfig {
|
|
|
56
56
|
};
|
|
57
57
|
/** @deprecated Use i18nextOptions instead */
|
|
58
58
|
serializeConfig?: boolean;
|
|
59
|
-
/**
|
|
59
|
+
/**
|
|
60
|
+
* Dev-only: when true (and `NODE_ENV !== 'production'`), reload translation
|
|
61
|
+
* resources from the backend before each render so edits to locale files
|
|
62
|
+
* appear without restarting `next dev`. No effect in production builds.
|
|
63
|
+
*/
|
|
60
64
|
reloadOnPrerender?: boolean;
|
|
61
65
|
/** Support non-explicit language codes like 'en' matching 'en-US' */
|
|
62
66
|
nonExplicitSupportedLngs?: boolean;
|
|
@@ -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;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;
|
|
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;EAQA;;;AAGF;;EALE,iBAAA;EAoBY;EAlBZ,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;;;iBClGc,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"}
|
|
@@ -132,6 +132,10 @@ async function getSharedInstance(config) {
|
|
|
132
132
|
})();
|
|
133
133
|
return _sharedInstancePromise;
|
|
134
134
|
}
|
|
135
|
+
const reloadResourcesForRender = (0, react.cache)(async (i18n, lng) => {
|
|
136
|
+
const ns = i18n.options.ns ?? [];
|
|
137
|
+
await i18n.reloadResources([lng], ns);
|
|
138
|
+
});
|
|
135
139
|
const detectLanguage = (0, react.cache)(async (config) => {
|
|
136
140
|
const fromHeader = (await (0, next_headers.headers)()).get(config.headerName);
|
|
137
141
|
if (fromHeader) return fromHeader;
|
|
@@ -167,6 +171,7 @@ async function getT(ns, options = {}) {
|
|
|
167
171
|
const config = getConfig();
|
|
168
172
|
const lng = options.lng || await detectLanguage(config);
|
|
169
173
|
const i18nInstance = await getSharedInstance(config);
|
|
174
|
+
if (config.reloadOnPrerender && process.env.NODE_ENV !== "production") await reloadResourcesForRender(i18nInstance, lng);
|
|
170
175
|
const missingNs = (ns ? Array.isArray(ns) ? ns : [ns] : config.ns).filter((n) => !i18nInstance.hasLoadedNamespace(n));
|
|
171
176
|
if (missingNs.length > 0) await i18nInstance.loadNamespaces(missingNs);
|
|
172
177
|
const resolvedNs = ns ? Array.isArray(ns) ? ns[0] : ns : config.defaultNS;
|
|
@@ -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 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
|
+
{"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// Dev-only hot-reload: refetch resources for the requested language so edits\n// to locale files appear without restarting `next dev`. Wrapped in `cache()`\n// so multiple `getT` calls within the same render dedupe to a single reload.\n// Gated on `NODE_ENV !== 'production'` at the call site so HTTP/locize/chained\n// backends are never refetched per-request in prod.\nconst reloadResourcesForRender = cache(\n async (i18n: I18NextClient, lng: string): Promise<void> => {\n const ns = (i18n.options.ns as string[] | undefined) ?? []\n await i18n.reloadResources([lng], ns)\n }\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 if (config.reloadOnPrerender && process.env.NODE_ENV !== 'production') {\n await reloadResourcesForRender(i18nInstance, lng)\n }\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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"}
|
|
@@ -55,7 +55,11 @@ interface I18nConfig {
|
|
|
55
55
|
};
|
|
56
56
|
/** @deprecated Use i18nextOptions instead */
|
|
57
57
|
serializeConfig?: boolean;
|
|
58
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Dev-only: when true (and `NODE_ENV !== 'production'`), reload translation
|
|
60
|
+
* resources from the backend before each render so edits to locale files
|
|
61
|
+
* appear without restarting `next dev`. No effect in production builds.
|
|
62
|
+
*/
|
|
59
63
|
reloadOnPrerender?: boolean;
|
|
60
64
|
/** Support non-explicit language codes like 'en' matching 'en-US' */
|
|
61
65
|
nonExplicitSupportedLngs?: boolean;
|
|
@@ -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;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;
|
|
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;EASF;EANA,eAAA;EAQwB;;AA6B1B;;;EA/BE,iBAAA;EA+BgD;EA7BhD,wBAAA;AAAA;AAAA,KA6BU,UAAA,YAAsB,aAAA,GAAgB,aAAA;EAChD,CAAA,EAAG,SAAA,CAAU,EAAA,EAAI,OAAA;EACjB,IAAA,EAAM,IAAA,EC4EkB;ED1ExB,GAAA;AAAA;;;;AAzGF;;;iBC8BgB,iBAAA,CAAkB,UAAA,EAAY,UAAA;;;;;;AD5B9C;;;;;;;;;;;;iBCiLsB,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;;;;;;;;;;;iBAwCV,YAAA,CACd,IAAA,EAAM,IAAA,EACN,UAAA,cACC,QAAA;;;;;;;;;;;;;iBA6Ba,wBAAA,CAAA;EAA8B,GAAA;AAAA"}
|
|
@@ -55,7 +55,11 @@ interface I18nConfig {
|
|
|
55
55
|
};
|
|
56
56
|
/** @deprecated Use i18nextOptions instead */
|
|
57
57
|
serializeConfig?: boolean;
|
|
58
|
-
/**
|
|
58
|
+
/**
|
|
59
|
+
* Dev-only: when true (and `NODE_ENV !== 'production'`), reload translation
|
|
60
|
+
* resources from the backend before each render so edits to locale files
|
|
61
|
+
* appear without restarting `next dev`. No effect in production builds.
|
|
62
|
+
*/
|
|
59
63
|
reloadOnPrerender?: boolean;
|
|
60
64
|
/** Support non-explicit language codes like 'en' matching 'en-US' */
|
|
61
65
|
nonExplicitSupportedLngs?: boolean;
|
|
@@ -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;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;
|
|
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;EASF;EANA,eAAA;EAQwB;;AA6B1B;;;EA/BE,iBAAA;EA+BgD;EA7BhD,wBAAA;AAAA;AAAA,KA6BU,UAAA,YAAsB,aAAA,GAAgB,aAAA;EAChD,CAAA,EAAG,SAAA,CAAU,EAAA,EAAI,OAAA;EACjB,IAAA,EAAM,IAAA,EC4EkB;ED1ExB,GAAA;AAAA;;;;AAzGF;;;iBC8BgB,iBAAA,CAAkB,UAAA,EAAY,UAAA;;;;;;AD5B9C;;;;;;;;;;;;iBCiLsB,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;;;;;;;;;;;iBAwCV,YAAA,CACd,IAAA,EAAM,IAAA,EACN,UAAA,cACC,QAAA;;;;;;;;;;;;;iBA6Ba,wBAAA,CAAA;EAA8B,GAAA;AAAA"}
|
|
@@ -108,6 +108,10 @@ async function getSharedInstance(config) {
|
|
|
108
108
|
})();
|
|
109
109
|
return _sharedInstancePromise;
|
|
110
110
|
}
|
|
111
|
+
const reloadResourcesForRender = cache(async (i18n, lng) => {
|
|
112
|
+
const ns = i18n.options.ns ?? [];
|
|
113
|
+
await i18n.reloadResources([lng], ns);
|
|
114
|
+
});
|
|
111
115
|
const detectLanguage = cache(async (config) => {
|
|
112
116
|
const fromHeader = (await headers()).get(config.headerName);
|
|
113
117
|
if (fromHeader) return fromHeader;
|
|
@@ -143,6 +147,7 @@ async function getT(ns, options = {}) {
|
|
|
143
147
|
const config = getConfig();
|
|
144
148
|
const lng = options.lng || await detectLanguage(config);
|
|
145
149
|
const i18nInstance = await getSharedInstance(config);
|
|
150
|
+
if (config.reloadOnPrerender && process.env.NODE_ENV !== "production") await reloadResourcesForRender(i18nInstance, lng);
|
|
146
151
|
const missingNs = (ns ? Array.isArray(ns) ? ns : [ns] : config.ns).filter((n) => !i18nInstance.hasLoadedNamespace(n));
|
|
147
152
|
if (missingNs.length > 0) await i18nInstance.loadNamespaces(missingNs);
|
|
148
153
|
const resolvedNs = ns ? Array.isArray(ns) ? ns[0] : ns : config.defaultNS;
|
|
@@ -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 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
|
+
{"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// Dev-only hot-reload: refetch resources for the requested language so edits\n// to locale files appear without restarting `next dev`. Wrapped in `cache()`\n// so multiple `getT` calls within the same render dedupe to a single reload.\n// Gated on `NODE_ENV !== 'production'` at the call site so HTTP/locize/chained\n// backends are never refetched per-request in prod.\nconst reloadResourcesForRender = cache(\n async (i18n: I18NextClient, lng: string): Promise<void> => {\n const ns = (i18n.options.ns as string[] | undefined) ?? []\n await i18n.reloadResources([lng], ns)\n }\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 if (config.reloadOnPrerender && process.env.NODE_ENV !== 'production') {\n await reloadResourcesForRender(i18nInstance, lng)\n }\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,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;;;;ACvCH,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;;AAQT,MAAM,2BAA2B,MAC/B,OAAO,MAAqB,QAA+B;CACzD,MAAM,KAAM,KAAK,QAAQ,MAA+B,EAAE;AAC1D,OAAM,KAAK,gBAAgB,CAAC,IAAI,EAAE,GAAG;EAExC;AAGD,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;AAEpD,KAAI,OAAO,qBAAqB,QAAQ,IAAI,aAAa,aACvD,OAAM,yBAAyB,cAAc,IAAI;CAOnD,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,4 +1,4 @@
|
|
|
1
|
-
import { SSRConfig, UserConfig } from "./types.cjs";
|
|
1
|
+
import { I18n, SSRConfig, TFunction, UseTranslation, UserConfig, WithTranslation, WithTranslationHocType } from "./types.cjs";
|
|
2
2
|
import { appWithTranslation, globalI18n } from "./appWithTranslation.cjs";
|
|
3
3
|
import { I18nContext, Trans, Translation, useTranslation, withTranslation } from "react-i18next";
|
|
4
|
-
export { I18nContext, type SSRConfig, Trans, Translation, type UserConfig, appWithTranslation, globalI18n as i18n, useTranslation, withTranslation };
|
|
4
|
+
export { type I18n, I18nContext, type SSRConfig, type TFunction, Trans, Translation, type UseTranslation, type UserConfig, type WithTranslation, type WithTranslationHocType, appWithTranslation, globalI18n as i18n, useTranslation, withTranslation };
|
|
@@ -2,7 +2,6 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
|
2
2
|
const require_runtime = require("./_virtual/_rolldown/runtime.cjs");
|
|
3
3
|
const require_utils = require("./utils.cjs");
|
|
4
4
|
const require_config_createConfig = require("./config/createConfig.cjs");
|
|
5
|
-
const require_appWithTranslation = require("./appWithTranslation.cjs");
|
|
6
5
|
const require_createClient_node = require("./createClient/node.cjs");
|
|
7
6
|
let fs = require("fs");
|
|
8
7
|
fs = require_runtime.__toESM(fs);
|
|
@@ -29,7 +28,6 @@ const serverSideTranslations = async (initialLocale, namespacesRequired = void 0
|
|
|
29
28
|
lng: initialLocale
|
|
30
29
|
});
|
|
31
30
|
const { localeExtension, localePath, fallbackLng, reloadOnPrerender } = config;
|
|
32
|
-
if (reloadOnPrerender) await require_appWithTranslation.globalI18n?.reloadResources();
|
|
33
31
|
const { i18n, initPromise } = require_createClient_node({
|
|
34
32
|
...config,
|
|
35
33
|
lng: initialLocale
|
|
@@ -45,6 +43,7 @@ const serverSideTranslations = async (initialLocale, namespacesRequired = void 0
|
|
|
45
43
|
const getLocaleNamespaces = (path$2) => fs.default.existsSync(path$2) ? fs.default.readdirSync(path$2).map((file) => file.replace(`.${localeExtension}`, "")) : [];
|
|
46
44
|
namespacesRequired = require_utils.unique(Object.keys(initialI18nStore).map((locale) => getLocaleNamespaces(path.default.resolve(process.cwd(), `${localePath}/${locale}`))).flat());
|
|
47
45
|
}
|
|
46
|
+
if (reloadOnPrerender && process.env.NODE_ENV !== "production") await i18n.reloadResources(Object.keys(initialI18nStore), namespacesRequired);
|
|
48
47
|
namespacesRequired.forEach((ns) => {
|
|
49
48
|
for (const locale in initialI18nStore) initialI18nStore[locale][ns] = (i18n.services.resourceStore.data[locale] || {})[ns] || {};
|
|
50
49
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"serverSideTranslations.cjs","names":["createConfig","
|
|
1
|
+
{"version":3,"file":"serverSideTranslations.cjs","names":["createConfig","createClient","path","unique"],"sources":["../../src/pagesRouter/serverSideTranslations.ts"],"sourcesContent":["import fs from 'fs'\nimport path from 'path'\nimport { createRequire } from 'module'\n\nimport { createConfig } from './config/createConfig'\nimport createClient from './createClient/node'\n\nimport { UserConfig, SSRConfig } from './types'\nimport { getFallbackForLng, unique } from './utils'\nimport { Module, Namespace } from 'i18next'\n\nlet DEFAULT_CONFIG_PATH = './next-i18next.config.js'\n\n/**\n * One line expression like `const { I18NEXT_DEFAULT_CONFIG_PATH: DEFAULT_CONFIG_PATH = './next-i18next.config.js' } = process.env;`\n * is breaking the build, so keep it like this.\n *\n * @see https://github.com/i18next/next-i18next/pull/2084#issuecomment-1420511358\n */\nif (process.env.I18NEXT_DEFAULT_CONFIG_PATH) {\n DEFAULT_CONFIG_PATH = process.env.I18NEXT_DEFAULT_CONFIG_PATH\n}\n\ntype ArrayElementOrSelf<T> = T extends ReadonlyArray<infer U> ? U[] : T[]\n\nexport const serverSideTranslations = async (\n initialLocale: string,\n namespacesRequired:\n | ArrayElementOrSelf<Namespace>\n | string\n | string[]\n | undefined = undefined,\n configOverride: UserConfig | null = null,\n extraLocales: string[] | false = false\n): Promise<SSRConfig> => {\n if (typeof initialLocale !== 'string') {\n throw new Error(\n 'Initial locale argument was not passed into serverSideTranslations'\n )\n }\n\n let userConfig = configOverride\n const configPath = path.resolve(DEFAULT_CONFIG_PATH)\n\n if (!userConfig && fs.existsSync(configPath)) {\n // Use createRequire to prevent Turbopack/webpack from tracing this dynamic require\n const nodeRequire = createRequire(__filename)\n userConfig = nodeRequire(configPath)\n }\n\n if (userConfig === null) {\n throw new Error(\n `next-i18next was unable to find a user config at ${configPath}`\n )\n }\n\n const config = createConfig({\n ...userConfig,\n lng: initialLocale,\n })\n\n const {\n localeExtension,\n localePath,\n fallbackLng,\n reloadOnPrerender,\n } = config\n\n const { i18n, initPromise } = createClient({\n ...config,\n lng: initialLocale,\n })\n\n await initPromise\n\n const hasCustomBackend = userConfig?.use?.filter(Boolean).some(\n (b: Module) => b.type === 'backend'\n )\n if (hasCustomBackend && namespacesRequired) {\n await i18n.loadNamespaces(Array.isArray(namespacesRequired) ? (namespacesRequired as string[]) : (namespacesRequired as string))\n }\n\n const initialI18nStore: Record<string, any> = {\n [initialLocale]: {},\n }\n\n getFallbackForLng(initialLocale, fallbackLng ?? false)\n .concat(extraLocales || [])\n .forEach((lng: string) => {\n initialI18nStore[lng] = {}\n })\n\n if (!Array.isArray(namespacesRequired)) {\n if (typeof localePath === 'function') {\n throw new Error(\n 'Must provide namespacesRequired to serverSideTranslations when using a function as localePath'\n )\n }\n\n const getLocaleNamespaces = (path: string) =>\n fs.existsSync(path)\n ? fs\n .readdirSync(path)\n .map(file => file.replace(`.${localeExtension}`, ''))\n : []\n\n const namespacesByLocale = Object.keys(initialI18nStore)\n .map(locale =>\n getLocaleNamespaces(\n path.resolve(process.cwd(), `${localePath}/${locale}`)\n )\n )\n .flat()\n\n namespacesRequired = unique(namespacesByLocale)\n }\n\n // Dev-only hot-reload: every backend (resources-to-backend, http, locize,\n // chained) refetches unconditionally with no dedup, so doing this in\n // production would hammer the source on every prerender call. Scope to\n // exactly the locales × namespaces this call will ship.\n if (reloadOnPrerender && process.env.NODE_ENV !== 'production') {\n await i18n.reloadResources(\n Object.keys(initialI18nStore),\n namespacesRequired as string[]\n )\n }\n\n namespacesRequired.forEach(ns => {\n for (const locale in initialI18nStore) {\n initialI18nStore[locale][ns] =\n (i18n.services.resourceStore.data[locale] || {})[ns] || {}\n }\n })\n\n return {\n _nextI18Next: {\n initialI18nStore,\n initialLocale,\n ns: namespacesRequired,\n userConfig: config.serializeConfig ? userConfig : null,\n },\n }\n}\n"],"mappings":";;;;;;;;;;;AAWA,IAAI,sBAAsB;;;;;;;AAQ1B,IAAI,QAAQ,IAAI,4BACd,uBAAsB,QAAQ,IAAI;AAKpC,MAAa,yBAAyB,OACpC,eACA,qBAIgB,KAAA,GAChB,iBAAoC,MACpC,eAAiC,UACV;AACvB,KAAI,OAAO,kBAAkB,SAC3B,OAAM,IAAI,MACR,qEACD;CAGH,IAAI,aAAa;CACjB,MAAM,aAAa,KAAA,QAAK,QAAQ,oBAAoB;AAEpD,KAAI,CAAC,cAAc,GAAA,QAAG,WAAW,WAAW,CAG1C,eAAA,GAAA,SAAA,eADkC,WAAW,CACpB,WAAW;AAGtC,KAAI,eAAe,KACjB,OAAM,IAAI,MACR,oDAAoD,aACrD;CAGH,MAAM,SAASA,4BAAAA,aAAa;EAC1B,GAAG;EACH,KAAK;EACN,CAAC;CAEF,MAAM,EACJ,iBACA,YACA,aACA,sBACE;CAEJ,MAAM,EAAE,MAAM,gBAAgBC,0BAAa;EACzC,GAAG;EACH,KAAK;EACN,CAAC;AAEF,OAAM;AAKN,KAHyB,YAAY,KAAK,OAAO,QAAQ,CAAC,MACvD,MAAc,EAAE,SAAS,UAC3B,IACuB,mBACtB,OAAM,KAAK,eAAe,MAAM,QAAQ,mBAAmB,GAAI,qBAAmC,mBAA8B;CAGlI,MAAM,mBAAwC,GAC3C,gBAAgB,EAAE,EACpB;AAED,eAAA,kBAAkB,eAAe,eAAe,MAAM,CACnD,OAAO,gBAAgB,EAAE,CAAC,CAC1B,SAAS,QAAgB;AACxB,mBAAiB,OAAO,EAAE;GAC1B;AAEJ,KAAI,CAAC,MAAM,QAAQ,mBAAmB,EAAE;AACtC,MAAI,OAAO,eAAe,WACxB,OAAM,IAAI,MACR,gGACD;EAGH,MAAM,uBAAuB,WAC3B,GAAA,QAAG,WAAWC,OAAK,GACf,GAAA,QACC,YAAYA,OAAK,CACjB,KAAI,SAAQ,KAAK,QAAQ,IAAI,mBAAmB,GAAG,CAAC,GACrD,EAAE;AAUR,uBAAqBC,cAAAA,OARM,OAAO,KAAK,iBAAiB,CACrD,KAAI,WACH,oBACE,KAAA,QAAK,QAAQ,QAAQ,KAAK,EAAE,GAAG,WAAW,GAAG,SAAS,CACvD,CACF,CACA,MAAM,CAEsC;;AAOjD,KAAI,qBAAqB,QAAQ,IAAI,aAAa,aAChD,OAAM,KAAK,gBACT,OAAO,KAAK,iBAAiB,EAC7B,mBACD;AAGH,oBAAmB,SAAQ,OAAM;AAC/B,OAAK,MAAM,UAAU,iBACnB,kBAAiB,QAAQ,OACtB,KAAK,SAAS,cAAc,KAAK,WAAW,EAAE,EAAE,OAAO,EAAE;GAE9D;AAEF,QAAO,EACL,cAAc;EACZ;EACA;EACA,IAAI;EACJ,YAAY,OAAO,kBAAkB,aAAa;EACnD,EACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "next-i18next",
|
|
3
|
-
"version": "16.0.
|
|
3
|
+
"version": "16.0.6",
|
|
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": "^26.0.
|
|
174
|
+
"i18next": "^26.0.10",
|
|
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": "^17.0.
|
|
183
|
+
"react-i18next": "^17.0.7",
|
|
184
184
|
"rimraf": "^6.1.3",
|
|
185
185
|
"start-server-and-test": "^2.1.5",
|
|
186
186
|
"tsdown": "0.21.4",
|