@better-i18n/use-intl 0.1.8 → 0.2.0

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 (53) 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/locale-dropdown.d.ts +62 -0
  7. package/dist/components/locale-dropdown.d.ts.map +1 -0
  8. package/dist/components/locale-dropdown.js +298 -0
  9. package/dist/components/locale-dropdown.js.map +1 -0
  10. package/dist/components.d.ts +44 -0
  11. package/dist/components.d.ts.map +1 -0
  12. package/dist/components.js +38 -0
  13. package/dist/components.js.map +1 -0
  14. package/dist/context.d.ts +43 -0
  15. package/dist/context.d.ts.map +1 -0
  16. package/{src/context.tsx → dist/context.js} +8 -17
  17. package/dist/context.js.map +1 -0
  18. package/dist/hooks/useLocaleRouter.d.ts +75 -0
  19. package/dist/hooks/useLocaleRouter.d.ts.map +1 -0
  20. package/dist/hooks/useLocaleRouter.js +89 -0
  21. package/dist/hooks/useLocaleRouter.js.map +1 -0
  22. package/dist/hooks.d.ts +63 -0
  23. package/dist/hooks.d.ts.map +1 -0
  24. package/{src/hooks.ts → dist/hooks.js} +13 -25
  25. package/dist/hooks.js.map +1 -0
  26. package/dist/index.d.ts +14 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +17 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/middleware/index.d.ts +3 -0
  31. package/dist/middleware/index.d.ts.map +1 -0
  32. package/dist/middleware/index.js +68 -0
  33. package/dist/middleware/index.js.map +1 -0
  34. package/dist/provider.d.ts +51 -0
  35. package/dist/provider.d.ts.map +1 -0
  36. package/dist/provider.js +138 -0
  37. package/dist/provider.js.map +1 -0
  38. package/dist/server.d.ts +79 -0
  39. package/dist/server.d.ts.map +1 -0
  40. package/dist/server.js +156 -0
  41. package/dist/server.js.map +1 -0
  42. package/dist/types.d.ts +71 -0
  43. package/dist/types.d.ts.map +1 -0
  44. package/dist/types.js +2 -0
  45. package/dist/types.js.map +1 -0
  46. package/package.json +22 -9
  47. package/src/components.tsx +0 -76
  48. package/src/hooks/useLocaleRouter.ts +0 -147
  49. package/src/index.ts +0 -46
  50. package/src/middleware/index.ts +0 -114
  51. package/src/provider.tsx +0 -183
  52. package/src/server.ts +0 -108
  53. package/src/types.ts +0 -83
