@better-i18n/use-intl 0.1.7 → 0.1.18

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 (49) hide show
  1. package/README.md +7 -0
  2. package/dist/__tests__/provider-config.test.d.ts +2 -0
  3. package/dist/__tests__/provider-config.test.d.ts.map +1 -0
  4. package/dist/__tests__/provider-config.test.js +79 -0
  5. package/dist/__tests__/provider-config.test.js.map +1 -0
  6. package/dist/components.d.ts +44 -0
  7. package/dist/components.d.ts.map +1 -0
  8. package/dist/components.js +38 -0
  9. package/dist/components.js.map +1 -0
  10. package/dist/context.d.ts +43 -0
  11. package/dist/context.d.ts.map +1 -0
  12. package/{src/context.tsx → dist/context.js} +8 -17
  13. package/dist/context.js.map +1 -0
  14. package/dist/hooks/useLocaleRouter.d.ts +75 -0
  15. package/dist/hooks/useLocaleRouter.d.ts.map +1 -0
  16. package/dist/hooks/useLocaleRouter.js +89 -0
  17. package/dist/hooks/useLocaleRouter.js.map +1 -0
  18. package/dist/hooks.d.ts +63 -0
  19. package/dist/hooks.d.ts.map +1 -0
  20. package/{src/hooks.ts → dist/hooks.js} +13 -25
  21. package/dist/hooks.js.map +1 -0
  22. package/dist/index.d.ts +12 -0
  23. package/dist/index.d.ts.map +1 -0
  24. package/dist/index.js +16 -0
  25. package/dist/index.js.map +1 -0
  26. package/dist/middleware/index.d.ts +3 -0
  27. package/dist/middleware/index.d.ts.map +1 -0
  28. package/dist/middleware/index.js +68 -0
  29. package/dist/middleware/index.js.map +1 -0
  30. package/dist/provider.d.ts +51 -0
  31. package/dist/provider.d.ts.map +1 -0
  32. package/dist/provider.js +138 -0
  33. package/dist/provider.js.map +1 -0
  34. package/dist/server.d.ts +79 -0
  35. package/dist/server.d.ts.map +1 -0
  36. package/dist/server.js +156 -0
  37. package/dist/server.js.map +1 -0
  38. package/dist/types.d.ts +71 -0
  39. package/dist/types.d.ts.map +1 -0
  40. package/dist/types.js +2 -0
  41. package/dist/types.js.map +1 -0
  42. package/package.json +22 -9
  43. package/src/components.tsx +0 -76
  44. package/src/hooks/useLocaleRouter.ts +0 -147
  45. package/src/index.ts +0 -46
  46. package/src/middleware/index.ts +0 -76
  47. package/src/provider.tsx +0 -183
  48. package/src/server.ts +0 -108
  49. package/src/types.ts +0 -83
package/README.md CHANGED
@@ -93,11 +93,18 @@ Main provider that combines Better i18n CDN with use-intl.
93
93
  timeZone="Europe/Berlin" // Optional: Timezone for formatting
94
94
  now={new Date()} // Optional: Current time (SSR)
95
95
  onLocaleChange={(l) => {}} // Optional: Locale change callback
96
+ // Fallback & Resilience
97
+ storage={storageAdapter} // Optional: Persistent cache (localStorage, etc.)
98
+ staticData={bundledData} // Optional: Bundled translations as last-resort
99
+ fetchTimeout={10000} // Optional: CDN timeout in ms (default: 10s)
100
+ retryCount={1} // Optional: CDN retry attempts (default: 1)
96
101
  >
97
102
  {children}
98
103
  </BetterI18nProvider>
