@intl-party/react 1.0.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/dist/index.mjs ADDED
@@ -0,0 +1,958 @@
1
+ // src/index.tsx
2
+ import React4 from "react";
3
+
4
+ // src/context/I18nContext.tsx
5
+ import {
6
+ createContext,
7
+ useContext,
8
+ useMemo,
9
+ useState,
10
+ useEffect
11
+ } from "react";
12
+ import {
13
+ createI18n
14
+ } from "@intl-party/core";
15
+ import { Fragment, jsx } from "react/jsx-runtime";
16
+ var I18nContext = createContext(null);
17
+ function I18nProvider({
18
+ children,
19
+ config,
20
+ i18n: externalI18n,
21
+ initialLocale,
22
+ initialNamespace,
23
+ loadingComponent,
24
+ fallbackComponent,
25
+ onLocaleChange,
26
+ onNamespaceChange,
27
+ onError
28
+ }) {
29
+ const i18nInstance = useMemo(() => {
30
+ if (externalI18n) {
31
+ return externalI18n;
32
+ }
33
+ if (!config) {
34
+ throw new Error(
35
+ "Either config or i18n instance must be provided to I18nProvider"
36
+ );
37
+ }
38
+ const instance = createI18n(config);
39
+ if (initialLocale && config.locales.includes(initialLocale)) {
40
+ instance.setLocale(initialLocale);
41
+ }
42
+ if (initialNamespace && config.namespaces.includes(initialNamespace)) {
43
+ instance.setNamespace(initialNamespace);
44
+ }
45
+ return instance;
46
+ }, [config, externalI18n, initialLocale, initialNamespace]);
47
+ const [locale, setLocaleState] = useState(i18nInstance.getLocale());
48
+ const [namespace, setNamespaceState] = useState(
49
+ i18nInstance.getNamespace()
50
+ );
51
+ const [isLoading, setIsLoading] = useState(false);
52
+ const [error, setError] = useState(null);
53
+ const handleLocaleChange = (newLocale) => {
54
+ try {
55
+ setIsLoading(true);
56
+ i18nInstance.setLocale(newLocale);
57
+ setLocaleState(newLocale);
58
+ onLocaleChange?.(newLocale);
59
+ } catch (err) {
60
+ const error2 = err instanceof Error ? err : new Error("Failed to change locale");
61
+ setError(error2);
62
+ onError?.(error2);
63
+ } finally {
64
+ setIsLoading(false);
65
+ }
66
+ };
67
+ const handleNamespaceChange = (newNamespace) => {
68
+ try {
69
+ i18nInstance.setNamespace(newNamespace);
70
+ setNamespaceState(newNamespace);
71
+ onNamespaceChange?.(newNamespace);
72
+ } catch (err) {
73
+ const error2 = err instanceof Error ? err : new Error("Failed to change namespace");
74
+ setError(error2);
75
+ onError?.(error2);
76
+ }
77
+ };
78
+ useEffect(() => {
79
+ const handleLocaleChangeEvent = ({
80
+ locale: newLocale
81
+ }) => {
82
+ setLocaleState(newLocale);
83
+ onLocaleChange?.(newLocale);
84
+ };
85
+ const handleNamespaceChangeEvent = ({
86
+ namespace: newNamespace
87
+ }) => {
88
+ setNamespaceState(newNamespace);
89
+ onNamespaceChange?.(newNamespace);
90
+ };
91
+ const handleTranslationsPreloading = () => setIsLoading(true);
92
+ const handleTranslationsPreloaded = () => setIsLoading(false);
93
+ i18nInstance.on("localeChange", handleLocaleChangeEvent);
94
+ i18nInstance.on("namespaceChange", handleNamespaceChangeEvent);
95
+ i18nInstance.on("translationsPreloading", handleTranslationsPreloading);
96
+ i18nInstance.on("translationsPreloaded", handleTranslationsPreloaded);
97
+ return () => {
98
+ i18nInstance.off("localeChange", handleLocaleChangeEvent);
99
+ i18nInstance.off("namespaceChange", handleNamespaceChangeEvent);
100
+ i18nInstance.off("translationsPreloading", handleTranslationsPreloading);
101
+ i18nInstance.off("translationsPreloaded", handleTranslationsPreloaded);
102
+ };
103
+ }, [i18nInstance, onLocaleChange, onNamespaceChange]);
104
+ const contextValue = useMemo(
105
+ () => ({
106
+ i18n: i18nInstance,
107
+ locale,
108
+ namespace,
109
+ t: i18nInstance.t,
110
+ setLocale: handleLocaleChange,
111
+ setNamespace: handleNamespaceChange,
112
+ isLoading
113
+ }),
114
+ [i18nInstance, locale, namespace, isLoading]
115
+ );
116
+ if (error && fallbackComponent) {
117
+ return /* @__PURE__ */ jsx(Fragment, { children: fallbackComponent });
118
+ }
119
+ if (error) {
120
+ throw error;
121
+ }
122
+ if (isLoading && loadingComponent) {
123
+ return /* @__PURE__ */ jsx(Fragment, { children: loadingComponent });
124
+ }
125
+ return /* @__PURE__ */ jsx(I18nContext.Provider, { value: contextValue, children });
126
+ }
127
+ function useI18nContext() {
128
+ const context = useContext(I18nContext);
129
+ if (!context) {
130
+ throw new Error("useI18nContext must be used within an I18nProvider");
131
+ }
132
+ return context;
133
+ }
134
+ var TypedI18nContext = createContext(null);
135
+ function TypedI18nProvider({
136
+ children,
137
+ ...props
138
+ }) {
139
+ return /* @__PURE__ */ jsx(I18nProvider, { ...props, children: /* @__PURE__ */ jsx(TypedI18nContextWrapper, { children }) });
140
+ }
141
+ function TypedI18nContextWrapper({
142
+ children
143
+ }) {
144
+ const { i18n, locale, namespace, setLocale, setNamespace, isLoading } = useI18nContext();
145
+ const typedContextValue = useMemo(
146
+ () => ({
147
+ i18n,
148
+ locale,
149
+ namespace,
150
+ t: i18n.t,
151
+ setLocale,
152
+ setNamespace,
153
+ isLoading
154
+ }),
155
+ [i18n, locale, namespace, setLocale, setNamespace, isLoading]
156
+ );
157
+ return /* @__PURE__ */ jsx(TypedI18nContext.Provider, { value: typedContextValue, children });
158
+ }
159
+ function useTypedI18nContext() {
160
+ const context = useContext(
161
+ TypedI18nContext
162
+ );
163
+ if (!context) {
164
+ throw new Error(
165
+ "useTypedI18nContext must be used within a TypedI18nProvider"
166
+ );
167
+ }
168
+ return context;
169
+ }
170
+ function withI18n(Component) {
171
+ const WrappedComponent = (props) => {
172
+ const i18nContext = useI18nContext();
173
+ return /* @__PURE__ */ jsx(Component, { ...props, i18n: i18nContext });
174
+ };
175
+ WrappedComponent.displayName = `withI18n(${Component.displayName || Component.name})`;
176
+ return WrappedComponent;
177
+ }
178
+ function ScopedI18nProvider({
179
+ children,
180
+ namespace,
181
+ locale
182
+ }) {
183
+ const parentContext = useI18nContext();
184
+ const scopedContextValue = useMemo(
185
+ () => ({
186
+ ...parentContext,
187
+ namespace,
188
+ locale: locale || parentContext.locale,
189
+ t: parentContext.i18n.createScopedTranslator(namespace)
190
+ }),
191
+ [parentContext, namespace, locale]
192
+ );
193
+ return /* @__PURE__ */ jsx(I18nContext.Provider, { value: scopedContextValue, children });
194
+ }
195
+
196
+ // src/hooks/useTranslations.ts
197
+ import { useCallback, useMemo as useMemo2 } from "react";
198
+ function useTranslations(namespace) {
199
+ const { i18n, namespace: currentNamespace } = useI18nContext();
200
+ const targetNamespace = namespace || currentNamespace;
201
+ return useCallback(
202
+ (key, options) => {
203
+ return i18n.t(key, { ...options, namespace: targetNamespace });
204
+ },
205
+ [i18n, targetNamespace]
206
+ );
207
+ }
208
+ function useTypedTranslations(namespace) {
209
+ const { i18n, namespace: currentNamespace } = useTypedI18nContext();
210
+ const targetNamespace = namespace || currentNamespace;
211
+ return useCallback(
212
+ (key, options) => {
213
+ return i18n.t(key, {
214
+ ...options,
215
+ namespace: targetNamespace
216
+ });
217
+ },
218
+ [i18n, targetNamespace]
219
+ );
220
+ }
221
+ var useT = useTranslations;
222
+ var useTypedT = useTypedTranslations;
223
+ function useScopedTranslations(namespace) {
224
+ const { i18n } = useI18nContext();
225
+ return useMemo2(() => {
226
+ return i18n.createScopedTranslator(namespace);
227
+ }, [i18n, namespace]);
228
+ }
229
+ function useMultipleTranslations(namespaces) {
230
+ const { i18n } = useI18nContext();
231
+ return useMemo2(() => {
232
+ const translators = {};
233
+ for (const namespace of namespaces) {
234
+ translators[namespace] = i18n.createScopedTranslator(namespace);
235
+ }
236
+ return translators;
237
+ }, [i18n, namespaces]);
238
+ }
239
+ function useOptionalTranslation(key, namespace, options) {
240
+ const { i18n, namespace: currentNamespace } = useI18nContext();
241
+ const targetNamespace = namespace || currentNamespace;
242
+ return useMemo2(() => {
243
+ if (i18n.hasTranslation(key, targetNamespace)) {
244
+ return i18n.t(key, { ...options, namespace: targetNamespace });
245
+ }
246
+ return void 0;
247
+ }, [i18n, key, targetNamespace, options]);
248
+ }
249
+ function useTranslationWithFallback(key, fallback, namespace, options) {
250
+ const { i18n, namespace: currentNamespace } = useI18nContext();
251
+ const targetNamespace = namespace || currentNamespace;
252
+ return useMemo2(() => {
253
+ return i18n.t(key, {
254
+ ...options,
255
+ namespace: targetNamespace,
256
+ fallback
257
+ });
258
+ }, [i18n, key, fallback, targetNamespace, options]);
259
+ }
260
+ function useHasTranslation() {
261
+ const { i18n, namespace: currentNamespace } = useI18nContext();
262
+ return useCallback(
263
+ (key, namespace) => {
264
+ const targetNamespace = namespace || currentNamespace;
265
+ return i18n.hasTranslation(key, targetNamespace);
266
+ },
267
+ [i18n, currentNamespace]
268
+ );
269
+ }
270
+ function useTranslationValue() {
271
+ const { i18n, namespace: currentNamespace } = useI18nContext();
272
+ return useCallback(
273
+ (key, namespace) => {
274
+ const targetNamespace = namespace || currentNamespace;
275
+ return i18n.getTranslation(key, targetNamespace);
276
+ },
277
+ [i18n, currentNamespace]
278
+ );
279
+ }
280
+ function useInterpolatedTranslation(key, variables, namespace) {
281
+ const t = useTranslations(namespace);
282
+ return useMemo2(() => {
283
+ return t(key, { interpolation: variables });
284
+ }, [t, key, variables]);
285
+ }
286
+ function usePluralization(key, count, namespace, additionalOptions) {
287
+ const t = useTranslations(namespace);
288
+ return useMemo2(() => {
289
+ return t(key, { ...additionalOptions, count });
290
+ }, [t, key, count, additionalOptions]);
291
+ }
292
+
293
+ // src/hooks/useLocale.ts
294
+ import { useCallback as useCallback2, useMemo as useMemo3 } from "react";
295
+ function useLocale() {
296
+ const { locale, setLocale } = useI18nContext();
297
+ return [locale, setLocale];
298
+ }
299
+ function useLocaleInfo() {
300
+ const { locale, i18n } = useI18nContext();
301
+ return useMemo3(() => {
302
+ const availableLocales = i18n.getAvailableLocales();
303
+ const fallbackChain = i18n.getFallbackChain(locale);
304
+ return {
305
+ current: locale,
306
+ available: availableLocales,
307
+ fallbackChain,
308
+ isRTL: isRTLLocale(locale),
309
+ direction: isRTLLocale(locale) ? "rtl" : "ltr"
310
+ };
311
+ }, [locale, i18n]);
312
+ }
313
+ function useLocaleSwitch() {
314
+ const { i18n, setLocale } = useI18nContext();
315
+ const switchLocale = useCallback2(
316
+ (locale) => {
317
+ const availableLocales = i18n.getAvailableLocales();
318
+ if (!availableLocales.includes(locale)) {
319
+ throw new Error(
320
+ `Locale "${locale}" is not available. Available locales: ${availableLocales.join(", ")}`
321
+ );
322
+ }
323
+ setLocale(locale);
324
+ },
325
+ [i18n, setLocale]
326
+ );
327
+ const isLocaleAvailable = useCallback2(
328
+ (locale) => {
329
+ return i18n.getAvailableLocales().includes(locale);
330
+ },
331
+ [i18n]
332
+ );
333
+ return {
334
+ switchLocale,
335
+ isLocaleAvailable,
336
+ availableLocales: i18n.getAvailableLocales()
337
+ };
338
+ }
339
+ function useBrowserLocale() {
340
+ const { i18n } = useI18nContext();
341
+ return useMemo3(() => {
342
+ if (typeof window === "undefined") return null;
343
+ const detected = i18n.detectLocale({
344
+ request: void 0,
345
+ url: window.location.href,
346
+ userAgent: navigator.userAgent
347
+ });
348
+ return {
349
+ detected,
350
+ browser: navigator.language,
351
+ supported: i18n.getAvailableLocales().includes(detected)
352
+ };
353
+ }, [i18n]);
354
+ }
355
+ function useLocalePreference() {
356
+ const { locale, setLocale, i18n } = useI18nContext();
357
+ const savePreference = useCallback2(
358
+ (newLocale) => {
359
+ if (typeof window !== "undefined") {
360
+ localStorage.setItem("intl-party-locale", newLocale);
361
+ }
362
+ setLocale(newLocale);
363
+ },
364
+ [setLocale]
365
+ );
366
+ const loadPreference = useCallback2(() => {
367
+ if (typeof window === "undefined") return null;
368
+ const saved = localStorage.getItem("intl-party-locale");
369
+ if (saved && i18n.getAvailableLocales().includes(saved)) {
370
+ return saved;
371
+ }
372
+ return null;
373
+ }, [i18n]);
374
+ const clearPreference = useCallback2(() => {
375
+ if (typeof window !== "undefined") {
376
+ localStorage.removeItem("intl-party-locale");
377
+ }
378
+ }, []);
379
+ return {
380
+ current: locale,
381
+ save: savePreference,
382
+ load: loadPreference,
383
+ clear: clearPreference
384
+ };
385
+ }
386
+ function isRTLLocale(locale) {
387
+ const rtlLocales = [
388
+ "ar",
389
+ "arc",
390
+ "ckb",
391
+ "dv",
392
+ "fa",
393
+ "ha",
394
+ "he",
395
+ "khw",
396
+ "ks",
397
+ "ku",
398
+ "ps",
399
+ "sd",
400
+ "ur",
401
+ "yi"
402
+ ];
403
+ const baseLocale = locale.split("-")[0];
404
+ return rtlLocales.includes(baseLocale);
405
+ }
406
+ function useDirection() {
407
+ const { locale } = useI18nContext();
408
+ return useMemo3(() => {
409
+ return isRTLLocale(locale) ? "rtl" : "ltr";
410
+ }, [locale]);
411
+ }
412
+ function useFormatting() {
413
+ const { i18n } = useI18nContext();
414
+ return useMemo3(
415
+ () => ({
416
+ formatDate: (date, options) => i18n.formatDate(date, options),
417
+ formatNumber: (number, options) => i18n.formatNumber(number, options),
418
+ formatCurrency: (amount, currency, options) => i18n.formatCurrency(amount, currency, options),
419
+ formatRelativeTime: (value, unit, options) => i18n.formatRelativeTime(value, unit, options)
420
+ }),
421
+ [i18n]
422
+ );
423
+ }
424
+
425
+ // src/hooks/useNamespace.ts
426
+ import { useCallback as useCallback3, useMemo as useMemo4 } from "react";
427
+ function useNamespace() {
428
+ const { namespace, setNamespace } = useI18nContext();
429
+ return [namespace, setNamespace];
430
+ }
431
+ function useNamespaceInfo() {
432
+ const { namespace, i18n } = useI18nContext();
433
+ return useMemo4(() => {
434
+ const availableNamespaces = i18n.getAvailableNamespaces();
435
+ return {
436
+ current: namespace,
437
+ available: availableNamespaces,
438
+ isAvailable: availableNamespaces.includes(namespace)
439
+ };
440
+ }, [namespace, i18n]);
441
+ }
442
+ function useNamespaceSwitch() {
443
+ const { i18n, setNamespace } = useI18nContext();
444
+ const switchNamespace = useCallback3(
445
+ (namespace) => {
446
+ const availableNamespaces = i18n.getAvailableNamespaces();
447
+ if (!availableNamespaces.includes(namespace)) {
448
+ throw new Error(
449
+ `Namespace "${namespace}" is not available. Available namespaces: ${availableNamespaces.join(", ")}`
450
+ );
451
+ }
452
+ setNamespace(namespace);
453
+ },
454
+ [i18n, setNamespace]
455
+ );
456
+ const isNamespaceAvailable = useCallback3(
457
+ (namespace) => {
458
+ return i18n.getAvailableNamespaces().includes(namespace);
459
+ },
460
+ [i18n]
461
+ );
462
+ return {
463
+ switchNamespace,
464
+ isNamespaceAvailable,
465
+ availableNamespaces: i18n.getAvailableNamespaces()
466
+ };
467
+ }
468
+ function useMultipleNamespaces(namespaces) {
469
+ const { i18n } = useI18nContext();
470
+ const translators = useMemo4(() => {
471
+ return namespaces.reduce(
472
+ (acc, ns) => {
473
+ acc[ns] = i18n.createScopedTranslator(ns);
474
+ return acc;
475
+ },
476
+ {}
477
+ );
478
+ }, [i18n, namespaces]);
479
+ const isAllAvailable = useMemo4(() => {
480
+ const available = i18n.getAvailableNamespaces();
481
+ return namespaces.every((ns) => available.includes(ns));
482
+ }, [i18n, namespaces]);
483
+ const getMissingNamespaces = useCallback3(() => {
484
+ const available = i18n.getAvailableNamespaces();
485
+ return namespaces.filter((ns) => !available.includes(ns));
486
+ }, [i18n, namespaces]);
487
+ return {
488
+ translators,
489
+ isAllAvailable,
490
+ getMissingNamespaces
491
+ };
492
+ }
493
+ function useNamespacePreloading() {
494
+ const { i18n, locale } = useI18nContext();
495
+ const preloadNamespace = useCallback3(
496
+ async (namespace) => {
497
+ await i18n.preloadTranslations(locale, namespace);
498
+ },
499
+ [i18n, locale]
500
+ );
501
+ const preloadNamespaces = useCallback3(
502
+ async (namespaces) => {
503
+ await i18n.preloadTranslations(locale, namespaces);
504
+ },
505
+ [i18n, locale]
506
+ );
507
+ return {
508
+ preloadNamespace,
509
+ preloadNamespaces
510
+ };
511
+ }
512
+
513
+ // src/components/Trans.tsx
514
+ import React2, { Fragment as Fragment2, useMemo as useMemo5 } from "react";
515
+ import { jsx as jsx2 } from "react/jsx-runtime";
516
+ function Trans({
517
+ i18nKey,
518
+ namespace,
519
+ values = {},
520
+ components = {},
521
+ count,
522
+ fallback,
523
+ children
524
+ }) {
525
+ const t = useTranslations(namespace);
526
+ const rendered = useMemo5(() => {
527
+ const options = {
528
+ interpolation: values,
529
+ count,
530
+ fallback
531
+ };
532
+ const translation = t(i18nKey, options);
533
+ if (Object.keys(components).length === 0) {
534
+ return translation;
535
+ }
536
+ return parseTranslationWithComponents(translation, components);
537
+ }, [t, i18nKey, values, components, count, fallback]);
538
+ if (typeof rendered === "string") {
539
+ return /* @__PURE__ */ jsx2(Fragment2, { children: rendered });
540
+ }
541
+ return /* @__PURE__ */ jsx2(Fragment2, { children: rendered });
542
+ }
543
+ function ConditionalTrans({
544
+ when = true,
545
+ fallbackComponent,
546
+ ...transProps
547
+ }) {
548
+ if (!when) {
549
+ return /* @__PURE__ */ jsx2(Fragment2, { children: fallbackComponent });
550
+ }
551
+ return /* @__PURE__ */ jsx2(Trans, { ...transProps });
552
+ }
553
+ function PluralTrans({
554
+ count,
555
+ zero,
556
+ one,
557
+ other,
558
+ i18nKey,
559
+ ...props
560
+ }) {
561
+ const selectedKey = useMemo5(() => {
562
+ if (count === 0 && zero) return zero;
563
+ if (count === 1 && one) return one;
564
+ if (other) return other;
565
+ return i18nKey;
566
+ }, [count, zero, one, other, i18nKey]);
567
+ return /* @__PURE__ */ jsx2(
568
+ Trans,
569
+ {
570
+ ...props,
571
+ i18nKey: selectedKey,
572
+ count,
573
+ values: { count, ...props.values }
574
+ }
575
+ );
576
+ }
577
+ function RichTrans({
578
+ allowedTags = ["strong", "em", "br", "span"],
579
+ sanitize = true,
580
+ ...transProps
581
+ }) {
582
+ const t = useTranslations(transProps.namespace);
583
+ const rendered = useMemo5(() => {
584
+ const translation = t(transProps.i18nKey, {
585
+ interpolation: transProps.values,
586
+ count: transProps.count,
587
+ fallback: transProps.fallback
588
+ });
589
+ if (sanitize) {
590
+ const sanitized = translation.replace(
591
+ /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi,
592
+ ""
593
+ );
594
+ return parseHTMLString(sanitized, allowedTags);
595
+ }
596
+ return parseHTMLString(translation, allowedTags);
597
+ }, [t, transProps, allowedTags, sanitize]);
598
+ return /* @__PURE__ */ jsx2(Fragment2, { children: rendered });
599
+ }
600
+ function parseTranslationWithComponents(text, components) {
601
+ const parts = [];
602
+ let currentIndex = 0;
603
+ const componentRegex = /<(\w+)>(.*?)<\/\1>/g;
604
+ let match;
605
+ while ((match = componentRegex.exec(text)) !== null) {
606
+ const [fullMatch, componentKey, content] = match;
607
+ const matchStart = match.index;
608
+ if (matchStart > currentIndex) {
609
+ parts.push(text.slice(currentIndex, matchStart));
610
+ }
611
+ if (components[componentKey]) {
612
+ if (React2.isValidElement(components[componentKey])) {
613
+ parts.push(
614
+ React2.cloneElement(
615
+ components[componentKey],
616
+ { key: parts.length },
617
+ content
618
+ )
619
+ );
620
+ } else {
621
+ parts.push(components[componentKey]);
622
+ }
623
+ } else {
624
+ parts.push(content);
625
+ }
626
+ currentIndex = matchStart + fullMatch.length;
627
+ }
628
+ if (currentIndex < text.length) {
629
+ parts.push(text.slice(currentIndex));
630
+ }
631
+ return parts.length > 0 ? parts : [text];
632
+ }
633
+ function parseHTMLString(html, allowedTags) {
634
+ if (!allowedTags.length) {
635
+ return html;
636
+ }
637
+ const tagRegex = new RegExp(
638
+ `<(/?)(${allowedTags.join("|")})(?:\\s[^>]*)?>`,
639
+ "gi"
640
+ );
641
+ const parts = html.split(tagRegex).filter(Boolean);
642
+ const elements = [];
643
+ let i = 0;
644
+ while (i < parts.length) {
645
+ const part = parts[i];
646
+ if (allowedTags.some((tag) => part.toLowerCase() === tag)) {
647
+ const tag = part.toLowerCase();
648
+ let content = "";
649
+ let depth = 1;
650
+ i++;
651
+ while (i < parts.length && depth > 0) {
652
+ const nextPart = parts[i];
653
+ if (nextPart === "/") {
654
+ i++;
655
+ if (i < parts.length && parts[i].toLowerCase() === tag) {
656
+ depth--;
657
+ i++;
658
+ }
659
+ } else if (allowedTags.some((t) => nextPart.toLowerCase() === t)) {
660
+ depth++;
661
+ content += `<${nextPart}>`;
662
+ i++;
663
+ } else {
664
+ content += nextPart;
665
+ i++;
666
+ }
667
+ }
668
+ elements.push(
669
+ React2.createElement(tag, { key: elements.length }, content)
670
+ );
671
+ } else {
672
+ elements.push(part);
673
+ i++;
674
+ }
675
+ }
676
+ return elements.length === 1 ? elements[0] : elements;
677
+ }
678
+
679
+ // src/components/LocaleSelector.tsx
680
+ import { useMemo as useMemo6 } from "react";
681
+ import { jsx as jsx3, jsxs } from "react/jsx-runtime";
682
+ function LocaleSelector({
683
+ className,
684
+ style,
685
+ placeholder = "Select language",
686
+ disabled = false,
687
+ showNativeNames = true,
688
+ filterLocales,
689
+ formatLocale,
690
+ onLocaleChange,
691
+ variant = "select"
692
+ }) {
693
+ const [currentLocale, setLocale] = useLocale();
694
+ const { available } = useLocaleInfo();
695
+ const filteredLocales = useMemo6(() => {
696
+ return filterLocales ? available.filter(filterLocales) : available;
697
+ }, [available, filterLocales]);
698
+ const handleLocaleChange = (locale) => {
699
+ setLocale(locale);
700
+ onLocaleChange?.(locale);
701
+ };
702
+ const formatLocaleDisplay = (locale) => {
703
+ if (formatLocale) {
704
+ return formatLocale(locale);
705
+ }
706
+ if (showNativeNames) {
707
+ try {
708
+ const intlLocale = new Intl.Locale(locale);
709
+ const displayNames = new Intl.DisplayNames([locale], {
710
+ type: "language"
711
+ });
712
+ return displayNames.of(intlLocale.language) || locale;
713
+ } catch {
714
+ return locale;
715
+ }
716
+ }
717
+ return locale;
718
+ };
719
+ if (variant === "select") {
720
+ return /* @__PURE__ */ jsxs(
721
+ "select",
722
+ {
723
+ className,
724
+ style,
725
+ value: currentLocale,
726
+ disabled,
727
+ onChange: (e) => handleLocaleChange(e.target.value),
728
+ children: [
729
+ placeholder && /* @__PURE__ */ jsx3("option", { value: "", disabled: true, children: placeholder }),
730
+ filteredLocales.map((locale) => /* @__PURE__ */ jsx3("option", { value: locale, children: formatLocaleDisplay(locale) }, locale))
731
+ ]
732
+ }
733
+ );
734
+ }
735
+ if (variant === "buttons") {
736
+ return /* @__PURE__ */ jsx3("div", { className, style, children: filteredLocales.map((locale) => /* @__PURE__ */ jsx3(
737
+ "button",
738
+ {
739
+ type: "button",
740
+ disabled,
741
+ onClick: () => handleLocaleChange(locale),
742
+ style: {
743
+ fontWeight: currentLocale === locale ? "bold" : "normal",
744
+ opacity: currentLocale === locale ? 1 : 0.7
745
+ },
746
+ children: formatLocaleDisplay(locale)
747
+ },
748
+ locale
749
+ )) });
750
+ }
751
+ return null;
752
+ }
753
+ function FlagLocaleSelector({
754
+ flagMap = {},
755
+ showFlags = true,
756
+ showLabels = true,
757
+ variant = "buttons",
758
+ ...props
759
+ }) {
760
+ const [currentLocale, setLocale] = useLocale();
761
+ const { available } = useLocaleInfo();
762
+ const defaultFlagMap = {
763
+ en: "\u{1F1FA}\u{1F1F8}",
764
+ es: "\u{1F1EA}\u{1F1F8}",
765
+ fr: "\u{1F1EB}\u{1F1F7}",
766
+ de: "\u{1F1E9}\u{1F1EA}",
767
+ it: "\u{1F1EE}\u{1F1F9}",
768
+ pt: "\u{1F1F5}\u{1F1F9}",
769
+ ru: "\u{1F1F7}\u{1F1FA}",
770
+ ja: "\u{1F1EF}\u{1F1F5}",
771
+ ko: "\u{1F1F0}\u{1F1F7}",
772
+ zh: "\u{1F1E8}\u{1F1F3}"
773
+ };
774
+ const combinedFlagMap = { ...defaultFlagMap, ...flagMap };
775
+ const formatLocaleWithFlag = (locale) => {
776
+ const parts = [];
777
+ if (showFlags && combinedFlagMap[locale]) {
778
+ parts.push(combinedFlagMap[locale]);
779
+ }
780
+ if (showLabels) {
781
+ if (props.formatLocale) {
782
+ parts.push(props.formatLocale(locale));
783
+ } else {
784
+ try {
785
+ const displayNames = new Intl.DisplayNames([locale], {
786
+ type: "language"
787
+ });
788
+ const intlLocale = new Intl.Locale(locale);
789
+ parts.push(displayNames.of(intlLocale.language) || locale);
790
+ } catch {
791
+ parts.push(locale);
792
+ }
793
+ }
794
+ }
795
+ return parts.join(" ");
796
+ };
797
+ return /* @__PURE__ */ jsx3(
798
+ LocaleSelector,
799
+ {
800
+ ...props,
801
+ variant,
802
+ formatLocale: formatLocaleWithFlag,
803
+ showNativeNames: false
804
+ }
805
+ );
806
+ }
807
+ function CompactLocaleSelector({
808
+ maxVisibleLocales = 3,
809
+ ...props
810
+ }) {
811
+ const { available } = useLocaleInfo();
812
+ if (available.length <= maxVisibleLocales) {
813
+ return /* @__PURE__ */ jsx3(LocaleSelector, { ...props, variant: "buttons" });
814
+ }
815
+ return /* @__PURE__ */ jsx3(LocaleSelector, { ...props, variant: "select" });
816
+ }
817
+ function AccessibleLocaleSelector({
818
+ ariaLabel = "Select language",
819
+ ariaDescribedBy,
820
+ ...props
821
+ }) {
822
+ const [currentLocale] = useLocale();
823
+ const enhancedProps = {
824
+ ...props,
825
+ style: {
826
+ ...props.style,
827
+ // Ensure minimum touch target size for accessibility
828
+ minHeight: "44px",
829
+ minWidth: "44px"
830
+ }
831
+ };
832
+ if (props.variant === "select") {
833
+ return /* @__PURE__ */ jsx3(
834
+ LocaleSelector,
835
+ {
836
+ ...enhancedProps,
837
+ className: `${props.className || ""} accessible-locale-selector`
838
+ }
839
+ );
840
+ }
841
+ return /* @__PURE__ */ jsxs(
842
+ "div",
843
+ {
844
+ role: "group",
845
+ "aria-label": ariaLabel,
846
+ "aria-describedby": ariaDescribedBy,
847
+ className: props.className,
848
+ style: props.style,
849
+ children: [
850
+ /* @__PURE__ */ jsx3(LocaleSelector, { ...enhancedProps }),
851
+ /* @__PURE__ */ jsxs("span", { className: "sr-only", children: [
852
+ "Current language: ",
853
+ currentLocale
854
+ ] })
855
+ ]
856
+ }
857
+ );
858
+ }
859
+
860
+ // src/index.tsx
861
+ import { jsx as jsx4, jsxs as jsxs2 } from "react/jsx-runtime";
862
+ var VERSION = "0.1.0";
863
+ function createI18nHook() {
864
+ return {
865
+ useTranslations: useTypedTranslations,
866
+ useT: useTypedTranslations
867
+ };
868
+ }
869
+ function createNamespaceHOC(namespace) {
870
+ return function withNamespace(Component) {
871
+ return function NamespacedComponent(props) {
872
+ return /* @__PURE__ */ jsx4(ScopedI18nProvider, { namespace, children: /* @__PURE__ */ jsx4(Component, { ...props }) });
873
+ };
874
+ };
875
+ }
876
+ var I18nErrorBoundary = class extends React4.Component {
877
+ constructor(props) {
878
+ super(props);
879
+ this.state = { hasError: false };
880
+ }
881
+ static getDerivedStateFromError(error) {
882
+ return { hasError: true, error };
883
+ }
884
+ componentDidCatch(error, errorInfo) {
885
+ this.props.onError?.(error, errorInfo);
886
+ }
887
+ render() {
888
+ if (this.state.hasError) {
889
+ const FallbackComponent = this.props.fallback;
890
+ if (FallbackComponent && this.state.error) {
891
+ return /* @__PURE__ */ jsx4(FallbackComponent, { error: this.state.error });
892
+ }
893
+ return /* @__PURE__ */ jsxs2(
894
+ "div",
895
+ {
896
+ style: {
897
+ padding: "1rem",
898
+ border: "1px solid red",
899
+ borderRadius: "4px"
900
+ },
901
+ children: [
902
+ /* @__PURE__ */ jsx4("h3", { children: "Translation Error" }),
903
+ /* @__PURE__ */ jsx4("p", { children: "Something went wrong with translations." }),
904
+ process.env.NODE_ENV === "development" && this.state.error && /* @__PURE__ */ jsxs2("details", { children: [
905
+ /* @__PURE__ */ jsx4("summary", { children: "Error details" }),
906
+ /* @__PURE__ */ jsx4("pre", { children: this.state.error.message })
907
+ ] })
908
+ ]
909
+ }
910
+ );
911
+ }
912
+ return this.props.children;
913
+ }
914
+ };
915
+ export {
916
+ AccessibleLocaleSelector,
917
+ CompactLocaleSelector,
918
+ ConditionalTrans,
919
+ FlagLocaleSelector,
920
+ I18nErrorBoundary,
921
+ I18nProvider,
922
+ LocaleSelector,
923
+ PluralTrans,
924
+ RichTrans,
925
+ ScopedI18nProvider,
926
+ Trans,
927
+ TypedI18nProvider,
928
+ VERSION,
929
+ createI18nHook,
930
+ createNamespaceHOC,
931
+ useBrowserLocale,
932
+ useDirection,
933
+ useFormatting,
934
+ useHasTranslation,
935
+ useI18nContext,
936
+ useInterpolatedTranslation,
937
+ useLocale,
938
+ useLocaleInfo,
939
+ useLocalePreference,
940
+ useLocaleSwitch,
941
+ useMultipleNamespaces,
942
+ useMultipleTranslations,
943
+ useNamespace,
944
+ useNamespaceInfo,
945
+ useNamespacePreloading,
946
+ useNamespaceSwitch,
947
+ useOptionalTranslation,
948
+ usePluralization,
949
+ useScopedTranslations,
950
+ useT,
951
+ useTranslationValue,
952
+ useTranslationWithFallback,
953
+ useTranslations,
954
+ useTypedI18nContext,
955
+ useTypedT,
956
+ useTypedTranslations,
957
+ withI18n
958
+ };