@better-i18n/use-intl 0.2.1 → 0.2.2

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/dist/context.d.ts CHANGED
@@ -6,38 +6,25 @@ export declare const BetterI18nContext: import("react").Context<BetterI18nContex
6
6
  /**
7
7
  * Hook to access Better i18n context
8
8
  *
9
- * Note: For locale switching, use useLocaleRouter() which integrates with TanStack Router.
9
+ * Provides locale state, language list, and `setLocale()` for switching.
10
+ * For higher-level APIs, use `useLocale()` or `useLocaleRouter()`.
10
11
  *
11
12
  * @example
12
13
  * ```tsx
13
14
  * function LanguageInfo() {
14
- * const { locale, languages, isLoadingLanguages } = useBetterI18n()
15
+ * const { locale, setLocale, languages, isLoadingLanguages } = useBetterI18n()
15
16
  *
16
17
  * if (isLoadingLanguages) return <div>Loading...</div>
17
18
  *
18
19
  * return (
19
20
  * <div>
20
- * Current: {locale}
21
- * Available: {languages.map(l => l.code).join(', ')}
21
+ * <p>Current: {locale}</p>
22
+ * <p>Available: {languages.map(l => l.code).join(', ')}</p>
23
+ * <button onClick={() => setLocale('tr')}>Switch to Turkish</button>
22
24
  * </div>
23
25
  * )
24
26
  * }
25
27
  * ```
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
28
  */
42
29
  export declare function useBetterI18n(): BetterI18nContextValue;
43
30
  //# sourceMappingURL=context.d.ts.map
@@ -1 +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
+ {"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;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAgB,aAAa,IAAI,sBAAsB,CAUtD"}
package/dist/context.js CHANGED
@@ -7,38 +7,25 @@ export const BetterI18nContext = createContext(null);
7
7
  /**
8
8
  * Hook to access Better i18n context
9
9
  *
10
- * Note: For locale switching, use useLocaleRouter() which integrates with TanStack Router.
10
+ * Provides locale state, language list, and `setLocale()` for switching.
11
+ * For higher-level APIs, use `useLocale()` or `useLocaleRouter()`.
11
12
  *
12
13
  * @example
13
14
  * ```tsx
14
15
  * function LanguageInfo() {
15
- * const { locale, languages, isLoadingLanguages } = useBetterI18n()
16
+ * const { locale, setLocale, languages, isLoadingLanguages } = useBetterI18n()
16
17
  *
17
18
  * if (isLoadingLanguages) return <div>Loading...</div>
18
19
  *
19
20
  * return (
20
21
  * <div>
21
- * Current: {locale}
22
- * Available: {languages.map(l => l.code).join(', ')}
22
+ * <p>Current: {locale}</p>
23
+ * <p>Available: {languages.map(l => l.code).join(', ')}</p>
24
+ * <button onClick={() => setLocale('tr')}>Switch to Turkish</button>
23
25
  * </div>
24
26
  * )
25
27
  * }
26
28
  * ```
27
- *
28
- * @example
29
- * ```tsx
30
- * // For language switching with proper router navigation:
31
- * import { useLocaleRouter } from '@better-i18n/use-intl'
32
- *
33
- * function LanguageSwitcher() {
34
- * const { locale, locales, navigate } = useLocaleRouter()
35
- * return (
36
- * <select value={locale} onChange={(e) => navigate(e.target.value)}>
37
- * {locales.map((loc) => <option key={loc} value={loc}>{loc}</option>)}
38
- * </select>
39
- * )
40
- * }
41
- * ```
42
29
  */