99
104
  ```
100
105
 
106
+ See [`@better-i18n/core`](https://www.npmjs.com/package/@better-i18n/core) for storage adapters and fallback chain details.
107
+
101
108
  ### Hooks
102
109
 
103
110
  #### `useTranslations(namespace?)`
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=provider-config.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-config.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/provider-config.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,79 @@
1
+ import { describe, it, expect } from "vitest";
2
+ // ─── Test fixtures ──────────────────────────────────────────────────
3
+ const mockStorage = {
4
+ get: async () => null,
5
+ set: async () => { },
6
+ };
7
+ const mockStaticData = {
8
+ en: { common: { hello: "Hello" } },
9
+ tr: { common: { hello: "Merhaba" } },
10
+ };
11
+ // ─── Tests ──────────────────────────────────────────────────────────
12
+ describe("BetterI18nProviderConfig — fallback fields", () => {
13
+ it("accepts storage field", () => {
14
+ const config = {
15
+ project: "acme/dashboard",
16
+ locale: "en",
17
+ storage: mockStorage,
18
+ };
19
+ expect(config.storage).toBe(mockStorage);
20
+ });
21
+ it("accepts staticData field", () => {
22
+ const config = {
23
+ project: "acme/dashboard",
24
+ locale: "en",
25
+ staticData: mockStaticData,
26
+ };
27
+ expect(config.staticData).toBe(mockStaticData);
28
+ });
29
+ it("accepts fetchTimeout field", () => {
30
+ const config = {
31
+ project: "acme/dashboard",
32
+ locale: "en",
33
+ fetchTimeout: 5000,
34
+ };
35
+ expect(config.fetchTimeout).toBe(5000);
36
+ });
37
+ it("accepts retryCount field", () => {
38
+ const config = {
39
+ project: "acme/dashboard",
40
+ locale: "en",
41
+ retryCount: 3,
42
+ };
43
+ expect(config.retryCount).toBe(3);
44
+ });
45
+ it("accepts all fallback fields together", () => {
46
+ const config = {
47
+ project: "acme/dashboard",
48
+ locale: "en",
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("works without any fallback fields (backward compat)", () => {
60
+ const config = {
61
+ project: "acme/dashboard",
62
+ locale: "en",
63
+ };
64
+ expect(config.storage).toBeUndefined();
65
+ expect(config.staticData).toBeUndefined();
66
+ expect(config.fetchTimeout).toBeUndefined();
67
+ expect(config.retryCount).toBeUndefined();
68
+ });
69
+ it("supports lazy staticData function", () => {
70
+ const lazyStaticData = async () => mockStaticData;
71
+ const config = {
72
+ project: "acme/dashboard",
73
+ locale: "en",
74
+ staticData: lazyStaticData,
75
+ };
76
+ expect(config.staticData).toBe(lazyStaticData);
77
+ });
78
+ });
79
+ //# sourceMappingURL=provider-config.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider-config.test.js","sourceRoot":"","sources":["../../src/__tests__/provider-config.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,44 @@
1
+ import type { ComponentProps, ReactNode } from "react";
2
+ export interface LanguageSwitcherProps extends Omit<ComponentProps<"select">, "value" | "onChange" | "children"> {
3
+ /**
4
+ * Render function for custom option display
5
+ */
6
+ renderOption?: (language: {
7
+ code: string;
8
+ name?: string;
9
+ nativeName?: string;
10
+ flagUrl?: string | null;
11
+ isDefault?: boolean;
12
+ }) => ReactNode;
13
+ /**
14
+ * Label for loading state
15
+ */
16
+ loadingLabel?: string;
17
+ }
18
+ /**
19
+ * Pre-built language switcher component with router integration
20
+ *
21
+ * Uses useLocaleRouter() internally for proper SPA navigation.
22
+ * Locale changes trigger router navigation, which re-executes loaders.
23
+ *
24
+ * @example
25
+ * ```tsx
26
+ * // Basic usage - just works!
27
+ * <LanguageSwitcher />
28
+ *
29
+ * // With custom styling
30
+ * <LanguageSwitcher className="my-select" />
31
+ *
32
+ * // Custom option rendering
33
+ * <LanguageSwitcher
34
+ * renderOption={(lang) => (
35
+ * <>
36
+ * {lang.flagUrl && <img src={lang.flagUrl} alt="" />}
37
+ * {lang.nativeName}
38
+ * </>
39
+ * )}
40
+ * />
41
+ * ```
42
+ */
43
+ export declare function LanguageSwitcher({ renderOption, loadingLabel, ...props }: LanguageSwitcherProps): import("react/jsx-runtime").JSX.Element;
44
+ //# sourceMappingURL=components.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.d.ts","sourceRoot":"","sources":["../src/components.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIvD,MAAM,WAAW,qBACf,SAAQ,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,EAAE,OAAO,GAAG,UAAU,GAAG,UAAU,CAAC;IACzE;;OAEG;IACH,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE;QACxB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,UAAU,CAAC,EAAE,MAAM,CAAC;QACpB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,SAAS,CAAC,EAAE,OAAO,CAAC;KACrB,KAAK,SAAS,CAAC;IAEhB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,YAAY,EACZ,YAA2B,EAC3B,GAAG,KAAK,EACT,EAAE,qBAAqB,2CAqBvB"}
@@ -0,0 +1,38 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useLocaleRouter } from "./hooks/useLocaleRouter.js";
4
+ import { useLanguages } from "./hooks.js";
5
+ /**
6
+ * Pre-built language switcher component with router integration
7
+ *
8
+ * Uses useLocaleRouter() internally for proper SPA navigation.
9
+ * Locale changes trigger router navigation, which re-executes loaders.
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * // Basic usage - just works!
14
+ * <LanguageSwitcher />
15
+ *
16
+ * // With custom styling
17
+ * <LanguageSwitcher className="my-select" />
18
+ *
19
+ * // Custom option rendering
20
+ * <LanguageSwitcher
21
+ * renderOption={(lang) => (
22
+ * <>
23
+ * {lang.flagUrl && <img src={lang.flagUrl} alt="" />}
24
+ * {lang.nativeName}
25
+ * </>
26
+ * )}
27
+ * />
28
+ * ```
29
+ */
30
+ export function LanguageSwitcher({ renderOption, loadingLabel = "Loading...", ...props }) {
31
+ const { locale, navigate, isReady } = useLocaleRouter();
32
+ const { languages } = useLanguages();
33
+ if (!isReady) {
34
+ return (_jsx("select", { disabled: true, ...props, children: _jsx("option", { children: loadingLabel }) }));
35
+ }
36
+ return (_jsx("select", { value: locale, onChange: (e) => navigate(e.target.value), ...props, children: languages.map((lang) => (_jsx("option", { value: lang.code, children: renderOption ? renderOption(lang) : lang.nativeName || lang.code }, lang.code))) }));
37
+ }
38
+ //# sourceMappingURL=components.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"components.js","sourceRoot":"","sources":["../src/components.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;;AAGb,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAqB1C;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC/B,YAAY,EACZ,YAAY,GAAG,YAAY,EAC3B,GAAG,KAAK,EACc;IACtB,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,eAAe,EAAE,CAAC;IACxD,MAAM,EAAE,SAAS,EAAE,GAAG,YAAY,EAAE,CAAC;IAErC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CACL,iBAAQ,QAAQ,WAAK,KAAK,YACxB,2BAAS,YAAY,GAAU,GACxB,CACV,CAAC;IACJ,CAAC;IAED,OAAO,CACL,iBAAQ,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAM,KAAK,YACxE,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CACvB,iBAAwB,KAAK,EAAE,IAAI,CAAC,IAAI,YACrC,YAAY,CAAC,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,IAAI,IADtD,IAAI,CAAC,IAAI,CAEb,CACV,CAAC,GACK,CACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { BetterI18nContextValue } from "./types.js";
2
+ /**
3
+ * Context for Better i18n specific state
4
+ */
5
+ export declare const BetterI18nContext: import("react").Context<BetterI18nContextValue | null>;
6
+ /**
7
+ * Hook to access Better i18n context
8
+ *
9
+ * Note: For locale switching, use useLocaleRouter() which integrates with TanStack Router.
10
+ *
11
+ * @example
12
+ * ```tsx
13
+ * function LanguageInfo() {
14
+ * const { locale, languages, isLoadingLanguages } = useBetterI18n()
15
+ *
16
+ * if (isLoadingLanguages) return <div>Loading...</div>
17
+ *
18
+ * return (
19
+ * <div>
20
+ * Current: {locale}
21
+ * Available: {languages.map(l => l.code).join(', ')}
22
+ * </div>
23
+ * )
24
+ * }
25
+ * ```
26
+ *
27
+ * @example
28
+ * ```tsx
29
+ * // For language switching with proper router navigation:
30
+ * import { useLocaleRouter } from '@better-i18n/use-intl'
31
+ *
32
+ * function LanguageSwitcher() {
33
+ * const { locale, locales, navigate } = useLocaleRouter()
34
+ * return (
35
+ * <select value={locale} onChange={(e) => navigate(e.target.value)}>
36
+ * {locales.map((loc) => <option key={loc} value={loc}>{loc}</option>)}
37
+ * </select>
38
+ * )
39
+ * }
40
+ * ```
41
+ */
42
+ export declare function useBetterI18n(): BetterI18nContextValue;
43
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAEzD;;GAEG;AACH,eAAO,MAAM,iBAAiB,wDAE7B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,aAAa,IAAI,sBAAsB,CAUtD"}
@@ -1,15 +1,9 @@
1
1
  "use client";
