@better-i18n/next 0.5.2 → 0.5.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -139,8 +139,13 @@ const languages = await getManifestLanguages(config);
139
139
  | `defaultLocale` | `string` | required | Default/fallback locale code |
140
140
  | `cdnBaseUrl` | `string` | auto | CDN base URL (auto-detected) |
141
141
  | `localePrefix` | `"as-needed"` \| `"always"` \| `"never"` | `"as-needed"` | URL locale prefix behavior |
142
+ | `cookieName` | `string` | `"locale"` | Cookie name for locale persistence |
142
143
  | `manifestRevalidateSeconds` | `number` | `3600` | Next.js ISR revalidation for manifest |
143
144
  | `messagesRevalidateSeconds` | `number` | `30` | Next.js ISR revalidation for messages |
145
+ | `storage` | `TranslationStorage` | `undefined` | Persistent cache for offline fallback (see `@better-i18n/core`) |
146
+ | `staticData` | `Record \| () => Promise` | `undefined` | Bundled translations as last-resort fallback |
147
+ | `fetchTimeout` | `number` | `10000` | CDN fetch timeout in ms |
148
+ | `retryCount` | `number` | `1` | Retry attempts on CDN failure |
144
149
  | `debug` | `boolean` | `false` | Enable debug logging |
145
150
  | `logLevel` | `LogLevel` | `"warn"` | Logging verbosity |
