@jsverse/transloco 7.5.1 → 7.6.1

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.
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, Injectable, Injector, Inject, Optional, Component, Input, TemplateRef, DestroyRef, ChangeDetectorRef, ElementRef, ViewContainerRef, Renderer2, Directive, Pipe, NgModule, makeEnvironmentProviders, APP_INITIALIZER } from '@angular/core';
2
+ import { InjectionToken, inject, Injectable, Injector, Inject, DestroyRef, Optional, Component, Input, TemplateRef, ChangeDetectorRef, ElementRef, ViewContainerRef, Renderer2, Directive, Pipe, NgModule, makeEnvironmentProviders, APP_INITIALIZER, assertInInjectionContext, runInInjectionContext, isSignal, computed } from '@angular/core';
3
3
  import { of, take, from, map, Subject, BehaviorSubject, forkJoin, retry, tap, catchError, shareReplay, switchMap, combineLatest, EMPTY } from 'rxjs';
4
- import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
+ import { takeUntilDestroyed, toObservable, toSignal } from '@angular/core/rxjs-interop';
5
5
 
6
6
  class DefaultLoader {
7
7
  translations;
@@ -12,7 +12,7 @@ class DefaultLoader {
12
12
  return of(this.translations.get(lang) || {});
13
13
  }
14
14
  }
15
- const TRANSLOCO_LOADER = new InjectionToken('TRANSLOCO_LOADER');
15
+ const TRANSLOCO_LOADER = new InjectionToken(ngDevMode ? 'TRANSLOCO_LOADER' : '');
16
16
 
17
17
  function getValue(obj, path) {
18
18
  if (!obj) {
@@ -143,7 +143,7 @@ function unflatten(obj) {
143
143
  return result;
144
144
  }
145
145
 
146
- const TRANSLOCO_CONFIG = new InjectionToken('TRANSLOCO_CONFIG', {
146
+ const TRANSLOCO_CONFIG = new InjectionToken(ngDevMode ? 'TRANSLOCO_CONFIG' : '', {
147
147
  providedIn: 'root',
148
148
  factory: () => defaultConfig,
149
149
  });
@@ -186,7 +186,7 @@ function translocoConfig(config = {}) {
186
186
  };
187
187
  }
188
188
 
189
- const TRANSLOCO_TRANSPILER = new InjectionToken('TRANSLOCO_TRANSPILER');
189
+ const TRANSLOCO_TRANSPILER = new InjectionToken(ngDevMode ? 'TRANSLOCO_TRANSPILER' : '');
190
190
  class DefaultTranspiler {
191
191
  config = inject(TRANSLOCO_CONFIG, { optional: true }) ?? defaultConfig;
192
192
  get interpolationMatcher() {
@@ -330,7 +330,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImpor
330
330
  type: Injectable
331
331
  }] });
332
332
 
333
- const TRANSLOCO_MISSING_HANDLER = new InjectionToken('TRANSLOCO_MISSING_HANDLER');
333
+ const TRANSLOCO_MISSING_HANDLER = new InjectionToken(ngDevMode ? 'TRANSLOCO_MISSING_HANDLER' : '');
334
334
  class DefaultMissingHandler {
335
335
  handle(key, config) {
336
336
  if (config.missingHandler.logMissingKey && !config.prodMode) {
@@ -346,7 +346,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImpor
346
346
  type: Injectable
347
347
  }] });
348
348
 
349
- const TRANSLOCO_INTERCEPTOR = new InjectionToken('TRANSLOCO_INTERCEPTOR');
349
+ const TRANSLOCO_INTERCEPTOR = new InjectionToken(ngDevMode ? 'TRANSLOCO_INTERCEPTOR' : '');
350
350
  class DefaultInterceptor {
351
351
  preSaveTranslation(translation) {
352
352
  return translation;
@@ -361,7 +361,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "18.2.9", ngImpor
361
361
  type: Injectable
362
362
  }] });
