@dcl/hooks 1.0.1-20251223125619.commit-1446f8b → 1.0.1-20251229200811.commit-7c01c67

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 CHANGED
@@ -20,6 +20,7 @@ npm install @dcl/hooks
20
20
  - `useAsyncEffect`: Async version of useEffect
21
21
  - `useAsyncMemo`: Async version of useMemo
22
22
  - `useInfiniteScroll`: Infinite scroll functionality for loading more content
23
+ - `useTranslation`: Simple and lightweight translation management
23
24
 
24
25
  ## Examples
25
26
 
@@ -464,6 +465,219 @@ function App() {
464
465
  }
465
466
  ```
466
467
 
468
+ ### useTranslation
469
+
470
+ The `useTranslation` hook provides i18n capabilities powered by `@formatjs/intl`, giving you access to advanced formatting functions for numbers, dates, currencies, and more.
471
+
472
+ Basic usage:
473
+
474
+ ```typescript
475
+ import { useTranslation } from '@dcl/hooks'
476
+
477
+ const translations = {
478
+ en: {
479
+ greeting: 'Hello, {name}!',
480
+ welcome: 'Welcome to our app',
481
+ items: '{count, plural, =0 {No items} one {# item} other {# items}}'
482
+ },
483
+ es: {
484
+ greeting: 'Hola, {name}!',
485
+ welcome: 'Bienvenido a nuestra aplicación',
486
+ items: '{count, plural, =0 {Sin elementos} one {# elemento} other {# elementos}}'
487
+ }
488
+ }
489
+
490
+ function MyComponent() {
491
+ const { t, intl, locale, setLocale } = useTranslation({
492
+ locale: 'en',
493
+ translations
494
+ })
495
+
496
+ return (
497
+ <div>
498
+ <p>{t('greeting', { name: 'John' })}</p>
499
+ <p>{t('items', { count: 5 })}</p>
500
+ <button onClick={() => setLocale('es')}>
501
+ Switch to Spanish
502
+ </button>
503
+ </div>
504
+ )
505
+ }
506
+ ```
507
+
508
+ Using the `intl` object for advanced formatting:
509
+
510
+ ```typescript
511
+ function AdvancedFormattingExample() {
512
+ const { t, intl } = useTranslation({
513
+ locale: 'en',
514
+ translations: {
515
+ en: {
516
+ product_price: 'Price: {price}'
517
+ }
518
+ }
519
+ })
520
+
521
+ return (
522
+ <div>
523
+ {/* Format numbers */}
524
+ <p>Count: {intl.formatNumber(1000)}</p>
525
+
526
+ {/* Format dates */}
527
+ <p>Today: {intl.formatDate(new Date(), {
528
+ year: 'numeric',
529
+ month: 'long',
530
+ day: 'numeric'
531
+ })}</p>
532
+
533
+ {/* Format currency */}
534
+ <p>{intl.formatNumber(99.99, {
535
+ style: 'currency',
536
+ currency: 'USD'
537
+ })}</p>
538
+
539
+ {/* Format relative time */}
540
+ <p>{intl.formatRelativeTime(-1, 'day')}</p>
541
+
542
+ {/* Use formatMessage directly */}
543
+ <p>{intl.formatMessage({ id: 'product_price' }, { price: '$99' })}</p>
544
+ </div>
545
+ )
546
+ }
547
+ ```
548
+
549
+ With fallback locale:
550
+
551
+ ```typescript
552
+ const translations = {
553
+ en: {
554
+ greeting: 'Hello!',
555
+ welcome: 'Welcome!'
556
+ },
557
+ es: {
558
+ greeting: 'Hola!'
559
+ // 'welcome' is missing in Spanish
560
+ }
561
+ }
562
+
563
+ function MyComponent() {
564
+ const { t } = useTranslation({
565
+ locale: 'es',
566
+ translations,
567
+ fallbackLocale: 'en' // Will use English if translation is missing
568
+ })
569
+
570
+ return (
571
+ <div>
572
+ <p>{t('greeting')}</p> {/* Shows: "Hola!" */}
573
+ <p>{t('welcome')}</p> {/* Shows: "Welcome!" (from fallback) */}
574
+ </div>
575
+ )
576
+ }
577
+ ```
578
+
579
+ Using TranslationProvider for context-based translations:
580
+
581
+ ```typescript
582
+ import { TranslationProvider, useTranslation } from '@dcl/hooks'
583
+
584
+ const translations = {
585
+ en: {
586
+ greeting: 'Hello!',
587
+ welcome: 'Welcome to our app'
588
+ },
589
+ es: {
590
+ greeting: 'Hola!',
591
+ welcome: 'Bienvenido a nuestra aplicación'
592
+ }
593
+ }
594
+
595
+ function App() {
596
+ return (
597
+ <TranslationProvider
598
+ locale="en"
599
+ translations={translations}
600
+ fallbackLocale="en"
601
+ >
602
+ <MyComponent />
603
+ </TranslationProvider>
604
+ )
605
+ }
606
+
607
+ function MyComponent() {
608
+ // Can be used without options when inside TranslationProvider
609
+ const { t, locale, setLocale } = useTranslation()
610
+
611
+ return (
612
+ <div>
613
+ <p>{t('greeting')}</p>
614
+ <p>{t('welcome')}</p>
615
+ <button onClick={() => setLocale('es')}>
616
+ Switch to Spanish
617
+ </button>
618
+ </div>
619
+ )
620
+ }
621
+ ```
622
+
623
+ Using ICU Message Syntax:
624
+
625
+ ```typescript
626
+ const translations = {
627
+ en: {
628
+ // Pluralization
629
+ items: '{count, plural, =0 {No items} one {# item} other {# items}}',
630
+ // Select syntax
631
+ gender: '{gender, select, male {He} female {She} other {They}}',
632
+ // Complex ICU
633
+ notification: '{count, plural, =0 {No notifications} =1 {You have one notification} other {You have # notifications}}'
634
+ }
635
+ }
636
+
637
+ function MyComponent() {
638
+ const { t } = useTranslation({
639
+ locale: 'en',
640
+ translations
641
+ })
642
+
643
+ return (
644
+ <div>
645
+ <p>{t('items', { count: 0 })}</p> {/* "No items" */}
646
+ <p>{t('items', { count: 1 })}</p> {/* "1 item" */}
647
+ <p>{t('items', { count: 5 })}</p> {/* "5 items" */}
648
+ <p>{t('gender', { gender: 'male' })}</p> {/* "He" */}
649
+ <p>{t('notification', { count: 3 })}</p> {/* "You have 3 notifications" */}
650
+ </div>
651
+ )
652
+ }
653
+ ```
654
+
655
+ Additional intl formatting functions:
656
+
657
+ ```typescript
658
+ function MyComponent() {
659
+ const { intl } = useTranslation({
660
+ locale: 'en',
661
+ translations: { en: {} }
662
+ })
663
+
664
+ return (
665
+ <div>
666
+ {/* Format lists */}
667
+ <p>{intl.formatList(['apple', 'banana', 'orange'])}</p>
668
+ {/* "apple, banana, and orange" */}
669
+
670
+ {/* Format display names */}
671
+ <p>{intl.formatDisplayName('es', { type: 'language' })}</p>
672
+ {/* "Spanish" */}
673
+
674
+ <p>{intl.formatDisplayName('US', { type: 'region' })}</p>
675
+ {/* "United States" */}
676
+ </div>
677
+ )
678
+ }
679
+ ```
680
+
467
681
  ## License
468
682
 
469
683
  This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
@@ -1,4 +1,5 @@
1
- import React, { createContext, useMemo, useRef, useState } from "react";
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { createContext, useMemo, useRef, useState } from "react";
2
3
  import { isbot } from "isbot";
3
4
  import { useAsyncEffect } from "../../hooks/useAsyncEffect";
4
5
  const AnalyticsContext = createContext(null);
@@ -53,7 +54,7 @@ const AnalyticsProvider = (props) => {
53
54
  },
54
55
  };
55
56
  }, [isInitialized]);
56
- return (React.createElement(AnalyticsContext.Provider, { value: contextValue }, children));
57
+ return (_jsx(AnalyticsContext.Provider, { value: contextValue, children: children }));
57
58
  };
58
59
  export { AnalyticsContext, AnalyticsProvider };
59
60
  //# sourceMappingURL=AnalyticsProvider.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"AnalyticsProvider.js","sourceRoot":"","sources":["../../../src/contexts/analytics/AnalyticsProvider.tsx"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"AnalyticsProvider.js","sourceRoot":"","sources":["../../../src/contexts/analytics/AnalyticsProvider.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAEvE,OAAO,EAAE,KAAK,EAAE,MAAM,OAAO,CAAA;AAC7B,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAO3D,MAAM,gBAAgB,GAAG,aAAa,CAA8B,IAAI,CAAC,CAAA;AAEzE,MAAM,iBAAiB,GAAqC,CAC1D,KAA6B,EAC7B,EAAE;IACF,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAA;IACpD,MAAM,YAAY,GAAG,MAAM,CAA0B,IAAI,CAAC,CAAA;IAC1D,MAAM,CAAC,aAAa,EAAE,gBAAgB,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAEzD,cAAc,CAAC,KAAK,IAAI,EAAE;QACxB,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,CAAC,GAAG,CAAC,kCAAkC,CAAC,CAAA;YAC/C,OAAM;QACR,CAAC;QAED,MAAM,SAAS,GAAG,SAAS,CAAC,SAAS,CAAA;QACrC,MAAM,KAAK,GAAG,KAAK,CAAC,SAAS,CAAC,CAAA;QAE9B,IAAI,KAAK,EAAE,CAAC;YACV,OAAO,CAAC,GAAG,CAAC,yCAAyC,CAAC,CAAA;YACtD,OAAM;QACR,CAAC;QAED,IAAI,CAAC;YACH,gEAAgE;YAChE,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,yBAAyB,CAAC,CAAA;YACpE,YAAY,CAAC,OAAO,GAAG,gBAAgB,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAA;YAE1D,IAAI,MAAM,EAAE,CAAC;gBACX,YAAY,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;YAC/C,CAAC;YAED,gBAAgB,CAAC,IAAI,CAAC,CAAA;QACxB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,mCAAmC,EAAE,KAAK,CAAC,CAAA;YACzD,YAAY,CAAC,OAAO,GAAG,IAAI,CAAA;QAC7B,CAAC;IACH,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAE9B,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,EAAE;QAChC,IAAI,CAAC,YAAY,CAAC,OAAO,IAAI,CAAC,aAAa,EAAE,CAAC;YAC5C,OAAO;gBACL,aAAa,EAAE,KAAK;gBACpB,KAAK,EAAE,GAAG,EAAE,GAAE,CAAC;gBACf,QAAQ,EAAE,GAAG,EAAE,GAAE,CAAC;gBAClB,IAAI,EAAE,GAAG,EAAE,GAAE,CAAC;aACf,CAAA;QACH,CAAC;QAED,OAAO;YACL,aAAa,EAAE,IAAI;YACnB,KAAK,EAAE,CAAC,KAAa,EAAE,OAAsB,EAAE,EAAE;gBAC/C,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,KAAK,EAAE,OAAO,CAAC,CAAA;YAC7C,CAAC;YACD,QAAQ,EAAE,CAAC,EAAU,EAAE,MAAgC,EAAE,EAAE;gBACzD,YAAY,CAAC,OAAO,EAAE,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;YAC5C,CAAC;YACD,IAAI,EAAE,CAAC,IAAY,EAAE,KAA+B,EAAE,EAAE;gBACtD,YAAY,CAAC,OAAO,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;YACzC,CAAC;SACF,CAAA;IACH,CAAC,EAAE,CAAC,aAAa,CAAC,CAAC,CAAA;IAEnB,OAAO,CACL,KAAC,gBAAgB,CAAC,QAAQ,IAAC,KAAK,EAAE,YAAY,YAC3C,QAAQ,GACiB,CAC7B,CAAA;AACH,CAAC,CAAA;AAED,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,CAAA"}
@@ -1,2 +1,5 @@
1
1
  export * from "./analytics/AnalyticsProvider";
2
2
  export type * from "./analytics/types";
3
+ export * from "./translation/TranslationContext";
4
+ export * from "./translation/TranslationProvider";
5
+ export type * from "./translation/types";
@@ -1,2 +1,4 @@
1
1
  export * from "./analytics/AnalyticsProvider";
2
+ export * from "./translation/TranslationContext";
3
+ export * from "./translation/TranslationProvider";
2
4
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contexts/index.ts"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/contexts/index.ts"],"names":[],"mappings":"AAAA,cAAc,+BAA+B,CAAA;AAE7C,cAAc,kCAAkC,CAAA;AAChD,cAAc,mCAAmC,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { TranslationContextType } from "./types";
2
+ declare const TranslationContext: import("react").Context<TranslationContextType | null>;
3
+ export { TranslationContext };
@@ -0,0 +1,4 @@
1
+ import { createContext } from "react";
2
+ const TranslationContext = createContext(null);
3
+ export { TranslationContext };
4
+ //# sourceMappingURL=TranslationContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TranslationContext.js","sourceRoot":"","sources":["../../../src/contexts/translation/TranslationContext.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,OAAO,CAAA;AAGrC,MAAM,kBAAkB,GAAG,aAAa,CAAgC,IAAI,CAAC,CAAA;AAE7E,OAAO,EAAE,kBAAkB,EAAE,CAAA"}
@@ -0,0 +1,3 @@
1
+ import type { TranslationProviderProps } from "./types";
2
+ declare const TranslationProvider: <L extends string = string>(props: TranslationProviderProps<L>) => import("react/jsx-runtime").JSX.Element;
3
+ export { TranslationProvider };
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useMemo } from "react";
3
+ import { TranslationContext } from "./TranslationContext";
4
+ import { useTranslation } from "../../hooks/useTranslation";
5
+ const TranslationProvider = (props) => {
6
+ const { locale, translations, fallbackLocale, children } = props;
7
+ const translationResult = useTranslation({
8
+ locale,
9
+ translations,
10
+ fallbackLocale,
11
+ });
12
+ const contextValue = useMemo(() => translationResult, [translationResult]);
13
+ return (_jsx(TranslationContext.Provider, { value: contextValue, children: children }));
14
+ };
15
+ export { TranslationProvider };
16
+ //# sourceMappingURL=TranslationProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"TranslationProvider.js","sourceRoot":"","sources":["../../../src/contexts/translation/TranslationProvider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAA;AAC/B,OAAO,EAAE,kBAAkB,EAAE,MAAM,sBAAsB,CAAA;AACzD,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAG3D,MAAM,mBAAmB,GAAG,CAC1B,KAAkC,EAClC,EAAE;IACF,MAAM,EAAE,MAAM,EAAE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,GAAG,KAAK,CAAA;IAEhE,MAAM,iBAAiB,GAAG,cAAc,CAAC;QACvC,MAAM;QACN,YAAY;QACZ,cAAc;KACf,CAAC,CAAA;IAEF,MAAM,YAAY,GAAG,OAAO,CAC1B,GAAG,EAAE,CAAC,iBAAiB,EACvB,CAAC,iBAAiB,CAAC,CACpB,CAAA;IAED,OAAO,CACL,KAAC,kBAAkB,CAAC,QAAQ,IAAC,KAAK,EAAE,YAAY,YAC7C,QAAQ,GACmB,CAC/B,CAAA;AACH,CAAC,CAAA;AAED,OAAO,EAAE,mBAAmB,EAAE,CAAA"}
@@ -0,0 +1,16 @@
1
+ import type { ReactNode } from "react";
2
+ import type { LanguageTranslations } from "../../hooks/useTranslation";
3
+ type TranslationProviderProps<L extends string = string> = {
4
+ locale: L;
5
+ translations: LanguageTranslations;
6
+ fallbackLocale?: L;
7
+ children: ReactNode;
8
+ };
9
+ type TranslationContextType = {
10
+ t: (key: string, values?: Record<string, string | number>) => string;
11
+ intl: import("@formatjs/intl").IntlShape;
12
+ locale: string;
13
+ setLocale: (locale: string) => void;
14
+ error: string | null;
15
+ };
16
+ export type { TranslationProviderProps, TranslationContextType };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/contexts/translation/types.ts"],"names":[],"mappings":""}
@@ -2,6 +2,7 @@ export type { AdvancedNavigatorUAData } from "./useAdvancedUserAgentData";
2
2
  export type { AsyncMemoResult, AsyncMemoResultState } from "./useAsyncMemo";
3
3
  export type { AsyncStateResult, AsyncStateResultState } from "./useAsyncState";
4
4
  export type { UseInfiniteScrollOptions } from "./useInfiniteScroll";
5
+ export type { Translations, LanguageTranslations, TranslationState, TranslationOptions, TranslationResult, } from "./useTranslation";
5
6
  export { useAdvancedUserAgentData } from "./useAdvancedUserAgentData";
6
7
  export { useAnalytics } from "./useAnalytics";
7
8
  export { useAsyncEffect } from "./useAsyncEffect";
@@ -12,3 +13,4 @@ export { useAsyncTasks } from "./useAsyncTasks";
12
13
  export { useInfiniteScroll } from "./useInfiniteScroll";
13
14
  export { usePageTracking } from "./usePageTracking";
14
15
  export { usePatchState } from "./usePatchState";
16
+ export { useTranslation } from "./useTranslation";
@@ -9,4 +9,5 @@ export { useAsyncTasks } from "./useAsyncTasks";
9
9
  export { useInfiniteScroll } from "./useInfiniteScroll";
10
10
  export { usePageTracking } from "./usePageTracking";
11
11
  export { usePatchState } from "./usePatchState";
12
+ export { useTranslation } from "./useTranslation";
12
13
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAMA,QAAQ;AACR,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACnE,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/hooks/index.ts"],"names":[],"mappings":"AAaA,QAAQ;AACR,OAAO,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AACrE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACjD,OAAO,EAAE,YAAY,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAA;AACnE,OAAO,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAA;AACtE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AAC7C,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AACvD,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AAC/C,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA"}
@@ -0,0 +1,2 @@
1
+ export { useTranslation } from "./useTranslation";
2
+ export type { Translations, LanguageTranslations, TranslationState, TranslationOptions, TranslationResult, } from "./useTranslation.type";
@@ -0,0 +1,2 @@
1
+ export { useTranslation } from "./useTranslation";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/hooks/useTranslation/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAA"}
@@ -0,0 +1,77 @@
1
+ import { TranslationOptions, TranslationResult } from "./useTranslation.type";
2
+ /**
3
+ * Hook to manage translations in a React application using @formatjs/intl
4
+ *
5
+ * Can be used in two ways:
6
+ * 1. With options (standalone mode): Provides translations directly
7
+ * 2. Without options (context mode): Uses TranslationProvider context
8
+ *
9
+ * @param options - Optional configuration options for translations. If not provided, will use TranslationProvider context.
10
+ * @param options.locale - The initial locale to use (required in standalone mode)
11
+ * @param options.translations - An object containing all translations for all locales (required in standalone mode)
12
+ * @param options.fallbackLocale - Optional fallback locale if a translation is not found
13
+ *
14
+ * @returns An object with translation utilities including the full IntlShape instance
15
+ *
16
+ * @example
17
+ * Standalone mode:
18
+ * ```tsx
19
+ * const translations = {
20
+ * en: {
21
+ * "greeting": "Hello, {name}!",
22
+ * "welcome": "Welcome to our app"
23
+ * },
24
+ * es: {
25
+ * "greeting": "Hola, {name}!",
26
+ * "welcome": "Bienvenido a nuestra aplicación"
27
+ * }
28
+ * }
29
+ *
30
+ * function MyComponent() {
31
+ * const { t, intl, locale, setLocale } = useTranslation({
32
+ * locale: 'en',
33
+ * translations
34
+ * })
35
+ *
36
+ * return (
37
+ * <div>
38
+ * <p>{t('greeting', { name: 'John' })}</p>
39
+ * <p>{intl.formatNumber(1000)}</p>
40
+ * <p>{intl.formatDate(new Date())}</p>
41
+ * <button onClick={() => setLocale('es')}>Switch to Spanish</button>
42
+ * </div>
43
+ * )
44
+ * }
45
+ * ```
46
+ *
47
+ * @example
48
+ * Context mode:
49
+ * ```tsx
50
+ * function App() {
51
+ * return (
52
+ * <TranslationProvider
53
+ * locale="en"
54
+ * translations={{
55
+ * en: { greeting: "Hello!" },
56
+ * es: { greeting: "Hola!" }
57
+ * }}
58
+ * >
59
+ * <MyComponent />
60
+ * </TranslationProvider>
61
+ * )
62
+ * }
63
+ *
64
+ * function MyComponent() {
65
+ * const { t, locale, setLocale } = useTranslation()
66
+ *
67
+ * return (
68
+ * <div>
69
+ * <p>{t('greeting')}</p>
70
+ * <button onClick={() => setLocale('es')}>Switch to Spanish</button>
71
+ * </div>
72
+ * )
73
+ * }
74
+ * ```
75
+ */
76
+ declare const useTranslation: <L extends string = string>(options?: Partial<TranslationOptions<L>>) => TranslationResult;
77
+ export { useTranslation };
@@ -0,0 +1,161 @@
1
+ import { useCallback, useContext, useMemo, useState } from "react";
2
+ import { createIntl, createIntlCache } from "@formatjs/intl";
3
+ import { TranslationContext } from "../../contexts/translation/TranslationContext";
4
+ // Create a cache for @formatjs/intl to improve performance
5
+ const cache = createIntlCache();
6
+ /**
7
+ * Hook to manage translations in a React application using @formatjs/intl
8
+ *
9
+ * Can be used in two ways:
10
+ * 1. With options (standalone mode): Provides translations directly
11
+ * 2. Without options (context mode): Uses TranslationProvider context
12
+ *
13
+ * @param options - Optional configuration options for translations. If not provided, will use TranslationProvider context.
14
+ * @param options.locale - The initial locale to use (required in standalone mode)
15
+ * @param options.translations - An object containing all translations for all locales (required in standalone mode)
16
+ * @param options.fallbackLocale - Optional fallback locale if a translation is not found
17
+ *
18
+ * @returns An object with translation utilities including the full IntlShape instance
19
+ *
20
+ * @example
21
+ * Standalone mode:
22
+ * ```tsx
23
+ * const translations = {
24
+ * en: {
25
+ * "greeting": "Hello, {name}!",
26
+ * "welcome": "Welcome to our app"
27
+ * },
28
+ * es: {
29
+ * "greeting": "Hola, {name}!",
30
+ * "welcome": "Bienvenido a nuestra aplicación"
31
+ * }
32
+ * }
33
+ *
34
+ * function MyComponent() {
35
+ * const { t, intl, locale, setLocale } = useTranslation({
36
+ * locale: 'en',
37
+ * translations
38
+ * })
39
+ *
40
+ * return (
41
+ * <div>
42
+ * <p>{t('greeting', { name: 'John' })}</p>
43
+ * <p>{intl.formatNumber(1000)}</p>
44
+ * <p>{intl.formatDate(new Date())}</p>
45
+ * <button onClick={() => setLocale('es')}>Switch to Spanish</button>
46
+ * </div>
47
+ * )
48
+ * }
49
+ * ```
50
+ *
51
+ * @example
52
+ * Context mode:
53
+ * ```tsx
54
+ * function App() {
55
+ * return (
56
+ * <TranslationProvider
57
+ * locale="en"
58
+ * translations={{
59
+ * en: { greeting: "Hello!" },
60
+ * es: { greeting: "Hola!" }
61
+ * }}
62
+ * >
63
+ * <MyComponent />
64
+ * </TranslationProvider>
65
+ * )
66
+ * }
67
+ *
68
+ * function MyComponent() {
69
+ * const { t, locale, setLocale } = useTranslation()
70
+ *
71
+ * return (
72
+ * <div>
73
+ * <p>{t('greeting')}</p>
74
+ * <button onClick={() => setLocale('es')}>Switch to Spanish</button>
75
+ * </div>
76
+ * )
77
+ * }
78
+ * ```
79
+ */
80
+ const useTranslation = (options) => {
81
+ const context = useContext(TranslationContext);
82
+ if (!options && !context) {
83
+ throw new Error("useTranslation must be used with either options or within a TranslationProvider");
84
+ }
85
+ if (!options && context) {
86
+ return context;
87
+ }
88
+ if (options && !options.locale) {
89
+ throw new Error("locale is required when using useTranslation with options");
90
+ }
91
+ if (options && !options.translations) {
92
+ throw new Error("translations is required when using useTranslation with options");
93
+ }
94
+ const validatedOptions = options;
95
+ const [state, setState] = useState({
96
+ locale: validatedOptions.locale,
97
+ translations: validatedOptions.translations,
98
+ error: null,
99
+ });
100
+ // Create intl instance with current locale and translations
101
+ const intl = useMemo(() => {
102
+ const currentTranslations = state.translations[state.locale];
103
+ const fallbackTranslations = validatedOptions.fallbackLocale &&
104
+ state.translations[validatedOptions.fallbackLocale];
105
+ if (!currentTranslations && fallbackTranslations) {
106
+ console.error(`No translations found for locale "${state.locale}". Using fallback locale "${validatedOptions.fallbackLocale}".`);
107
+ return createIntl({
108
+ locale: state.locale,
109
+ messages: fallbackTranslations,
110
+ }, cache);
111
+ }
112
+ if (!currentTranslations) {
113
+ console.error(`No translations found for locale "${state.locale}". Using empty translations.`);
114
+ return createIntl({
115
+ locale: state.locale,
116
+ messages: {},
117
+ }, cache);
118
+ }
119
+ const shouldMergeFallback = validatedOptions.fallbackLocale &&
120
+ state.locale !== validatedOptions.fallbackLocale &&
121
+ fallbackTranslations;
122
+ const messages = shouldMergeFallback
123
+ ? {
124
+ ...fallbackTranslations,
125
+ ...currentTranslations,
126
+ }
127
+ : currentTranslations;
128
+ return createIntl({
129
+ locale: state.locale,
130
+ messages,
131
+ }, cache);
132
+ }, [state.locale, state.translations, validatedOptions.fallbackLocale]);
133
+ // Simplified t() function using intl.formatMessage
134
+ const t = useCallback((key, values) => {
135
+ return intl.formatMessage({ id: key }, values);
136
+ }, [intl]);
137
+ const setLocale = useCallback((newLocale) => {
138
+ if (!state.translations[newLocale]) {
139
+ console.error(`Locale "${newLocale}" not found in translations. Available locales: ${Object.keys(state.translations).join(", ")}`);
140
+ setState((current) => ({
141
+ ...current,
142
+ error: `Locale "${newLocale}" not found`,
143
+ }));
144
+ return;
145
+ }
146
+ setState((current) => ({
147
+ ...current,
148
+ locale: newLocale,
149
+ error: null,
150
+ }));
151
+ }, [state.translations]);
152
+ return {
153
+ t,
154
+ intl,
155
+ locale: state.locale,
156
+ setLocale,
157
+ error: state.error,
158
+ };
159
+ };
160
+ export { useTranslation };
161
+ //# sourceMappingURL=useTranslation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTranslation.js","sourceRoot":"","sources":["../../../src/hooks/useTranslation/useTranslation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { IntlShape } from "@formatjs/intl";
2
+ type Translations = Record<string, string>;
3
+ type LanguageTranslations = Record<string, Translations>;
4
+ type TranslationState<L extends string = string> = {
5
+ locale: L;
6
+ translations: LanguageTranslations;
7
+ error: string | null;
8
+ };
9
+ type TranslationOptions<L extends string = string> = {
10
+ locale: L;
11
+ translations: LanguageTranslations;
12
+ fallbackLocale?: L;
13
+ };
14
+ type TranslationResult = {
15
+ t: (key: string, values?: Record<string, string | number>) => string;
16
+ intl: IntlShape;
17
+ locale: string;
18
+ setLocale: (locale: string) => void;
19
+ error: string | null;
20
+ };
21
+ export type { LanguageTranslations, TranslationOptions, TranslationResult, TranslationState, Translations, };
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=useTranslation.type.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useTranslation.type.js","sourceRoot":"","sources":["../../../src/hooks/useTranslation/useTranslation.type.ts"],"names":[],"mappings":""}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dcl/hooks",
3
- "version": "1.0.1-20251223125619.commit-1446f8b",
3
+ "version": "1.0.1-20251229200811.commit-7c01c67",
4
4
  "type": "module",
5
5
  "main": "esm/index.js",
6
6
  "module": "esm/index.js",
@@ -48,6 +48,7 @@
48
48
  "typescript-eslint": "^7.15.0"
49
49
  },
50
50
  "dependencies": {
51
+ "@formatjs/intl": "^3.1.8",
51
52
  "@segment/analytics-next": "^1.79.0",
52
53
  "@sentry/browser": "^9.0.0",
53
54
  "isbot": "^5.1.25",
@@ -77,5 +78,5 @@
77
78
  "<rootDir>/test/setup.ts"
78
79
  ]
79
80
  },
80
- "commit": "1446f8b56b46bc5df904fb4755a45341bab801ff"
81
+ "commit": "7c01c67ea4f59e6cd131b191224dc86e24b568c5"
81
82
  }