146
151
 
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=client-fallback.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-fallback.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/client-fallback.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,108 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { normalizeConfig } from "../config.js";
3
+ // ─── Test fixtures ──────────────────────────────────────────────────
4
+ const BASE_CONFIG = {
5
+ project: "acme/dashboard",
6
+ defaultLocale: "en",
7
+ };
8
+ const mockStorage = {
9
+ get: async () => null,
10
+ set: async () => { },
11
+ };
12
+ const mockStaticData = {
13
+ en: { common: { hello: "Hello" } },
14
+ tr: { common: { hello: "Merhaba" } },
15
+ };
16
+ // ─── Tests ──────────────────────────────────────────────────────────
17
+ describe("normalizeConfig — fallback fields passthrough", () => {
18
+ it("passes storage to core config", () => {
19
+ const config = normalizeConfig({
20
+ ...BASE_CONFIG,
21
+ storage: mockStorage,
22
+ });
23
+ expect(config.storage).toBe(mockStorage);
24
+ });
25
+ it("passes staticData to core config", () => {
26
+ const config = normalizeConfig({
27
+ ...BASE_CONFIG,
28
+ staticData: mockStaticData,
29
+ });
30
+ expect(config.staticData).toBe(mockStaticData);
31
+ });
32
+ it("passes fetchTimeout to core config", () => {
33
+ const config = normalizeConfig({
34
+ ...BASE_CONFIG,
35
+ fetchTimeout: 5000,
36
+ });
37
+ expect(config.fetchTimeout).toBe(5000);
38
+ });
39
+ it("passes retryCount to core config", () => {
40
+ const config = normalizeConfig({
41
+ ...BASE_CONFIG,
42
+ retryCount: 3,
43
+ });
44
+ expect(config.retryCount).toBe(3);
45
+ });
46
+ it("passes all fallback fields together", () => {
47
+ const config = normalizeConfig({
48
+ ...BASE_CONFIG,
49
+ storage: mockStorage,
50
+ staticData: mockStaticData,
51
+ fetchTimeout: 7000,
52
+ retryCount: 2,
53
+ });
54
+ expect(config.storage).toBe(mockStorage);
55
+ expect(config.staticData).toBe(mockStaticData);
56
+ expect(config.fetchTimeout).toBe(7000);
57
+ expect(config.retryCount).toBe(2);
58
+ });
59
+ it("applies default fetchTimeout when not specified", () => {
60
+ const config = normalizeConfig(BASE_CONFIG);
61
+ expect(config.fetchTimeout).toBe(10000);
62
+ });
63
+ it("applies default retryCount when not specified", () => {
64
+ const config = normalizeConfig(BASE_CONFIG);
65
+ expect(config.retryCount).toBe(1);
66
+ });
67
+ it("leaves storage undefined when not specified", () => {
68
+ const config = normalizeConfig(BASE_CONFIG);
69
+ expect(config.storage).toBeUndefined();
70
+ });
71
+ it("leaves staticData undefined when not specified", () => {
72
+ const config = normalizeConfig(BASE_CONFIG);
73
+ expect(config.staticData).toBeUndefined();
74
+ });
75
+ it("preserves Next.js-specific defaults alongside fallback fields", () => {
76
+ const config = normalizeConfig({
77
+ ...BASE_CONFIG,
78
+ storage: mockStorage,
79
+ });
80
+ // Next.js defaults should still be set
81
+ expect(config.localePrefix).toBe("as-needed");
82
+ expect(config.cookieName).toBe("locale");
83
+ expect(config.manifestRevalidateSeconds).toBe(3600);
84
+ expect(config.messagesRevalidateSeconds).toBe(30);
85
+ // Fallback field should be present
86
+ expect(config.storage).toBe(mockStorage);
87
+ });
88
+ it("supports lazy staticData function", () => {
89
+ const lazyStaticData = async () => mockStaticData;
90
+ const config = normalizeConfig({
91
+ ...BASE_CONFIG,
92
+ staticData: lazyStaticData,
93
+ });
94
+ expect(config.staticData).toBe(lazyStaticData);
95
+ });
96
+ it("passes timeZone when provided", () => {
97
+ const config = normalizeConfig({
98
+ ...BASE_CONFIG,
99
+ timeZone: "Europe/Istanbul",
100
+ });
101
+ expect(config.timeZone).toBe("Europe/Istanbul");
102
+ });
103
+ it("leaves timeZone undefined when not specified", () => {
104
+ const config = normalizeConfig(BASE_CONFIG);
105
+ expect(config.timeZone).toBeUndefined();
106
+ });
107
+ });
108
+ //# sourceMappingURL=client-fallback.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client-fallback.test.js","sourceRoot":"","sources":["../../src/__tests__/client-fallback.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,104 @@
1
+ import { type ReactNode } from "react";
2
+ import type { LanguageOption, Messages } from "@better-i18n/core";
3
+ import type { I18nConfig } from "./types.js";
4
+ export interface BetterI18nProviderProps {
5
+ /** Initial locale from server (getLocale()) */
6
+ locale: string;
7
+ /** Initial messages from server (getMessages()) */
8
+ messages: Messages;
9
+ /** i18n config — only project and defaultLocale are required */
10
+ config: I18nConfig;
11
+ /**
12
+ * IANA time zone for date/time formatting consistency.
13
+ * Falls back to `config.timeZone`, then auto-detects via `Intl.DateTimeFormat`.
14
+ */
15
+ timeZone?: string;
16
+ children: ReactNode;
17
+ }
18
+ /**
19
+ * Provider that wraps `NextIntlClientProvider` and enables instant locale
20
+ * switching without a server round-trip.
21
+ *
22
+ * When `useSetLocale()` is called inside this provider, it:
23
+ * 1. Sets a cookie (for server-side persistence on next navigation)
24
+ * 2. Fetches new messages from CDN on the client
25
+ * 3. Re-renders the tree with new locale + messages instantly
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * // app/layout.tsx
30
+ * import { BetterI18nProvider } from '@better-i18n/next/client'
31
+ *
32
+ * export default async function RootLayout({ children }) {
33
+ * const locale = await getLocale()
34
+ * const messages = await getMessages()
35
+ *
36
+ * return (
37
+ * <BetterI18nProvider
38
+ * locale={locale}
39
+ * messages={messages}
40
+ * config={{ project: 'acme/dashboard', defaultLocale: 'en' }}
41
+ * >
42
+ * {children}
43
+ * </BetterI18nProvider>
44
+ * )
45
+ * }
46
+ * ```
47
+ */
48
+ export declare function BetterI18nProvider({ locale: initialLocale, messages: initialMessages, config, timeZone: timeZoneProp, children, }: BetterI18nProviderProps): import("react/jsx-runtime").JSX.Element;
49
+ export type UseManifestLanguagesResult = {
50
+ languages: LanguageOption[];
51
+ isLoading: boolean;
52
+ error: Error | null;
53
+ };
54
+ /**
55
+ * React hook to fetch manifest languages on the client
56
+ *
57
+ * Uses `createI18nCore` from `@better-i18n/core` internally with
58
+ * request deduplication to prevent duplicate fetches.
59
+ *
60
+ * @example
61
+ * ```tsx
62
+ * const { languages, isLoading, error } = useManifestLanguages({
63
+ * project: 'acme/dashboard',
64
+ * defaultLocale: 'en',
65
+ * })
66
+ *
67
+ * if (isLoading) return <Spinner />
68
+ * if (error) return <Error message={error.message} />
69
+ *
70
+ * return (
71
+ * <select>
72
+ * {languages.map(lang => (
73
+ * <option key={lang.code} value={lang.code}>
74
+ * {lang.nativeName || lang.name || lang.code}
75
+ * </option>
76
+ * ))}
77
+ * </select>
78
+ * )
79
+ * ```
80
+ */
81
+ export declare const useManifestLanguages: (config: I18nConfig) => UseManifestLanguagesResult;
82
+ /**
83
+ * React hook that returns a function to switch the active locale.
84
+ *
85
+ * - **With `BetterI18nProvider`**: Fetches new messages client-side from CDN
86
+ * and updates the UI instantly — no page refresh needed.
87
+ * - **Without provider (standalone)**: Pass a config object. Sets a cookie and
88
+ * calls `router.refresh()` for a soft server re-render.
89
+ *
90
+ * @example With provider (recommended — instant switching)
91
+ * ```tsx
92
+ * // Wrap your layout with BetterI18nProvider, then:
93
+ * const setLocale = useSetLocale()
94
+ * <button onClick={() => setLocale('tr')}>Türkçe</button>
95
+ * ```
96
+ *
97
+ * @example Standalone (soft refresh)
98
+ * ```tsx
99
+ * const setLocale = useSetLocale({ project: 'acme/dashboard', defaultLocale: 'en' })
100
+ * <button onClick={() => setLocale('tr')}>Türkçe</button>
101
+ * ```
102
+ */
103
+ export declare function useSetLocale(config?: I18nConfig): (locale: string) => void;
104
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"AAEA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAIf,OAAO,KAAK,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAGlE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAU7C,MAAM,WAAW,uBAAuB;IACtC,+CAA+C;IAC/C,MAAM,EAAE,MAAM,CAAC;IACf,mDAAmD;IACnD,QAAQ,EAAE,QAAQ,CAAC;IACnB,gEAAgE;IAChE,MAAM,EAAE,UAAU,CAAC;IACnB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6BG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,eAAe,EACzB,MAAM,EACN,QAAQ,EAAE,YAAY,EACtB,QAAQ,GACT,EAAE,uBAAuB,2CAmEzB;AAID,MAAM,MAAM,0BAA0B,GAAG;IACvC,SAAS,EAAE,cAAc,EAAE,CAAC;IAC5B,SAAS,EAAE,OAAO,CAAC;IACnB,KAAK,EAAE,KAAK,GAAG,IAAI,CAAC;CACrB,CAAC;AAcF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,oBAAoB,GAAI,QAAQ,UAAU,KAAG,0BAoFzD,CAAC;AAIF;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,YAAY,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAkB1E"}
package/dist/client.js ADDED
@@ -0,0 +1,233 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from "react";
4
+ import { useRouter } from "next/navigation";
5
+ import { NextIntlClientProvider } from "next-intl";
6
+ import { createI18nCore } from "@better-i18n/core";
7
+ import { normalizeConfig } from "./config.js";
8
+ const BetterI18nContext = createContext(null);
9
+ /**
10
+ * Provider that wraps `NextIntlClientProvider` and enables instant locale
11
+ * switching without a server round-trip.
12
+ *
13
+ * When `useSetLocale()` is called inside this provider, it:
14
+ * 1. Sets a cookie (for server-side persistence on next navigation)
15
+ * 2. Fetches new messages from CDN on the client
16
+ * 3. Re-renders the tree with new locale + messages instantly
17
+ *
18
+ * @example
19
+ * ```tsx
20
+ * // app/layout.tsx
21
+ * import { BetterI18nProvider } from '@better-i18n/next/client'
22
+ *
23
+ * export default async function RootLayout({ children }) {
24
+ * const locale = await getLocale()
25
+ * const messages = await getMessages()
26
+ *
27
+ * return (
28
+ * <BetterI18nProvider
29
+ * locale={locale}
30
+ * messages={messages}
31
+ * config={{ project: 'acme/dashboard', defaultLocale: 'en' }}
32
+ * >
33
+ * {children}
34
+ * </BetterI18nProvider>
35
+ * )
36
+ * }
37
+ * ```
38
+ */
39
+ export function BetterI18nProvider({ locale: initialLocale, messages: initialMessages, config, timeZone: timeZoneProp, children, }) {
40
+ // Resolve timeZone: prop > config > runtime auto-detection.
41
+ // This avoids next-intl's ENVIRONMENT_FALLBACK warning.
42
+ const timeZone = useMemo(() => timeZoneProp ??
43
+ config.timeZone ??
44
+ Intl.DateTimeFormat().resolvedOptions().timeZone, [timeZoneProp, config.timeZone]);
45
+ const normalized = useMemo(() => normalizeConfig(config), [
46
+ config.project,
47
+ config.defaultLocale,
48
+ config.cookieName,
49
+ config.cdnBaseUrl,
50
+ config.storage,
51
+ config.staticData,
52
+ config.fetchTimeout,
53
+ config.retryCount,
54
+ ]);
55
+ // Provider-level memoized core instance — reused by setLocale
56
+ const i18nCore = useMemo(() => createI18nCore(normalized), [normalized]);
57
+ const [locale, setLocaleState] = useState(initialLocale);
58
+ const [messages, setMessages] = useState(initialMessages);
59
+ // Sync with server-provided values when they change (e.g. navigation to a new page)
60
+ useEffect(() => {
61
+ setLocaleState(initialLocale);
62
+ setMessages(initialMessages);
63
+ }, [initialLocale, initialMessages]);
64
+ const setLocale = useCallback(async (newLocale) => {
65
+ if (newLocale === locale)
66
+ return;
67
+ // 1. Set cookie for server-side persistence
68
+ document.cookie = `${normalized.cookieName}=${newLocale}; path=/; max-age=31536000; samesite=lax`;
69
+ // 2. Fetch new messages with full fallback chain (CDN → Storage → StaticData)
70
+ try {
71
+ const newMessages = await i18nCore.getMessages(newLocale);
72
+ // 3. Instant client-side update — no server round-trip
73
+ setLocaleState(newLocale);
74
+ setMessages(newMessages);
75
+ }
76
+ catch (err) {
77
+ console.error("[better-i18n] Client-side locale switch failed, falling back to refresh:", err);
78
+ // Fallback: let the server handle it
79
+ window.location.reload();
80
+ }
81
+ }, [locale, normalized.cookieName, i18nCore]);
82
+ return (_jsx(BetterI18nContext.Provider, { value: { setLocale }, children: _jsx(NextIntlClientProvider, { locale: locale, messages: messages, timeZone: timeZone, children: children }) }));
83
+ }
84
+ // Client-side request deduplication cache
85
+ const clientCache = new Map();
86
+ const getCacheKey = (project, cdnBaseUrl) => `${cdnBaseUrl || "https://cdn.better-i18n.com"}|${project}`;
87
+ /**
88
+ * React hook to fetch manifest languages on the client
89
+ *
90
+ * Uses `createI18nCore` from `@better-i18n/core` internally with
91
+ * request deduplication to prevent duplicate fetches.
92
+ *
93
+ * @example
94
+ * ```tsx
95
+ * const { languages, isLoading, error } = useManifestLanguages({
96
+ * project: 'acme/dashboard',
97
+ * defaultLocale: 'en',
98
+ * })
99
+ *
100
+ * if (isLoading) return <Spinner />
101
+ * if (error) return <Error message={error.message} />
102
+ *
103
+ * return (
104
+ * <select>
105
+ * {languages.map(lang => (
106
+ * <option key={lang.code} value={lang.code}>
107
+ * {lang.nativeName || lang.name || lang.code}
108
+ * </option>
109
+ * ))}
110
+ * </select>
111
+ * )
112
+ * ```
113
+ */
114
+ export const useManifestLanguages = (config) => {
115
+ const normalized = useMemo(() => normalizeConfig(config), [
116
+ config.project,
117
+ config.defaultLocale,
118
+ config.cdnBaseUrl,
119
+ config.debug,
120
+ config.logLevel,
121
+ config.storage,
122
+ config.staticData,
123
+ config.fetchTimeout,
124
+ config.retryCount,
125
+ ]);
126
+ const i18nCore = useMemo(() => createI18nCore(normalized), [normalized]);
127
+ const cacheKey = getCacheKey(normalized.project, normalized.cdnBaseUrl);
128
+ const cached = clientCache.get(cacheKey);
129
+ const [languages, setLanguages] = useState(cached?.data ?? []);
130
+ const [isLoading, setIsLoading] = useState(!cached?.data);
131
+ const [error, setError] = useState(cached?.error ?? null);
132
+ useEffect(() => {
133
+ let isMounted = true;
134
+ const run = async () => {
135
+ const entry = clientCache.get(cacheKey) ?? {};
136
+ // Return cached data if available
137
+ if (entry.data) {
138
+ if (isMounted) {
139
+ setLanguages(entry.data);
140
+ setIsLoading(false);
141
+ }
142
+ return;
143
+ }
144
+ // Deduplicate in-flight requests
145
+ if (!entry.promise) {
146
+ entry.promise = i18nCore
147
+ .getLanguages()
148
+ .then((langs) => {
149
+ clientCache.set(cacheKey, { data: langs });
150
+ return langs;
151
+ })
152
+ .catch((err) => {
153
+ const error = err instanceof Error ? err : new Error(String(err));
154
+ clientCache.set(cacheKey, { error });
155
+ throw error;
156
+ });
157
+ clientCache.set(cacheKey, entry);
158
+ }
159
+ try {
160
+ const langs = await entry.promise;
161
+ if (isMounted) {
162
+ setLanguages(langs);
163
+ setError(null);
164
+ }
165
+ }
166
+ catch (err) {
167
+ if (isMounted) {
168
+ setError(err instanceof Error ? err : new Error(String(err)));
169
+ }
170
+ }
171
+ finally {
172
+ if (isMounted) {
173
+ setIsLoading(false);
174
+ }
175
+ }
176
+ };
177
+ run();
178
+ return () => {
179
+ isMounted = false;
180
+ };
181
+ }, [cacheKey, i18nCore]);
182
+ return { languages, isLoading, error };
183
+ };
184
+ // ─── useSetLocale ────────────────────────────────────────────────────
185
+ /**
186
+ * React hook that returns a function to switch the active locale.
187
+ *
188
+ * - **With `BetterI18nProvider`**: Fetches new messages client-side from CDN
189
+ * and updates the UI instantly — no page refresh needed.
190
+ * - **Without provider (standalone)**: Pass a config object. Sets a cookie and
191
+ * calls `router.refresh()` for a soft server re-render.
192
+ *
193
+ * @example With provider (recommended — instant switching)
194
+ * ```tsx
195
+ * // Wrap your layout with BetterI18nProvider, then:
196
+ * const setLocale = useSetLocale()
197
+ * <button onClick={() => setLocale('tr')}>Türkçe</button>
198
+ * ```
199
+ *
200
+ * @example Standalone (soft refresh)
201
+ * ```tsx
202
+ * const setLocale = useSetLocale({ project: 'acme/dashboard', defaultLocale: 'en' })
203
+ * <button onClick={() => setLocale('tr')}>Türkçe</button>
204
+ * ```
205
+ */
206
+ export function useSetLocale(config) {
207
+ const ctx = useContext(BetterI18nContext);
208
+ // If BetterI18nProvider is in the tree, use instant client-side switching
209
+ if (ctx) {
210
+ return ctx.setLocale;
211
+ }
212
+ // Standalone fallback: cookie + router.refresh()
213
+ if (!config) {
214
+ throw new Error("[better-i18n] useSetLocale() requires either a <BetterI18nProvider> ancestor " +
215
+ "or a config argument. Use useSetLocale({ project, defaultLocale }) for standalone mode.");
216
+ }
217
+ // eslint-disable-next-line react-hooks/rules-of-hooks
218
+ return useStandaloneSetLocale(config);
219
+ }
220
+ /** Internal standalone hook — cookie + router.refresh() */
221
+ function useStandaloneSetLocale(config) {
222
+ const normalized = useMemo(() => normalizeConfig(config), [
223
+ config.project,
224
+ config.defaultLocale,
225
+ config.cookieName,
226
+ ]);
227
+ const router = useRouter();
228
+ return useCallback((locale) => {
229
+ document.cookie = `${normalized.cookieName}=${locale}; path=/; max-age=31536000; samesite=lax`;
230
+ router.refresh();
231
+ }, [normalized.cookieName, router]);
232
+ }
233
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../src/client.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { parseProject } from "@better-i18n/core";
2
+ import type { I18nConfig, NormalizedConfig } from "./types.js";
3
+ export { parseProject };
4
+ /**
5
+ * Normalize Next.js i18n config with defaults
6
+ */
7
+ export declare const normalizeConfig: (config: I18nConfig) => NormalizedConfig;
8
+ /**
9
+ * Get the project base URL on CDN
10
+ */
11
+ export declare const getProjectBaseUrl: (config: NormalizedConfig) => string;
12
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EAGb,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAG/D,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,QAAQ,UAAU,KAAG,gBAsBpD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,QAAQ,gBAAgB,KAAG,MAC9B,CAAC"}
package/dist/config.js ADDED
@@ -0,0 +1,25 @@
1
+ import { parseProject, getProjectBaseUrl as coreGetProjectBaseUrl, normalizeConfig as coreNormalizeConfig, } from "@better-i18n/core";
2
+ // Re-export parseProject from core
3
+ export { parseProject };
4
+ /**
5
+ * Normalize Next.js i18n config with defaults
6
+ */
7
+ export const normalizeConfig = (config) => {
8
+ // Destructure Next.js-specific fields, pass the rest to core as-is.
9
+ // This way, when core adds new fields, this file doesn't need updating.
10
+ const { localePrefix, cookieName, manifestRevalidateSeconds, messagesRevalidateSeconds, timeZone, ...coreFields } = config;
11
+ const coreConfig = coreNormalizeConfig(coreFields);
12
+ return {
13
+ ...coreConfig,
14
+ localePrefix: localePrefix ?? "as-needed",
15
+ cookieName: cookieName ?? "locale",
16
+ manifestRevalidateSeconds: manifestRevalidateSeconds ?? 3600,
17
+ messagesRevalidateSeconds: messagesRevalidateSeconds ?? 30,
18
+ timeZone,
19
+ };
20
+ };
21
+ /**
22
+ * Get the project base URL on CDN
23
+ */
24
+ export const getProjectBaseUrl = (config) => coreGetProjectBaseUrl(config);
25
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,iBAAiB,IAAI,qBAAqB,EAC1C,eAAe,IAAI,mBAAmB,GACvC,MAAM,mBAAmB,CAAC;AAG3B,mCAAmC;AACnC,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB;;GAEG;AACH,MAAM,CAAC,MAAM,eAAe,GAAG,CAAC,MAAkB,EAAoB,EAAE;IACtE,oEAAoE;IACpE,wEAAwE;IACxE,MAAM,EACJ,YAAY,EACZ,UAAU,EACV,yBAAyB,EACzB,yBAAyB,EACzB,QAAQ,EACR,GAAG,UAAU,EACd,GAAG,MAAM,CAAC;IAEX,MAAM,UAAU,GAAG,mBAAmB,CAAC,UAAU,CAAC,CAAC;IAEnD,OAAO;QACL,GAAG,UAAU;QACb,YAAY,EAAE,YAAY,IAAI,WAAW;QACzC,UAAU,EAAE,UAAU,IAAI,QAAQ;QAClC,yBAAyB,EAAE,yBAAyB,IAAI,IAAI;QAC5D,yBAAyB,EAAE,yBAAyB,IAAI,EAAE;QAC1D,QAAQ;KACT,CAAC;AACJ,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,CAAC,MAAwB,EAAU,EAAE,CACpE,qBAAqB,CAAC,MAAM,CAAC,CAAC"}
@@ -0,0 +1,69 @@
1
+ import type { I18nConfig } from "./types.js";
2
+ import { createBetterI18nMiddleware, composeMiddleware, type MiddlewareCallback } from "./middleware.js";
3
+ /**
4
+ * Create a complete i18n setup for Next.js
5
+ *
6
+ * @example
7
+ * ```ts
8
+ * // i18n/config.ts
9
+ * import { createI18n } from '@better-i18n/next'
10
+ *
11
+ * export const i18n = createI18n({
12
+ * project: 'acme/dashboard',
13
+ * defaultLocale: 'en',
14
+ * localePrefix: 'always',
15
+ * })
16
+ *
17
+ * // i18n/request.ts
18
+ * export default i18n.requestConfig
19
+ *
20
+ * // middleware.ts (simple)
21
+ * export default i18n.middleware
22
+ *
23
+ * // middleware.ts (with auth - Clerk-style)
24
+ * export default i18n.betterMiddleware(async (request, { locale }) => {
25
+ * if (needsLogin) {
26
+ * return NextResponse.redirect(new URL(`/${locale}/login`, request.url));
27
+ * }
28
+ * });
29
+ * ```
30
+ */
31
+ export declare const createI18n: (config: I18nConfig) => {
32
+ config: import("./types.js").NormalizedConfig;
33
+ requestConfig: (params: import("next-intl/server").GetRequestConfigParams) => import("next-intl/server").RequestConfig | Promise<import("next-intl/server").RequestConfig>;
34
+ /** @deprecated Use betterMiddleware() for Clerk-style callback support */
35
+ middleware: (request: import("next/server.js").NextRequest) => Promise<import("next/server.js").NextResponse<unknown>>;
36
+ proxy: (request: import("next/server.js").NextRequest) => Promise<import("next/server.js").NextResponse<unknown>>;
37
+ getManifest: (options?: {
38
+ forceRefresh?: boolean;
39
+ }) => Promise<import("@better-i18n/core").ManifestResponse>;
40
+ getLocales: () => Promise<string[]>;
41
+ getMessages: (locale: string) => Promise<import("@better-i18n/core").Messages>;
42
+ /**
43
+ * Create middleware with optional Clerk-style callback for auth integration
44
+ *
45
+ * @example Simple usage
46
+ * ```ts
47
+ * export default i18n.betterMiddleware();
48
+ * ```
49
+ *
50
+ * @example With auth callback
51
+ * ```ts
52
+ * export default i18n.betterMiddleware(async (request, { locale, response }) => {
53
+ * if (needsLogin) {
54
+ * return NextResponse.redirect(new URL(`/${locale}/login`, request.url));
55
+ * }
56
+ * // Return nothing = i18n response is used (headers preserved!)
57
+ * });
58
+ * ```
59
+ */
60
+ betterMiddleware: (callback?: MiddlewareCallback) => (request: import("next/server.js").NextRequest) => Promise<import("next/server.js").NextResponse>;
61
+ };
62
+ export { createBetterI18nMiddleware, composeMiddleware };
63
+ export type { MiddlewareContext, MiddlewareCallback } from "./middleware.js";
64
+ export { createNextI18nCore } from "./server.js";
65
+ export { BetterI18nProvider, useManifestLanguages, useSetLocale } from "./client.js";
66
+ export type { BetterI18nProviderProps } from "./client.js";
67
+ export type { I18nConfig, LanguageOption, Locale, LocalePrefix, LogLevel, ManifestLanguage, ManifestResponse, Messages, } from "./types.js";
68
+ export type { I18nMiddlewareConfig } from "@better-i18n/core";
69
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAM7C,OAAO,EAGL,0BAA0B,EAC1B,iBAAiB,EACjB,KAAK,kBAAkB,EACxB,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,eAAO,MAAM,UAAU,GAAI,QAAQ,UAAU;;;IAOzC,0EAA0E;;;;oBAmE8nC,CAAC;;;;IA5DzsC;;;;;;;;;;;;;;;;;OAiBG;kCAC2B,kBAAkB;CAgBnD,CAAC;AAGF,OAAO,EAAE,0BAA0B,EAAE,iBAAiB,EAAE,CAAC;AACzD,YAAY,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAG7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGjD,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACrF,YAAY,EAAE,uBAAuB,EAAE,MAAM,aAAa,CAAC;AAG3D,YAAY,EACV,UAAU,EACV,cAAc,EACd,MAAM,EACN,YAAY,EACZ,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,QAAQ,GACT,MAAM,YAAY,CAAC;AAEpB,YAAY,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC"}