@mmstack/translate 21.1.12 → 21.1.13

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
@@ -592,15 +592,7 @@ const valueThatNeedsProps = t('remote.myOtherKey', {
592
592
 
593
593
  ## Formatters
594
594
 
595
- The library includes a set of reactive formatters that automatically adapt to the current locale. They are standalone functions that do not require dependency injection, making them easy to use anywhere.
596
-
597
- **Note:** For reactivity, wrap them in a `computed()` if the input signals change or if you want them to react to dynamic locale changes.
598
-
599
- **SSR note:** Formatters read the active locale from a process-level signal. This is safe for all client-side usage and for SSR on serverless platforms (Lambda, Vercel, Netlify Edge) or worker-thread pools, where each request runs in an isolated V8 context. If you run a traditional single-process Node.js SSR server and concurrently render pages for **different** locales, pass the locale explicitly via the `locale` option on each formatter to avoid a potential cross-request read:
600
-
601
- ```typescript
602
- readonly displayDate = computed(() => formatDate(this.date, { locale: this.currentLocale() }));
603
- ```
595
+ The library includes a set of reactive formatters that wrap the standard `Intl.*` APIs and integrate with the dynamic locale signal.
604
596
 
605
597
  Available formatters:
606
598
 
@@ -612,24 +604,82 @@ Available formatters:
612
604
  - **`formatRelativeTime`**: Wraps `Intl.RelativeTimeFormat`
613
605
  - **`formatDisplayName`**: Wraps `Intl.DisplayNames`
614
606
 
615
- **Example:**
607
+ Each formatter ships in two flavors:
608
+
609
+ 1. **Standalone function** (`formatDate`, `formatList`, …) — pass `locale` explicitly, either as a string or via the options object. Three overloads per formatter: `(value, locale)`, `(value, opt)` with a required `locale` field, and a deprecated unsafe form that omits the locale (kept for backwards compatibility, see SSR note below).
610
+ 2. **`injectFormat*()` companion** (`injectFormatDate`, `injectFormatList`, …) — call inside an injection context to get a function that auto-resolves locale from `injectDynamicLocale()` and respects any defaults registered via `provideFormat*Defaults`. **This is the recommended path for components and services.**
611
+
612
+ You can grab them all at once via the `injectFormatters()` facade:
616
613
 
617
614
  ```typescript
618
615
  import { computed, signal } from '@angular/core';
619
- import { formatCurrency, formatDate } from '@mmstack/translate';
616
+ import { injectFormatters } from '@mmstack/translate';
620
617
 
621
618
  export class MyComponent {
619
+ private readonly fmt = injectFormatters();
620
+
622
621
  readonly price = signal(1234.56);
623
622
  readonly date = new Date();
624
623
 
625
- // Reacts to price changes OR locale changes
626
- readonly displayPrice = computed(() => formatCurrency(this.price(), 'EUR'));
627
-
628
- // Reacts to locale changes
629
- readonly displayDate = computed(() => formatDate(this.date));
624
+ // Reacts to price changes AND locale changes — no explicit locale needed
625
+ readonly displayPrice = computed(() => this.fmt.currency(this.price(), 'EUR'));
626
+ readonly displayDate = computed(() => this.fmt.date(this.date));
630
627
  }
631
628
  ```
632
629
 
630
+ If you'd rather call the standalone forms (e.g. outside an injection context), pass the locale explicitly:
631
+
632
+ ```typescript
633
+ import { computed } from '@angular/core';
634
+ import { formatCurrency, formatDate, injectDynamicLocale } from '@mmstack/translate';
635
+
636
+ const locale = injectDynamicLocale();
637
+
638
+ readonly displayPrice = computed(() => formatCurrency(this.price(), 'EUR', { locale: locale() }));
639
+ readonly displayDate = computed(() => formatDate(this.date, locale()));
640
+ ```
641
+
642
+ ### Provider defaults
643
+
644
+ Each formatter has a paired `provideFormat*Defaults` provider, and they can be registered together via `provideFormatDefaults`:
645
+
646
+ ```typescript
647
+ import { provideFormatDefaults } from '@mmstack/translate';
648
+
649
+ bootstrapApplication(AppComponent, {
650
+ providers: [
651
+ provideFormatDefaults({
652
+ date: { format: 'mediumDate' },
653
+ number: { useGrouping: true, maxFractionDigits: 2 },
654
+ currency: { display: 'code' },
655
+ relativeTime: { numeric: 'auto' },
656
+ list: { type: 'disjunction' },
657
+ percent: { maxFractionDigits: 1 },
658
+ displayName: { style: 'short' },
659
+ }),
660
+ ],
661
+ });
662
+ ```
663
+
664
+ The injected formatter functions (`injectFormat*()`) automatically merge these defaults with the dynamic locale.
665
+
666
+ ### SSR note
667
+
668
+ The deprecated unsafe overload (omitting `locale`) reads from a **process-level global signal**, which can cross-contaminate concurrent requests on a single-process Node.js SSR server rendering pages for different locales. The new overloads with required `locale` and the `injectFormat*()` companions are SSR-safe.
669
+
670
+ For SSR-bound code, prefer either:
671
+
672
+ ```typescript
673
+ // 1. Recommended — let the injected formatter handle locale
674
+ private readonly formatDate = injectFormatDate();
675
+ readonly displayDate = computed(() => this.formatDate(this.date));
676
+
677
+ // 2. Or pass locale explicitly to the standalone function
678
+ readonly displayDate = computed(() => formatDate(this.date, this.currentLocale()));
679
+ ```
680
+
681
+ The unsafe overload is kept for backwards compatibility and will be removed in a future release.
682
+
633
683
  ## Testing
634
684
 
635
685
  When testing components that use `@mmstack/translate` (via `injectNamespaceT`, `Translate` directive, or `Translator` pipe), you don't need to configure actual translated namespaces or deal with Intl loading. Instead, use the provided `provideMockTranslations()` utility.