43
30
  export function useBetterI18n() {
44
31
  const context = useContext(BetterI18nContext);
@@ -1 +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"}
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;;;;;;;;;;;;;;;;;;;;;;GAsBG;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"}
@@ -15,8 +15,9 @@ export interface UseLocaleRouterReturn {
15
15
  */
16
16
  defaultLocale: string;
17
17
  /**
18
- * Navigate to the same page with a new locale
19
- * Uses TanStack Router's navigate() for proper SPA navigation
18
+ * Navigate to the same page with a new locale.
19
+ * Uses TanStack Router navigation when available,
20
+ * falls back to context-based `setLocale()` otherwise.
20
21
  */
21
22
  navigate: (locale: string) => void;
22
23
  /**
@@ -31,16 +32,16 @@ export interface UseLocaleRouterReturn {
31
32
  isReady: boolean;
32
33
  }
33
34
  /**
34
- * Hook for router-integrated locale navigation
35
+ * Hook for locale navigation with automatic router detection.
35
36
  *
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
37
+ * When TanStack Router context is available, locale changes trigger
38
+ * proper SPA navigation. When no router is present (e.g., plain Vite
39
+ * apps), falls back to the provider's `setLocale()` for state-based
40
+ * locale switching.
41
41
  *
42
42
  * @example
43
43
  * ```tsx
44
+ * // Works in both router and non-router environments
44
45
  * function LanguageSwitcher() {
45
46
  * const { locale, locales, navigate, isReady } = useLocaleRouter();
46
47
  *
@@ -1 +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"}
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;;;;OAIG;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,CA6FvD"}
@@ -4,16 +4,16 @@ import { useRouter, useLocation } from "@tanstack/react-router";
4
4
  import { getLocaleFromPath, replaceLocaleInPath, addLocalePrefix, } from "@better-i18n/core";
5
5
  import { useBetterI18n } from "../context.js";
6
6
  /**
7
- * Hook for router-integrated locale navigation
7
+ * Hook for locale navigation with automatic router detection.
8
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
9
+ * When TanStack Router context is available, locale changes trigger
10
+ * proper SPA navigation. When no router is present (e.g., plain Vite
11
+ * apps), falls back to the provider's `setLocale()` for state-based
12
+ * locale switching.
14
13
  *
15
14
  * @example
16
15
  * ```tsx
16
+ * // Works in both router and non-router environments
17
17
  * function LanguageSwitcher() {
18
18
  * const { locale, locales, navigate, isReady } = useLocaleRouter();
19
19
  *
@@ -45,16 +45,41 @@ import { useBetterI18n } from "../context.js";
45
45
  * ```
46
46
  */
47
47
  export function useLocaleRouter() {
48
- const router = useRouter();
49
- const location = useLocation();
50
- const { languages, isLoadingLanguages } = useBetterI18n();
51
- // Build config from CDN manifest
48
+ const { languages, isLoadingLanguages, localePrefix, locale: contextLocale, setLocale, } = useBetterI18n();
49
+ // Safely detect TanStack Router context.
50
+ // When no <RouterProvider> is in the tree, these hooks throw
51
+ // we catch gracefully and fall back to context-based locale switching.
52
+ //
53
+ // This is safe because:
54
+ // - The router presence never changes during a component's lifecycle
55
+ // - React hook calls (useContext) inside useRouter/useLocation always execute
56
+ // before any throw, so the hook count is consistent between renders
57
+ let router = null;
58
+ let location = null;
59
+ try {
60
+ router = useRouter();
61
+ }
62
+ catch {
63
+ // No TanStack Router context — will use context-based navigation
64
+ }
65
+ try {
66
+ location = useLocation();
67
+ }
68
+ catch {
69
+ // No TanStack Router context — will use context-based navigation
70
+ }
71
+ const hasRouter = router != null && location != null;
72
+ // Build config from CDN manifest — includes localePrefix for URL strategy
52
73
  const config = useMemo(() => ({
53
74
  locales: languages.map((lang) => lang.code),
54
75
  defaultLocale: languages.find((l) => l.isDefault)?.code || "en",
55
- }), [languages]);
56
- // Get effective locale from URL (handles default without prefix)
76
+ localePrefix,
77
+ }), [languages, localePrefix]);
78
+ // Get effective locale: from URL when router is available, from context otherwise
57
79
  const locale = useMemo(() => {
80
+ if (!hasRouter || !location) {
81
+ return contextLocale;
82
+ }
58
83
  // If no languages loaded yet, extract from path manually
59
84
  if (languages.length === 0) {
60
85
  const segments = location.pathname.split("/").filter(Boolean);
@@ -66,12 +91,18 @@ export function useLocaleRouter() {
66
91
  return "en"; // fallback
67
92
  }
68
93
  return getLocaleFromPath(location.pathname, config);
69
- }, [location.pathname, config, languages]);
70
- // Navigate to same page with new locale (SPA navigation!)
94
+ }, [hasRouter, location, config, languages, contextLocale]);
95
+ // Navigate: router-based SPA navigation or context-based state update
71
96
  const navigate = useCallback((newLocale) => {
72
- const newPath = replaceLocaleInPath(location.pathname, newLocale, config);
73
- router.navigate({ to: newPath });
74
- }, [location.pathname, config, router]);
97
+ if (hasRouter && router && location) {
98
+ const newPath = replaceLocaleInPath(location.pathname, newLocale, config);
99
+ router.navigate({ to: newPath });
100
+ }
101
+ else {
102
+ // No router — update locale via provider state
103
+ setLocale(newLocale);
104
+ }
105
+ }, [hasRouter, location, config, router, setLocale]);
75
106
  // Get localized path for links
