@fluenti/next 0.4.0-rc.1 → 0.4.0-rc.3

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.
Files changed (44) hide show
  1. package/dist/create-navigation.d.ts +34 -0
  2. package/dist/create-navigation.d.ts.map +1 -0
  3. package/dist/i18n-config.cjs +1 -1
  4. package/dist/i18n-config.cjs.map +1 -1
  5. package/dist/index.cjs +11 -11
  6. package/dist/index.cjs.map +1 -1
  7. package/dist/index.d.ts +2 -0
  8. package/dist/index.d.ts.map +1 -1
  9. package/dist/index.js +121 -104
  10. package/dist/index.js.map +1 -1
  11. package/dist/loader.cjs +1 -1
  12. package/dist/loader.cjs.map +1 -1
  13. package/dist/middleware.cjs +1 -1
  14. package/dist/middleware.cjs.map +1 -1
  15. package/dist/middleware.d.ts +111 -75
  16. package/dist/middleware.d.ts.map +1 -1
  17. package/dist/middleware.js +116 -36
  18. package/dist/middleware.js.map +1 -1
  19. package/dist/navigation.cjs +1 -1
  20. package/dist/navigation.cjs.map +1 -1
  21. package/dist/navigation.d.ts +42 -2
  22. package/dist/navigation.d.ts.map +1 -1
  23. package/dist/navigation.js +103 -17
  24. package/dist/navigation.js.map +1 -1
  25. package/dist/provider.cjs +1 -1
  26. package/dist/provider.cjs.map +1 -1
  27. package/dist/read-config.d.ts.map +1 -1
  28. package/dist/routing-DBVu_7tk.js +72 -0
  29. package/dist/routing-DBVu_7tk.js.map +1 -0
  30. package/dist/routing-Dy57-Uao.cjs +2 -0
  31. package/dist/routing-Dy57-Uao.cjs.map +1 -0
  32. package/dist/routing.d.ts +52 -0
  33. package/dist/routing.d.ts.map +1 -0
  34. package/dist/server.cjs +1 -1
  35. package/dist/server.cjs.map +1 -1
  36. package/dist/server.d.ts +39 -2
  37. package/dist/server.d.ts.map +1 -1
  38. package/dist/server.js +21 -2
  39. package/dist/server.js.map +1 -1
  40. package/dist/types.d.ts +10 -0
  41. package/dist/types.d.ts.map +1 -1
  42. package/dist/with-fluenti.d.ts +4 -6
  43. package/dist/with-fluenti.d.ts.map +1 -1
  44. package/package.json +3 -3
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Shared routing configuration for middleware and navigation.
3
+ *
4
+ * Define once, use everywhere:
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * // src/i18n/routing.ts
9
+ * import { defineRouting } from '@fluenti/next'
10
+ *
11
+ * export const routing = defineRouting({
12
+ * locales: ['en', 'fr', 'ja'],
13
+ * sourceLocale: 'en',
14
+ * localePrefix: 'as-needed',
15
+ * pathnames: {
16
+ * '/about': { fr: '/a-propos' },
17
+ * '/blog/[slug]': { fr: '/articles/[slug]' },
18
+ * },
19
+ * })
20
+ * ```
21
+ *
22
+ * Then pass to both middleware and navigation:
23
+ * ```ts
24
+ * // middleware.ts
25
+ * export default createI18nMiddleware({ NextResponse, ...routing })
26
+ *
27
+ * // src/i18n/navigation.ts
28
+ * export const { Link, useRouter } = createNavigation(routing)
29
+ * ```
30
+ */
31
+ export interface RoutingConfig<L extends string = string, P extends string = string> {
32
+ locales: readonly L[];
33
+ sourceLocale: L;
34
+ localePrefix?: 'always' | 'as-needed' | 'never';
35
+ pathnames?: Record<P, Partial<Record<L, string>>>;
36
+ }
37
+ /**
38
+ * Define a routing configuration for use with both `createI18nMiddleware` and `createNavigation`.
39
+ *
40
+ * This is a type-only helper — it returns the input unchanged but captures
41
+ * the literal types of locales and pathname keys for type-safe navigation.
42
+ */
43
+ export declare function defineRouting<const L extends string, const P extends string>(config: RoutingConfig<L, P>): RoutingConfig<L, P>;
44
+ /** Match a pathname against a pattern with [param] and [...slug] segments. */
45
+ export declare function matchPattern(pattern: string, pathname: string): Record<string, string> | null;
46
+ /** Substitute params into a pattern. */
47
+ export declare function substituteParams(pattern: string, params: Record<string, string>): string;
48
+ /** Reverse lookup: localized path → internal path. */
49
+ export declare function resolveInternalPath(localizedPath: string, locale: string, pathnames: Record<string, Record<string, string>>): string | null;
50
+ /** Forward lookup: internal path → localized path. */
51
+ export declare function resolveLocalizedPath(internalPath: string, locale: string, pathnames: Record<string, Record<string, string>>): string | null;
52
+ //# sourceMappingURL=routing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"routing.d.ts","sourceRoot":"","sources":["../src/routing.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AAEH,MAAM,WAAW,aAAa,CAC5B,CAAC,SAAS,MAAM,GAAG,MAAM,EACzB,CAAC,SAAS,MAAM,GAAG,MAAM;IAEzB,OAAO,EAAE,SAAS,CAAC,EAAE,CAAA;IACrB,YAAY,EAAE,CAAC,CAAA;IACf,YAAY,CAAC,EAAE,QAAQ,GAAG,WAAW,GAAG,OAAO,CAAA;IAC/C,SAAS,CAAC,EAAE,MAAM,CAAC,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC,CAAA;CAClD;AAED;;;;;GAKG;AACH,wBAAgB,aAAa,CAC3B,KAAK,CAAC,CAAC,SAAS,MAAM,EACtB,KAAK,CAAC,CAAC,SAAS,MAAM,EACtB,MAAM,EAAE,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG,aAAa,CAAC,CAAC,EAAE,CAAC,CAAC,CAElD;AAID,8EAA8E;AAC9E,wBAAgB,YAAY,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAuB7F;AAED,wCAAwC;AACxC,wBAAgB,gBAAgB,CAAC,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAKxF;AAED,sDAAsD;AACtD,wBAAgB,mBAAmB,CACjC,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAChD,MAAM,GAAG,IAAI,CASf;AAED,sDAAsD;AACtD,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,MAAM,EACpB,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAChD,MAAM,GAAG,IAAI,CASf"}
package/dist/server.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});async function e(e,t,n){if(!n)throw Error(`[fluenti] withLocale requires a server module reference. Pass the generated server module as the third argument: withLocale("ja", fn, serverI18n)`);let r=(await n.getI18n()).locale;try{return n.setLocale(e),await n.getI18n(),await t()}finally{n.setLocale(r),await n.getI18n()}}exports.withLocale=e;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`}),require(`./routing-Dy57-Uao.cjs`);let e=require(`react`);async function t(e,t,n){if(!n)throw Error(`[fluenti] withLocale requires a server module reference. Pass the generated server module as the third argument: withLocale("ja", fn, serverI18n)`);let r=(await n.getI18n()).locale;try{return n.setLocale(e),await n.getI18n(),await t()}finally{n.setLocale(r),await n.getI18n()}}var n=(0,e.cache)(()=>({locale:``}));function r(e){n().locale=e}async function i(){let e=n();if(e.locale)return e.locale;try{let{headers:e}=await import(`next/headers`),t=(await e()).get(`x-fluenti-locale`);if(t)return t}catch{}return`en`}function a(e){return e.map(e=>({locale:e}))}exports.generateLocaleParams=a,exports.getLocale=i,exports.setRequestLocale=r,exports.withLocale=t;
2
2
  //# sourceMappingURL=server.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.cjs","names":[],"sources":["../src/with-locale.ts"],"sourcesContent":["/**\n * Per-component locale isolation for RSC.\n *\n * Temporarily switches the request-scoped locale, executes a function,\n * then restores the previous locale. This allows rendering a subtree in\n * a different locale without affecting the rest of the page.\n *\n * @param locale - The locale to switch to for the duration of `fn`.\n * @param fn - The function to execute with the switched locale.\n * @param serverModule - The generated server module reference (auto-injected\n * by the webpack loader in production; only needed when calling manually).\n * @returns The return value of `fn`.\n *\n * @example Basic usage in a Server Component\n * ```tsx\n * // app/page.tsx (Server Component)\n * import { withLocale } from '@fluenti/next/server'\n *\n * export default async function Page() {\n * return (\n * <div>\n * <h1>{t`Main content`}</h1>\n * {await withLocale('ja', async () => (\n * <JapaneseWidget />\n * ))}\n * <Footer />\n * </div>\n * )\n * }\n * ```\n *\n * @example Rendering a multilingual page section\n * ```tsx\n * import { withLocale } from '@fluenti/next/server'\n *\n * export default async function MultilingualPage() {\n * const englishContent = await withLocale('en', async () => (\n * <p>{t`Welcome`}</p>\n * ))\n * const japaneseContent = await withLocale('ja', async () => (\n * <p>{t`Welcome`}</p>\n * ))\n * return (\n * <div>\n * {englishContent}\n * {japaneseContent}\n * </div>\n * )\n * }\n * ```\n */\nexport async function withLocale<T>(\n locale: string,\n fn: () => T | Promise<T>,\n serverModule?: { setLocale: (l: string) => void; getI18n: () => Promise<unknown> },\n): Promise<T> {\n if (!serverModule) {\n throw new Error(\n '[fluenti] withLocale requires a server module reference. ' +\n 'Pass the generated server module as the third argument: ' +\n 'withLocale(\"ja\", fn, serverI18n)',\n )\n }\n\n // Read current locale from the module's request store\n // We need to save/restore state. Since we can't access the store directly,\n // we use setLocale to switch and rely on the caller to restore.\n const prevInstance = await serverModule.getI18n()\n const prevLocale = (prevInstance as { locale: string }).locale\n\n try {\n serverModule.setLocale(locale)\n // Force instance recreation by calling getI18n again\n await serverModule.getI18n()\n return await fn()\n } finally {\n serverModule.setLocale(prevLocale)\n await serverModule.getI18n()\n }\n}\n"],"mappings":"mEAmDA,eAAsB,EACpB,EACA,EACA,EACY,CACZ,GAAI,CAAC,EACH,MAAU,MACR,oJAGD,CAOH,IAAM,GADe,MAAM,EAAa,SAAS,EACO,OAExD,GAAI,CAIF,OAHA,EAAa,UAAU,EAAO,CAE9B,MAAM,EAAa,SAAS,CACrB,MAAM,GAAI,QACT,CACR,EAAa,UAAU,EAAW,CAClC,MAAM,EAAa,SAAS"}