363
363
 
364
- const TRANSLOCO_FALLBACK_STRATEGY = new InjectionToken('TRANSLOCO_FALLBACK_STRATEGY');
364
+ const TRANSLOCO_FALLBACK_STRATEGY = new InjectionToken(ngDevMode ? 'TRANSLOCO_FALLBACK_STRATEGY' : '');
365
365
  class DefaultFallbackStrategy {
366
366
  userConfig;
367
367
  constructor(userConfig) {
@@ -503,6 +503,7 @@ class TranslocoService {
503
503
  events = new Subject();
504
504
  events$ = this.events.asObservable();
505
505
  config;
506
+ destroyRef = inject(DestroyRef);
506
507
  constructor(loader, parser, missingHandler, interceptor, userConfig, fallbackStrategy) {
507
508
  this.loader = loader;
508
509
  this.parser = parser;
@@ -524,11 +525,21 @@ class TranslocoService {
524
525
  /**
525
526
  * When we have a failure, we want to define the next language that succeeded as the active
526
527
  */
527
- this.events$.pipe(takeUntilDestroyed()).subscribe((e) => {
528
+ this.events$.subscribe((e) => {
528
529
  if (e.type === 'translationLoadSuccess' && e.wasFailure) {
529
530
  this.setActiveLang(e.payload.langName);
530
531
  }
531
532
  });
533
+ this.destroyRef.onDestroy(() => {
534
+ // Complete subjects to release observers if users forget to unsubscribe manually.
535
+ // This is important in server-side rendering.
536
+ this.lang.complete();
537
+ this.events.complete();
538
+ // As a root provider, this service is destroyed with when the application is destroyed.
539
+ // Cached values retain `this`, causing circular references that block garbage collection,
540
+ // leading to memory leaks during server-side rendering.
541
+ this.cache.clear();
542
+ });
532
543
  }
533
544
  getDefaultLang() {
534
545
  return this.defaultLang;
@@ -610,7 +621,7 @@ class TranslocoService {
610
621
  console.error(`Error while trying to load "${path}"`, error);
611
622
  }
612
623
  return this.handleFailure(path, options);
613
- }), shareReplay(1));
624
+ }), shareReplay(1), takeUntilDestroyed(this.destroyRef));
614
625
  this.cache.set(path, load$);
615
626
  return load$;
616
627
  }
@@ -892,13 +903,6 @@ class TranslocoService {
892
903
  }
893
904
  this.config.scopeMapping[scope] = alias;
894
905
  }
895
- ngOnDestroy() {
896
- // Caretaker note: since this is the root provider, it'll be destroyed when the `NgModuleRef.destroy()` is run.
897
- // Cached values capture `this`, thus leading to a circular reference and preventing the `TranslocoService` from
898
- // being GC'd. This would lead to a memory leak when server-side rendering is used since the service is created
899
- // and destroyed per each HTTP request, but any service is not getting GC'd.
900
- this.cache.clear();
901
- }
902
906
  isLoadedTranslation(lang) {
903
907
  return size(this.getTranslation(lang));
904
908
  }
@@ -1085,11 +1089,11 @@ class TemplateHandler {
1085
1089
  }
1086
1090
  }
1087
1091
 
1088
- const TRANSLOCO_LANG = new InjectionToken('TRANSLOCO_LANG');
1092
+ const TRANSLOCO_LANG = new InjectionToken(ngDevMode ? 'TRANSLOCO_LANG' : '');
1089
1093
 
1090
- const TRANSLOCO_LOADING_TEMPLATE = new InjectionToken('TRANSLOCO_LOADING_TEMPLATE');
1094
+ const TRANSLOCO_LOADING_TEMPLATE = new InjectionToken(ngDevMode ? 'TRANSLOCO_LOADING_TEMPLATE' : '');
1091
1095
 
