@better-i18n/use-intl 0.1.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.
package/README.md ADDED
@@ -0,0 +1,221 @@
1
+ # @better-i18n/use-intl
2
+
3
+ Better i18n integration for [use-intl](https://github.com/amannn/next-intl/tree/main/packages/use-intl) - perfect for React, TanStack Start, and any JavaScript framework.
4
+
5
+ ## Features
6
+
7
+ - 🚀 **CDN-powered** - Fetch translations from Better i18n CDN
8
+ - âš¡ **SSR Ready** - Full server-side rendering support for TanStack Start
9
+ - 🔄 **Dynamic Loading** - Load messages on-demand per locale
10
+ - 🎯 **Type Safe** - Full TypeScript support
11
+ - 🪶 **Lightweight** - Built on use-intl (~2KB)
12
+
13
+ ## Installation
14
+
15
+ ```bash
16
+ npm install @better-i18n/use-intl use-intl
17
+ # or
18
+ bun add @better-i18n/use-intl use-intl
19
+ ```
20
+
21
+ ## Quick Start
22
+
23
+ ### Client-Side (CSR)
24
+
25
+ ```tsx
26
+ import { BetterI18nProvider, useTranslations } from '@better-i18n/use-intl'
27
+
28
+ function App() {
29
+ return (
30
+ <BetterI18nProvider
31
+ project="your-org/your-project"
32
+ locale="en"
33
+ >
34
+ <HomePage />
35
+ </BetterI18nProvider>
36
+ )
37
+ }
38
+
39
+ function HomePage() {
40
+ const t = useTranslations('home')
41
+ return <h1>{t('title')}</h1>
42
+ }
43
+ ```
44
+
45
+ ### Server-Side (TanStack Start)
46
+
47
+ ```tsx
48
+ // routes/__root.tsx
49
+ import { createRootRoute } from '@tanstack/react-router'
50
+ import { BetterI18nProvider } from '@better-i18n/use-intl'
51
+ import { getMessages } from '@better-i18n/use-intl/server'
52
+
53
+ export const Route = createRootRoute({
54
+ loader: async ({ context }) => {
55
+ const messages = await getMessages({
56
+ project: 'your-org/your-project',
57
+ locale: context.locale,
58
+ })
59
+ return { messages }
60
+ },
61
+ component: RootComponent,
62
+ })
63
+
64
+ function RootComponent() {
65
+ const { messages } = Route.useLoaderData()
66
+ const { locale } = Route.useRouteContext()
67
+
68
+ return (
69
+ <BetterI18nProvider
70
+ project="your-org/your-project"
71
+ locale={locale}
72
+ messages={messages}
73
+ >
74
+ <Outlet />
75
+ </BetterI18nProvider>
76
+ )
77
+ }
78
+ ```
79
+
80
+ ## API Reference
81
+
82
+ ### Components
83
+
84
+ #### `<BetterI18nProvider>`
85
+
86
+ Main provider that combines Better i18n CDN with use-intl.
87
+
88
+ ```tsx
89
+ <BetterI18nProvider
90
+ project="org/project" // Required: Better i18n project
91
+ locale="en" // Required: Current locale
92
+ messages={messages} // Optional: Pre-loaded messages (SSR)
93
+ timeZone="Europe/Berlin" // Optional: Timezone for formatting
94
+ now={new Date()} // Optional: Current time (SSR)
95
+ onLocaleChange={(l) => {}} // Optional: Locale change callback
96
+ >
97
+ {children}
98
+ </BetterI18nProvider>
99
+ ```
100
+
101
+ ### Hooks
102
+
103
+ #### `useTranslations(namespace?)`
104
+
105
+ Access translations (re-exported from use-intl).
106
+
107
+ ```tsx
108
+ const t = useTranslations('common')
109
+ t('greeting', { name: 'World' }) // "Hello, World!"
110
+ ```
111
+
112
+ #### `useLocale()`
113
+
114
+ Get and set the current locale.
115
+
116
+ ```tsx
117
+ const { locale, setLocale, isLoading } = useLocale()
118
+ ```
119
+
120
+ #### `useLanguages()`
121
+
122
+ Get available languages with metadata.
123
+
124
+ ```tsx
125
+ const { languages, isLoading } = useLanguages()
126
+ // [{ code: 'en', name: 'English', nativeName: 'English', flagUrl: '...' }]
127
+ ```
128
+
129
+ #### `useBetterI18n()`
130
+
131
+ Access the full Better i18n context.
132
+
133
+ ```tsx
134
+ const { locale, setLocale, languages, isLoadingMessages, project } = useBetterI18n()
135
+ ```
136
+
137
+ #### `useFormatter()`
138
+
139
+ Format dates, numbers, and lists (re-exported from use-intl).
140
+
141
+ ```tsx
142
+ const format = useFormatter()
143
+ format.dateTime(new Date(), { dateStyle: 'full' })
144
+ format.number(1000, { style: 'currency', currency: 'USD' })
145
+ ```
146
+
147
+ ### Server Utilities
148
+
149
+ Import from `@better-i18n/use-intl/server`:
150
+
151
+ #### `getMessages(config)`
152
+
153
+ Fetch messages for SSR.
154
+
155
+ ```ts
156
+ const messages = await getMessages({
157
+ project: 'org/project',
158
+ locale: 'en',
159
+ })
160
+ ```
161
+
162
+ #### `getLocales(config)`
163
+
164
+ Get available locale codes.
165
+
166
+ ```ts
167
+ const locales = await getLocales({ project: 'org/project' })
168
+ // ['en', 'tr', 'de']
169
+ ```
170
+
171
+ #### `getLanguages(config)`
172
+
173
+ Get languages with metadata.
174
+
175
+ ```ts
176
+ const languages = await getLanguages({ project: 'org/project' })
177
+ ```
178
+
179
+ #### `createServerTranslator(config)`
180
+
181
+ Create translator for non-React contexts (metadata, emails).
182
+
183
+ ```ts
184
+ const t = createServerTranslator({ locale: 'en', messages })
185
+ const title = t('page.title')
186
+ ```
187
+
188
+ ## Language Switcher Example
189
+
190
+ ```tsx
191
+ import { useLocale, useLanguages } from '@better-i18n/use-intl'
192
+
193
+ function LanguageSwitcher() {
194
+ const { locale, setLocale } = useLocale()
195
+ const { languages, isLoading } = useLanguages()
196
+
197
+ if (isLoading) return <span>Loading...</span>
198
+
199
+ return (
200
+ <select value={locale} onChange={(e) => setLocale(e.target.value)}>
201
+ {languages.map((lang) => (
202
+ <option key={lang.code} value={lang.code}>
203
+ {lang.nativeName}
204
+ </option>
205
+ ))}
206
+ </select>
207
+ )
208
+ }
209
+ ```
210
+
211
+ ## TanStack Start Full Example
212
+
213
+ See our [TanStack Start guide](/docs/react/tanstack-start) for a complete example with:
214
+ - Locale middleware
215
+ - URL-based locale detection
216
+ - SEO-friendly routing
217
+ - Hydration without mismatches
218
+
219
+ ## License
220
+
221
+ MIT
@@ -0,0 +1,40 @@
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
+ }) => ReactNode;
12
+ /**
13
+ * Label for loading state
14
+ */
15
+ loadingLabel?: string;
16
+ }
17
+ /**
18
+ * Pre-built language switcher component
19
+ *
20
+ * @example
21
+ * ```tsx
22
+ * // Basic usage
23
+ * <LanguageSwitcher />
24
+ *
25
+ * // With custom styling
26
+ * <LanguageSwitcher className="my-select" />
27
+ *
28
+ * // Custom option rendering
29
+ * <LanguageSwitcher
30
+ * renderOption={(lang) => (
31
+ * <>
32
+ * {lang.flagUrl && <img src={lang.flagUrl} alt="" />}
33
+ * {lang.nativeName}
34
+ * </>
35
+ * )}
36
+ * />
37
+ * ```
38
+ */
39
+ export declare function LanguageSwitcher({ renderOption, loadingLabel, ...props }: LanguageSwitcherProps): import("react/jsx-runtime").JSX.Element;
40
+ //# 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;AAGvD,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;KACzB,KAAK,SAAS,CAAC;IAEhB;;OAEG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,YAAY,EACZ,YAA2B,EAC3B,GAAG,KAAK,EACT,EAAE,qBAAqB,2CAoBvB"}
@@ -0,0 +1,33 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { useBetterI18n } from "./context";
4
+ /**
5
+ * Pre-built language switcher component
6
+ *
7
+ * @example
8
+ * ```tsx
9
+ * // Basic usage
10
+ * <LanguageSwitcher />
11
+ *
12
+ * // With custom styling
13
+ * <LanguageSwitcher className="my-select" />
14
+ *
15
+ * // Custom option rendering
16
+ * <LanguageSwitcher
17
+ * renderOption={(lang) => (
18
+ * <>
19
+ * {lang.flagUrl && <img src={lang.flagUrl} alt="" />}
20
+ * {lang.nativeName}
21
+ * </>
22
+ * )}
23
+ * />
24
+ * ```
25
+ */
26
+ export function LanguageSwitcher({ renderOption, loadingLabel = "Loading...", ...props }) {
27
+ const { locale, setLocale, languages, isLoadingLanguages } = useBetterI18n();
28
+ if (isLoadingLanguages) {
29
+ return (_jsx("select", { disabled: true, ...props, children: _jsx("option", { children: loadingLabel }) }));
30
+ }
31
+ return (_jsx("select", { value: locale, onChange: (e) => setLocale(e.target.value), ...props, children: languages.map((lang) => (_jsx("option", { value: lang.code, children: renderOption ? renderOption(lang) : lang.nativeName || lang.code }, lang.code))) }));
32
+ }
33
+ //# 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,aAAa,EAAE,MAAM,WAAW,CAAC;AAoB1C;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,MAAM,UAAU,gBAAgB,CAAC,EAC/B,YAAY,EACZ,YAAY,GAAG,YAAY,EAC3B,GAAG,KAAK,EACc;IACtB,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,kBAAkB,EAAE,GAAG,aAAa,EAAE,CAAC;IAE7E,IAAI,kBAAkB,EAAE,CAAC;QACvB,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,SAAS,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,KAAM,KAAK,YACzE,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,27 @@
1
+ import type { BetterI18nContextValue } from "./types";
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
+ * @example
10
+ * ```tsx
11
+ * function LanguageSwitcher() {
12
+ * const { locale, setLocale, languages } = useBetterI18n()
13
+ *
14
+ * return (
15
+ * <select value={locale} onChange={(e) => setLocale(e.target.value)}>
16
+ * {languages.map((lang) => (
17
+ * <option key={lang.code} value={lang.code}>
18
+ * {lang.nativeName}
19
+ * </option>
20
+ * ))}
21
+ * </select>
22
+ * )
23
+ * }
24
+ * ```
25
+ */
26
+ export declare function useBetterI18n(): BetterI18nContextValue;
27
+ //# 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,SAAS,CAAC;AAEtD;;GAEG;AACH,eAAO,MAAM,iBAAiB,wDAE7B,CAAC;AAEF;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,aAAa,IAAI,sBAAsB,CAUtD"}
@@ -0,0 +1,34 @@
1
+ "use client";
2
+ import { createContext, useContext } from "react";
3
+ /**
4
+ * Context for Better i18n specific state
5
+ */
6
+ export const BetterI18nContext = createContext(null);
7
+ /**
8
+ * Hook to access Better i18n context
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * function LanguageSwitcher() {
13
+ * const { locale, setLocale, languages } = useBetterI18n()
14
+ *
15
+ * return (
16
+ * <select value={locale} onChange={(e) => setLocale(e.target.value)}>
17
+ * {languages.map((lang) => (
18
+ * <option key={lang.code} value={lang.code}>
19
+ * {lang.nativeName}
20
+ * </option>
21
+ * ))}
22
+ * </select>
23
+ * )
24
+ * }
25
+ * ```
26
+ */
27
+ export function useBetterI18n() {
28
+ const context = useContext(BetterI18nContext);
29
+ if (!context) {
30
+ throw new Error("[better-i18n] useBetterI18n must be used within a BetterI18nProvider");
31
+ }
32
+ return context;
33
+ }
34
+ //# 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;;;;;;;;;;;;;;;;;;;GAmBG;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,49 @@
1
+ /**
2
+ * Hook to get available languages
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("packages/i18n-core/dist").LanguageOption[];
25
+ isLoading: boolean;
26
+ };
27
+ /**
28
+ * Hook to get and set current locale
29
+ *
30
+ * @example
31
+ * ```tsx
32
+ * function LocaleSwitcher() {
33
+ * const { locale, setLocale } = useLocale()
34
+ *
35
+ * return (
36
+ * <button onClick={() => setLocale(locale === 'en' ? 'tr' : 'en')}>
37
+ * Current: {locale}
38
+ * </button>
39
+ * )
40
+ * }
41
+ * ```
42
+ */
43
+ export declare function useLocale(): {
44
+ locale: string;
45
+ setLocale: (locale: string) => void;
46
+ isLoading: boolean;
47
+ };
48
+ export { useTranslations, useFormatter, useMessages, useNow, useTimeZone, useLocale as useIntlLocale, } from "use-intl";
49
+ //# 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;;;;;;;;;;;;;;;GAeG;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 ADDED
@@ -0,0 +1,58 @@
1
+ "use client";
2
+ import { useBetterI18n } from "./context";
3
+ /**
4
+ * Hook to get available languages
5
+ *
6
+ * @example
7
+ * ```tsx
8
+ * function LanguageList() {
9
+ * const { languages, isLoading } = useLanguages()
10
+ *
11
+ * if (isLoading) return <div>Loading...</div>
12
+ *
13
+ * return (
14
+ * <ul>
15
+ * {languages.map((lang) => (
16
+ * <li key={lang.code}>
17
+ * {lang.nativeName} ({lang.code})
18
+ * </li>
19
+ * ))}
20
+ * </ul>
21
+ * )
22
+ * }
23
+ * ```
24
+ */
25
+ export function useLanguages() {
26
+ const { languages, isLoadingLanguages } = useBetterI18n();
27
+ return {
28
+ languages,
29
+ isLoading: isLoadingLanguages,
30
+ };
31
+ }
32
+ /**
33
+ * Hook to get and set current locale
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * function LocaleSwitcher() {
38
+ * const { locale, setLocale } = useLocale()
39
+ *
40
+ * return (
41
+ * <button onClick={() => setLocale(locale === 'en' ? 'tr' : 'en')}>
42
+ * Current: {locale}
43
+ * </button>
44
+ * )
45
+ * }
46
+ * ```
47
+ */
48
+ export function useLocale() {
49
+ const { locale, setLocale, isLoadingMessages } = useBetterI18n();
50
+ return {
51
+ locale,
52
+ setLocale,
53
+ isLoading: isLoadingMessages,
54
+ };
55
+ }
56
+ // Re-export use-intl hooks for convenience
57
+ export { useTranslations, useFormatter, useMessages, useNow, useTimeZone, useLocale as useIntlLocale, } from "use-intl";
58
+ //# 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,WAAW,CAAC;AAE1C;;;;;;;;;;;;;;;;;;;;;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;;;;;;;;;;;;;;;GAeG;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"}
@@ -0,0 +1,9 @@
1
+ export { BetterI18nProvider } from "./provider";
2
+ export type { BetterI18nProviderProps } from "./provider";
3
+ export { useBetterI18n } from "./context";
4
+ export { useLanguages, useLocale, useTranslations, useFormatter, useMessages, useNow, useTimeZone, } from "./hooks";
5
+ export { LanguageSwitcher } from "./components";
6
+ export type { LanguageSwitcherProps } from "./components";
7
+ export type { Messages, BetterI18nProviderConfig, BetterI18nContextValue, } from "./types";
8
+ export { IntlProvider } from "use-intl";
9
+ //# 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,YAAY,CAAC;AAChD,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AAG1D,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACL,YAAY,EACZ,SAAS,EAET,eAAe,EACf,YAAY,EACZ,WAAW,EACX,MAAM,EACN,WAAW,GACZ,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChD,YAAY,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AAG1D,YAAY,EACV,QAAQ,EACR,wBAAwB,EACxB,sBAAsB,GACvB,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,12 @@
1
+ // Provider
2
+ export { BetterI18nProvider } from "./provider";
3
+ // Context & Hooks
4
+ export { useBetterI18n } from "./context";
5
+ export { useLanguages, useLocale,
6
+ // Re-exported from use-intl
7
+ useTranslations, useFormatter, useMessages, useNow, useTimeZone, } from "./hooks";
8
+ // Components
9
+ export { LanguageSwitcher } from "./components";
10
+ // Re-export commonly used use-intl components
11
+ export { IntlProvider } from "use-intl";
12
+ //# 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,YAAY,CAAC;AAGhD,kBAAkB;AAClB,OAAO,EAAE,aAAa,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EACL,YAAY,EACZ,SAAS;AACT,4BAA4B;AAC5B,eAAe,EACf,YAAY,EACZ,WAAW,EACX,MAAM,EACN,WAAW,GACZ,MAAM,SAAS,CAAC;AAEjB,aAAa;AACb,OAAO,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAUhD,8CAA8C;AAC9C,OAAO,EAAE,YAAY,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,38 @@
1
+ import { type ReactNode } from "react";
2
+ import type { BetterI18nProviderConfig } from "./types";
3
+ export interface BetterI18nProviderProps extends BetterI18nProviderConfig {
4
+ children: ReactNode;
5
+ }
6
+ /**
7
+ * Provider component that combines Better i18n CDN with use-intl
8
+ *
9
+ * @example
10
+ * ```tsx
11
+ * // Basic usage (CSR - fetches messages on client)
12
+ * function App() {
13
+ * return (
14
+ * <BetterI18nProvider
15
+ * project="acme/dashboard"
16
+ * locale="en"
17
+ * >
18
+ * <MyComponent />
19
+ * </BetterI18nProvider>
20
+ * )
21
+ * }
22
+ *
23
+ * // SSR usage (pre-loaded messages)
24
+ * function App({ locale, messages }) {
25
+ * return (
26
+ * <BetterI18nProvider
27
+ * project="acme/dashboard"
28
+ * locale={locale}
29
+ * messages={messages}
30
+ * >
31
+ * <MyComponent />
32
+ * </BetterI18nProvider>
33
+ * )
34
+ * }
35
+ * ```
36
+ */
37
+ export declare function BetterI18nProvider({ children, project, locale: initialLocale, messages: initialMessages, timeZone, now, onLocaleChange, onError, cdnBaseUrl, debug, logLevel, fetch: customFetch, }: BetterI18nProviderProps): import("react/jsx-runtime").JSX.Element | null;
38
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"AAIA,OAAO,EAKL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,OAAO,KAAK,EAAE,wBAAwB,EAAY,MAAM,SAAS,CAAC;AAElE,MAAM,WAAW,uBAAwB,SAAQ,wBAAwB;IACvE,QAAQ,EAAE,SAAS,CAAC;CACrB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,OAAO,EACP,MAAM,EAAE,aAAa,EACrB,QAAQ,EAAE,eAAe,EACzB,QAAQ,EACR,GAAG,EACH,cAAc,EACd,OAAO,EACP,UAAU,EACV,KAAK,EACL,QAAQ,EACR,KAAK,EAAE,WAAW,GACnB,EAAE,uBAAuB,kDA6HzB"}
@@ -0,0 +1,126 @@
1
+ "use client";
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createI18nCore } from "@better-i18n/i18n-core";
4
+ import { useCallback, useEffect, useMemo, useState, } from "react";
5
+ import { IntlProvider } from "use-intl";
6
+ import { BetterI18nContext } from "./context";
7
+ /**
8
+ * Provider component that combines Better i18n CDN with use-intl
9
+ *
10
+ * @example
11
+ * ```tsx
12
+ * // Basic usage (CSR - fetches messages on client)
13
+ * function App() {
14
+ * return (
15
+ * <BetterI18nProvider
16
+ * project="acme/dashboard"
17
+ * locale="en"
18
+ * >
19
+ * <MyComponent />
20
+ * </BetterI18nProvider>
21
+ * )
22
+ * }
23
+ *
24
+ * // SSR usage (pre-loaded messages)
25
+ * function App({ locale, messages }) {
26
+ * return (
27
+ * <BetterI18nProvider
28
+ * project="acme/dashboard"
29
+ * locale={locale}
30
+ * messages={messages}
31
+ * >
32
+ * <MyComponent />
33
+ * </BetterI18nProvider>
34
+ * )
35
+ * }
36
+ * ```
37
+ */
38
+ export function BetterI18nProvider({ children, project, locale: initialLocale, messages: initialMessages, timeZone, now, onLocaleChange, onError, cdnBaseUrl, debug, logLevel, fetch: customFetch, }) {
39
+ const [locale, setLocaleState] = useState(initialLocale);
40
+ const [messages, setMessages] = useState(initialMessages);
41
+ const [languages, setLanguages] = useState([]);
42
+ const [isLoadingMessages, setIsLoadingMessages] = useState(!initialMessages);
43
+ const [isLoadingLanguages, setIsLoadingLanguages] = useState(true);
44
+ // Create i18n core instance
45
+ const i18nCore = useMemo(() => createI18nCore({
46
+ project,
47
+ defaultLocale: initialLocale,
48
+ cdnBaseUrl,
49
+ debug,
50
+ logLevel,
51
+ fetch: customFetch,
52
+ }), [project, initialLocale, cdnBaseUrl, debug, logLevel, customFetch]);
53
+ // Load languages on mount
54
+ useEffect(() => {
55
+ let cancelled = false;
56
+ const loadLanguages = async () => {
57
+ try {
58
+ const langs = await i18nCore.getLanguages();
59
+ if (!cancelled) {
60
+ setLanguages(langs);
61
+ }
62
+ }
63
+ catch (error) {
64
+ console.error("[better-i18n] Failed to load languages:", error);
65
+ }
66
+ finally {
67
+ if (!cancelled) {
68
+ setIsLoadingLanguages(false);
69
+ }
70
+ }
71
+ };
72
+ loadLanguages();
73
+ return () => {
74
+ cancelled = true;
75
+ };
76
+ }, [i18nCore]);
77
+ // Load messages when locale changes (if not pre-loaded)
78
+ useEffect(() => {
79
+ // Skip if we have initial messages for the current locale
80
+ if (initialMessages && locale === initialLocale) {
81
+ return;
82
+ }
83
+ let cancelled = false;
84
+ const loadMessages = async () => {
85
+ setIsLoadingMessages(true);
86
+ try {
87
+ const msgs = await i18nCore.getMessages(locale);
88
+ if (!cancelled) {
89
+ setMessages(msgs);
90
+ }
91
+ }
92
+ catch (error) {
93
+ console.error(`[better-i18n] Failed to load messages for locale "${locale}":`, error);
94
+ }
95
+ finally {
96
+ if (!cancelled) {
97
+ setIsLoadingMessages(false);
98
+ }
99
+ }
100
+ };
101
+ loadMessages();
102
+ return () => {
103
+ cancelled = true;
104
+ };
105
+ }, [locale, i18nCore, initialMessages, initialLocale]);
106
+ // Locale change handler
107
+ const setLocale = useCallback((newLocale) => {
108
+ setLocaleState(newLocale);
109
+ onLocaleChange?.(newLocale);
110
+ }, [onLocaleChange]);
111
+ // Context value
112
+ const contextValue = useMemo(() => ({
113
+ locale,
114
+ setLocale,
115
+ languages,
116
+ isLoadingLanguages,
117
+ isLoadingMessages,
118
+ project,
119
+ }), [locale, setLocale, languages, isLoadingLanguages, isLoadingMessages, project]);
120
+ // Don't render until we have messages
121
+ if (!messages) {
122
+ return null;
123
+ }
124
+ return (_jsx(BetterI18nContext.Provider, { value: contextValue, children: _jsx(IntlProvider, { locale: locale, messages: messages, timeZone: timeZone, now: now, onError: onError, children: children }) }));
125
+ }
126
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../src/provider.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,107 @@
1
+ import type { I18nCoreConfig, LanguageOption } from "@better-i18n/i18n-core";
2
+ import type { Messages } from "./types";
3
+ export interface GetMessagesConfig extends Omit<I18nCoreConfig, "defaultLocale"> {
4
+ /**
5
+ * Locale to fetch messages for
6
+ */
7
+ locale: string;
8
+ }
9
+ /**
10
+ * Fetch messages for a locale (server-side)
11
+ *
12
+ * Use this in your server loaders (TanStack Start, Remix, etc.)
13
+ *
14
+ * @example
15
+ * ```ts
16
+ * // TanStack Start loader
17
+ * export async function loader({ params }) {
18
+ * const locale = params.locale || 'en'
19
+ * const messages = await getMessages({
20
+ * project: 'acme/dashboard',
21
+ * locale,
22
+ * })
23
+ * return { locale, messages }
24
+ * }
25
+ * ```
26
+ */
27
+ export declare function getMessages(config: GetMessagesConfig): Promise<Messages>;
28
+ /**
29
+ * Fetch available locales (server-side)
30
+ *
31
+ * @example
32
+ * ```ts
33
+ * const locales = await getLocales({ project: 'acme/dashboard' })
34
+ * // ['en', 'tr', 'de', ...]
35
+ * ```
36
+ */
37
+ export declare function getLocales(config: Omit<I18nCoreConfig, "defaultLocale"> & {
38
+ defaultLocale?: string;
39
+ }): Promise<string[]>;
40
+ /**
41
+ * Fetch available languages with metadata (server-side)
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * const languages = await getLanguages({ project: 'acme/dashboard' })
46
+ * // [{ code: 'en', name: 'English', nativeName: 'English', flagUrl: '...' }, ...]
47
+ * ```
48
+ */
49
+ export declare function getLanguages(config: Omit<I18nCoreConfig, "defaultLocale"> & {
50
+ defaultLocale?: string;
51
+ }): Promise<LanguageOption[]>;
52
+ /**
53
+ * Create a translator function for use outside React (server-side)
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * const messages = await getMessages({ project: 'acme/dashboard', locale: 'en' })
58
+ * const t = createServerTranslator({ locale: 'en', messages })
59
+ *
60
+ * // Use in metadata, emails, etc.
61
+ * const title = t('page.title')
62
+ * ```
63
+ */
64
+ export declare function createServerTranslator(config: {
65
+ locale: string;
66
+ messages: Messages;
67
+ namespace?: string;
68
+ }): import("use-intl")._Translator<{
69
+ [x: string]: any;
70
+ }, string>;
71
+ /**
72
+ * Create a formatter for use outside React (server-side)
73
+ *
74
+ * @example
75
+ * ```ts
76
+ * const format = createServerFormatter({ locale: 'en' })
77
+ * const date = format.dateTime(new Date(), { dateStyle: 'full' })
78
+ * ```
79
+ */
80
+ export declare function createServerFormatter(config: {
81
+ locale: string;
82
+ timeZone?: string;
83
+ now?: Date;
84
+ }): {
85
+ dateTime: {
86
+ (value: Date | number, options?: import("use-intl").DateTimeFormatOptions): string;
87
+ (value: Date | number, format?: string, options?: import("use-intl").DateTimeFormatOptions): string;
88
+ };
89
+ number: {
90
+ (value: number | bigint, options?: import("use-intl").NumberFormatOptions): string;
91
+ (value: number | bigint, format?: string, options?: import("use-intl").NumberFormatOptions): string;
92
+ };
93
+ relativeTime: {
94
+ (date: number | Date, now?: import("use-intl").RelativeTimeFormatOptions["now"]): string;
95
+ (date: number | Date, options?: import("use-intl").RelativeTimeFormatOptions): string;
96
+ };
97
+ list: {
98
+ <Value extends string | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>>(value: Iterable<Value>, options?: Intl.ListFormatOptions): Value extends string ? string : Iterable<import("react").ReactElement>;
99
+ <Value extends string | import("react").ReactElement<unknown, string | import("react").JSXElementConstructor<any>>>(value: Iterable<Value>, format?: string, options?: Intl.ListFormatOptions): Value extends string ? string : Iterable<import("react").ReactElement>;
100
+ };
101
+ dateTimeRange: {
102
+ (start: Date | number, end: Date | number, options?: import("use-intl").DateTimeFormatOptions): string;
103
+ (start: Date | number, end: Date | number, format?: string, options?: import("use-intl").DateTimeFormatOptions): string;
104
+ };
105
+ };
106
+ export { createTranslator, createFormatter } from "use-intl/core";
107
+ //# sourceMappingURL=server.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAE7E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAExC,MAAM,WAAW,iBAAkB,SAAQ,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC;IAC9E;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,QAAQ,CAAC,CAW9E;AAED;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAC9B,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC,GAAG;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GACzE,OAAO,CAAC,MAAM,EAAE,CAAC,CAWnB;AAED;;;;;;;;GAQG;AACH,wBAAsB,YAAY,CAChC,MAAM,EAAE,IAAI,CAAC,cAAc,EAAE,eAAe,CAAC,GAAG;IAAE,aAAa,CAAC,EAAE,MAAM,CAAA;CAAE,GACzE,OAAO,CAAC,cAAc,EAAE,CAAC,CAW3B;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,sBAAsB,CAAC,MAAM,EAAE;IAC7C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,QAAQ,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;;WAMA;AAED;;;;;;;;GAQG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE;IAC5C,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,IAAI,CAAC;CACZ;;sCAlGiD,CAAC;qCAE3C,CAAC,iBACK,CAAC;;;wCAGC,CAAC;uCAIQ,CAAC,iBAAgC,CAAC;;;iCAQ1C,CAAC;qCAEf,CAAC;;;2JAMmB,CAAC;0JAYf,CAAC,iBAA8B,CAAC;;;0DAKiB,CAAC;yDAIxC,CAAC,iBAAkC,CAAC;;EAyDtD;AAGD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
package/dist/server.js ADDED
@@ -0,0 +1,109 @@
1
+ import { createI18nCore } from "@better-i18n/i18n-core";
2
+ import { createFormatter, createTranslator } from "use-intl/core";
3
+ /**
4
+ * Fetch messages for a locale (server-side)
5
+ *
6
+ * Use this in your server loaders (TanStack Start, Remix, etc.)
7
+ *
8
+ * @example
9
+ * ```ts
10
+ * // TanStack Start loader
11
+ * export async function loader({ params }) {
12
+ * const locale = params.locale || 'en'
13
+ * const messages = await getMessages({
14
+ * project: 'acme/dashboard',
15
+ * locale,
16
+ * })
17
+ * return { locale, messages }
18
+ * }
19
+ * ```
20
+ */
21
+ export async function getMessages(config) {
22
+ const i18n = createI18nCore({
23
+ project: config.project,
24
+ defaultLocale: config.locale,
25
+ cdnBaseUrl: config.cdnBaseUrl,
26
+ debug: config.debug,
27
+ logLevel: config.logLevel,
28
+ fetch: config.fetch,
29
+ });
30
+ return (await i18n.getMessages(config.locale));
31
+ }
32
+ /**
33
+ * Fetch available locales (server-side)
34
+ *
35
+ * @example
36
+ * ```ts
37
+ * const locales = await getLocales({ project: 'acme/dashboard' })
38
+ * // ['en', 'tr', 'de', ...]
39
+ * ```
40
+ */
41
+ export async function getLocales(config) {
42
+ const i18n = createI18nCore({
43
+ project: config.project,
44
+ defaultLocale: config.defaultLocale || "en",
45
+ cdnBaseUrl: config.cdnBaseUrl,
46
+ debug: config.debug,
47
+ logLevel: config.logLevel,
48
+ fetch: config.fetch,
49
+ });
50
+ return i18n.getLocales();
51
+ }
52
+ /**
53
+ * Fetch available languages with metadata (server-side)
54
+ *
55
+ * @example
56
+ * ```ts
57
+ * const languages = await getLanguages({ project: 'acme/dashboard' })
58
+ * // [{ code: 'en', name: 'English', nativeName: 'English', flagUrl: '...' }, ...]
59
+ * ```
60
+ */
61
+ export async function getLanguages(config) {
62
+ const i18n = createI18nCore({
63
+ project: config.project,
64
+ defaultLocale: config.defaultLocale || "en",
65
+ cdnBaseUrl: config.cdnBaseUrl,
66
+ debug: config.debug,
67
+ logLevel: config.logLevel,
68
+ fetch: config.fetch,
69
+ });
70
+ return i18n.getLanguages();
71
+ }
72
+ /**
73
+ * Create a translator function for use outside React (server-side)
74
+ *
75
+ * @example
76
+ * ```ts
77
+ * const messages = await getMessages({ project: 'acme/dashboard', locale: 'en' })
78
+ * const t = createServerTranslator({ locale: 'en', messages })
79
+ *
80
+ * // Use in metadata, emails, etc.
81
+ * const title = t('page.title')
82
+ * ```
83
+ */
84
+ export function createServerTranslator(config) {
85
+ return createTranslator({
86
+ locale: config.locale,
87
+ messages: config.messages,
88
+ namespace: config.namespace,
89
+ });
90
+ }
91
+ /**
92
+ * Create a formatter for use outside React (server-side)
93
+ *
94
+ * @example
95
+ * ```ts
96
+ * const format = createServerFormatter({ locale: 'en' })
97
+ * const date = format.dateTime(new Date(), { dateStyle: 'full' })
98
+ * ```
99
+ */
100
+ export function createServerFormatter(config) {
101
+ return createFormatter({
102
+ locale: config.locale,
103
+ timeZone: config.timeZone,
104
+ now: config.now,
105
+ });
106
+ }
107
+ // Re-export core functions from use-intl for advanced usage
108
+ export { createTranslator, createFormatter } from "use-intl/core";
109
+ //# sourceMappingURL=server.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,OAAO,EAAE,eAAe,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAUlE;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,MAAyB;IACzD,MAAM,IAAI,GAAG,cAAc,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,aAAa,EAAE,MAAM,CAAC,MAAM;QAC5B,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,KAAK,EAAE,MAAM,CAAC,KAAK;KACpB,CAAC,CAAC;IAEH,OAAO,CAAC,MAAM,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC,MAAM,CAAC,CAAa,CAAC;AAC7D,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,MAA0E;IAE1E,MAAM,IAAI,GAAG,cAAc,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,aAAa,EAAE,MAAM,CAAC,aAAa,IAAI,IAAI;QAC3C,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,KAAK,EAAE,MAAM,CAAC,KAAK;KACpB,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC,UAAU,EAAE,CAAC;AAC3B,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,MAA0E;IAE1E,MAAM,IAAI,GAAG,cAAc,CAAC;QAC1B,OAAO,EAAE,MAAM,CAAC,OAAO;QACvB,aAAa,EAAE,MAAM,CAAC,aAAa,IAAI,IAAI;QAC3C,UAAU,EAAE,MAAM,CAAC,UAAU;QAC7B,KAAK,EAAE,MAAM,CAAC,KAAK;QACnB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,KAAK,EAAE,MAAM,CAAC,KAAK;KACpB,CAAC,CAAC;IAEH,OAAO,IAAI,CAAC,YAAY,EAAE,CAAC;AAC7B,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,sBAAsB,CAAC,MAItC;IACC,OAAO,gBAAgB,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,MAAM,CAAC,QAA8D;QAC/E,SAAS,EAAE,MAAM,CAAC,SAAS;KAC5B,CAAC,CAAC;AACL,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,UAAU,qBAAqB,CAAC,MAIrC;IACC,OAAO,eAAe,CAAC;QACrB,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,QAAQ,EAAE,MAAM,CAAC,QAAQ;QACzB,GAAG,EAAE,MAAM,CAAC,GAAG;KAChB,CAAC,CAAC;AACL,CAAC;AAED,4DAA4D;AAC5D,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,76 @@
1
+ import type { I18nCoreConfig, LanguageOption } from "@better-i18n/i18n-core";
2
+ import type { ComponentProps } from "react";
3
+ import type { IntlProvider } from "use-intl";
4
+ /**
5
+ * Messages type (compatible with use-intl)
6
+ */
7
+ export type Messages = ComponentProps<typeof IntlProvider>["messages"];
8
+ /**
9
+ * Configuration for BetterI18nProvider
10
+ */
11
+ export interface BetterI18nProviderConfig extends Omit<I18nCoreConfig, "defaultLocale"> {
12
+ /**
13
+ * Current locale
14
+ */
15
+ locale: string;
16
+ /**
17
+ * Pre-loaded messages (for SSR hydration)
18
+ */
19
+ messages?: Messages;
20
+ /**
21
+ * Timezone for date/time formatting
22
+ * @default undefined (uses browser timezone)
23
+ */
24
+ timeZone?: string;
25
+ /**
26
+ * Current date/time (useful for SSR to prevent hydration mismatches)
27
+ */
28
+ now?: Date;
29
+ /**
30
+ * Callback when locale changes
31
+ */
32
+ onLocaleChange?: (locale: string) => void;
33
+ /**
34
+ * Error handler for missing translations
35
+ */
36
+ onError?: ComponentProps<typeof IntlProvider>["onError"];
37
+ }
38
+ /**
39
+ * Better i18n context value
40
+ */
41
+ export interface BetterI18nContextValue {
42
+ /**
43
+ * Current locale
44
+ */
45
+ locale: string;
46
+ /**
47
+ * Change the current locale
48
+ */
49
+ setLocale: (locale: string) => void;
50
+ /**
51
+ * Available languages with metadata
52
+ */
53
+ languages: LanguageOption[];
54
+ /**
55
+ * Whether languages are still loading
56
+ */
57
+ isLoadingLanguages: boolean;
58
+ /**
59
+ * Whether messages are still loading
60
+ */
61
+ isLoadingMessages: boolean;
62
+ /**
63
+ * Project identifier
64
+ */
65
+ project: string;
66
+ }
67
+ /**
68
+ * Server-side configuration for getMessages
69
+ */
70
+ export interface GetMessagesConfig extends I18nCoreConfig {
71
+ /**
72
+ * Locale to fetch messages for
73
+ */
74
+ locale: string;
75
+ }
76
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAC7E,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,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IAE1C;;OAEG;IACH,OAAO,CAAC,EAAE,cAAc,CAAC,OAAO,YAAY,CAAC,CAAC,SAAS,CAAC,CAAC;CAC1D;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;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;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAkB,SAAQ,cAAc;IACvD;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;CAChB"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
package/package.json ADDED
@@ -0,0 +1,66 @@
1
+ {
2
+ "name": "@better-i18n/use-intl",
3
+ "version": "0.1.0",
4
+ "description": "Better i18n integration for use-intl (React, TanStack Start)",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/better-i18n/better-i18n.git",
9
+ "directory": "packages/use-intl"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/better-i18n/better-i18n/issues"
13
+ },
14
+ "homepage": "https://github.com/better-i18n/better-i18n/tree/main/packages/use-intl",
15
+ "type": "module",
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "default": "./dist/index.js"
22
+ },
23
+ "./server": {
24
+ "types": "./dist/server.d.ts",
25
+ "default": "./dist/server.js"
26
+ },
27
+ "./package.json": "./package.json"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "package.json",
32
+ "README.md"
33
+ ],
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "keywords": [
38
+ "i18n",
39
+ "localization",
40
+ "internationalization",
41
+ "react",
42
+ "tanstack",
43
+ "use-intl",
44
+ "next-intl"
45
+ ],
46
+ "scripts": {
47
+ "build": "tsc",
48
+ "typecheck": "tsc --noEmit",
49
+ "clean": "rm -rf dist",
50
+ "prepublishOnly": "npm run clean && npm run build"
51
+ },
52
+ "dependencies": {
53
+ "@better-i18n/i18n-core": "workspace:*"
54
+ },
55
+ "peerDependencies": {
56
+ "react": ">=18.0.0",
57
+ "use-intl": ">=4.0.0"
58
+ },
59
+ "devDependencies": {
60
+ "@better-i18n/typescript-config": "workspace:*",
61
+ "@types/react": "^19.0.0",
62
+ "react": "^19.0.0",
63
+ "use-intl": "^4.7.0",
64
+ "typescript": "~5.9.2"
65
+ }
66
+ }