1
+ {"version":3,"file":"server.cjs","names":[],"sources":["../src/with-locale.ts","../src/server.ts"],"sourcesContent":["/**\n * Per-component locale isolation for RSC.\n *\n * Temporarily switches the request-scoped locale, executes a function,\n * then restores the previous locale. This allows rendering a subtree in\n * a different locale without affecting the rest of the page.\n *\n * @param locale - The locale to switch to for the duration of `fn`.\n * @param fn - The function to execute with the switched locale.\n * @param serverModule - The generated server module reference (auto-injected\n * by the webpack loader in production; only needed when calling manually).\n * @returns The return value of `fn`.\n *\n * @example Basic usage in a Server Component\n * ```tsx\n * // app/page.tsx (Server Component)\n * import { withLocale } from '@fluenti/next/server'\n *\n * export default async function Page() {\n * return (\n * <div>\n * <h1>{t`Main content`}</h1>\n * {await withLocale('ja', async () => (\n * <JapaneseWidget />\n * ))}\n * <Footer />\n * </div>\n * )\n * }\n * ```\n *\n * @example Rendering a multilingual page section\n * ```tsx\n * import { withLocale } from '@fluenti/next/server'\n *\n * export default async function MultilingualPage() {\n * const englishContent = await withLocale('en', async () => (\n * <p>{t`Welcome`}</p>\n * ))\n * const japaneseContent = await withLocale('ja', async () => (\n * <p>{t`Welcome`}</p>\n * ))\n * return (\n * <div>\n * {englishContent}\n * {japaneseContent}\n * </div>\n * )\n * }\n * ```\n */\nexport async function withLocale<T>(\n locale: string,\n fn: () => T | Promise<T>,\n serverModule?: { setLocale: (l: string) => void; getI18n: () => Promise<unknown> },\n): Promise<T> {\n if (!serverModule) {\n throw new Error(\n '[fluenti] withLocale requires a server module reference. ' +\n 'Pass the generated server module as the third argument: ' +\n 'withLocale(\"ja\", fn, serverI18n)',\n )\n }\n\n // Read current locale from the module's request store\n // We need to save/restore state. Since we can't access the store directly,\n // we use setLocale to switch and rely on the caller to restore.\n const prevInstance = await serverModule.getI18n()\n const prevLocale = (prevInstance as { locale: string }).locale\n\n try {\n serverModule.setLocale(locale)\n // Force instance recreation by calling getI18n again\n await serverModule.getI18n()\n return await fn()\n } finally {\n serverModule.setLocale(prevLocale)\n await serverModule.getI18n()\n }\n}\n","/**\n * @module @fluenti/next/server\n *\n * Server-side utilities for Next.js App Router.\n *\n * @example\n * ```tsx\n * import { getLocale, setRequestLocale } from '@fluenti/next/server'\n *\n * export default async function Page({ params }) {\n * const { locale } = await params\n * setRequestLocale(locale)\n * const currentLocale = await getLocale()\n * }\n * ```\n */\n\nimport { cache } from 'react'\n\nexport { withLocale } from './with-locale'\n\n// ── Request-scoped locale store (React.cache) ─────────────────────────────\n\nconst getRequestStore = cache(() => ({ locale: '' }))\n\n/**\n * Set the locale for the current request scope.\n *\n * Call this at the top of page/layout components to enable static rendering.\n * Required for `generateStaticParams` pages where no middleware header is available.\n */\nexport function setRequestLocale(locale: string): void {\n getRequestStore().locale = locale\n}\n\n/**\n * Get the current locale in a Server Component.\n *\n * Resolution: setRequestLocale() → x-fluenti-locale header → 'en'\n */\nexport async function getLocale(): Promise<string> {\n const store = getRequestStore()\n if (store.locale) return store.locale\n\n try {\n const { headers } = await import('next/headers')\n const h = await (headers as () => Promise<{ get(name: string): string | null }>)()\n const headerLocale = h.get('x-fluenti-locale')\n if (headerLocale) return headerLocale\n } catch {\n // headers() not available during static generation\n }\n\n return 'en'\n}\n\n/**\n * Generate static params for all configured locales.\n *\n * @example\n * ```tsx\n * export function generateStaticParams() {\n * return generateLocaleParams(['en', 'ja', 'zh-CN'])\n * }\n * ```\n */\nexport function generateLocaleParams(locales: string[]): Array<{ locale: string }> {\n return locales.map(locale => ({ locale }))\n}\n"],"mappings":"4HAmDA,eAAsB,EACpB,EACA,EACA,EACY,CACZ,GAAI,CAAC,EACH,MAAU,MACR,oJAGD,CAOH,IAAM,GADe,MAAM,EAAa,SAAS,EACO,OAExD,GAAI,CAIF,OAHA,EAAa,UAAU,EAAO,CAE9B,MAAM,EAAa,SAAS,CACrB,MAAM,GAAI,QACT,CACR,EAAa,UAAU,EAAW,CAClC,MAAM,EAAa,SAAS,ECtDhC,IAAM,GAAA,EAAA,EAAA,YAA+B,CAAE,OAAQ,GAAI,EAAE,CAQrD,SAAgB,EAAiB,EAAsB,CACrD,GAAiB,CAAC,OAAS,EAQ7B,eAAsB,GAA6B,CACjD,IAAM,EAAQ,GAAiB,CAC/B,GAAI,EAAM,OAAQ,OAAO,EAAM,OAE/B,GAAI,CACF,GAAM,CAAE,WAAY,MAAM,OAAO,gBAE3B,GADI,MAAO,GAAiE,EAC3D,IAAI,mBAAmB,CAC9C,GAAI,EAAc,OAAO,OACnB,EAIR,MAAO,KAaT,SAAgB,EAAqB,EAA8C,CACjF,OAAO,EAAQ,IAAI,IAAW,CAAE,SAAQ,EAAE"}
package/dist/server.d.ts CHANGED
@@ -1,7 +1,44 @@
1
1
  /**
2
- * Server-side utilities for @fluenti/next.
2
+ * @module @fluenti/next/server
3
3
  *
4
- * Provides `withLocale()` for per-component locale isolation in RSC.
4
+ * Server-side utilities for Next.js App Router.
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * import { getLocale, setRequestLocale } from '@fluenti/next/server'
9
+ *
10
+ * export default async function Page({ params }) {
11
+ * const { locale } = await params
12
+ * setRequestLocale(locale)
13
+ * const currentLocale = await getLocale()
14
+ * }
15
+ * ```
5
16
  */
