@mmstack/translate 20.5.10 → 20.5.12
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 +148 -26
- package/fesm2022/mmstack-translate.mjs +526 -79
- package/fesm2022/mmstack-translate.mjs.map +1 -1
- package/index.d.ts +385 -72
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { computed, inject, InjectionToken, LOCALE_ID, signal, isDevMode, effect, resource,
|
|
2
|
+
import { computed, inject, InjectionToken, LOCALE_ID, signal, untracked, isDevMode, effect, resource, Injectable, isSignal, input, Renderer2, ElementRef, afterRenderEffect, Directive, ChangeDetectorRef } from '@angular/core';
|
|
3
3
|
import { createIntlCache, createIntl } from '@formatjs/intl';
|
|
4
4
|
import { toSignal } from '@angular/core/rxjs-interop';
|
|
5
5
|
import { Router, ActivatedRoute } from '@angular/router';
|
|
@@ -132,9 +132,27 @@ function injectSupportedLocales() {
|
|
|
132
132
|
* the actual locale signal used to store the current locale string
|
|
133
133
|
*/
|
|
134
134
|
const STORE_LOCALE = signal('en-US', ...(ngDevMode ? [{ debugName: "STORE_LOCALE" }] : []));
|
|
135
|
+
/**
|
|
136
|
+
* @internal
|
|
137
|
+
* @deprecated will be removed when ng23 drops
|
|
138
|
+
*/
|
|
139
|
+
function readLocaleUnsafe() {
|
|
140
|
+
return STORE_LOCALE();
|
|
141
|
+
}
|
|
135
142
|
function injectLocaleInternal() {
|
|
136
143
|
return STORE_LOCALE;
|
|
137
144
|
}
|
|
145
|
+
function proxyToGlobalSignleton(src) {
|
|
146
|
+
const originalSet = src.set;
|
|
147
|
+
src.set = (next) => {
|
|
148
|
+
originalSet(next);
|
|
149
|
+
STORE_LOCALE.set(next);
|
|
150
|
+
};
|
|
151
|
+
src.update = (updater) => {
|
|
152
|
+
src.set(updater(untracked(src)));
|
|
153
|
+
};
|
|
154
|
+
return src;
|
|
155
|
+
}
|
|
138
156
|
function isDynamicConfig(cfg) {
|
|
139
157
|
return !!cfg && 'localeStorage' in cfg && !!cfg.localeStorage;
|
|
140
158
|
}
|
|
@@ -232,7 +250,7 @@ class TranslationStore {
|
|
|
232
250
|
messages: this.messages(),
|
|
233
251
|
}, this.cache), ...(ngDevMode ? [{ debugName: "intl" }] : []));
|
|
234
252
|
constructor() {
|
|
235
|
-
this.locale = initLocale(
|
|
253
|
+
this.locale = proxyToGlobalSignleton(initLocale(signal('en-US')));
|
|
236
254
|
const paramName = this.config?.localeParamName;
|
|
237
255
|
if (paramName) {
|
|
238
256
|
const param = pathParam(paramName);
|
|
@@ -382,6 +400,96 @@ function injectDynamicLocale() {
|
|
|
382
400
|
source.isLoading = store.dynamicLocaleLoader.isLoading;
|
|
383
401
|
return source;
|
|
384
402
|
}
|
|
403
|
+
/**
|
|
404
|
+
* Power-user escape hatch for adding translations imperatively (e.g. content
|
|
405
|
+
* loaded from a remote API after bootstrap). Returns a function that registers
|
|
406
|
+
* a flat per-locale map of keys under a given namespace
|
|
407
|
+
*
|
|
408
|
+
* Pair with {@link injectUnsafeT} to read the added keys without compile-time
|
|
409
|
+
* constraints.
|
|
410
|
+
*
|
|
411
|
+
* @example
|
|
412
|
+
* ```ts
|
|
413
|
+
* const addTranslations = injectAddTranslations();
|
|
414
|
+
* addTranslations('remote', {
|
|
415
|
+
* 'en-US': { greeting: 'Hi {name}' },
|
|
416
|
+
* 'sl-SI': { greeting: 'Zdravo {name}' },
|
|
417
|
+
* });
|
|
418
|
+
* ```
|
|
419
|
+
*/
|
|
420
|
+
function injectAddTranslations() {
|
|
421
|
+
const store = inject(TranslationStore);
|
|
422
|
+
const supportedLocales = injectIntlConfig()?.supportedLocales;
|
|
423
|
+
const supportedLocalesSet = supportedLocales
|
|
424
|
+
? new Set(supportedLocales)
|
|
425
|
+
: null;
|
|
426
|
+
const validate = supportedLocalesSet
|
|
427
|
+
? (translations) => {
|
|
428
|
+
const clean = {};
|
|
429
|
+
const invalidLocales = [];
|
|
430
|
+
for (const [locale, translation] of Object.entries(translations)) {
|
|
431
|
+
if (!supportedLocalesSet.has(locale)) {
|
|
432
|
+
invalidLocales.push(locale);
|
|
433
|
+
continue;
|
|
434
|
+
}
|
|
435
|
+
clean[locale] = translation;
|
|
436
|
+
}
|
|
437
|
+
if (isDevMode() && invalidLocales.length > 0)
|
|
438
|
+
console.warn(`[Translate] Attempted to add translations for unsupported locales: ${invalidLocales.join(', ')}. These translations were ignored. Supported locales are: ${(supportedLocales ?? []).join(', ')}.`);
|
|
439
|
+
return clean;
|
|
440
|
+
}
|
|
441
|
+
: (translations) => translations;
|
|
442
|
+
return (ns, translations) => {
|
|
443
|
+
store.register(ns, validate(translations));
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function equalLocale(a, b) {
|
|
448
|
+
return a.locale === b.locale;
|
|
449
|
+
}
|
|
450
|
+
function createFormatterProvider(formatterName, libraryDefaults, nonLocaleEqual) {
|
|
451
|
+
const token = new InjectionToken(`@mmstack/translate:format-${formatterName}-config`, {
|
|
452
|
+
factory: () => {
|
|
453
|
+
const loc = injectDynamicLocale();
|
|
454
|
+
return computed(() => ({
|
|
455
|
+
...libraryDefaults,
|
|
456
|
+
locale: loc(),
|
|
457
|
+
}), { equal: equalLocale });
|
|
458
|
+
},
|
|
459
|
+
});
|
|
460
|
+
const provider = (valueOrFn) => {
|
|
461
|
+
const fnProvider = typeof valueOrFn === 'function'
|
|
462
|
+
? valueOrFn
|
|
463
|
+
: () => valueOrFn;
|
|
464
|
+
return {
|
|
465
|
+
provide: token,
|
|
466
|
+
useFactory: () => {
|
|
467
|
+
const loc = injectDynamicLocale();
|
|
468
|
+
const providedDefaultsOrSignal = fnProvider();
|
|
469
|
+
if (isSignal(providedDefaultsOrSignal))
|
|
470
|
+
return computed(() => ({
|
|
471
|
+
...libraryDefaults,
|
|
472
|
+
...providedDefaultsOrSignal(),
|
|
473
|
+
locale: loc(),
|
|
474
|
+
}), {
|
|
475
|
+
equal: (a, b) => equalLocale(a, b) && nonLocaleEqual(a, b),
|
|
476
|
+
});
|
|
477
|
+
const defaults = {
|
|
478
|
+
...libraryDefaults,
|
|
479
|
+
...providedDefaultsOrSignal,
|
|
480
|
+
};
|
|
481
|
+
return computed(() => ({
|
|
482
|
+
...defaults,
|
|
483
|
+
locale: loc(),
|
|
484
|
+
}), {
|
|
485
|
+
equal: equalLocale,
|
|
486
|
+
});
|
|
487
|
+
},
|
|
488
|
+
};
|
|
489
|
+
};
|
|
490
|
+
const injectFn = () => inject(token);
|
|
491
|
+
return [provider, injectFn];
|
|
492
|
+
}
|
|
385
493
|
|
|
386
494
|
function unwrap(value) {
|
|
387
495
|
return isSignal(value) ? value() : value;
|
|
@@ -409,32 +517,68 @@ function validDateOrNull(date) {
|
|
|
409
517
|
}
|
|
410
518
|
const cache$4 = new Map();
|
|
411
519
|
function getFormatter$4(locale, format, timeZone) {
|
|
412
|
-
const cacheKey = `${locale}|${format}|${timeZone ?? ''}`;
|
|
520
|
+
const cacheKey = `${locale}|${typeof format === 'string' ? format : JSON.stringify(format)}|${timeZone ?? ''}`;
|
|
413
521
|
let formatter = cache$4.get(cacheKey);
|
|
414
522
|
if (!formatter) {
|
|
415
523
|
formatter = new Intl.DateTimeFormat(locale, {
|
|
416
|
-
...FORMAT_PRESETS[format],
|
|
524
|
+
...(typeof format === 'string' ? FORMAT_PRESETS[format] : format),
|
|
417
525
|
timeZone,
|
|
418
526
|
});
|
|
419
527
|
cache$4.set(cacheKey, formatter);
|
|
420
528
|
}
|
|
421
529
|
return formatter;
|
|
422
530
|
}
|
|
423
|
-
|
|
424
|
-
* Format a date using the current or provided locale & timezone
|
|
425
|
-
* By default it is reactive to the global dynamic locale, works best when wrapped in a computed() if you need to react to locale changes
|
|
426
|
-
*
|
|
427
|
-
* @param date - Date to format
|
|
428
|
-
* @param opt - Options for formatting
|
|
429
|
-
* @returns Formatted date string
|
|
430
|
-
*/
|
|
431
|
-
function formatDate(date, opt) {
|
|
531
|
+
function formatDate(date, optOrLocale) {
|
|
432
532
|
const validDate = validDateOrNull(unwrap(date));
|
|
433
533
|
if (validDate === null)
|
|
434
534
|
return '';
|
|
435
|
-
const
|
|
436
|
-
|
|
437
|
-
|
|
535
|
+
const unwrappedArgs = unwrap(optOrLocale);
|
|
536
|
+
let locale;
|
|
537
|
+
let format = 'medium';
|
|
538
|
+
let tz;
|
|
539
|
+
if (typeof unwrappedArgs === 'string') {
|
|
540
|
+
locale = unwrappedArgs;
|
|
541
|
+
}
|
|
542
|
+
else if (unwrappedArgs && typeof unwrappedArgs === 'object') {
|
|
543
|
+
locale = unwrappedArgs.locale ?? readLocaleUnsafe();
|
|
544
|
+
format = unwrappedArgs.format ?? 'medium';
|
|
545
|
+
tz = unwrappedArgs.tz;
|
|
546
|
+
}
|
|
547
|
+
else {
|
|
548
|
+
locale = readLocaleUnsafe();
|
|
549
|
+
}
|
|
550
|
+
return getFormatter$4(locale, format, tz).format(validDate);
|
|
551
|
+
}
|
|
552
|
+
const [provideFormatDateDefaults, injectFormatDateOptions] = createFormatterProvider('date', {
|
|
553
|
+
format: 'medium',
|
|
554
|
+
}, (a, b) => {
|
|
555
|
+
if (a.tz !== b.tz)
|
|
556
|
+
return false;
|
|
557
|
+
if (a.format === b.format)
|
|
558
|
+
return true;
|
|
559
|
+
return JSON.stringify(a.format) === JSON.stringify(b.format);
|
|
560
|
+
});
|
|
561
|
+
/**
|
|
562
|
+
* Inject a context-safe date formatting function tied to the current injector.
|
|
563
|
+
* Uses the libraries locale signal & provided default configuration to react to locale/config changes
|
|
564
|
+
* @example
|
|
565
|
+
* const formatDate = injectFormatDate();
|
|
566
|
+
* readonly displayDate = computed(() => formatDate(this.date()));
|
|
567
|
+
*/
|
|
568
|
+
function injectFormatDate() {
|
|
569
|
+
const defaults = injectFormatDateOptions();
|
|
570
|
+
return (date, optOrLocale) => {
|
|
571
|
+
if (!optOrLocale)
|
|
572
|
+
return formatDate(date, defaults());
|
|
573
|
+
const unwrapped = unwrap(optOrLocale);
|
|
574
|
+
const opt = typeof unwrapped === 'object'
|
|
575
|
+
? { ...defaults(), ...unwrapped }
|
|
576
|
+
: {
|
|
577
|
+
...defaults(),
|
|
578
|
+
locale: unwrapped,
|
|
579
|
+
};
|
|
580
|
+
return formatDate(date, opt);
|
|
581
|
+
};
|
|
438
582
|
}
|
|
439
583
|
|
|
440
584
|
const cache$3 = new Map();
|
|
@@ -452,21 +596,48 @@ function getFormatter$3(locale, type, style) {
|
|
|
452
596
|
}
|
|
453
597
|
/**
|
|
454
598
|
* Format a display name using the current or provided locale
|
|
455
|
-
* By default it is reactive to the global dynamic locale, works best when wrapped in a computed() if you need to react to locale changes
|
|
456
599
|
*
|
|
457
600
|
* @param value - The code to format
|
|
458
601
|
* @param type - The type of display name to format
|
|
459
602
|
* @param opt - Options for formatting
|
|
460
603
|
* @returns Formatted display name string
|
|
461
604
|
*/
|
|
462
|
-
function formatDisplayName(value, type,
|
|
463
|
-
const
|
|
464
|
-
if (!
|
|
605
|
+
function formatDisplayName(value, type, localeOrOpt) {
|
|
606
|
+
const unwrappedValue = unwrap(value);
|
|
607
|
+
if (!unwrappedValue?.trim())
|
|
465
608
|
return '';
|
|
466
609
|
const unwrappedType = unwrap(type);
|
|
467
|
-
const
|
|
468
|
-
const locale =
|
|
469
|
-
|
|
610
|
+
const unwrapped = unwrap(localeOrOpt);
|
|
611
|
+
const locale = typeof unwrapped === 'string'
|
|
612
|
+
? unwrapped
|
|
613
|
+
: (unwrapped?.locale ?? readLocaleUnsafe());
|
|
614
|
+
const opt = typeof unwrapped === 'object' ? unwrapped : undefined;
|
|
615
|
+
return (getFormatter$3(locale, unwrappedType, opt?.style ?? 'long').of(unwrappedValue) ?? '');
|
|
616
|
+
}
|
|
617
|
+
const [provideFormatDisplayNameDefaults, injectFormatDisplayNameDefaults] = createFormatterProvider('displayName', {
|
|
618
|
+
style: 'long',
|
|
619
|
+
}, (a, b) => a.style === b.style);
|
|
620
|
+
/**
|
|
621
|
+
* Inject a context-safe date formatting function tied to the current injector.
|
|
622
|
+
* Uses the libraries locale signal & provided default configuration to react to locale/config changes
|
|
623
|
+
* @example
|
|
624
|
+
* const formatDisplayName = injectFormatDisplayName();
|
|
625
|
+
* readonly region = computed(() => formatDisplayName('US', 'region'));
|
|
626
|
+
*/
|
|
627
|
+
function injectFormatDisplayName() {
|
|
628
|
+
const defaults = injectFormatDisplayNameDefaults();
|
|
629
|
+
return (value, type, localeOrOpt) => {
|
|
630
|
+
if (!localeOrOpt)
|
|
631
|
+
return formatDisplayName(value, type, defaults());
|
|
632
|
+
const unwrapped = unwrap(localeOrOpt);
|
|
633
|
+
const opt = typeof unwrapped === 'object'
|
|
634
|
+
? { ...defaults, ...unwrapped }
|
|
635
|
+
: {
|
|
636
|
+
...defaults,
|
|
637
|
+
locale: unwrapped,
|
|
638
|
+
};
|
|
639
|
+
return formatDisplayName(value, type, opt);
|
|
640
|
+
};
|
|
470
641
|
}
|
|
471
642
|
|
|
472
643
|
const cache$2 = new Map();
|
|
@@ -484,21 +655,52 @@ function getFormatter$2(locale, type, style) {
|
|
|
484
655
|
}
|
|
485
656
|
return formatter;
|
|
486
657
|
}
|
|
658
|
+
function formatList(value, optOrLocale) {
|
|
659
|
+
const unwrappedValue = unwrapList(value);
|
|
660
|
+
if (unwrappedValue.length === 0)
|
|
661
|
+
return '';
|
|
662
|
+
const unwrappedArgs = unwrap(optOrLocale);
|
|
663
|
+
let locale;
|
|
664
|
+
let type = 'conjunction';
|
|
665
|
+
let style = 'long';
|
|
666
|
+
if (typeof unwrappedArgs === 'string') {
|
|
667
|
+
locale = unwrappedArgs;
|
|
668
|
+
}
|
|
669
|
+
else if (unwrappedArgs && typeof unwrappedArgs === 'object') {
|
|
670
|
+
locale = unwrappedArgs.locale ?? readLocaleUnsafe();
|
|
671
|
+
type = unwrappedArgs.type ?? 'conjunction';
|
|
672
|
+
style = unwrappedArgs.style ?? 'long';
|
|
673
|
+
}
|
|
674
|
+
else {
|
|
675
|
+
locale = readLocaleUnsafe();
|
|
676
|
+
}
|
|
677
|
+
return getFormatter$2(locale, type, style).format(unwrappedValue);
|
|
678
|
+
}
|
|
679
|
+
const [provideFormatListDefaults, injectFormatListOptions] = createFormatterProvider('list', {
|
|
680
|
+
type: 'conjunction',
|
|
681
|
+
style: 'long',
|
|
682
|
+
}, (a, b) => a.type === b.type && a.style === b.style);
|
|
487
683
|
/**
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
* @returns Formatted list string
|
|
684
|
+
* Inject a context-safe list formatting function tied to the current injector.
|
|
685
|
+
* Uses the libraries locale signal & provided default configuration to react to locale/config changes
|
|
686
|
+
* @example
|
|
687
|
+
* const formatList = injectFormatList();
|
|
688
|
+
* readonly displayList = computed(() => formatList(this.items()));
|
|
494
689
|
*/
|
|
495
|
-
function
|
|
496
|
-
const
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
690
|
+
function injectFormatList() {
|
|
691
|
+
const defaults = injectFormatListOptions();
|
|
692
|
+
return (value, optOrLocale) => {
|
|
693
|
+
if (!optOrLocale)
|
|
694
|
+
return formatList(value, defaults());
|
|
695
|
+
const unwrapped = unwrap(optOrLocale);
|
|
696
|
+
const opt = typeof unwrapped === 'object'
|
|
697
|
+
? { ...defaults(), ...unwrapped }
|
|
698
|
+
: {
|
|
699
|
+
...defaults(),
|
|
700
|
+
locale: unwrapped,
|
|
701
|
+
};
|
|
702
|
+
return formatList(value, opt);
|
|
703
|
+
};
|
|
502
704
|
}
|
|
503
705
|
|
|
504
706
|
const cache$1 = new Map();
|
|
@@ -525,45 +727,151 @@ function getFormatter$1(locale, minFractionDigits, maxFractionDigits, useGroupin
|
|
|
525
727
|
}
|
|
526
728
|
return formatter;
|
|
527
729
|
}
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
* @param opt - Options for formatting
|
|
534
|
-
* @returns Formatted number string
|
|
535
|
-
*/
|
|
536
|
-
function formatNumber(value, opt) {
|
|
537
|
-
const unwrappedOpt = unwrap(opt);
|
|
538
|
-
const unwrappedNumber = unwrapValue(value, unwrappedOpt?.fallbackToZero);
|
|
730
|
+
function formatNumber(value, optOrLocale) {
|
|
731
|
+
const unwrappedArgs = unwrap(optOrLocale);
|
|
732
|
+
const isOpt = unwrappedArgs != null && typeof unwrappedArgs === 'object';
|
|
733
|
+
const fallbackToZero = isOpt ? unwrappedArgs.fallbackToZero : undefined;
|
|
734
|
+
const unwrappedNumber = unwrapValue(value, fallbackToZero);
|
|
539
735
|
if (unwrappedNumber === null)
|
|
540
736
|
return '';
|
|
541
|
-
|
|
542
|
-
|
|
737
|
+
let locale;
|
|
738
|
+
let notation;
|
|
739
|
+
let minFractionDigits;
|
|
740
|
+
let maxFractionDigits;
|
|
741
|
+
let useGrouping = true;
|
|
742
|
+
if (typeof unwrappedArgs === 'string') {
|
|
743
|
+
locale = unwrappedArgs;
|
|
744
|
+
}
|
|
745
|
+
else if (isOpt) {
|
|
746
|
+
locale = unwrappedArgs.locale ?? readLocaleUnsafe();
|
|
747
|
+
notation = unwrappedArgs.notation ?? 'standard';
|
|
748
|
+
minFractionDigits = unwrappedArgs.minFractionDigits;
|
|
749
|
+
maxFractionDigits = unwrappedArgs.maxFractionDigits;
|
|
750
|
+
useGrouping = unwrappedArgs.useGrouping ?? true;
|
|
751
|
+
}
|
|
752
|
+
else {
|
|
753
|
+
locale = readLocaleUnsafe();
|
|
754
|
+
notation = 'standard';
|
|
755
|
+
}
|
|
756
|
+
return getFormatter$1(locale, minFractionDigits, maxFractionDigits, useGrouping, notation).format(unwrappedNumber);
|
|
543
757
|
}
|
|
758
|
+
const [provideFormatNumberDefaults, injectFormatNumberOptions] = createFormatterProvider('number', {
|
|
759
|
+
notation: 'standard',
|
|
760
|
+
useGrouping: true,
|
|
761
|
+
}, (a, b) => a.notation === b.notation &&
|
|
762
|
+
a.minFractionDigits === b.minFractionDigits &&
|
|
763
|
+
a.maxFractionDigits === b.maxFractionDigits &&
|
|
764
|
+
a.useGrouping === b.useGrouping &&
|
|
765
|
+
a.fallbackToZero === b.fallbackToZero);
|
|
544
766
|
/**
|
|
545
|
-
*
|
|
546
|
-
*
|
|
547
|
-
*
|
|
548
|
-
*
|
|
549
|
-
*
|
|
550
|
-
* @returns Formatted percentage string
|
|
767
|
+
* Inject a context-safe number formatting function tied to the current injector.
|
|
768
|
+
* Uses the libraries locale signal & provided default configuration to react to locale/config changes
|
|
769
|
+
* @example
|
|
770
|
+
* const formatNumber = injectFormatNumber();
|
|
771
|
+
* readonly display = computed(() => formatNumber(this.value()));
|
|
551
772
|
*/
|
|
552
|
-
function
|
|
553
|
-
const
|
|
554
|
-
|
|
773
|
+
function injectFormatNumber() {
|
|
774
|
+
const defaults = injectFormatNumberOptions();
|
|
775
|
+
return (value, optOrLocale) => {
|
|
776
|
+
if (!optOrLocale)
|
|
777
|
+
return formatNumber(value, defaults());
|
|
778
|
+
const unwrapped = unwrap(optOrLocale);
|
|
779
|
+
const opt = typeof unwrapped === 'object'
|
|
780
|
+
? { ...defaults(), ...unwrapped }
|
|
781
|
+
: {
|
|
782
|
+
...defaults(),
|
|
783
|
+
locale: unwrapped,
|
|
784
|
+
};
|
|
785
|
+
return formatNumber(value, opt);
|
|
786
|
+
};
|
|
787
|
+
}
|
|
788
|
+
function formatPercent(value, optOrLocale) {
|
|
789
|
+
const unwrappedArgs = unwrap(optOrLocale);
|
|
790
|
+
const isOpt = unwrappedArgs != null && typeof unwrappedArgs === 'object';
|
|
791
|
+
const fallbackToZero = isOpt ? unwrappedArgs.fallbackToZero : undefined;
|
|
792
|
+
const unwrappedNumber = unwrapValue(value, fallbackToZero);
|
|
555
793
|
if (unwrappedNumber === null)
|
|
556
794
|
return '';
|
|
557
|
-
|
|
558
|
-
|
|
795
|
+
let locale;
|
|
796
|
+
let minFractionDigits;
|
|
797
|
+
let maxFractionDigits;
|
|
798
|
+
if (typeof unwrappedArgs === 'string') {
|
|
799
|
+
locale = unwrappedArgs;
|
|
800
|
+
}
|
|
801
|
+
else if (isOpt) {
|
|
802
|
+
locale = unwrappedArgs.locale ?? readLocaleUnsafe();
|
|
803
|
+
minFractionDigits = unwrappedArgs.minFractionDigits;
|
|
804
|
+
maxFractionDigits = unwrappedArgs.maxFractionDigits;
|
|
805
|
+
}
|
|
806
|
+
else {
|
|
807
|
+
locale = readLocaleUnsafe();
|
|
808
|
+
}
|
|
809
|
+
return getFormatter$1(locale, minFractionDigits, maxFractionDigits, undefined, undefined, undefined, undefined, 'percent').format(unwrappedNumber);
|
|
810
|
+
}
|
|
811
|
+
const [provideFormatPercentDefaults, injectFormatPercentOptions] = createFormatterProvider('percent', {}, (a, b) => a.minFractionDigits === b.minFractionDigits &&
|
|
812
|
+
a.maxFractionDigits === b.maxFractionDigits &&
|
|
813
|
+
a.fallbackToZero === b.fallbackToZero);
|
|
814
|
+
/**
|
|
815
|
+
* Inject a context-safe percent formatting function tied to the current injector.
|
|
816
|
+
* Uses the libraries locale signal & provided default configuration to react to locale/config changes
|
|
817
|
+
*/
|
|
818
|
+
function injectFormatPercent() {
|
|
819
|
+
const defaults = injectFormatPercentOptions();
|
|
820
|
+
return (value, optOrLocale) => {
|
|
821
|
+
if (!optOrLocale)
|
|
822
|
+
return formatPercent(value, defaults());
|
|
823
|
+
const unwrapped = unwrap(optOrLocale);
|
|
824
|
+
const opt = typeof unwrapped === 'object'
|
|
825
|
+
? { ...defaults(), ...unwrapped }
|
|
826
|
+
: {
|
|
827
|
+
...defaults(),
|
|
828
|
+
locale: unwrapped,
|
|
829
|
+
};
|
|
830
|
+
return formatPercent(value, opt);
|
|
831
|
+
};
|
|
559
832
|
}
|
|
560
|
-
function formatCurrency(value, currency,
|
|
561
|
-
const
|
|
562
|
-
const
|
|
833
|
+
function formatCurrency(value, currency, optOrLocale) {
|
|
834
|
+
const unwrappedArgs = unwrap(optOrLocale);
|
|
835
|
+
const isOpt = unwrappedArgs != null && typeof unwrappedArgs === 'object';
|
|
836
|
+
const fallbackToZero = isOpt ? unwrappedArgs.fallbackToZero : undefined;
|
|
837
|
+
const unwrappedValue = unwrapValue(value, fallbackToZero);
|
|
563
838
|
if (unwrappedValue === null)
|
|
564
839
|
return '';
|
|
565
|
-
|
|
566
|
-
|
|
840
|
+
let locale;
|
|
841
|
+
let display = 'symbol';
|
|
842
|
+
if (typeof unwrappedArgs === 'string') {
|
|
843
|
+
locale = unwrappedArgs;
|
|
844
|
+
}
|
|
845
|
+
else if (isOpt) {
|
|
846
|
+
locale = unwrappedArgs.locale ?? readLocaleUnsafe();
|
|
847
|
+
display = unwrappedArgs.display ?? 'symbol';
|
|
848
|
+
}
|
|
849
|
+
else {
|
|
850
|
+
locale = readLocaleUnsafe();
|
|
851
|
+
}
|
|
852
|
+
return getFormatter$1(locale, undefined, undefined, undefined, undefined, unwrap(currency), display, 'currency').format(unwrappedValue);
|
|
853
|
+
}
|
|
854
|
+
const [provideFormatCurrencyDefaults, injectFormatCurrencyOptions] = createFormatterProvider('currency', {
|
|
855
|
+
display: 'symbol',
|
|
856
|
+
}, (a, b) => a.display === b.display && a.fallbackToZero === b.fallbackToZero);
|
|
857
|
+
/**
|
|
858
|
+
* Inject a context-safe currency formatting function tied to the current injector.
|
|
859
|
+
* Uses the libraries locale signal & provided default configuration to react to locale/config changes
|
|
860
|
+
*/
|
|
861
|
+
function injectFormatCurrency() {
|
|
862
|
+
const defaults = injectFormatCurrencyOptions();
|
|
863
|
+
return (value, currency, optOrLocale) => {
|
|
864
|
+
if (!optOrLocale)
|
|
865
|
+
return formatCurrency(value, currency, defaults());
|
|
866
|
+
const unwrapped = unwrap(optOrLocale);
|
|
867
|
+
const opt = typeof unwrapped === 'object'
|
|
868
|
+
? { ...defaults(), ...unwrapped }
|
|
869
|
+
: {
|
|
870
|
+
...defaults(),
|
|
871
|
+
locale: unwrapped,
|
|
872
|
+
};
|
|
873
|
+
return formatCurrency(value, currency, opt);
|
|
874
|
+
};
|
|
567
875
|
}
|
|
568
876
|
|
|
569
877
|
const cache = new Map();
|
|
@@ -576,16 +884,7 @@ function getFormatter(locale, style, numeric) {
|
|
|
576
884
|
}
|
|
577
885
|
return formatter;
|
|
578
886
|
}
|
|
579
|
-
|
|
580
|
-
* Format a relative time using the current or provided locale
|
|
581
|
-
* By default it is reactive to the global dynamic locale, works best when wrapped in a computed() if you need to react to locale changes
|
|
582
|
-
*
|
|
583
|
-
* @param value - The numeric value to use in the relative time internationalization message
|
|
584
|
-
* @param unit - The unit to use in the relative time internationalization message
|
|
585
|
-
* @param opt - Options for formatting
|
|
586
|
-
* @returns Formatted relative time string
|
|
587
|
-
*/
|
|
588
|
-
function formatRelativeTime(value, unit, opt) {
|
|
887
|
+
function formatRelativeTime(value, unit, optOrLocale) {
|
|
589
888
|
const unwrappedValue = unwrap(value);
|
|
590
889
|
if (unwrappedValue === null ||
|
|
591
890
|
unwrappedValue === undefined ||
|
|
@@ -594,9 +893,78 @@ function formatRelativeTime(value, unit, opt) {
|
|
|
594
893
|
const unwrappedUnit = unwrap(unit);
|
|
595
894
|
if (!unwrappedUnit)
|
|
596
895
|
return '';
|
|
597
|
-
const
|
|
598
|
-
|
|
599
|
-
|
|
896
|
+
const unwrappedArgs = unwrap(optOrLocale);
|
|
897
|
+
let locale;
|
|
898
|
+
let style = 'long';
|
|
899
|
+
let numeric = 'always';
|
|
900
|
+
if (typeof unwrappedArgs === 'string') {
|
|
901
|
+
locale = unwrappedArgs;
|
|
902
|
+
}
|
|
903
|
+
else if (unwrappedArgs && typeof unwrappedArgs === 'object') {
|
|
904
|
+
locale = unwrappedArgs.locale ?? readLocaleUnsafe();
|
|
905
|
+
style = unwrappedArgs.style ?? 'long';
|
|
906
|
+
numeric = unwrappedArgs.numeric ?? 'always';
|
|
907
|
+
}
|
|
908
|
+
else {
|
|
909
|
+
locale = readLocaleUnsafe();
|
|
910
|
+
}
|
|
911
|
+
return getFormatter(locale, style, numeric).format(unwrappedValue, unwrappedUnit);
|
|
912
|
+
}
|
|
913
|
+
const [provideFormatRelativeTimeDefaults, injectFormatRelativeTimeOptions] = createFormatterProvider('relativeTime', {
|
|
914
|
+
style: 'long',
|
|
915
|
+
numeric: 'always',
|
|
916
|
+
}, (a, b) => a.style === b.style && a.numeric === b.numeric);
|
|
917
|
+
/**
|
|
918
|
+
* Inject a context-safe relative time formatting function tied to the current injector.
|
|
919
|
+
* Uses the libraries locale signal & provided default configuration to react to locale/config changes
|
|
920
|
+
* @example
|
|
921
|
+
* const formatRelativeTime = injectFormatRelativeTime();
|
|
922
|
+
* readonly relativeAge = computed(() => formatRelativeTime(this.delta(), 'day'));
|
|
923
|
+
*/
|
|
924
|
+
function injectFormatRelativeTime() {
|
|
925
|
+
const defaults = injectFormatRelativeTimeOptions();
|
|
926
|
+
return (value, unit, optOrLocale) => {
|
|
927
|
+
if (!optOrLocale)
|
|
928
|
+
return formatRelativeTime(value, unit, defaults());
|
|
929
|
+
const unwrapped = unwrap(optOrLocale);
|
|
930
|
+
const opt = typeof unwrapped === 'object'
|
|
931
|
+
? { ...defaults(), ...unwrapped }
|
|
932
|
+
: {
|
|
933
|
+
...defaults(),
|
|
934
|
+
locale: unwrapped,
|
|
935
|
+
};
|
|
936
|
+
return formatRelativeTime(value, unit, opt);
|
|
937
|
+
};
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
function provideFormatDefaults(cfg) {
|
|
941
|
+
const providers = [];
|
|
942
|
+
if (cfg.date)
|
|
943
|
+
providers.push(provideFormatDateDefaults(cfg.date));
|
|
944
|
+
if (cfg.displayName)
|
|
945
|
+
providers.push(provideFormatDisplayNameDefaults(cfg.displayName));
|
|
946
|
+
if (cfg.list)
|
|
947
|
+
providers.push(provideFormatListDefaults(cfg.list));
|
|
948
|
+
if (cfg.relativeTime)
|
|
949
|
+
providers.push(provideFormatRelativeTimeDefaults(cfg.relativeTime));
|
|
950
|
+
if (cfg.number)
|
|
951
|
+
providers.push(provideFormatNumberDefaults(cfg.number));
|
|
952
|
+
if (cfg.percent)
|
|
953
|
+
providers.push(provideFormatPercentDefaults(cfg.percent));
|
|
954
|
+
if (cfg.currency)
|
|
955
|
+
providers.push(provideFormatCurrencyDefaults(cfg.currency));
|
|
956
|
+
return providers;
|
|
957
|
+
}
|
|
958
|
+
function injectFormatters() {
|
|
959
|
+
return {
|
|
960
|
+
date: injectFormatDate(),
|
|
961
|
+
displayName: injectFormatDisplayName(),
|
|
962
|
+
list: injectFormatList(),
|
|
963
|
+
relativeTime: injectFormatRelativeTime(),
|
|
964
|
+
number: injectFormatNumber(),
|
|
965
|
+
percent: injectFormatPercent(),
|
|
966
|
+
currency: injectFormatCurrency(),
|
|
967
|
+
};
|
|
600
968
|
}
|
|
601
969
|
|
|
602
970
|
function injectResolveParamLocale(snapshot) {
|
|
@@ -809,6 +1177,58 @@ function registerRemoteNamespace(ns, defaultTranslation, other) {
|
|
|
809
1177
|
resolveNamespaceTranslation: resolver,
|
|
810
1178
|
};
|
|
811
1179
|
}
|
|
1180
|
+
class UnsafeTKeyMap {
|
|
1181
|
+
map = new Map();
|
|
1182
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: UnsafeTKeyMap, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
1183
|
+
static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: UnsafeTKeyMap, providedIn: 'root' });
|
|
1184
|
+
}
|
|
1185
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: UnsafeTKeyMap, decorators: [{
|
|
1186
|
+
type: Injectable,
|
|
1187
|
+
args: [{
|
|
1188
|
+
providedIn: 'root',
|
|
1189
|
+
}]
|
|
1190
|
+
}] });
|
|
1191
|
+
/**
|
|
1192
|
+
* Power-user escape hatch that returns a fully untyped translation function.
|
|
1193
|
+
* Intended for use alongside {@link injectAddTranslations} when translations
|
|
1194
|
+
* are added imperatively (e.g. from a remote API), or for cross-namespace
|
|
1195
|
+
* lookups where the typed API would be impractical.
|
|
1196
|
+
*
|
|
1197
|
+
* @example
|
|
1198
|
+
* ```ts
|
|
1199
|
+
* const t = injectUnsafeT();
|
|
1200
|
+
* t('any.namespace.key', { name: 'Alice', count: 3 });
|
|
1201
|
+
* const sig = t.asSignal('any.namespace.key', () => ({ name: name() }));
|
|
1202
|
+
* ```
|
|
1203
|
+
*/
|
|
1204
|
+
function injectUnsafeT() {
|
|
1205
|
+
const store = inject(TranslationStore);
|
|
1206
|
+
const map = inject(UnsafeTKeyMap).map;
|
|
1207
|
+
const fn = (key, params) => {
|
|
1208
|
+
let k = map.get(key);
|
|
1209
|
+
if (k === undefined) {
|
|
1210
|
+
k = replaceWithDelim(key);
|
|
1211
|
+
map.set(key, k);
|
|
1212
|
+
}
|
|
1213
|
+
return store.formatMessage(k, params);
|
|
1214
|
+
};
|
|
1215
|
+
fn.asSignal = (key, params) => {
|
|
1216
|
+
let k = map.get(key);
|
|
1217
|
+
if (k === undefined) {
|
|
1218
|
+
k = replaceWithDelim(key);
|
|
1219
|
+
map.set(key, k);
|
|
1220
|
+
}
|
|
1221
|
+
if (!params)
|
|
1222
|
+
return store.buildSimpleKeySignal(k);
|
|
1223
|
+
const paramsSignal = isSignal(params)
|
|
1224
|
+
? params
|
|
1225
|
+
: computed(() => params(), {
|
|
1226
|
+
equal: createEqualsRecord(Object.keys(params())),
|
|
1227
|
+
});
|
|
1228
|
+
return computed(() => store.formatMessage(k, paramsSignal()));
|
|
1229
|
+
};
|
|
1230
|
+
return fn;
|
|
1231
|
+
}
|
|
812
1232
|
|
|
813
1233
|
/**
|
|
814
1234
|
* Guard that validates the locale parameter against supported locales.
|
|
@@ -983,9 +1403,36 @@ class Translator {
|
|
|
983
1403
|
transform = createTransformFn();
|
|
984
1404
|
}
|
|
985
1405
|
|
|
1406
|
+
/**
|
|
1407
|
+
* Power-user escape hatch for ICU messages whose parameters can't be inferred
|
|
1408
|
+
* from the message string — typically variables nested inside `plural` /
|
|
1409
|
+
* `select` / `selectordinal` arms, which the type-level extractor skips.
|
|
1410
|
+
*
|
|
1411
|
+
* Declared params are merged with auto-extracted ones; on key conflict, the
|
|
1412
|
+
* declared params win. Non-default locales for a key wrapped with `withParams`
|
|
1413
|
+
* may be plain strings — they don't need to repeat the wrapper.
|
|
1414
|
+
*
|
|
1415
|
+
* @example
|
|
1416
|
+
* ```ts
|
|
1417
|
+
* const ns = createNamespace('quote', {
|
|
1418
|
+
* // auto-extracts `count`; `name` is declared explicitly because it
|
|
1419
|
+
* // lives inside the plural arms and can't be inferred
|
|
1420
|
+
* stats: withParams<{ name: string }>(
|
|
1421
|
+
* '{count, plural, one {1 quote from {name}} other {# quotes from {name}}}',
|
|
1422
|
+
* ),
|
|
1423
|
+
* });
|
|
1424
|
+
*
|
|
1425
|
+
* // t inferred as: (key, { count: number; name: string }) => string
|
|
1426
|
+
* t('quote.stats', { count: 3, name: 'Alice' });
|
|
1427
|
+
* ```
|
|
1428
|
+
*/
|
|
1429
|
+
function withParams(message) {
|
|
1430
|
+
return message;
|
|
1431
|
+
}
|
|
1432
|
+
|
|
986
1433
|
/**
|
|
987
1434
|
* Generated bundle index. Do not edit.
|
|
988
1435
|
*/
|
|
989
1436
|
|
|
990
|
-
export { Translate, Translator, canMatchLocale, compileTranslation, createNamespace, formatCurrency, formatDate, formatDisplayName, formatList, formatNumber, formatPercent, formatRelativeTime, injectDynamicLocale, injectIntl, injectResolveParamLocale, injectSupportedLocales, provideIntlConfig, provideMockTranslations, registerNamespace, registerRemoteNamespace };
|
|
1437
|
+
export { Translate, Translator, canMatchLocale, compileTranslation, createNamespace, formatCurrency, formatDate, formatDisplayName, formatList, formatNumber, formatPercent, formatRelativeTime, injectAddTranslations, injectDynamicLocale, injectFormatCurrency, injectFormatDate, injectFormatDisplayName, injectFormatList, injectFormatNumber, injectFormatPercent, injectFormatRelativeTime, injectFormatters, injectIntl, injectResolveParamLocale, injectSupportedLocales, injectUnsafeT, provideFormatCurrencyDefaults, provideFormatDateDefaults, provideFormatDefaults, provideFormatDisplayNameDefaults, provideFormatListDefaults, provideFormatNumberDefaults, provideFormatPercentDefaults, provideFormatRelativeTimeDefaults, provideIntlConfig, provideMockTranslations, registerNamespace, registerRemoteNamespace, withParams };
|
|
991
1438
|
//# sourceMappingURL=mmstack-translate.mjs.map
|