76
107
  const localePath = useCallback((path, targetLocale) => {
77
108
  const loc = targetLocale || locale;
@@ -1 +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"}
1
+ {"version":3,"file":"useLocaleRouter.js","sourceRoot":"","sources":["../../src/hooks/useLocaleRouter.ts"],"names":[],"mappings":"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"}
package/dist/hooks.d.ts CHANGED
@@ -25,38 +25,27 @@ export declare function useLanguages(): {
25
25
  isLoading: boolean;
26
26
  };
27
27
  /**
28
- * Hook to get current locale (read-only)
29
- *
30
- * For locale switching with proper router navigation, use useLocaleRouter() instead.
28
+ * Hook to get current locale and switch between locales
31
29
  *
32
30
  * @example
33
31
  * ```tsx
34
32
  * function LocaleDisplay() {
35
- * const { locale, isLoading } = useLocale()
33
+ * const { locale, setLocale, isLoading } = useLocale()
36
34
  *
37
35
  * if (isLoading) return <div>Loading...</div>
38
36
  *
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
37
  * return (
51
- * <button onClick={() => navigate(locale === 'en' ? 'tr' : 'en')}>
52
- * Toggle: {locale}
53
- * </button>
38
+ * <div>
39
+ * <span>Current: {locale}</span>
40
+ * <button onClick={() => setLocale('tr')}>Switch to Turkish</button>
41
+ * </div>
54
42
  * )
55
43
  * }
56
44
  * ```
57
45
  */
58
46
  export declare function useLocale(): {
59
47
  locale: string;
48
+ setLocale: (locale: string) => void;
60
49
  isLoading: boolean;
61
50
  };
62
51
  export { useTranslations, useFormatter, useMessages, useNow, useTimeZone, useLocale as useIntlLocale, } from "use-intl";
@@ -1 +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
+ {"version":3,"file":"hooks.d.ts","sourceRoot":"","sources":["../src/hooks.ts"],"names":[],"mappings":"AAIA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,YAAY;;;EAO3B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,SAAS;;;;EAQxB;AAGD,OAAO,EACL,eAAe,EACf,YAAY,EACZ,WAAW,EACX,MAAM,EACN,WAAW,EACX,SAAS,IAAI,aAAa,GAC3B,MAAM,UAAU,CAAC"}
package/dist/hooks.js CHANGED
@@ -30,40 +30,29 @@ export function useLanguages() {
30
30
  };
31
31
  }
32
32
  /**
33
- * Hook to get current locale (read-only)
34
- *
35
- * For locale switching with proper router navigation, use useLocaleRouter() instead.
33
+ * Hook to get current locale and switch between locales
36
34
  *
37
35
  * @example
38
36
  * ```tsx
39
37
  * function LocaleDisplay() {
40
- * const { locale, isLoading } = useLocale()
38
+ * const { locale, setLocale, isLoading } = useLocale()
41
39
  *
42
40
  * if (isLoading) return <div>Loading...</div>
43
41
  *
44
- * return <span>Current locale: {locale}</span>
45
- * }
46
- * ```
47
- *
48
- * @example
49
- * ```tsx
50
- * // For locale switching, use useLocaleRouter:
51
- * import { useLocaleRouter } from '@better-i18n/use-intl'
52
- *
53
- * function LocaleSwitcher() {
54
- * const { locale, navigate } = useLocaleRouter()
55
42
  * return (
56
- * <button onClick={() => navigate(locale === 'en' ? 'tr' : 'en')}>
57
- * Toggle: {locale}
58
- * </button>
43
+ * <div>
44
+ * <span>Current: {locale}</span>
45
+ * <button onClick={() => setLocale('tr')}>Switch to Turkish</button>
46
+ * </div>
59
47
  * )
60
48
  * }
61
49
  * ```
62
50
  */
63
51
  export function useLocale() {
64
- const { locale, isLoadingMessages } = useBetterI18n();
52
+ const { locale, setLocale, isLoadingMessages } = useBetterI18n();
65
53
  return {
66
54
  locale,
55
+ setLocale,
67
56
  isLoading: isLoadingMessages,
68
57
  };
69
58
  }
package/dist/hooks.js.map CHANGED
@@ -1 +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"}
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;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,UAAU,SAAS;IACvB,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,iBAAiB,EAAE,GAAG,aAAa,EAAE,CAAC;IAEjE,OAAO;QACL,MAAM;QACN,SAAS;QACT,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"}
@@ -5,28 +5,49 @@ export interface BetterI18nProviderProps extends BetterI18nProviderConfig {
5
5
  children: ReactNode;
6
6
  /** Pre-loaded languages from SSR loader — skips loading state on first render */
7
7
  initialLanguages?: LanguageOption[];
8
+ /**
9
+ * URL prefix strategy for locale codes.
10
+ * - `"as-needed"` (default): default locale has no URL prefix
11
+ * - `"always"`: all locales get a URL prefix (e.g., TanStack Router `$locale/` routes)
12
+ * @default "as-needed"
13
+ */
14
+ localePrefix?: "always" | "as-needed";
8
15
  /** Custom fallback when a message key is missing */
9
16
  getMessageFallback?: (info: {
10
17
  error: Error;
11
18
  key: string;
12
19
  namespace?: string;
13
20
  }) => string;
21
+ /**
22
+ * Called when locale changes via `setLocale()` (e.g., from `LocaleDropdown`).
23
+ * In router-based apps, use this to trigger URL navigation:
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * <BetterI18nProvider
28
+ * project="acme/web"
29
+ * locale={locale}
30
+ * onLocaleChange={(newLocale) => {
31
+ * navigate({ to: `/${newLocale}${pathname}` })
32
+ * }}
33
+ * >
34
+ * ```
35
+ */
36
+ onLocaleChange?: (locale: string) => void;
14
37
  }
15
38
  /**
16
39
  * Provider component that combines Better i18n CDN with use-intl
17
40
  *
18
- * The locale is controlled externally (from URL/router). Use useLocaleRouter()
19
- * for locale switching with proper router integration.
41
+ * Manages locale state internally, enabling locale switching via
42
+ * `setLocale()` from `useLocale()` or `useLocaleRouter()`.
20
43
  *
21
44
  * @example
22
45
  * ```tsx
23
- * // Basic usage (CSR - fetches messages on client)
46
+ * // Plain Vite / React app (no router)
24
47
  * function App() {
25
48
  * return (
26
- * <BetterI18nProvider
27
- * project="acme/dashboard"
28
- * locale="en"
29
- * >
49
+ * <BetterI18nProvider project="acme/dashboard" locale="en">
50
+ * <LocaleDropdown />
30
51
  * <MyComponent />
31
52
  * </BetterI18nProvider>
32
53
  * )
@@ -47,5 +68,5 @@ export interface BetterI18nProviderProps extends BetterI18nProviderConfig {
47
68
  * }
48
69
  * ```
49
70
  */
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;
71
+ export declare function BetterI18nProvider({ children, project, locale: propLocale, messages: propMessages, timeZone, now, onError, cdnBaseUrl, debug, logLevel, fetch: customFetch, storage, staticData, fetchTimeout, retryCount, initialLanguages, localePrefix, getMessageFallback: customGetMessageFallback, onLocaleChange, }: BetterI18nProviderProps): import("react/jsx-runtime").JSX.Element;
51
72
  //# sourceMappingURL=provider.d.ts.map
@@ -1 +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,EAAwC,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAG7E,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,2CAoJzB"}
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,EAAqD,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAG1F,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;;;;;OAKG;IACH,YAAY,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IACtC,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;IACb;;;;;;;;;;;;;;OAcG;IACH,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;CAC3C;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgCG;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,YAA0B,EAC1B,kBAAkB,EAAE,wBAAwB,EAC5C,cAAc,GACf,EAAE,uBAAuB,2CAsKzB"}
package/dist/provider.js CHANGED
@@ -1,24 +1,22 @@
1
1
  "use client";
2
2
  import { jsx as _jsx } from "react/jsx-runtime";
3
3
  import { createI18nCore } from "@better-i18n/core";
4
- import { useEffect, useMemo, useRef, useState } from "react";
4
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
5
5
  import { IntlProvider } from "use-intl";
6
6
  import { BetterI18nContext } from "./context.js";
7
7
  /**
8
8
  * Provider component that combines Better i18n CDN with use-intl
9
9
  *
10
- * The locale is controlled externally (from URL/router). Use useLocaleRouter()
11
- * for locale switching with proper router integration.
10
+ * Manages locale state internally, enabling locale switching via
11
+ * `setLocale()` from `useLocale()` or `useLocaleRouter()`.
12
12
  *
13
13
  * @example
14
14
  * ```tsx
15
- * // Basic usage (CSR - fetches messages on client)
15
+ * // Plain Vite / React app (no router)
16
16
  * function App() {
17
17
  * return (
18
- * <BetterI18nProvider
19
- * project="acme/dashboard"
20
- * locale="en"
21
- * >
18
+ * <BetterI18nProvider project="acme/dashboard" locale="en">
19
+ * <LocaleDropdown />
22
20
  * <MyComponent />
23
21
  * </BetterI18nProvider>
24
22
  * )
@@ -39,9 +37,20 @@ import { BetterI18nContext } from "./context.js";
39
37
  * }
40
38
  * ```
41
39
  */
42
- export function BetterI18nProvider({ children, project, locale: propLocale, messages: propMessages, timeZone, now, onError, cdnBaseUrl, debug, logLevel, fetch: customFetch, storage, staticData, fetchTimeout, retryCount, initialLanguages, getMessageFallback: customGetMessageFallback, }) {
43
- // Locale is controlled by props (from URL/router)
44
- const locale = propLocale;
40
+ export function BetterI18nProvider({ children, project, locale: propLocale, messages: propMessages, timeZone, now, onError, cdnBaseUrl, debug, logLevel, fetch: customFetch, storage, staticData, fetchTimeout, retryCount, initialLanguages, localePrefix = "as-needed", getMessageFallback: customGetMessageFallback, onLocaleChange, }) {
41
+ // Internal locale state enables non-router apps to switch locale
42
+ // via setLocale() without needing URL-based navigation.
43
+ const [managedLocale, setManagedLocale] = useState(propLocale);
44
+ // Sync when the external prop changes (e.g., router navigation updates propLocale)
45
+ useEffect(() => {
46
+ setManagedLocale(propLocale);
47
+ }, [propLocale]);
48
+ const locale = managedLocale;
49
+ // setLocale: updates internal state + notifies parent via callback
50
+ const setLocale = useCallback((newLocale) => {
51
+ setManagedLocale(newLocale);
52
+ onLocaleChange?.(newLocale);
53
+ }, [onLocaleChange]);
45
54
  // Track the locale that propMessages was originally provided for.
46
55
  // In SSR apps, propMessages comes from a one-time loader (script tag / SSR side-channel)
47
56
  // and stays frozen at the initial locale. When the user SPA-navigates to a new locale,
@@ -123,14 +132,15 @@ export function BetterI18nProvider({ children, project, locale: propLocale, mess
123
132
  cancelled = true;
124
133
  };
125
134
  }, [locale, i18nCore, propMessages]);
126
- // Context value (read-only locale - use useLocaleRouter for navigation)
127
135
  const contextValue = useMemo(() => ({
128
136
  locale,
137
+ setLocale,
129
138
  languages,
130
139
  isLoadingLanguages,
131
140
  isLoadingMessages,
132
141
  project,
133
- }), [locale, languages, isLoadingLanguages, isLoadingMessages, project]);
142
+ localePrefix,
143
+ }), [locale, setLocale, languages, isLoadingLanguages, isLoadingMessages, project, localePrefix]);
134
144
  if (!messages) {
135
145
  // Render children with empty messages instead of blanking the screen.
136
146
  // This converts a full DOM structural mismatch (null vs full tree)
@@ -1 +1 @@
1
- {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -34,14 +34,21 @@ export interface BetterI18nProviderConfig extends Omit<I18nCoreConfig, "defaultL
34
34
  /**
35
35
  * Better i18n context value
36
36
  *
37
- * Note: Locale is read-only. Use useLocaleRouter().navigate() for locale changes
38
- * to ensure proper router integration.
37
+ * Use `setLocale()` for locale switching. In router-based apps,
38
+ * the provider's `onLocaleChange` callback handles navigation.
39
39
  */
40
40
  export interface BetterI18nContextValue {
41
41
  /**
42
- * Current locale (read-only - use useLocaleRouter().navigate() to change)
42
+ * Current locale
43
43
  */
44
44
  locale: string;
45
+ /**
46
+ * Change the active locale.
47
+ * Updates internal state and triggers message loading.
48
+ * In router-based apps, also fires the `onLocaleChange` callback
49
+ * so the parent can handle URL navigation.
50
+ */
51
+ setLocale: (locale: string) => void;
45
52
  /**
46
53
  * Available languages with metadata from CDN manifest
47
54
  */
@@ -58,6 +65,12 @@ export interface BetterI18nContextValue {
58
65
  * Project identifier
59
66
  */
60
67
  project: string;
68
+ /**
69
+ * URL prefix strategy for locale codes.
70
+ * - `"as-needed"`: default locale has no URL prefix
71
+ * - `"always"`: all locales get a URL prefix (e.g., TanStack Router `$locale/`)
72
+ */
73
+ localePrefix: "always" | "as-needed";
61
74
  }
62
75
  /**
63
76
  * Server-side configuration for getMessages
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC,UAAU,CAAC,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,wBACf,SAAQ,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC;IAC7C;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAEpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,GAAG,CAAC,EAAE,IAAI,CAAC;IAEX;;OAEG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC,SAAS,CAAC,CAAC;CAC1D;AAED;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,SAAS,EAAE,cAAc,EAAE,CAAC;IAE5B;;OAEG;IACH,kBAAkB,EAAE,OAAO,CAAC;IAE5B;;OAEG;IACH,iBAAiB,EAAE,OAAO,CAAC;IAE3B;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,cAAc;IACvD;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAC5C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC;AAE7C;;GAEG;AACH,MAAM,MAAM,QAAQ,GAAG,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC,UAAU,CAAC,CAAC;AAEvE;;GAEG;AACH,MAAM,WAAW,wBACf,SAAQ,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC;IAC7C;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAC;IAEpB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,GAAG,CAAC,EAAE,IAAI,CAAC;IAEX;;OAEG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC,SAAS,CAAC,CAAC;CAC1D;AAED;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;;;;OAKG;IACH,SAAS,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAEpC;;OAEG;IACH,SAAS,EAAE,cAAc,EAAE,CAAC;IAE5B;;OAEG;IACH,kBAAkB,EAAE,OAAO,CAAC;IAE5B;;OAEG;IACH,iBAAiB,EAAE,OAAO,CAAC;IAE3B;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;;OAIG;IACH,YAAY,EAAE,QAAQ,GAAG,WAAW,CAAC;CACtC;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,cAAc;IACvD;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@better-i18n/use-intl",
3
- "version": "0.2.1",
3
+ "version": "0.2.2",
4
4
  "description": "Better i18n integration for use-intl (React, TanStack Start)",
5
5
  "license": "MIT",
6
6
  "repository": {