6
17
  export { withLocale } from './with-locale';
18
+ /**
19
+ * Set the locale for the current request scope.
20
+ *
21
+ * Call this at the top of page/layout components to enable static rendering.
22
+ * Required for `generateStaticParams` pages where no middleware header is available.
23
+ */
24
+ export declare function setRequestLocale(locale: string): void;
25
+ /**
26
+ * Get the current locale in a Server Component.
27
+ *
28
+ * Resolution: setRequestLocale() → x-fluenti-locale header → 'en'
29
+ */
30
+ export declare function getLocale(): Promise<string>;
31
+ /**
32
+ * Generate static params for all configured locales.
33
+ *
34
+ * @example
35
+ * ```tsx
36
+ * export function generateStaticParams() {
37
+ * return generateLocaleParams(['en', 'ja', 'zh-CN'])
38
+ * }
39
+ * ```
40
+ */
41
+ export declare function generateLocaleParams(locales: string[]): Array<{
42
+ locale: string;
43
+ }>;
7
44
  //# sourceMappingURL=server.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA"}
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAA;AAM1C;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAErD;AAED;;;;GAIG;AACH,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAcjD;AAED;;;;;;;;;GASG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,KAAK,CAAC;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAEjF"}
package/dist/server.js CHANGED
@@ -1,5 +1,6 @@
1
+ import { cache as e } from "react";
1
2
  //#region src/with-locale.ts