2
-
3
2
  import { createContext, useContext } from "react";
4
- import type { BetterI18nContextValue } from "./types";
5
-
6
3
  /**
7
4
  * Context for Better i18n specific state
8
5
  */
9
- export const BetterI18nContext = createContext<BetterI18nContextValue | null>(
10
- null
11
- );
12
-
6
+ export const BetterI18nContext = createContext(null);
13
7
  /**
14
8
  * Hook to access Better i18n context
15
9
  *
@@ -46,14 +40,11 @@ export const BetterI18nContext = createContext<BetterI18nContextValue | null>(
46
40
  * }
47
41
  * ```
48
42
  */
49
- export function useBetterI18n(): BetterI18nContextValue {
50
- const context = useContext(BetterI18nContext);
51
-
52
- if (!context) {
53
- throw new Error(
54
- "[better-i18n] useBetterI18n must be used within a BetterI18nProvider"
55
- );
56
- }
57
-
58
- return context;
43
+ export function useBetterI18n() {
44
+ const context = useContext(BetterI18nContext);
45
+ if (!context) {
46
+ throw new Error("[better-i18n] useBetterI18n must be used within a BetterI18nProvider");
47
+ }
48
+ return context;
59
49
  }
50
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAGlD;;GAEG;AACH,MAAM,CAAC,MAAM,iBAAiB,GAAG,aAAa,CAC5C,IAAI,CACL,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,OAAO,GAAG,UAAU,CAAC,iBAAiB,CAAC,CAAC;IAE9C,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,MAAM,IAAI,KAAK,CACb,sEAAsE,CACvE,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Return type for useLocaleRouter hook
3
+ */
4
+ export interface UseLocaleRouterReturn {
5
+ /**
6
+ * Current locale (extracted from URL or default)
7
+ */
8
+ locale: string;
9
+ /**
10
+ * Available locales from CDN manifest
11
+ */
12
+ locales: string[];
13
+ /**
14
+ * Default locale (no URL prefix)
15
+ */
16
+ defaultLocale: string;
17
+ /**
18
+ * Navigate to the same page with a new locale
19
+ * Uses TanStack Router's navigate() for proper SPA navigation
20
+ */
21
+ navigate: (locale: string) => void;
22
+ /**
23
+ * Get a localized path for link building
24
+ * @param path - The path to localize
25
+ * @param locale - Target locale (optional, uses current if not specified)
26
+ */
27
+ localePath: (path: string, locale?: string) => string;
28
+ /**
29
+ * Whether languages have been loaded from CDN
30
+ */
31
+ isReady: boolean;
32
+ }
33
+ /**
34
+ * Hook for router-integrated locale navigation
35
+ *
36
+ * This hook provides a navigation-first approach to locale switching:
37
+ * - Locale changes trigger proper router navigation
38
+ * - Loaders re-execute with the new locale
39
+ * - No state synchronization issues
40
+ * - Works with TanStack Router's file-based routing
41
+ *
42
+ * @example
43
+ * ```tsx
44
+ * function LanguageSwitcher() {
45
+ * const { locale, locales, navigate, isReady } = useLocaleRouter();
46
+ *
47
+ * if (!isReady) return <Skeleton />;
48
+ *
49
+ * return (
50
+ * <select value={locale} onChange={(e) => navigate(e.target.value)}>
51
+ * {locales.map((loc) => (
52
+ * <option key={loc} value={loc}>{loc}</option>
53
+ * ))}
54
+ * </select>
55
+ * );
56
+ * }
57
+ * ```
58
+ *
59
+ * @example
60
+ * ```tsx
61
+ * // Building localized links
62
+ * function Navigation() {
63
+ * const { localePath } = useLocaleRouter();
64
+ *
65
+ * return (
66
+ * <nav>
67
+ * <Link to={localePath('/about')}>About</Link>
68
+ * <Link to={localePath('/contact', 'tr')}>İletişim (TR)</Link>
69
+ * </nav>
70
+ * );
71
+ * }
72
+ * ```
73
+ */
74
+ export declare function useLocaleRouter(): UseLocaleRouterReturn;
75
+ //# sourceMappingURL=useLocaleRouter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLocaleRouter.d.ts","sourceRoot":"","sources":["../../src/hooks/useLocaleRouter.ts"],"names":[],"mappings":"AAYA;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,OAAO,EAAE,MAAM,EAAE,CAAC;IAElB;;OAEG;IACH,aAAa,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAEnC;;;;OAIG;IACH,UAAU,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAEtD;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;CAClB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,wBAAgB,eAAe,IAAI,qBAAqB,CAuDvD"}
@@ -0,0 +1,89 @@
1
+ "use client";
2
+ import { useCallback, useMemo } from "react";
3
+ import { useRouter, useLocation } from "@tanstack/react-router";
4
+ import { getLocaleFromPath, replaceLocaleInPath, addLocalePrefix, } from "@better-i18n/core";
5
+ import { useBetterI18n } from "../context.js";
6
+ /**
7
+ * Hook for router-integrated locale navigation
8
+ *
9
+ * This hook provides a navigation-first approach to locale switching:
10
+ * - Locale changes trigger proper router navigation
11
+ * - Loaders re-execute with the new locale
12
+ * - No state synchronization issues
13
+ * - Works with TanStack Router's file-based routing
14
+ *
15
+ * @example
16
+ * ```tsx
17
+ * function LanguageSwitcher() {
18
+ * const { locale, locales, navigate, isReady } = useLocaleRouter();
19
+ *
20
+ * if (!isReady) return <Skeleton />;
21
+ *
22
+ * return (
23
+ * <select value={locale} onChange={(e) => navigate(e.target.value)}>
24
+ * {locales.map((loc) => (
25
+ * <option key={loc} value={loc}>{loc}</option>
26
+ * ))}
27
+ * </select>
28
+ * );
29
+ * }
30
+ * ```
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * // Building localized links
35
+ * function Navigation() {
36
+ * const { localePath } = useLocaleRouter();
37
+ *
38
+ * return (
39
+ * <nav>
40
+ * <Link to={localePath('/about')}>About</Link>
41
+ * <Link to={localePath('/contact', 'tr')}>İletişim (TR)</Link>
42
+ * </nav>
43
+ * );
44
+ * }
45
+ * ```
46
+ */
47
+ export function useLocaleRouter() {
48
+ const router = useRouter();
49
+ const location = useLocation();
50
+ const { languages, isLoadingLanguages } = useBetterI18n();
51
+ // Build config from CDN manifest
52
+ const config = useMemo(() => ({
53
+ locales: languages.map((lang) => lang.code),
54
+ defaultLocale: languages.find((l) => l.isDefault)?.code || "en",
55
+ }), [languages]);
56
+ // Get effective locale from URL (handles default without prefix)
57
+ const locale = useMemo(() => {
58
+ // If no languages loaded yet, extract from path manually
59
+ if (languages.length === 0) {
60
+ const segments = location.pathname.split("/").filter(Boolean);
61
+ const firstSegment = segments[0];
62
+ // Check if it looks like a locale code (2 letters)
63
+ if (firstSegment && /^[a-z]{2}$/i.test(firstSegment)) {
64
+ return firstSegment;
65
+ }
66
+ return "en"; // fallback
67
+ }
68
+ return getLocaleFromPath(location.pathname, config);
69
+ }, [location.pathname, config, languages]);
70
+ // Navigate to same page with new locale (SPA navigation!)
71
+ const navigate = useCallback((newLocale) => {
72
+ const newPath = replaceLocaleInPath(location.pathname, newLocale, config);
73
+ router.navigate({ to: newPath });
74
+ }, [location.pathname, config, router]);
75
+ // Get localized path for links
76
+ const localePath = useCallback((path, targetLocale) => {
77
+ const loc = targetLocale || locale;
78
+ return addLocalePrefix(path, loc, config);
79
+ }, [locale, config]);
80
+ return {
81
+ locale,
82
+ locales: config.locales,
83
+ defaultLocale: config.defaultLocale,
84
+ navigate,
85
+ localePath,
86
+ isReady: !isLoadingLanguages && config.locales.length > 0,
87
+ };
88
+ }
89
+ //# sourceMappingURL=useLocaleRouter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useLocaleRouter.js","sourceRoot":"","sources":["../../src/hooks/useLocaleRouter.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,WAAW,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAC7C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EACL,iBAAiB,EACjB,mBAAmB,EACnB,eAAe,GAEhB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAwC9C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AACH,MAAM,UAAU,eAAe;IAC7B,MAAM,MAAM,GAAG,SAAS,EAAE,CAAC;IAC3B,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,EAAE,SAAS,EAAE,kBAAkB,EAAE,GAAG,aAAa,EAAE,CAAC;IAE1D,iCAAiC;IACjC,MAAM,MAAM,GAAiB,OAAO,CAClC,GAAG,EAAE,CAAC,CAAC;QACL,OAAO,EAAE,SAAS,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC;QAC3C,aAAa,EAAE,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,IAAI,IAAI,IAAI;KAChE,CAAC,EACF,CAAC,SAAS,CAAC,CACZ,CAAC;IAEF,iEAAiE;IACjE,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,EAAE;QAC1B,yDAAyD;QACzD,IAAI,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YAC3B,MAAM,QAAQ,GAAG,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YAC9D,MAAM,YAAY,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;YACjC,mDAAmD;YACnD,IAAI,YAAY,IAAI,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,EAAE,CAAC;gBACrD,OAAO,YAAY,CAAC;YACtB,CAAC;YACD,OAAO,IAAI,CAAC,CAAC,WAAW;QAC1B,CAAC;QACD,OAAO,iBAAiB,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IACtD,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC,CAAC;IAE3C,0DAA0D;IAC1D,MAAM,QAAQ,GAAG,WAAW,CAC1B,CAAC,SAAiB,EAAE,EAAE;QACpB,MAAM,OAAO,GAAG,mBAAmB,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC;QAC1E,MAAM,CAAC,QAAQ,CAAC,EAAE,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;IACnC,CAAC,EACD,CAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,CACpC,CAAC;IAEF,+BAA+B;IAC/B,MAAM,UAAU,GAAG,WAAW,CAC5B,CAAC,IAAY,EAAE,YAAqB,EAAE,EAAE;QACtC,MAAM,GAAG,GAAG,YAAY,IAAI,MAAM,CAAC;QACnC,OAAO,eAAe,CAAC,IAAI,EAAE,GAAG,EAAE,MAAM,CAAC,CAAC;IAC5C,CAAC,EACD,CAAC,MAAM,EAAE,MAAM,CAAC,CACjB,CAAC;IAEF,OAAO;QACL,MAAM;QACN,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,aAAa,EAAE,MAAM,CAAC,aAAa;QACnC,QAAQ;QACR,UAAU;QACV,OAAO,EAAE,CAAC,kBAAkB,IAAI,MAAM,CAAC,OAAO,CAAC,MAAM,GAAG,CAAC;KAC1D,CAAC;AACJ,CAAC"}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Hook to get available languages from CDN manifest
3
+ *
4
+ * @example
5
+ * ```tsx
6
+ * function LanguageList() {
7
+ * const { languages, isLoading } = useLanguages()
8
+ *
9
+ * if (isLoading) return <div>Loading...</div>
10
+ *
11
+ * return (
12
+ * <ul>
13
+ * {languages.map((lang) => (
14
+ * <li key={lang.code}>
15
+ * {lang.nativeName} ({lang.code})
16
+ * </li>
17
+ * ))}
18
+ * </ul>
19
+ * )
20
+ * }
21
+ * ```
22
+ */
23
+ export declare function useLanguages(): {
24
+ languages: import("@better-i18n/core").LanguageOption[];
25
+ isLoading: boolean;
26
+ };
27
+ /**
28
+ * Hook to get current locale (read-only)
29
+ *
30
+ * For locale switching with proper router navigation, use useLocaleRouter() instead.
31
+ *
32
+ * @example
33
+ * ```tsx
34
+ * function LocaleDisplay() {
35
+ * const { locale, isLoading } = useLocale()
36
+ *
37
+ * if (isLoading) return <div>Loading...</div>
38
+ *
39
+ * return <span>Current locale: {locale}</span>
40
+ * }
41
+ * ```
42
+ *
43
+ * @example
44
+ * ```tsx
45
+ * // For locale switching, use useLocaleRouter:
46
+ * import { useLocaleRouter } from '@better-i18n/use-intl'
47
+ *
48
+ * function LocaleSwitcher() {
49
+ * const { locale, navigate } = useLocaleRouter()
50
+ * return (
51
+ * <button onClick={() => navigate(locale === 'en' ? 'tr' : 'en')}>
52
+ * Toggle: {locale}
53
+ * </button>
54
+ * )
55
+ * }
56
+ * ```
57
+ */
58
+ export declare function useLocale(): {
59
+ locale: string;
60
+ isLoading: boolean;
61
+ };
62
+ export { useTranslations, useFormatter, useMessages, useNow, useTimeZone, useLocale as useIntlLocale, } from "use-intl";
63
+ //# sourceMappingURL=hooks.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY;;;EAO3B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,SAAS;;;EAOxB;AAGD,OAAO,EACL,eAAe,EACf,YAAY,EACZ,WAAW,EACX,MAAM,EACN,WAAW,EACX,SAAS,IAAI,aAAa,GAC3B,MAAM,UAAU,CAAC"}
@@ -1,7 +1,5 @@
1
1
  "use client";
2
-
3
- import { useBetterI18n } from "./context";
4
-
2
+ import { useBetterI18n } from "./context.js";
5
3
  /**
6
4
  * Hook to get available languages from CDN manifest
7
5
  *
@@ -25,14 +23,12 @@ import { useBetterI18n } from "./context";
25
23
  * ```
26
24
  */
27
25
  export function useLanguages() {
28
- const { languages, isLoadingLanguages } = useBetterI18n();
29
-
30
- return {
31
- languages,
32
- isLoading: isLoadingLanguages,
33
- };
26
+ const { languages, isLoadingLanguages } = useBetterI18n();
27
+ return {
28
+ languages,
29
+ isLoading: isLoadingLanguages,
30
+ };
34
31
  }
35
-
36
32
  /**
37
33
  * Hook to get current locale (read-only)
38
34
  *
@@ -65,20 +61,12 @@ export function useLanguages() {
65
61
  * ```
66
62
  */
67
63
  export function useLocale() {
68
- const { locale, isLoadingMessages } = useBetterI18n();
69
-
70
- return {
71
- locale,
72
- isLoading: isLoadingMessages,
73
- };
64
+ const { locale, isLoadingMessages } = useBetterI18n();
65
+ return {
66
+ locale,
67
+ isLoading: isLoadingMessages,
68
+ };
74
69
  }
75
-
76
70
  // Re-export use-intl hooks for convenience
77
- export {
78
- useTranslations,
79
- useFormatter,
80
- useMessages,
81
- useNow,
82
- useTimeZone,
83
- useLocale as useIntlLocale,
84
- } from "use-intl";
71
+ export { useTranslations, useFormatter, useMessages, useNow, useTimeZone, useLocale as useIntlLocale, } from "use-intl";
72
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAAA,YAAY,CAAC;AAEb,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAE7C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,YAAY;IAC1B,MAAM,EAAE,SAAS,EAAE,kBAAkB,EAAE,GAAG,aAAa,EAAE,CAAC;IAE1D,OAAO;QACL,SAAS;QACT,SAAS,EAAE,kBAAkB;KAC9B,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,EAAE,MAAM,EAAE,iBAAiB,EAAE,GAAG,aAAa,EAAE,CAAC;IAEtD,OAAO;QACL,MAAM;QACN,SAAS,EAAE,iBAAiB;KAC7B,CAAC;AACJ,CAAC;AAED,2CAA2C;AAC3C,OAAO,EACL,eAAe,EACf,YAAY,EACZ,WAAW,EACX,MAAM,EACN,WAAW,EACX,SAAS,IAAI,aAAa,GAC3B,MAAM,UAAU,CAAC"}
@@ -0,0 +1,12 @@
1
+ export { BetterI18nProvider } from "./provider.js";
2
+ export type { BetterI18nProviderProps } from "./provider.js";
3
+ export { useBetterI18n } from "./context.js";
4
+ export { useLanguages, useLocale, useTranslations, useFormatter, useMessages, useNow, useTimeZone, } from "./hooks.js";
5
+ export { useLocaleRouter } from "./hooks/useLocaleRouter.js";
6
+ export type { UseLocaleRouterReturn } from "./hooks/useLocaleRouter.js";
7
+ export { LanguageSwitcher } from "./components.js";
8
+ export type { LanguageSwitcherProps } from "./components.js";
9
+ export type { Messages, BetterI18nProviderConfig, BetterI18nContextValue, } from "./types.js";
10
+ export { extractLocale, getLocaleFromPath, hasLocalePrefix, removeLocalePrefix, addLocalePrefix, replaceLocaleInPath, createLocalePath, type LocaleConfig, } from "@better-i18n/core";
11
+ export { IntlProvider } from "use-intl";
12
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACnD,YAAY,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAG7D,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EACL,YAAY,EACZ,SAAS,EAET,eAAe,EACf,YAAY,EACZ,WAAW,EACX,MAAM,EACN,WAAW,GACZ,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAC7D,YAAY,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AAGxE,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AACnD,YAAY,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAG7D,YAAY,EACV,QAAQ,EACR,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,gBAAgB,EAChB,KAAK,YAAY,GAClB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,16 @@
1
+ // Provider
2
+ export { BetterI18nProvider } from "./provider.js";
3
+ // Context & Hooks
4
+ export { useBetterI18n } from "./context.js";
5
+ export { useLanguages, useLocale,
6
+ // Re-exported from use-intl
7
+ useTranslations, useFormatter, useMessages, useNow, useTimeZone, } from "./hooks.js";
8
+ // Router Integration (TanStack Router)
9
+ export { useLocaleRouter } from "./hooks/useLocaleRouter.js";
10
+ // Components
11
+ export { LanguageSwitcher } from "./components.js";
12
+ // Re-export locale utilities from core (convenience)
13
+ export { extractLocale, getLocaleFromPath, hasLocalePrefix, removeLocalePrefix, addLocalePrefix, replaceLocaleInPath, createLocalePath, } from "@better-i18n/core";
14
+ // Re-export commonly used use-intl components
15
+ export { IntlProvider } from "use-intl";
16
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,WAAW;AACX,OAAO,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAGnD,kBAAkB;AAClB,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EACL,YAAY,EACZ,SAAS;AACT,4BAA4B;AAC5B,eAAe,EACf,YAAY,EACZ,WAAW,EACX,MAAM,EACN,WAAW,GACZ,MAAM,YAAY,CAAC;AAEpB,uCAAuC;AACvC,OAAO,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAG7D,aAAa;AACb,OAAO,EAAE,gBAAgB,EAAE,MAAM,iBAAiB,CAAC;AAUnD,qDAAqD;AACrD,OAAO,EACL,aAAa,EACb,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,eAAe,EACf,mBAAmB,EACnB,gBAAgB,GAEjB,MAAM,mBAAmB,CAAC;AAE3B,8CAA8C;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { I18nMiddlewareConfig } from "@better-i18n/core";
2
+ export declare function createBetterI18nMiddleware(config: I18nMiddlewareConfig): any;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAE9D,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,oBAAoB,OAsGtE"}