@@ -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,14 @@
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 { LocaleDropdown } from "./components/locale-dropdown.js";
10
+ export type { LocaleDropdownProps, LocaleDropdownRenderContext, } from "./components/locale-dropdown.js";
11
+ export type { Messages, BetterI18nProviderConfig, BetterI18nContextValue, } from "./types.js";
12
+ export { extractLocale, getLocaleFromPath, hasLocalePrefix, removeLocalePrefix, addLocalePrefix, replaceLocaleInPath, createLocalePath, type LocaleConfig, } from "@better-i18n/core";
13
+ export { IntlProvider } from "use-intl";
14
+ //# 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;AAE7D,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,YAAY,EACV,mBAAmB,EACnB,2BAA2B,GAC5B,MAAM,iCAAiC,CAAC;AAGzC,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,17 @@
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
+ export { LocaleDropdown } from "./components/locale-dropdown.js";
13
+ // Re-export locale utilities from core (convenience)
14
+ export { extractLocale, getLocaleFromPath, hasLocalePrefix, removeLocalePrefix, addLocalePrefix, replaceLocaleInPath, createLocalePath, } from "@better-i18n/core";
15
+ // Re-export commonly used use-intl components
16
+ export { IntlProvider } from "use-intl";
17
+ //# 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;AAGnD,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAajE,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"}
@@ -0,0 +1,68 @@
1
+ // @ts-expect-error - internal workspace dependency
2
+ import { createMiddleware } from "@tanstack/react-router";
3
+ // @ts-expect-error - internal workspace dependency
4
+ import { detectLocale, getLocales } from "@better-i18n/core";
5
+ export function createBetterI18nMiddleware(config) {
6
+ const { project, defaultLocale, localePrefix = "as-needed", detection = {} } = config;
7
+ const { cookie = true, browserLanguage = true, cookieName = "locale", cookieMaxAge = 31536000, } = detection;
8
+ return createMiddleware().server(async ({ next, request, }) => {
9
+ // 1. Fetch available locales from CDN (cached)
10
+ const availableLocales = await getLocales({ project });
11
+ // 2. Extract locale indicators
12
+ const url = new URL(request.url);
13
+ const pathSegment = url.pathname.split("/")[1];
14
+ const hasLocaleInPath = !!pathSegment && availableLocales.includes(pathSegment);
15
+ // Dynamic imports for TanStack Start server functions to avoid bundling them in client
16
+ // @ts-ignore - optional runtime dependency, types may not be available
17
+ const startServer = await import("@tanstack/react-start/server");
18
+ const getCookie = startServer.getCookie;
19
+ const setCookie = startServer.setCookie;
20
+ const getRequestHeader = startServer.getRequestHeader;
21
+ const cookieLocale = cookie ? getCookie(cookieName) : null;
22
+ const headerLocale = browserLanguage
23
+ ? getRequestHeader("accept-language")?.split(",")[0]?.split("-")[0]
24
+ : null;
25
+ // 3. Detect locale using core logic
26
+ const result = detectLocale({
27
+ project,
28
+ defaultLocale,
29
+ pathLocale: pathSegment,
30
+ cookieLocale,
31
+ headerLocale,
32
+ availableLocales,
33
+ });
34
+ // 4. Redirect if locale prefix is needed but missing
35
+ // Skip API routes and paths that already have a locale prefix
36
+ const isApiRoute = url.pathname.startsWith("/api/");
37
+ if (localePrefix !== "never" &&
38
+ !hasLocaleInPath &&
39
+ !isApiRoute &&
40
+ result.detectedFrom !== "path") {
41
+ const shouldRedirect = localePrefix === "always" ||
42
+ (localePrefix === "as-needed" && result.locale !== defaultLocale);
43
+ if (shouldRedirect) {
44
+ const redirectUrl = new URL(`/${result.locale}${url.pathname}`, url.origin);
45
+ redirectUrl.search = url.search;
46
+ // Build redirect response with locale cookie
47
+ const headers = new Headers({
48
+ Location: redirectUrl.toString(),
49
+ });
50
+ if (cookie && result.shouldSetCookie) {
51
+ headers.set("Set-Cookie", `${cookieName}=${result.locale}; Path=/; Max-Age=${cookieMaxAge}; SameSite=Lax`);
52
+ }
53
+ return new Response(null, { status: 302, headers });
54
+ }
55
+ }
56
+ // 5. Set cookie if needed (non-redirect path)
57
+ if (cookie && result.shouldSetCookie) {
58
+ setCookie(cookieName, result.locale, {
59
+ path: "/",
60
+ maxAge: cookieMaxAge,
61
+ sameSite: "lax",
62
+ });
63
+ }
64
+ // 6. Pass locale to route context
65
+ return next({ context: { locale: result.locale } });
66
+ });
67
+ }
68
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/middleware/index.ts"],"names":[],"mappings":"AAAA,mDAAmD;AACnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,mDAAmD;AACnD,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAG7D,MAAM,UAAU,0BAA0B,CAAC,MAA4B;IACrE,MAAM,EAAE,OAAO,EAAE,aAAa,EAAE,YAAY,GAAG,WAAW,EAAE,SAAS,GAAG,EAAE,EAAE,GAAG,MAAM,CAAC;IAEtF,MAAM,EACJ,MAAM,GAAG,IAAI,EACb,eAAe,GAAG,IAAI,EACtB,UAAU,GAAG,QAAQ,EACrB,YAAY,GAAG,QAAQ,GACxB,GAAG,SAAS,CAAC;IAEd,OAAO,gBAAgB,EAAE,CAAC,MAAM,CAC9B,KAAK,EAAE,EACL,IAAI,EACJ,OAAO,GAIR,EAAE,EAAE;QACH,+CAA+C;QAC/C,MAAM,gBAAgB,GAAG,MAAM,UAAU,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;QAEvD,+BAA+B;QAC/B,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QACjC,MAAM,WAAW,GAAG,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC;QAC/C,MAAM,eAAe,GACnB,CAAC,CAAC,WAAW,IAAI,gBAAgB,CAAC,QAAQ,CAAC,WAAW,CAAC,CAAC;QAE1D,uFAAuF;QACvF,uEAAuE;QACvE,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,8BAA8B,CAAC,CAAC;QACjE,MAAM,SAAS,GAAG,WAAW,CAAC,SAA4C,CAAC;QAC3E,MAAM,SAAS,GAAG,WAAW,CAAC,SAIrB,CAAC;QACV,MAAM,gBAAgB,GAAG,WAAW,CAAC,gBAAwD,CAAC;QAE9F,MAAM,YAAY,GAAG,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QAC3D,MAAM,YAAY,GAAG,eAAe;YAClC,CAAC,CAAC,gBAAgB,CAAC,iBAAiB,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;YACnE,CAAC,CAAC,IAAI,CAAC;QAET,oCAAoC;QACpC,MAAM,MAAM,GAAG,YAAY,CAAC;YAC1B,OAAO;YACP,aAAa;YACb,UAAU,EAAE,WAAW;YACvB,YAAY;YACZ,YAAY;YACZ,gBAAgB;SACjB,CAAC,CAAC;QAEH,qDAAqD;QACrD,iEAAiE;QACjE,MAAM,UAAU,GAAG,GAAG,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACpD,IACE,YAAY,KAAK,OAAO;YACxB,CAAC,eAAe;YAChB,CAAC,UAAU;YACX,MAAM,CAAC,YAAY,KAAK,MAAM,EAC9B,CAAC;YACD,MAAM,cAAc,GAClB,YAAY,KAAK,QAAQ;gBACzB,CAAC,YAAY,KAAK,WAAW,IAAI,MAAM,CAAC,MAAM,KAAK,aAAa,CAAC,CAAC;YAEpE,IAAI,cAAc,EAAE,CAAC;gBACnB,MAAM,WAAW,GAAG,IAAI,GAAG,CACzB,IAAI,MAAM,CAAC,MAAM,GAAG,GAAG,CAAC,QAAQ,EAAE,EAClC,GAAG,CAAC,MAAM,CACX,CAAC;gBACF,WAAW,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;gBAEhC,6CAA6C;gBAC7C,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC;oBAC1B,QAAQ,EAAE,WAAW,CAAC,QAAQ,EAAE;iBACjC,CAAC,CAAC;gBAEH,IAAI,MAAM,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;oBACrC,OAAO,CAAC,GAAG,CACT,YAAY,EACZ,GAAG,UAAU,IAAI,MAAM,CAAC,MAAM,qBAAqB,YAAY,gBAAgB,CAChF,CAAC;gBACJ,CAAC;gBAED,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,CAAC,CAAC;YACtD,CAAC;QACH,CAAC;QAED,8CAA8C;QAC9C,IAAI,MAAM,IAAI,MAAM,CAAC,eAAe,EAAE,CAAC;YACrC,SAAS,CAAC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE;gBACnC,IAAI,EAAE,GAAG;gBACT,MAAM,EAAE,YAAY;gBACpB,QAAQ,EAAE,KAAK;aAChB,CAAC,CAAC;QACL,CAAC;QAED,kCAAkC;QAClC,OAAO,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC;IACtD,CAAC,CACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,51 @@
1
+ import type { LanguageOption } from "@better-i18n/core";
2
+ import { type ReactNode } from "react";
3
+ import type { BetterI18nProviderConfig } from "./types.js";
4
+ export interface BetterI18nProviderProps extends BetterI18nProviderConfig {
5
+ children: ReactNode;
6
+ /** Pre-loaded languages from SSR loader — skips loading state on first render */
7
+ initialLanguages?: LanguageOption[];
8
+ /** Custom fallback when a message key is missing */
9
+ getMessageFallback?: (info: {
10
+ error: Error;
11
+ key: string;
12
+ namespace?: string;
13
+ }) => string;
14
+ }
15
+ /**
16
+ * Provider component that combines Better i18n CDN with use-intl
17
+ *
18
+ * The locale is controlled externally (from URL/router). Use useLocaleRouter()
19
+ * for locale switching with proper router integration.
20
+ *
21
+ * @example
22
+ * ```tsx
23
+ * // Basic usage (CSR - fetches messages on client)
24
+ * function App() {
25
+ * return (
26
+ * <BetterI18nProvider
27
+ * project="acme/dashboard"
28
+ * locale="en"
29
+ * >
30
+ * <MyComponent />
31
+ * </BetterI18nProvider>
32
+ * )
33
+ * }
34
+ *
35
+ * // TanStack Router SSR usage (pre-loaded messages from loader)
36
+ * function RootComponent() {
37
+ * const { messages, locale } = Route.useLoaderData()
38
+ * return (
39
+ * <BetterI18nProvider
40
+ * project="acme/dashboard"
41
+ * locale={locale}
42
+ * messages={messages}
43
+ * >
44
+ * <Outlet />
45
+ * </BetterI18nProvider>
46
+ * )
47
+ * }
48
+ * ```
49
+ */
50
+ export declare function BetterI18nProvider({ children, project, locale: propLocale, messages: propMessages, timeZone, now, onError, cdnBaseUrl, debug, logLevel, fetch: customFetch, storage, staticData, fetchTimeout, retryCount, initialLanguages, getMessageFallback: customGetMessageFallback, }: BetterI18nProviderProps): import("react/jsx-runtime").JSX.Element;
51
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAgC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAGrE,OAAO,KAAK,EAAE,wBAAwB,EAAY,MAAM,YAAY,CAAC;AAErE,MAAM,WAAW,uBAAwB,SAAQ,wBAAwB;IACvE,QAAQ,EAAE,SAAS,CAAC;IACpB,iFAAiF;IACjF,gBAAgB,CAAC,EAAE,cAAc,EAAE,CAAC;IACpC,oDAAoD;IACpD,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE;QAC1B,KAAK,EAAE,KAAK,CAAC;QACb,GAAG,EAAE,MAAM,CAAC;QACZ,SAAS,CAAC,EAAE,MAAM,CAAC;KACpB,KAAK,MAAM,CAAC;CACd;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,OAAO,EACP,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,YAAY,EACtB,QAAQ,EACR,GAAG,EACH,OAAO,EACP,UAAU,EACV,KAAK,EACL,QAAQ,EACR,KAAK,EAAE,WAAW,EAClB,OAAO,EACP,UAAU,EACV,YAAY,EACZ,UAAU,EACV,gBAAgB,EAChB,kBAAkB,EAAE,wBAAwB,GAC7C,EAAE,uBAAuB,2CA4IzB"}