2
- async function e(e, t, n) {
3
+ async function t(e, t, n) {
3
4
  if (!n) throw Error("[fluenti] withLocale requires a server module reference. Pass the generated server module as the third argument: withLocale(\"ja\", fn, serverI18n)");
4
5
  let r = (await n.getI18n()).locale;
5
6
  try {
@@ -9,6 +10,24 @@ async function e(e, t, n) {
9
10
  }
10
11
  }
11
12
  //#endregion
12
- export { e as withLocale };
13
+ //#region src/server.ts
14
+ var n = e(() => ({ locale: "" }));
15
+ function r(e) {
16
+ n().locale = e;
17
+ }
18
+ async function i() {
19
+ let e = n();
20
+ if (e.locale) return e.locale;
21
+ try {
22
+ let { headers: e } = await import("next/headers"), t = (await e()).get("x-fluenti-locale");
23
+ if (t) return t;
24
+ } catch {}
25
+ return "en";
26
+ }
27
+ function a(e) {
28
+ return e.map((e) => ({ locale: e }));
29
+ }
30
+ //#endregion
31
+ export { a as generateLocaleParams, i as getLocale, r as setRequestLocale, t as withLocale };
13
32
 
14
33
  //# sourceMappingURL=server.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"server.js","names":[],"sources":["../src/with-locale.ts"],"sourcesContent":["/**\n * Per-component locale isolation for RSC.\n *\n * Temporarily switches the request-scoped locale, executes a function,\n * then restores the previous locale. This allows rendering a subtree in\n * a different locale without affecting the rest of the page.\n *\n * @param locale - The locale to switch to for the duration of `fn`.\n * @param fn - The function to execute with the switched locale.\n * @param serverModule - The generated server module reference (auto-injected\n * by the webpack loader in production; only needed when calling manually).\n * @returns The return value of `fn`.\n *\n * @example Basic usage in a Server Component\n * ```tsx\n * // app/page.tsx (Server Component)\n * import { withLocale } from '@fluenti/next/server'\n *\n * export default async function Page() {\n * return (\n * <div>\n * <h1>{t`Main content`}</h1>\n * {await withLocale('ja', async () => (\n * <JapaneseWidget />\n * ))}\n * <Footer />\n * </div>\n * )\n * }\n * ```\n *\n * @example Rendering a multilingual page section\n * ```tsx\n * import { withLocale } from '@fluenti/next/server'\n *\n * export default async function MultilingualPage() {\n * const englishContent = await withLocale('en', async () => (\n * <p>{t`Welcome`}</p>\n * ))\n * const japaneseContent = await withLocale('ja', async () => (\n * <p>{t`Welcome`}</p>\n * ))\n * return (\n * <div>\n * {englishContent}\n * {japaneseContent}\n * </div>\n * )\n * }\n * ```\n */\nexport async function withLocale<T>(\n locale: string,\n fn: () => T | Promise<T>,\n serverModule?: { setLocale: (l: string) => void; getI18n: () => Promise<unknown> },\n): Promise<T> {\n if (!serverModule) {\n throw new Error(\n '[fluenti] withLocale requires a server module reference. ' +\n 'Pass the generated server module as the third argument: ' +\n 'withLocale(\"ja\", fn, serverI18n)',\n )\n }\n\n // Read current locale from the module's request store\n // We need to save/restore state. Since we can't access the store directly,\n // we use setLocale to switch and rely on the caller to restore.\n const prevInstance = await serverModule.getI18n()\n const prevLocale = (prevInstance as { locale: string }).locale\n\n try {\n serverModule.setLocale(locale)\n // Force instance recreation by calling getI18n again\n await serverModule.getI18n()\n return await fn()\n } finally {\n serverModule.setLocale(prevLocale)\n await serverModule.getI18n()\n }\n}\n"],"mappings":";AAmDA,eAAsB,EACpB,GACA,GACA,GACY;AACZ,KAAI,CAAC,EACH,OAAU,MACR,sJAGD;CAOH,IAAM,KADe,MAAM,EAAa,SAAS,EACO;AAExD,KAAI;AAIF,SAHA,EAAa,UAAU,EAAO,EAE9B,MAAM,EAAa,SAAS,EACrB,MAAM,GAAI;WACT;AAER,EADA,EAAa,UAAU,EAAW,EAClC,MAAM,EAAa,SAAS"}
1
+ {"version":3,"file":"server.js","names":[],"sources":["../src/with-locale.ts","../src/server.ts"],"sourcesContent":["/**\n * Per-component locale isolation for RSC.\n *\n * Temporarily switches the request-scoped locale, executes a function,\n * then restores the previous locale. This allows rendering a subtree in\n * a different locale without affecting the rest of the page.\n *\n * @param locale - The locale to switch to for the duration of `fn`.\n * @param fn - The function to execute with the switched locale.\n * @param serverModule - The generated server module reference (auto-injected\n * by the webpack loader in production; only needed when calling manually).\n * @returns The return value of `fn`.\n *\n * @example Basic usage in a Server Component\n * ```tsx\n * // app/page.tsx (Server Component)\n * import { withLocale } from '@fluenti/next/server'\n *\n * export default async function Page() {\n * return (\n * <div>\n * <h1>{t`Main content`}</h1>\n * {await withLocale('ja', async () => (\n * <JapaneseWidget />\n * ))}\n * <Footer />\n * </div>\n * )\n * }\n * ```\n *\n * @example Rendering a multilingual page section\n * ```tsx\n * import { withLocale } from '@fluenti/next/server'\n *\n * export default async function MultilingualPage() {\n * const englishContent = await withLocale('en', async () => (\n * <p>{t`Welcome`}</p>\n * ))\n * const japaneseContent = await withLocale('ja', async () => (\n * <p>{t`Welcome`}</p>\n * ))\n * return (\n * <div>\n * {englishContent}\n * {japaneseContent}\n * </div>\n * )\n * }\n * ```\n */\nexport async function withLocale<T>(\n locale: string,\n fn: () => T | Promise<T>,\n serverModule?: { setLocale: (l: string) => void; getI18n: () => Promise<unknown> },\n): Promise<T> {\n if (!serverModule) {\n throw new Error(\n '[fluenti] withLocale requires a server module reference. ' +\n 'Pass the generated server module as the third argument: ' +\n 'withLocale(\"ja\", fn, serverI18n)',\n )\n }\n\n // Read current locale from the module's request store\n // We need to save/restore state. Since we can't access the store directly,\n // we use setLocale to switch and rely on the caller to restore.\n const prevInstance = await serverModule.getI18n()\n const prevLocale = (prevInstance as { locale: string }).locale\n\n try {\n serverModule.setLocale(locale)\n // Force instance recreation by calling getI18n again\n await serverModule.getI18n()\n return await fn()\n } finally {\n serverModule.setLocale(prevLocale)\n await serverModule.getI18n()\n }\n}\n","/**\n * @module @fluenti/next/server\n *\n * Server-side utilities for Next.js App Router.\n *\n * @example\n * ```tsx\n * import { getLocale, setRequestLocale } from '@fluenti/next/server'\n *\n * export default async function Page({ params }) {\n * const { locale } = await params\n * setRequestLocale(locale)\n * const currentLocale = await getLocale()\n * }\n * ```\n */\n\nimport { cache } from 'react'\n\nexport { withLocale } from './with-locale'\n\n// ── Request-scoped locale store (React.cache) ─────────────────────────────\n\nconst getRequestStore = cache(() => ({ locale: '' }))\n\n/**\n * Set the locale for the current request scope.\n *\n * Call this at the top of page/layout components to enable static rendering.\n * Required for `generateStaticParams` pages where no middleware header is available.\n */\nexport function setRequestLocale(locale: string): void {\n getRequestStore().locale = locale\n}\n\n/**\n * Get the current locale in a Server Component.\n *\n * Resolution: setRequestLocale() → x-fluenti-locale header → 'en'\n */\nexport async function getLocale(): Promise<string> {\n const store = getRequestStore()\n if (store.locale) return store.locale\n\n try {\n const { headers } = await import('next/headers')\n const h = await (headers as () => Promise<{ get(name: string): string | null }>)()\n const headerLocale = h.get('x-fluenti-locale')\n if (headerLocale) return headerLocale\n } catch {\n // headers() not available during static generation\n }\n\n return 'en'\n}\n\n/**\n * Generate static params for all configured locales.\n *\n * @example\n * ```tsx\n * export function generateStaticParams() {\n * return generateLocaleParams(['en', 'ja', 'zh-CN'])\n * }\n * ```\n */\nexport function generateLocaleParams(locales: string[]): Array<{ locale: string }> {\n return locales.map(locale => ({ locale }))\n}\n"],"mappings":";;AAmDA,eAAsB,EACpB,GACA,GACA,GACY;AACZ,KAAI,CAAC,EACH,OAAU,MACR,sJAGD;CAOH,IAAM,KADe,MAAM,EAAa,SAAS,EACO;AAExD,KAAI;AAIF,SAHA,EAAa,UAAU,EAAO,EAE9B,MAAM,EAAa,SAAS,EACrB,MAAM,GAAI;WACT;AAER,EADA,EAAa,UAAU,EAAW,EAClC,MAAM,EAAa,SAAS;;;;;ACtDhC,IAAM,IAAkB,SAAa,EAAE,QAAQ,IAAI,EAAE;AAQrD,SAAgB,EAAiB,GAAsB;AACrD,IAAiB,CAAC,SAAS;;AAQ7B,eAAsB,IAA6B;CACjD,IAAM,IAAQ,GAAiB;AAC/B,KAAI,EAAM,OAAQ,QAAO,EAAM;AAE/B,KAAI;EACF,IAAM,EAAE,eAAY,MAAM,OAAO,iBAE3B,KADI,MAAO,GAAiE,EAC3D,IAAI,mBAAmB;AAC9C,MAAI,EAAc,QAAO;SACnB;AAIR,QAAO;;AAaT,SAAgB,EAAqB,GAA8C;AACjF,QAAO,EAAQ,KAAI,OAAW,EAAE,WAAQ,EAAE"}
package/dist/types.d.ts CHANGED
@@ -8,6 +8,16 @@ import { FluentiBuildConfig } from '@fluenti/core/internal';
8
8
  export interface WithFluentConfig {
9
9
  /** fluenti.config.ts path or inline config */
10
10
  config?: string | FluentiBuildConfig;
11
+ /**
12
+ * Shorthand for `config: { locales }`. Specify target locales without a separate config file.
13
+ * Ignored if `config` is also provided.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * export default withFluenti({ locales: ['en', 'ja', 'zh-CN'] })({ reactStrictMode: true })
18
+ * ```
19
+ */
20
+ locales?: string[];
11
21
  /** Custom serverModule path (skip auto-generation) */
12
22
  serverModule?: string;
13
23
  /** Where to generate the serverModule (default: .fluenti) */
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAEhE;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAAA;IAIpC,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6DAA6D;IAC7D,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAE3B;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,CAAA;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,4CAA4C;IAC5C,aAAa,EAAE,kBAAkB,CAAA;IACjC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAA;AAEhE;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,8CAA8C;IAC9C,MAAM,CAAC,EAAE,MAAM,GAAG,kBAAkB,CAAA;IACpC;;;;;;;;OAQG;IACH,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAIlB,sDAAsD;IACtD,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,6DAA6D;IAC7D,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAE3B;;;;;;;;;OASG;IACH,aAAa,CAAC,EAAE,MAAM,CAAA;IAEtB,gEAAgE;IAChE,UAAU,CAAC,EAAE,MAAM,CAAA;IAEnB;;;;;OAKG;IACH,aAAa,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,SAAS,CAAA;CAC3C;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACnC,4CAA4C;IAC5C,aAAa,EAAE,kBAAkB,CAAA;IACjC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,kBAAkB,EAAE,MAAM,CAAA;IAC1B,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B"}
@@ -6,18 +6,16 @@ type NextConfig = Record<string, any>;
6
6
  * Adds a webpack loader that transforms `t\`\`` and `t()` calls,
7
7
  * and generates a server module for RSC i18n.
8
8
  *
9
- * @example
9
+ * @example Minimal — no fluenti.config.ts needed
10
10
  * ```ts
11
- * // next.config.ts — function style (recommended)
12
11
  * import { withFluenti } from '@fluenti/next'
13
- * export default withFluenti()({ reactStrictMode: true })
12
+ * export default withFluenti({ locales: ['en', 'ja'] })({ reactStrictMode: true })
14
13
  * ```
15
14
  *
16
- * @example
15
+ * @example With fluenti.config.ts (advanced)
17
16
  * ```ts
18
- * // next.config.ts — direct style
19
17
  * import { withFluenti } from '@fluenti/next'
20
- * export default withFluenti({ reactStrictMode: true })
18
+ * export default withFluenti()({ reactStrictMode: true })
21
19
  * ```
22
20
  */
23
21
  export declare function withFluenti(fluentConfig?: WithFluentConfig): (nextConfig?: NextConfig) => NextConfig;
@@ -1 +1 @@
1
- {"version":3,"file":"with-fluenti.d.ts","sourceRoot":"","sources":["../src/with-fluenti.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAQ/C,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;AAErC;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,WAAW,CAAC,YAAY,CAAC,EAAE,gBAAgB,GAAG,CAAC,UAAU,CAAC,EAAE,UAAU,KAAK,UAAU,CAAA;AACrG,wBAAgB,WAAW,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,CAAA"}
1
+ {"version":3,"file":"with-fluenti.d.ts","sourceRoot":"","sources":["../src/with-fluenti.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAA;AAQ/C,KAAK,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAA;AAErC;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,WAAW,CAAC,YAAY,CAAC,EAAE,gBAAgB,GAAG,CAAC,UAAU,CAAC,EAAE,UAAU,KAAK,UAAU,CAAA;AACrG,wBAAgB,WAAW,CAAC,UAAU,EAAE,UAAU,GAAG,UAAU,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fluenti/next",
3
- "version": "0.4.0-rc.1",
3
+ "version": "0.4.0-rc.3",
4
4
  "type": "module",
5
5
  "description": "Next.js plugin for Fluenti — withFluenti, I18nProvider, t`` transforms for App Router and Pages Router",
6
6
  "homepage": "https://fluenti.dev",
@@ -107,8 +107,8 @@
107
107
  "dependencies": {
108
108
  "jiti": "^2",
109
109
  "picomatch": "^4",
110
- "@fluenti/core": "0.4.0-rc.1",
111
- "@fluenti/react": "0.4.0-rc.1"
110
+ "@fluenti/core": "0.4.0-rc.3",
111
+ "@fluenti/react": "0.4.0-rc.3"
112
112
  },
113
113
  "devDependencies": {
114
114
  "@types/node": "^25",