1092
- const TRANSLOCO_SCOPE = new InjectionToken('TRANSLOCO_SCOPE');
1096
+ const TRANSLOCO_SCOPE = new InjectionToken(ngDevMode ? 'TRANSLOCO_SCOPE' : '');
1093
1097
 
1094
1098
  class LangResolver {
1095
1099
  initialized = false;
@@ -1604,9 +1608,103 @@ function getBrowserCultureLang() {
1604
1608
  return navigator.languages?.[0] ?? navigator.language;
1605
1609
  }
1606
1610
 
1611
+ /**
1612
+ * Gets the translated value of a key as Signal
1613
+ *
1614
+ * @example
1615
+ * text = translateSignal('hello');
1616
+ * textList = translateSignal(['green', 'blue']);
1617
+ * textVar = translateSignal('hello', { variable: 'world' });
1618
+ * textSpanish = translateSignal('hello', { variable: 'world' }, 'es');
1619
+ * textTodosScope = translateSignal('hello', { variable: 'world' }, { scope: 'todos' });
1620
+ *
1621
+ * @example
1622
+ * dynamicKey = signal('hello');
1623
+ * dynamicParam = signal({ variable: 'world' });
1624
+ * text = translateSignal(this.dynamicKey, this.dynamicParam);
1625
+ *
1626
+ */
1627
+ function translateSignal(key, params, lang, injector) {
1628
+ if (!injector) {
1629
+ assertInInjectionContext(translateSignal);
1630
+ }
1631
+ injector ??= inject(Injector);
1632
+ const result = runInInjectionContext(injector, () => {
1633
+ const service = inject(TranslocoService);
1634
+ const scope = resolveScope(lang);
1635
+ return toObservable(computerKeysAndParams(key, params)).pipe(switchMap((dynamic) => service.selectTranslate(dynamic.key, dynamic.params, scope)));
1636
+ });
1637
+ return toSignal(result, { initialValue: Array.isArray(key) ? [''] : '' });
1638
+ }
1639
+ /**
1640
+ * Gets the translated object of a key as Signal
1641
+ *
1642
+ * @example
1643
+ * object = translateObjectSignal('nested.object');
1644
+ * title = object().title;
1645
+ *
1646
+ * @example
1647
+ * dynamicKey = signal('nested.object');
1648
+ * dynamicParam = signal({ variable: 'world' });
1649
+ * object = translateObjectSignal(this.dynamicKey, this.dynamicParam);
1650
+ */
1651
+ function translateObjectSignal(key, params, lang, injector) {
1652
+ if (!injector) {
1653
+ assertInInjectionContext(translateObjectSignal);
1654
+ }
1655
+ injector ??= inject(Injector);
1656
+ const result = runInInjectionContext(injector, () => {
1657
+ const service = inject(TranslocoService);
1658
+ const scope = resolveScope(lang);
1659
+ return toObservable(computerKeysAndParams(key, params)).pipe(switchMap((dynamic) => service.selectTranslateObject(dynamic.key, dynamic.params, scope)));
1660
+ });
1661
+ return toSignal(result, { initialValue: Array.isArray(key) ? [] : {} });
1662
+ }
1663
+ function computerParams(params) {
1664
+ if (isSignal(params)) {
1665
+ return computed(() => params());
1666
+ }
1667
+ return computed(() => {
1668
+ return Object.entries(params).reduce((acc, [key, value]) => {
1669
+ acc[key] = isSignal(value) ? value() : value;
1670
+ return acc;
1671
+ }, {});
1672
+ });
1673
+ }
1674
+ function computerKeys(keys) {
1675
+ if (Array.isArray(keys)) {
1676
+ return computed(() => keys.map((key) => (isSignal(key) ? key() : key)));
1677
+ }
1678
+ return computed(() => keys());
1679
+ }
1680
+ function isSignalKey(key) {
1681
+ return Array.isArray(key) ? key.some(isSignal) : isSignal(key);
1682
+ }
1683
+ function isSignalParams(params) {
1684
+ return params
1685
+ ? isSignal(params) || Object.values(params).some(isSignal)
1686
+ : false;
1687
+ }
1688
+ function computerKeysAndParams(key, params) {
1689
+ const computedKeys = isSignalKey(key)
1690
+ ? computerKeys(key)
1691
+ : computed(() => key);
1692
+ const computedParams = isSignalParams(params)
1693
+ ? computerParams(params)
1694
+ : computed(() => params);
1695
+ return computed(() => ({ key: computedKeys(), params: computedParams() }));
1696
+ }
1697
+ function resolveScope(scope) {
1698
+ if (typeof scope === 'undefined' || scope === '') {
1699
+ const translocoScope = inject(TRANSLOCO_SCOPE, { optional: true });
1700
+ return translocoScope ?? undefined;
1701
+ }
1702
+ return scope;
1703
+ }
1704
+
1607
1705
  /**
1608
1706
  * Generated bundle index. Do not edit.
1609
1707
  */
1610
1708
 
1611
- export { DefaultFallbackStrategy, DefaultInterceptor, DefaultMissingHandler, DefaultTranspiler, FunctionalTranspiler, TRANSLOCO_CONFIG, TRANSLOCO_FALLBACK_STRATEGY, TRANSLOCO_INTERCEPTOR, TRANSLOCO_LANG, TRANSLOCO_LOADER, TRANSLOCO_LOADING_TEMPLATE, TRANSLOCO_MISSING_HANDLER, TRANSLOCO_SCOPE, TRANSLOCO_TRANSPILER, TestingLoader, TranslocoDirective, TranslocoModule, TranslocoPipe, TranslocoService, TranslocoTestingModule, coerceArray, defaultConfig, flatten, getBrowserCultureLang, getBrowserLang, getFunctionArgs, getLangFromScope, getPipeValue, getScopeFromLang, getValue, hasInlineLoader, isBrowser, isDefined, isEmpty, isFunction, isNil, isNumber, isObject, isScopeObject, isString, provideTransloco, provideTranslocoConfig, provideTranslocoFallbackStrategy, provideTranslocoInterceptor, provideTranslocoLang, provideTranslocoLoader, provideTranslocoLoadingTpl, provideTranslocoMissingHandler, provideTranslocoScope, provideTranslocoTranspiler, setValue, size, toCamelCase, toNumber, translate, translateObject, translocoConfig, unflatten };
1709
+ export { DefaultFallbackStrategy, DefaultInterceptor, DefaultMissingHandler, DefaultTranspiler, FunctionalTranspiler, TRANSLOCO_CONFIG, TRANSLOCO_FALLBACK_STRATEGY, TRANSLOCO_INTERCEPTOR, TRANSLOCO_LANG, TRANSLOCO_LOADER, TRANSLOCO_LOADING_TEMPLATE, TRANSLOCO_MISSING_HANDLER, TRANSLOCO_SCOPE, TRANSLOCO_TRANSPILER, TestingLoader, TranslocoDirective, TranslocoModule, TranslocoPipe, TranslocoService, TranslocoTestingModule, coerceArray, defaultConfig, flatten, getBrowserCultureLang, getBrowserLang, getFunctionArgs, getLangFromScope, getPipeValue, getScopeFromLang, getValue, hasInlineLoader, isBrowser, isDefined, isEmpty, isFunction, isNil, isNumber, isObject, isScopeObject, isString, provideTransloco, provideTranslocoConfig, provideTranslocoFallbackStrategy, provideTranslocoInterceptor, provideTranslocoLang, provideTranslocoLoader, provideTranslocoLoadingTpl, provideTranslocoMissingHandler, provideTranslocoScope, provideTranslocoTranspiler, setValue, size, toCamelCase, toNumber, translate, translateObject, translateObjectSignal, translateSignal, translocoConfig, unflatten };
1612
1710
  //# sourceMappingURL=jsverse-transloco.mjs.map