@derschmidtler/ngx-translate-router 10.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.
@@ -0,0 +1,1401 @@
1
+ import * as i0 from '@angular/core';
2
+ import { InjectionToken, Inject, Injectable, Pipe, inject, PLATFORM_ID, Compiler, runInInjectionContext, NgModuleFactory, Optional, SkipSelf, APP_INITIALIZER, NgModule } from '@angular/core';
3
+ import * as i3 from '@angular/router';
4
+ import { NavigationStart, NavigationCancel, Router, ActivatedRoute, ROUTES, PRIMARY_OUTLET, ɵEmptyOutletComponent as _EmptyOutletComponent, RouteReuseStrategy, RouterModule } from '@angular/router';
5
+ import { firstValueFrom, Observable, Subject, ReplaySubject, isObservable } from 'rxjs';
6
+ import { filter, pairwise } from 'rxjs/operators';
7
+ import * as i1 from '@ngx-translate/core';
8
+ import { TranslateService, TranslateModule } from '@ngx-translate/core';
9
+ import * as i2 from '@angular/common';
10
+ import { Location, isPlatformBrowser, CommonModule } from '@angular/common';
11
+ import { HttpParams } from '@angular/common/http';
12
+
13
+ /**
14
+ * Guard to make sure we have single initialization of forRoot
15
+ */
16
+ const LOCALIZE_ROUTER_FORROOT_GUARD = new InjectionToken('LOCALIZE_ROUTER_FORROOT_GUARD');
17
+ /**
18
+ * Static provider for keeping track of routes
19
+ */
20
+ const RAW_ROUTES = new InjectionToken('RAW_ROUTES');
21
+ /**
22
+ * Type for Caching of default language
23
+ */
24
+ // export type CacheMechanism = 'LocalStorage' | 'Cookie';
25
+ /**
26
+ * Namespace for fail proof access of CacheMechanism
27
+ */
28
+ var CacheMechanism;
29
+ (function (CacheMechanism) {
30
+ CacheMechanism["LocalStorage"] = "LocalStorage";
31
+ CacheMechanism["SessionStorage"] = "SessionStorage";
32
+ CacheMechanism["Cookie"] = "Cookie";
33
+ })(CacheMechanism || (CacheMechanism = {}));
34
+ /**
35
+ * Boolean to indicate whether to use cached language value
36
+ */
37
+ const USE_CACHED_LANG = new InjectionToken('USE_CACHED_LANG');
38
+ /**
39
+ * Cache mechanism type
40
+ */
41
+ const CACHE_MECHANISM = new InjectionToken('CACHE_MECHANISM');
42
+ /**
43
+ * Cache name
44
+ */
45
+ const CACHE_NAME = new InjectionToken('CACHE_NAME');
46
+ /**
47
+ * Cookie cache format
48
+ */
49
+ const COOKIE_FORMAT = new InjectionToken('COOKIE_FORMAT');
50
+ /**
51
+ * Cookie cache format
52
+ */
53
+ const INITIAL_NAVIGATION = new InjectionToken('INITIAL_NAVIGATION');
54
+ /**
55
+ * Function for calculating default language
56
+ */
57
+ const DEFAULT_LANG_FUNCTION = new InjectionToken('DEFAULT_LANG_FUNCTION');
58
+ /**
59
+ * Boolean to indicate whether prefix should be set for single language scenarios
60
+ */
61
+ const ALWAYS_SET_PREFIX = new InjectionToken('ALWAYS_SET_PREFIX');
62
+ const LOCALIZE_CACHE_NAME = 'LOCALIZE_DEFAULT_LANGUAGE';
63
+ const DEFAULT_COOKIE_FORMAT = '{{value}};{{expires}}';
64
+ const DEFAULT_INITIAL_NAVIGATION = false;
65
+ class LocalizeRouterSettings {
66
+ /**
67
+ * Settings for localize router
68
+ */
69
+ constructor(useCachedLang = true, alwaysSetPrefix = true, cacheMechanism = CacheMechanism.LocalStorage, cacheName = LOCALIZE_CACHE_NAME, defaultLangFunction = void 0, cookieFormat = DEFAULT_COOKIE_FORMAT, initialNavigation = DEFAULT_INITIAL_NAVIGATION) {
70
+ this.useCachedLang = useCachedLang;
71
+ this.alwaysSetPrefix = alwaysSetPrefix;
72
+ this.cacheName = cacheName;
73
+ this.cookieFormat = cookieFormat;
74
+ this.initialNavigation = initialNavigation;
75
+ this.cacheMechanism = cacheMechanism;
76
+ this.defaultLangFunction = defaultLangFunction;
77
+ }
78
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizeRouterSettings, deps: [{ token: USE_CACHED_LANG }, { token: ALWAYS_SET_PREFIX }, { token: CACHE_MECHANISM }, { token: CACHE_NAME }, { token: DEFAULT_LANG_FUNCTION }, { token: COOKIE_FORMAT }, { token: INITIAL_NAVIGATION }], target: i0.ɵɵFactoryTarget.Injectable }); }
79
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizeRouterSettings }); }
80
+ }
81
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizeRouterSettings, decorators: [{
82
+ type: Injectable
83
+ }], ctorParameters: () => [{ type: undefined, decorators: [{
84
+ type: Inject,
85
+ args: [USE_CACHED_LANG]
86
+ }] }, { type: undefined, decorators: [{
87
+ type: Inject,
88
+ args: [ALWAYS_SET_PREFIX]
89
+ }] }, { type: undefined, decorators: [{
90
+ type: Inject,
91
+ args: [CACHE_MECHANISM]
92
+ }] }, { type: undefined, decorators: [{
93
+ type: Inject,
94
+ args: [CACHE_NAME]
95
+ }] }, { type: undefined, decorators: [{
96
+ type: Inject,
97
+ args: [DEFAULT_LANG_FUNCTION]
98
+ }] }, { type: undefined, decorators: [{
99
+ type: Inject,
100
+ args: [COOKIE_FORMAT]
101
+ }] }, { type: undefined, decorators: [{
102
+ type: Inject,
103
+ args: [INITIAL_NAVIGATION]
104
+ }] }] });
105
+
106
+ const COOKIE_EXPIRY = 30; // 1 month
107
+ /**
108
+ * Abstract class for parsing localization
109
+ */
110
+ class LocalizeParser {
111
+ /**
112
+ * Loader constructor
113
+ */
114
+ constructor(translate, location, settings) {
115
+ this.translate = translate;
116
+ this.location = location;
117
+ this.settings = settings;
118
+ }
119
+ /**
120
+ * Prepare routes to be fully usable by ngx-translate-router
121
+ * @param routes
122
+ */
123
+ /* private initRoutes(routes: Routes, prefix = '') {
124
+ routes.forEach(route => {
125
+ if (route.path !== '**') {
126
+ const routeData: any = route.data = route.data || {};
127
+ routeData.localizeRouter = {};
128
+ routeData.localizeRouter.fullPath = `${prefix}/${route.path}`;
129
+ if (route.children && route.children.length > 0) {
130
+ this.initRoutes(route.children, routeData.localizeRouter.fullPath);
131
+ }
132
+ }
133
+ });
134
+ } */
135
+ /**
136
+ * Initialize language and routes
137
+ */
138
+ init(routes) {
139
+ let selectedLanguage;
140
+ // this.initRoutes(routes);
141
+ this.routes = routes;
142
+ if (!this.locales || !this.locales.length) {
143
+ return Promise.resolve();
144
+ }
145
+ /** detect current language */
146
+ const locationLang = this.getLocationLang();
147
+ const browserLang = this._getBrowserLang();
148
+ if (this.settings.defaultLangFunction) {
149
+ this.defaultLang = this.settings.defaultLangFunction(this.locales, this._cachedLang, browserLang);
150
+ }
151
+ else {
152
+ this.defaultLang = this._cachedLang || browserLang || this.locales[0];
153
+ }
154
+ selectedLanguage = locationLang || this.defaultLang;
155
+ this.translate.setDefaultLang(this.defaultLang);
156
+ let children = [];
157
+ /** if set prefix is enforced */
158
+ if (this.settings.alwaysSetPrefix) {
159
+ const baseRoute = { path: '', redirectTo: this.defaultLang, pathMatch: 'full' };
160
+ /** extract potential wildcard route */
161
+ const wildcardIndex = routes.findIndex((route) => route.path === '**');
162
+ if (wildcardIndex !== -1) {
163
+ this._wildcardRoute = routes.splice(wildcardIndex, 1)[0];
164
+ }
165
+ children = this.routes.splice(0, this.routes.length, baseRoute);
166
+ }
167
+ else {
168
+ children = [...this.routes]; // shallow copy of routes
169
+ }
170
+ /** exclude certain routes */
171
+ for (let i = children.length - 1; i >= 0; i--) {
172
+ if (children[i].data && children[i].data['skipRouteLocalization']) {
173
+ if (this.settings.alwaysSetPrefix) {
174
+ // add directly to routes
175
+ this.routes.push(children[i]);
176
+ }
177
+ // remove from routes to translate only if doesn't have to translate `redirectTo` property
178
+ if (children[i].redirectTo === undefined || !(children[i].data['skipRouteLocalization']['localizeRedirectTo']) || typeof children[i].redirectTo === 'function') {
179
+ children.splice(i, 1);
180
+ }
181
+ }
182
+ }
183
+ /** append children routes */
184
+ if (children && children.length) {
185
+ if (this.locales.length > 1 || this.settings.alwaysSetPrefix) {
186
+ this._languageRoute = { children: children };
187
+ this.routes.unshift(this._languageRoute);
188
+ }
189
+ }
190
+ /** ...and potential wildcard route */
191
+ if (this._wildcardRoute && this.settings.alwaysSetPrefix) {
192
+ this.routes.push(this._wildcardRoute);
193
+ }
194
+ /** translate routes */
195
+ return firstValueFrom(this.translateRoutes(selectedLanguage));
196
+ }
197
+ initChildRoutes(routes) {
198
+ this._translateRouteTree(routes);
199
+ return routes;
200
+ }
201
+ /**
202
+ * Translate routes to selected language
203
+ */
204
+ translateRoutes(language) {
205
+ return new Observable((observer) => {
206
+ this._cachedLang = language;
207
+ if (this._languageRoute) {
208
+ this._languageRoute.path = language;
209
+ }
210
+ this.translate.use(language).subscribe((translations) => {
211
+ this._translationObject = translations;
212
+ this.currentLang = language;
213
+ if (this._languageRoute) {
214
+ this._translateRouteTree(this._languageRoute.children, true);
215
+ // if there is wildcard route
216
+ if (this._wildcardRoute && this._wildcardRoute.redirectTo) {
217
+ this._translateProperty(this._wildcardRoute, 'redirectTo', true);
218
+ }
219
+ }
220
+ else {
221
+ this._translateRouteTree(this.routes, true);
222
+ }
223
+ observer.next(void 0);
224
+ observer.complete();
225
+ });
226
+ });
227
+ }
228
+ /**
229
+ * Translate the route node and recursively call for all it's children
230
+ */
231
+ _translateRouteTree(routes, isRootTree) {
232
+ routes.forEach((route) => {
233
+ const skipRouteLocalization = (route.data && route.data['skipRouteLocalization']);
234
+ const localizeRedirection = !skipRouteLocalization || skipRouteLocalization['localizeRedirectTo'];
235
+ if (route.redirectTo && localizeRedirection && !(typeof route.redirectTo === 'function')) {
236
+ const prefixLang = route.redirectTo.indexOf('/') === 0 || isRootTree;
237
+ this._translateProperty(route, 'redirectTo', prefixLang);
238
+ }
239
+ if (skipRouteLocalization) {
240
+ return;
241
+ }
242
+ if (route.path !== null && route.path !== undefined /* && route.path !== '**'*/) {
243
+ this._translateProperty(route, 'path');
244
+ }
245
+ if (route.children) {
246
+ this._translateRouteTree(route.children);
247
+ }
248
+ if (route.loadChildren && route._loadedRoutes?.length) {
249
+ this._translateRouteTree(route._loadedRoutes);
250
+ }
251
+ });
252
+ }
253
+ /**
254
+ * Translate property
255
+ * If first time translation then add original to route data object
256
+ */
257
+ _translateProperty(route, property, prefixLang) {
258
+ // set property to data if not there yet
259
+ const routeData = route.data = route.data || {};
260
+ if (!routeData.localizeRouter) {
261
+ routeData.localizeRouter = {};
262
+ }
263
+ if (!routeData.localizeRouter[property]) {
264
+ routeData.localizeRouter = { ...routeData.localizeRouter, [property]: route[property] };
265
+ }
266
+ const result = this.translateRoute(routeData.localizeRouter[property]);
267
+ route[property] = prefixLang ? this.addPrefixToUrl(result) : result;
268
+ }
269
+ get urlPrefix() {
270
+ if (this.settings.alwaysSetPrefix || this.currentLang !== this.defaultLang) {
271
+ return this.currentLang ? this.currentLang : this.defaultLang;
272
+ }
273
+ else {
274
+ return '';
275
+ }
276
+ }
277
+ /**
278
+ * Add current lang as prefix to given url.
279
+ */
280
+ addPrefixToUrl(url) {
281
+ const splitUrl = url.split('?');
282
+ const isRootPath = splitUrl[0].length === 1 && splitUrl[0] === '/';
283
+ splitUrl[0] = splitUrl[0].replace(/\/$/, '');
284
+ const joinedUrl = splitUrl.join('?');
285
+ if (this.urlPrefix === '') {
286
+ return joinedUrl;
287
+ }
288
+ if (!joinedUrl.startsWith('/') && !isRootPath) {
289
+ return `${this.urlPrefix}/${joinedUrl}`;
290
+ }
291
+ return `/${this.urlPrefix}${joinedUrl}`;
292
+ }
293
+ /**
294
+ * Translate route and return observable
295
+ */
296
+ translateRoute(path) {
297
+ const queryParts = path.split('?');
298
+ if (queryParts.length > 2) {
299
+ throw Error('There should be only one query parameter block in the URL');
300
+ }
301
+ const pathSegments = queryParts[0].split('/');
302
+ /** collect observables */
303
+ return pathSegments
304
+ .map((part) => part.length ? this.translateText(part) : part)
305
+ .join('/') +
306
+ (queryParts.length > 1 ? `?${queryParts[1]}` : '');
307
+ }
308
+ /**
309
+ * Get language from url
310
+ */
311
+ getLocationLang(url) {
312
+ const queryParamSplit = (url || this.location.path()).split(/[\?;]/);
313
+ let pathSlices = [];
314
+ if (queryParamSplit.length > 0) {
315
+ pathSlices = queryParamSplit[0].split('/');
316
+ }
317
+ if (pathSlices.length > 1 && this.locales.indexOf(pathSlices[1]) !== -1) {
318
+ return pathSlices[1];
319
+ }
320
+ if (pathSlices.length && this.locales.indexOf(pathSlices[0]) !== -1) {
321
+ return pathSlices[0];
322
+ }
323
+ return null;
324
+ }
325
+ /**
326
+ * Get user's language set in the browser
327
+ */
328
+ _getBrowserLang() {
329
+ return this._returnIfInLocales(this.translate.getBrowserLang());
330
+ }
331
+ /**
332
+ * Get language from local storage or cookie
333
+ */
334
+ get _cachedLang() {
335
+ if (!this.settings.useCachedLang) {
336
+ return;
337
+ }
338
+ if (this.settings.cacheMechanism === CacheMechanism.LocalStorage) {
339
+ return this._cacheWithLocalStorage();
340
+ }
341
+ if (this.settings.cacheMechanism === CacheMechanism.SessionStorage) {
342
+ return this._cacheWithSessionStorage();
343
+ }
344
+ if (this.settings.cacheMechanism === CacheMechanism.Cookie) {
345
+ return this._cacheWithCookies();
346
+ }
347
+ }
348
+ /**
349
+ * Save language to local storage or cookie
350
+ */
351
+ set _cachedLang(value) {
352
+ if (!this.settings.useCachedLang) {
353
+ return;
354
+ }
355
+ if (this.settings.cacheMechanism === CacheMechanism.LocalStorage) {
356
+ this._cacheWithLocalStorage(value);
357
+ }
358
+ if (this.settings.cacheMechanism === CacheMechanism.SessionStorage) {
359
+ this._cacheWithSessionStorage(value);
360
+ }
361
+ if (this.settings.cacheMechanism === CacheMechanism.Cookie) {
362
+ this._cacheWithCookies(value);
363
+ }
364
+ }
365
+ /**
366
+ * Cache value to local storage
367
+ */
368
+ _cacheWithLocalStorage(value) {
369
+ try {
370
+ if (typeof window === 'undefined' || typeof window.localStorage === 'undefined') {
371
+ return;
372
+ }
373
+ if (value) {
374
+ window.localStorage.setItem(this.settings.cacheName, value);
375
+ return;
376
+ }
377
+ return this._returnIfInLocales(window.localStorage.getItem(this.settings.cacheName));
378
+ }
379
+ catch (e) {
380
+ // weird Safari issue in private mode, where LocalStorage is defined but throws error on access
381
+ return;
382
+ }
383
+ }
384
+ /**
385
+ * Cache value to session storage
386
+ */
387
+ _cacheWithSessionStorage(value) {
388
+ try {
389
+ if (typeof window === 'undefined' || typeof window.sessionStorage === 'undefined') {
390
+ return;
391
+ }
392
+ if (value) {
393
+ window.sessionStorage.setItem(this.settings.cacheName, value);
394
+ return;
395
+ }
396
+ return this._returnIfInLocales(window.sessionStorage.getItem(this.settings.cacheName));
397
+ }
398
+ catch (e) {
399
+ return;
400
+ }
401
+ }
402
+ /**
403
+ * Cache value via cookies
404
+ */
405
+ _cacheWithCookies(value) {
406
+ try {
407
+ if (typeof document === 'undefined' || typeof document.cookie === 'undefined') {
408
+ return;
409
+ }
410
+ const name = encodeURIComponent(this.settings.cacheName);
411
+ if (value) {
412
+ let cookieTemplate = `${this.settings.cookieFormat}`;
413
+ cookieTemplate = cookieTemplate
414
+ .replace('{{value}}', `${name}=${encodeURIComponent(value)}`)
415
+ .replace(/{{expires:?(\d+)?}}/g, (fullMatch, groupMatch) => {
416
+ const days = groupMatch === undefined ? COOKIE_EXPIRY : parseInt(groupMatch, 10);
417
+ const date = new Date();
418
+ date.setTime(date.getTime() + days * 86400000);
419
+ return `expires=${date.toUTCString()}`;
420
+ });
421
+ document.cookie = cookieTemplate;
422
+ return;
423
+ }
424
+ const regexp = new RegExp('(?:^' + name + '|;\\s*' + name + ')=(.*?)(?:;|$)', 'g');
425
+ const result = regexp.exec(document.cookie);
426
+ return decodeURIComponent(result[1]);
427
+ }
428
+ catch (e) {
429
+ return; // should not happen but better safe than sorry (can happen by using domino)
430
+ }
431
+ }
432
+ /**
433
+ * Check if value exists in locales list
434
+ */
435
+ _returnIfInLocales(value) {
436
+ if (value && this.locales.indexOf(value) !== -1) {
437
+ return value;
438
+ }
439
+ return null;
440
+ }
441
+ /**
442
+ * Get translated value
443
+ */
444
+ translateText(key) {
445
+ if (this.escapePrefix && key.startsWith(this.escapePrefix)) {
446
+ return key.replace(this.escapePrefix, '');
447
+ }
448
+ else {
449
+ if (!this._translationObject) {
450
+ return key;
451
+ }
452
+ const fullKey = this.prefix + key;
453
+ const res = this.translate.getParsedResult(this._translationObject, fullKey);
454
+ return res !== fullKey ? res : key;
455
+ }
456
+ }
457
+ /**
458
+ * Strategy to choose between new or old queryParams
459
+ * @param newExtras extras that containes new QueryParams
460
+ * @param currentQueryParams current query params
461
+ */
462
+ chooseQueryParams(newExtras, currentQueryParams) {
463
+ let queryParamsObj;
464
+ if (newExtras && newExtras.queryParams) {
465
+ queryParamsObj = newExtras.queryParams;
466
+ }
467
+ else if (currentQueryParams) {
468
+ queryParamsObj = currentQueryParams;
469
+ }
470
+ return queryParamsObj;
471
+ }
472
+ /**
473
+ * Format query params from object to string.
474
+ * Exemple of result: `param=value&param2=value2`
475
+ * @param params query params object
476
+ */
477
+ formatQueryParams(params) {
478
+ return new HttpParams({ fromObject: params }).toString();
479
+ }
480
+ /**
481
+ * Get translation key prefix from config
482
+ */
483
+ getPrefix() {
484
+ return this.prefix;
485
+ }
486
+ /**
487
+ * Get escape translation prefix from config
488
+ */
489
+ getEscapePrefix() {
490
+ return this.escapePrefix;
491
+ }
492
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizeParser, deps: [{ token: TranslateService }, { token: Location }, { token: LocalizeRouterSettings }], target: i0.ɵɵFactoryTarget.Injectable }); }
493
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizeParser }); }
494
+ }
495
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizeParser, decorators: [{
496
+ type: Injectable
497
+ }], ctorParameters: () => [{ type: i1.TranslateService, decorators: [{
498
+ type: Inject,
499
+ args: [TranslateService]
500
+ }] }, { type: i2.Location, decorators: [{
501
+ type: Inject,
502
+ args: [Location]
503
+ }] }, { type: LocalizeRouterSettings, decorators: [{
504
+ type: Inject,
505
+ args: [LocalizeRouterSettings]
506
+ }] }] });
507
+ /**
508
+ * Manually set configuration
509
+ */
510
+ class ManualParserLoader extends LocalizeParser {
511
+ /**
512
+ * CTOR
513
+ */
514
+ constructor(translate, location, settings, locales = ['en'], prefix = 'ROUTES.', escapePrefix = '') {
515
+ super(translate, location, settings);
516
+ this.locales = locales;
517
+ this.prefix = prefix || '';
518
+ this.escapePrefix = escapePrefix || '';
519
+ }
520
+ /**
521
+ * Initialize or append routes
522
+ */
523
+ load(routes) {
524
+ return new Promise((resolve) => {
525
+ this.init(routes).then(resolve);
526
+ });
527
+ }
528
+ }
529
+ class DummyLocalizeParser extends LocalizeParser {
530
+ load(routes) {
531
+ return new Promise((resolve) => {
532
+ this.init(routes).then(resolve);
533
+ });
534
+ }
535
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DummyLocalizeParser, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
536
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DummyLocalizeParser }); }
537
+ }
538
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: DummyLocalizeParser, decorators: [{
539
+ type: Injectable
540
+ }] });
541
+
542
+ /**
543
+ * Compare if two objects are same
544
+ */
545
+ function equals(o1, o2) {
546
+ if (o1 === o2) {
547
+ return true;
548
+ }
549
+ if (o1 === null || o2 === null) {
550
+ return false;
551
+ }
552
+ if (o1 !== o1 && o2 !== o2) {
553
+ return true; // NaN === NaN
554
+ }
555
+ const t1 = typeof o1, t2 = typeof o2;
556
+ let length, key, keySet;
557
+ if (t1 === t2 && t1 === 'object') {
558
+ if (Array.isArray(o1)) {
559
+ if (!Array.isArray(o2)) {
560
+ return false;
561
+ }
562
+ if ((length = o1.length) === o2.length) {
563
+ for (key = 0; key < length; key++) {
564
+ if (!equals(o1[key], o2[key])) {
565
+ return false;
566
+ }
567
+ }
568
+ return true;
569
+ }
570
+ }
571
+ else {
572
+ if (Array.isArray(o2)) {
573
+ return false;
574
+ }
575
+ keySet = Object.create(null);
576
+ for (key in o1) {
577
+ if (o1.hasOwnProperty(key)) {
578
+ if (!equals(o1[key], o2[key])) {
579
+ return false;
580
+ }
581
+ keySet[key] = true;
582
+ }
583
+ }
584
+ for (key in o2) {
585
+ if (o2.hasOwnProperty(key)) {
586
+ if (!(key in keySet) && typeof o2[key] !== 'undefined') {
587
+ return false;
588
+ }
589
+ }
590
+ }
591
+ return true;
592
+ }
593
+ }
594
+ return false;
595
+ }
596
+ /**
597
+ * Determine if the argument is shaped like a Promise
598
+ */
599
+ function isPromise(obj) {
600
+ // allow any Promise/A+ compliant thenable.
601
+ // It's up to the caller to ensure that obj.then conforms to the spec
602
+ return !!obj && typeof obj.then === 'function';
603
+ }
604
+ /**
605
+ * Deep copy of object and array
606
+ */
607
+ function deepCopy(object) {
608
+ const output = Array.isArray(object) ? [] : {};
609
+ for (const data in object) {
610
+ if (data) {
611
+ const value = object[data];
612
+ output[data] = (typeof value === 'object') ? deepCopy(value) : value;
613
+ }
614
+ }
615
+ return output;
616
+ }
617
+ function flatten(list) {
618
+ return list.reduce((flat, item) => {
619
+ const flatItem = Array.isArray(item) ? flatten(item) : item;
620
+ return flat.concat(flatItem);
621
+ }, []);
622
+ }
623
+
624
+ /**
625
+ * Localization service
626
+ * modifyRoutes
627
+ */
628
+ class LocalizeRouterService {
629
+ /**
630
+ * CTOR
631
+ */
632
+ constructor(parser, settings, router, route /*,
633
+ @Inject(Location) private location: Location*/) {
634
+ this.parser = parser;
635
+ this.settings = settings;
636
+ this.router = router;
637
+ this.route = route;
638
+ this.routerEvents = new Subject();
639
+ const initializedSubject = new ReplaySubject(1);
640
+ this.hooks = {
641
+ _initializedSubject: initializedSubject,
642
+ initialized: initializedSubject.asObservable()
643
+ };
644
+ }
645
+ /**
646
+ * Start up the service
647
+ */
648
+ init() {
649
+ this.applyConfigToRouter(this.parser.routes);
650
+ // subscribe to router events
651
+ this.router.events
652
+ .pipe(filter(event => event instanceof NavigationStart), pairwise())
653
+ .subscribe(this._routeChanged());
654
+ if (this.settings.initialNavigation) {
655
+ this.router.initialNavigation();
656
+ }
657
+ }
658
+ /**
659
+ * Change language and navigate to translated route
660
+ */
661
+ changeLanguage(lang, extras, useNavigateMethod) {
662
+ if (lang !== this.parser.currentLang) {
663
+ const rootSnapshot = this.router.routerState.snapshot.root;
664
+ this.parser.translateRoutes(lang).subscribe(() => {
665
+ let url = this.traverseRouteSnapshot(rootSnapshot);
666
+ url = this.translateRoute(url);
667
+ if (!this.settings.alwaysSetPrefix) {
668
+ let urlSegments = url.split('/');
669
+ const languageSegmentIndex = urlSegments.indexOf(this.parser.currentLang);
670
+ // If the default language has no prefix make sure to remove and add it when necessary
671
+ if (this.parser.currentLang === this.parser.defaultLang) {
672
+ // Remove the language prefix from url when current language is the default language
673
+ if (languageSegmentIndex === 0 || (languageSegmentIndex === 1 && urlSegments[0] === '')) {
674
+ // Remove the current aka default language prefix from the url
675
+ urlSegments = urlSegments.slice(0, languageSegmentIndex).concat(urlSegments.slice(languageSegmentIndex + 1));
676
+ }
677
+ }
678
+ else {
679
+ // When coming from a default language it's possible that the url doesn't contain the language, make sure it does.
680
+ if (languageSegmentIndex === -1) {
681
+ // If the url starts with a slash make sure to keep it.
682
+ const injectionIndex = urlSegments[0] === '' ? 1 : 0;
683
+ urlSegments = urlSegments.slice(0, injectionIndex).concat(this.parser.currentLang, urlSegments.slice(injectionIndex));
684
+ }
685
+ }
686
+ url = urlSegments.join('/');
687
+ }
688
+ // Prevent multiple "/" character
689
+ url = url.replace(/\/+/g, '/');
690
+ const lastSlashIndex = url.lastIndexOf('/');
691
+ if (lastSlashIndex > 0 && lastSlashIndex === url.length - 1) {
692
+ url = url.slice(0, -1);
693
+ }
694
+ const queryParamsObj = this.parser.chooseQueryParams(extras, this.route.snapshot.queryParams);
695
+ this.applyConfigToRouter(this.parser.routes);
696
+ this.lastExtras = extras;
697
+ if (useNavigateMethod) {
698
+ const extrasToApply = extras ? { ...extras } : {};
699
+ if (queryParamsObj) {
700
+ extrasToApply.queryParams = queryParamsObj;
701
+ }
702
+ this.router.navigate([url], extrasToApply);
703
+ }
704
+ else {
705
+ let queryParams = this.parser.formatQueryParams(queryParamsObj);
706
+ queryParams = queryParams ? `?${queryParams}` : '';
707
+ this.router.navigateByUrl(`${url}${queryParams}`, extras);
708
+ }
709
+ });
710
+ }
711
+ }
712
+ /**
713
+ * Traverses through the tree to assemble new translated url
714
+ */
715
+ traverseRouteSnapshot(snapshot) {
716
+ if (snapshot.firstChild && snapshot.routeConfig) {
717
+ return `${this.parseSegmentValue(snapshot)}/${this.traverseRouteSnapshot(snapshot.firstChild)}`;
718
+ }
719
+ else if (snapshot.firstChild) {
720
+ return this.traverseRouteSnapshot(snapshot.firstChild);
721
+ }
722
+ else {
723
+ return this.parseSegmentValue(snapshot);
724
+ }
725
+ /* if (snapshot.firstChild && snapshot.firstChild.routeConfig && snapshot.firstChild.routeConfig.path) {
726
+ if (snapshot.firstChild.routeConfig.path !== '**') {
727
+ return this.parseSegmentValue(snapshot) + '/' + this.traverseRouteSnapshot(snapshot.firstChild);
728
+ } else {
729
+ return this.parseSegmentValue(snapshot.firstChild);
730
+ }
731
+ }
732
+ return this.parseSegmentValue(snapshot); */
733
+ }
734
+ /**
735
+ * Build URL from segments and snapshot (for params)
736
+ */
737
+ buildUrlFromSegments(snapshot, segments) {
738
+ return segments.map((s, i) => s.indexOf(':') === 0 ? snapshot.url[i].path : s).join('/');
739
+ }
740
+ /**
741
+ * Extracts new segment value based on routeConfig and url
742
+ */
743
+ parseSegmentValue(snapshot) {
744
+ if (snapshot.routeConfig && snapshot.routeConfig.matcher) {
745
+ const subPathMatchedSegments = this.parseSegmentValueMatcher(snapshot);
746
+ return this.buildUrlFromSegments(snapshot, subPathMatchedSegments);
747
+ }
748
+ else if (snapshot.data.localizeRouter) {
749
+ const path = snapshot.data.localizeRouter.path;
750
+ const subPathSegments = path.split('/');
751
+ return this.buildUrlFromSegments(snapshot, subPathSegments);
752
+ }
753
+ else if (snapshot.parent && snapshot.parent.parent) { // Not lang route and no localizeRouter data = excluded path
754
+ const path = snapshot.routeConfig.path;
755
+ const subPathSegments = path.split('/');
756
+ return this.buildUrlFromSegments(snapshot, subPathSegments);
757
+ }
758
+ else {
759
+ return '';
760
+ }
761
+ /* if (snapshot.routeConfig) {
762
+ if (snapshot.routeConfig.path === '**') {
763
+ return snapshot.url.filter((segment: UrlSegment) => segment.path).map((segment: UrlSegment) => segment.path).join('/');
764
+ } else {
765
+ const subPathSegments = snapshot.routeConfig.path.split('/');
766
+ return subPathSegments.map((s: string, i: number) => s.indexOf(':') === 0 ? snapshot.url[i].path : s).join('/');
767
+ }
768
+ }
769
+ return ''; */
770
+ }
771
+ parseSegmentValueMatcher(snapshot) {
772
+ const localizeMatcherParams = snapshot.data && snapshot.data.localizeMatcher && snapshot.data.localizeMatcher.params || {};
773
+ const subPathSegments = snapshot.url
774
+ .map((segment) => {
775
+ const currentPath = segment.path;
776
+ const matchedParamName = segment.localizedParamName;
777
+ const val = (matchedParamName && localizeMatcherParams[matchedParamName]) ?
778
+ localizeMatcherParams[matchedParamName](currentPath) : null;
779
+ return val || `${this.parser.getEscapePrefix()}${currentPath}`;
780
+ });
781
+ return subPathSegments;
782
+ }
783
+ /**
784
+ * Translate route to current language
785
+ * If new language is explicitly provided then replace language part in url with new language
786
+ */
787
+ translateRoute(path) {
788
+ if (typeof path === 'string') {
789
+ const url = this.parser.translateRoute(path);
790
+ return !path.indexOf('/') ? this.parser.addPrefixToUrl(url) : url;
791
+ }
792
+ // it's an array
793
+ const result = [];
794
+ path.forEach((segment, index) => {
795
+ if (typeof segment === 'string') {
796
+ const res = this.parser.translateRoute(segment);
797
+ if (!index && !segment.indexOf('/')) {
798
+ result.push(this.parser.addPrefixToUrl(res));
799
+ }
800
+ else {
801
+ result.push(res);
802
+ }
803
+ }
804
+ else {
805
+ result.push(segment);
806
+ }
807
+ });
808
+ return result;
809
+ }
810
+ /**
811
+ * Event handler to react on route change
812
+ */
813
+ _routeChanged() {
814
+ return ([previousEvent, currentEvent]) => {
815
+ const previousLang = this.parser.getLocationLang(previousEvent.url) || this.parser.defaultLang;
816
+ const currentLang = this.parser.getLocationLang(currentEvent.url) || this.parser.defaultLang;
817
+ const lastExtras = this.lastExtras;
818
+ if (currentLang !== previousLang && this.latestUrl !== currentEvent.url) {
819
+ this.latestUrl = currentEvent.url;
820
+ this.cancelCurrentNavigation();
821
+ this.parser.translateRoutes(currentLang)
822
+ .subscribe(() => {
823
+ // Reset routes again once they are all translated
824
+ this.applyConfigToRouter(this.parser.routes);
825
+ // Clear global extras
826
+ this.lastExtras = undefined;
827
+ // Init new navigation with same url to take new config in consideration
828
+ this.router.navigateByUrl(currentEvent.url, lastExtras);
829
+ // Fire route change event
830
+ this.routerEvents.next(currentLang);
831
+ });
832
+ }
833
+ this.latestUrl = currentEvent.url;
834
+ };
835
+ }
836
+ /**
837
+ * Drop the current Navigation
838
+ */
839
+ cancelCurrentNavigation() {
840
+ const currentNavigation = this.router.currentNavigation();
841
+ const url = this.router.serializeUrl(currentNavigation.extractedUrl);
842
+ this.router.events.next(new NavigationCancel(currentNavigation.id, url, ''));
843
+ this.router.navigationTransitions.transitions.next({
844
+ ...this.router.navigationTransitions.transitions.getValue(),
845
+ id: 0,
846
+ });
847
+ }
848
+ /**
849
+ * Apply config to Angular RouterModule
850
+ * @param config routes to apply
851
+ */
852
+ applyConfigToRouter(config) {
853
+ this.router.resetConfig(deepCopy(config));
854
+ }
855
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizeRouterService, deps: [{ token: LocalizeParser }, { token: LocalizeRouterSettings }, { token: Router }, { token: ActivatedRoute }], target: i0.ɵɵFactoryTarget.Injectable }); }
856
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizeRouterService }); }
857
+ }
858
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizeRouterService, decorators: [{
859
+ type: Injectable
860
+ }], ctorParameters: () => [{ type: LocalizeParser, decorators: [{
861
+ type: Inject,
862
+ args: [LocalizeParser]
863
+ }] }, { type: LocalizeRouterSettings, decorators: [{
864
+ type: Inject,
865
+ args: [LocalizeRouterSettings]
866
+ }] }, { type: i3.Router, decorators: [{
867
+ type: Inject,
868
+ args: [Router]
869
+ }] }, { type: i3.ActivatedRoute, decorators: [{
870
+ type: Inject,
871
+ args: [ActivatedRoute]
872
+ }] }] });
873
+
874
+ const VIEW_DESTROYED_STATE = 128;
875
+ class LocalizeRouterPipe {
876
+ /**
877
+ * CTOR
878
+ */
879
+ constructor(localize, _ref) {
880
+ this.localize = localize;
881
+ this._ref = _ref;
882
+ this.value = '';
883
+ this.subscription = this.localize.routerEvents.subscribe(() => {
884
+ this.transform(this.lastKey);
885
+ });
886
+ }
887
+ ngOnDestroy() {
888
+ if (this.subscription) {
889
+ this.subscription.unsubscribe();
890
+ }
891
+ }
892
+ /**
893
+ * Transform current url to localized one
894
+ */
895
+ transform(query) {
896
+ if (!query || query.length === 0 || !this.localize.parser.currentLang) {
897
+ return query;
898
+ }
899
+ if (equals(query, this.lastKey) && equals(this.lastLanguage, this.localize.parser.currentLang)) {
900
+ return this.value;
901
+ }
902
+ this.lastKey = query;
903
+ this.lastLanguage = this.localize.parser.currentLang;
904
+ /** translate key and update values */
905
+ this.value = this.localize.translateRoute(query);
906
+ this.lastKey = query;
907
+ // if view is already destroyed, ignore firing change detection
908
+ const view = this._ref._view;
909
+ if (view && (view.state & VIEW_DESTROYED_STATE)) {
910
+ return this.value;
911
+ }
912
+ setTimeout(() => {
913
+ this._ref.detectChanges();
914
+ }, 0);
915
+ return this.value;
916
+ }
917
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizeRouterPipe, deps: [{ token: LocalizeRouterService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Pipe }); }
918
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "21.2.17", ngImport: i0, type: LocalizeRouterPipe, isStandalone: true, name: "localize", pure: false }); }
919
+ }
920
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizeRouterPipe, decorators: [{
921
+ type: Pipe,
922
+ args: [{
923
+ name: 'localize',
924
+ pure: false, // required to update the value when the promise is resolved
925
+ standalone: true
926
+ }]
927
+ }], ctorParameters: () => [{ type: LocalizeRouterService }, { type: i0.ChangeDetectorRef }] });
928
+
929
+ class GilsdavReuseStrategy {
930
+ // private handlers: {[key: string]: DetachedRouteHandle} = {};
931
+ constructor() {
932
+ }
933
+ shouldDetach(route) {
934
+ // console.log('shouldDetach', route);
935
+ return false;
936
+ }
937
+ store(route, handle) {
938
+ // console.log('store', route, handle);
939
+ // console.log('store url', this.getKey(route));
940
+ // this.handlers[this.getKey(route)] = handle;
941
+ }
942
+ shouldAttach(route) {
943
+ // console.log('shouldAttach', route, this.getKey(route));
944
+ // return !!this.handlers[this.getKey(route)];
945
+ return false;
946
+ }
947
+ retrieve(route) {
948
+ // console.log('retrieve', route);
949
+ // console.log('retrieve url', this.getKey(route));
950
+ // const result = this.handlers[this.getKey(route)];
951
+ // delete this.handlers[this.getKey(route)];
952
+ // return result;
953
+ return null;
954
+ }
955
+ shouldReuseRoute(future, curr) {
956
+ // console.log('shouldReuseRoute', future, curr, this.getKey(future) === this.getKey(curr));
957
+ // console.log('shouldReuseRoute', future && curr ? this.getKey(future) === this.getKey(curr) : false);
958
+ return future && curr ? this.getKey(future) === this.getKey(curr) : false;
959
+ }
960
+ getKey(route) {
961
+ // console.log(route.parent.component.toString());
962
+ if (route.firstChild && route.firstChild.routeConfig && route.firstChild.routeConfig.path &&
963
+ route.firstChild.routeConfig.path.indexOf('**') !== -1) { // WildCard
964
+ return 'WILDCARD';
965
+ }
966
+ else if (!route.data.localizeRouter && (!route.parent || !route.parent.parent) && !route.data.skipRouteLocalization) { // Lang route
967
+ return 'LANG';
968
+ }
969
+ else if (route.routeConfig.matcher) {
970
+ let keyM = `${this.getKey(route.parent)}/${route.routeConfig.matcher.name}`;
971
+ if (route.data.discriminantPathKey) {
972
+ keyM = `${keyM}-${route.data.discriminantPathKey}`;
973
+ }
974
+ return keyM;
975
+ }
976
+ else if (route.data.localizeRouter) {
977
+ let key = `${this.getKey(route.parent)}/${route.data.localizeRouter.path}`;
978
+ if (route.data.discriminantPathKey) {
979
+ key = `${key}-${route.data.discriminantPathKey}`;
980
+ }
981
+ return key;
982
+ }
983
+ else {
984
+ let key = route.routeConfig.path;
985
+ if (route.parent) {
986
+ key = `${this.getKey(route.parent)}/${route.routeConfig.path}`;
987
+ }
988
+ if (route.data.discriminantPathKey) {
989
+ key = `${key}-${route.data.discriminantPathKey}`;
990
+ }
991
+ return key;
992
+ }
993
+ }
994
+ }
995
+
996
+ /**
997
+ * LocalizedRouter
998
+ * ----------------
999
+ * TL;DR: extends the angular router, especially the parts responsible for loading
1000
+ * routes + child routes.
1001
+ *
1002
+ * This class extends Angular's Router to transparently localize routes that
1003
+ * are loaded lazily.
1004
+ *
1005
+ * Why does this exist?
1006
+ * ====================
1007
+ *
1008
+ * ngx-translate-router modifies route definitions by translating the `path`
1009
+ * properties before Angular starts matching URLs.
1010
+ *
1011
+ * Prior to Angular 21 the library could recursively walk the route tree,
1012
+ * including lazily loaded children, by accessing Router internals such as
1013
+ * `_loadedRoutes` and repeatedly calling `router.resetConfig()`.
1014
+ *
1015
+ * Angular 21 completely changed the lazy loading pipeline. Child routes are now
1016
+ * resolved asynchronously through RouterConfigLoader and no longer exist at the
1017
+ * time `resetConfig()` executes.
1018
+ *
1019
+ * As a consequence, lazily loaded routes remained untranslated which eventually
1020
+ * caused router failures during matching (for example
1021
+ * `containsEmptyPathMatches()` receiving an undefined route array).
1022
+ *
1023
+ * Instead of rewriting Angular's routing pipeline, this class intercepts the
1024
+ * lazy loading process itself.
1025
+ *
1026
+ * High level flow
1027
+ * ===============
1028
+ *
1029
+ * Angular navigation
1030
+ * │
1031
+ * ▼
1032
+ * RouterConfigLoader.loadChildren()
1033
+ * │
1034
+ * ▼
1035
+ * LocalizedRouter.customLoadChildren()
1036
+ * │
1037
+ * ├── load original lazy module/routes
1038
+ * ├── translate child routes
1039
+ * ├── return translated Routes
1040
+ * ▼
1041
+ * Angular continues normally
1042
+ *
1043
+ * Angular itself never sees untranslated child routes.
1044
+ *
1045
+ * Design
1046
+ * ======
1047
+ *
1048
+ * The implementation overrides Angular's internal
1049
+ * RouterConfigLoader.loadChildren() method.
1050
+ *
1051
+ * This is intentionally done in one place instead of modifying the router state
1052
+ * afterwards via resetConfig(), because Angular now expects lazy routes to be
1053
+ * resolved through this loader.
1054
+ *
1055
+ * customLoadChildren() reproduces Angular's original implementation almost
1056
+ * verbatim while inserting the localization step immediately before the loaded
1057
+ * Routes are returned.
1058
+ *
1059
+ * Module-based lazy loading requires an additional trick:
1060
+ *
1061
+ * injector.get(ROUTES)
1062
+ *
1063
+ * is intercepted so the ROUTES injection token returns already-localized route
1064
+ * definitions. This avoids having to patch Angular's module loading process.
1065
+ *
1066
+ * Caching
1067
+ * =======
1068
+ *
1069
+ * Angular expects concurrent requests for the same lazy route to share a single
1070
+ * Promise. childrenLoaders reproduces that behaviour using a WeakMap keyed by
1071
+ * Route.
1072
+ *
1073
+ * Compatibility
1074
+ * =============
1075
+ *
1076
+ * This implementation intentionally accesses Angular private APIs:
1077
+ *
1078
+ * - navigationTransitions.configLoader
1079
+ * - RouterConfigLoader.loadChildren()
1080
+ * - _loadedRoutes
1081
+ * - _loadedInjector
1082
+ * - _loadedNgModuleFactory
1083
+ *
1084
+ * These APIs are not stable and may change between Angular releases.
1085
+ *
1086
+ * This file should therefore be considered an Angular-version compatibility
1087
+ * layer. If a future Angular update breaks lazy route localization, this is the
1088
+ * first file that should be investigated.
1089
+ *
1090
+ * Verified against:
1091
+ *
1092
+ * Angular 21.x
1093
+ *
1094
+ * A cleaner implementation using only public Router APIs is planned for a
1095
+ * future major version once Angular exposes sufficient extension points.
1096
+ */
1097
+ class LocalizedRouter extends Router {
1098
+ constructor() {
1099
+ super();
1100
+ this.platformId = inject(PLATFORM_ID);
1101
+ this.compiler = inject(Compiler);
1102
+ this.localize = inject(LocalizeParser);
1103
+ this.childrenLoaders = new WeakMap();
1104
+ // get the configLoader in order to get access to loadChildren().
1105
+ const isBrowser = isPlatformBrowser(this.platformId);
1106
+ // __proto__ is needed for preloaded modules be doesn't work with SSR
1107
+ // @ts-ignore
1108
+ const configLoader = isBrowser
1109
+ ? this.navigationTransitions.configLoader.__proto__
1110
+ : this.navigationTransitions.configLoader;
1111
+ // Overrides default Angular RouterConfigLoader.loadChildren method so we can extend it
1112
+ configLoader.loadChildren = (parentInjector, route) => {
1113
+ if (this.childrenLoaders.get(route)) {
1114
+ return this.childrenLoaders.get(route);
1115
+ }
1116
+ else if (route._loadedRoutes) {
1117
+ return Promise.resolve({
1118
+ routes: route._loadedRoutes,
1119
+ injector: route._loadedInjector,
1120
+ });
1121
+ }
1122
+ if (this.onLoadStartListener) {
1123
+ this.onLoadStartListener(route);
1124
+ }
1125
+ const loader = (async () => {
1126
+ try {
1127
+ const result = await this.customLoadChildren(route, this.compiler, parentInjector, this.onLoadEndListener);
1128
+ route._loadedRoutes = result.routes;
1129
+ route._loadedInjector = result.injector;
1130
+ route._loadedNgModuleFactory = result.factory;
1131
+ return result;
1132
+ }
1133
+ finally {
1134
+ this.childrenLoaders.delete(route);
1135
+ }
1136
+ })();
1137
+ this.childrenLoaders.set(route, loader);
1138
+ return loader;
1139
+ };
1140
+ }
1141
+ async customLoadChildren(route, compiler, parentInjector, onLoadEndListener) {
1142
+ const loaded = await wrapIntoPromise(runInInjectionContext(parentInjector, () => route.loadChildren()));
1143
+ const t = maybeUnwrapDefaultExport(loaded);
1144
+ let factoryOrRoutes;
1145
+ if (t instanceof NgModuleFactory || Array.isArray(t)) {
1146
+ factoryOrRoutes = t;
1147
+ }
1148
+ else {
1149
+ factoryOrRoutes = await compiler.compileModuleAsync(t);
1150
+ }
1151
+ if (onLoadEndListener) {
1152
+ onLoadEndListener(route);
1153
+ }
1154
+ let injector;
1155
+ let rawRoutes;
1156
+ let factory = undefined;
1157
+ if (Array.isArray(factoryOrRoutes)) {
1158
+ rawRoutes = this.localize.initChildRoutes([].concat(...factoryOrRoutes));
1159
+ }
1160
+ else {
1161
+ injector = factoryOrRoutes.create(parentInjector).injector;
1162
+ factory = factoryOrRoutes;
1163
+ // instead of having to overwrite the whole routes function stack,
1164
+ // we simply override the ROUTES injection token
1165
+ const getMethod = injector.get.bind(injector);
1166
+ injector["get"] = (token, notFoundValue, flags) => {
1167
+ const getResult = getMethod(token, notFoundValue, flags);
1168
+ if (token === ROUTES) {
1169
+ return this.localize.initChildRoutes([].concat(...getResult));
1170
+ }
1171
+ else {
1172
+ return getResult;
1173
+ }
1174
+ };
1175
+ rawRoutes = injector
1176
+ .get(ROUTES, [], { optional: true, self: true })
1177
+ .flat();
1178
+ }
1179
+ const routes = rawRoutes.map(standardizeConfig);
1180
+ return { routes, injector, factory };
1181
+ }
1182
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizedRouter, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1183
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizedRouter, providedIn: "root" }); }
1184
+ }
1185
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizedRouter, decorators: [{
1186
+ type: Injectable,
1187
+ args: [{ providedIn: "root" }]
1188
+ }], ctorParameters: () => [] });
1189
+ function standardizeConfig(r) {
1190
+ const children = r.children && r.children.map(standardizeConfig);
1191
+ const c = children ? { ...r, children } : { ...r };
1192
+ if (!c.component &&
1193
+ !c.loadComponent &&
1194
+ (children || c.loadChildren) &&
1195
+ c.outlet &&
1196
+ c.outlet !== PRIMARY_OUTLET) {
1197
+ c.component = _EmptyOutletComponent;
1198
+ }
1199
+ return c;
1200
+ }
1201
+ /**
1202
+ * see
1203
+ * @param value
1204
+ * @returns
1205
+ */
1206
+ function isWrappedDefaultExport(value) {
1207
+ // We use `in` here with a string key `'default'`, because we expect `DefaultExport` objects to be
1208
+ // dynamically imported ES modules with a spec-mandated `default` key. Thus we don't expect that
1209
+ // `default` will be a renamed property.
1210
+ return value && typeof value === "object" && "default" in value;
1211
+ }
1212
+ function maybeUnwrapDefaultExport(input) {
1213
+ // As per `isWrappedDefaultExport`, the `default` key here is generated by the browser and not
1214
+ // subject to property renaming, so we reference it with bracket access.
1215
+ return isWrappedDefaultExport(input) ? input["default"] : input;
1216
+ }
1217
+ /**
1218
+ * see https://github.com/angular/angular/blob/main/packages/router/src/utils/collection.ts#L88
1219
+ * @param value
1220
+ * @returns
1221
+ */
1222
+ function wrapIntoPromise(value) {
1223
+ if (isObservable(value)) {
1224
+ return firstValueFrom(value);
1225
+ }
1226
+ return Promise.resolve(value);
1227
+ }
1228
+
1229
+ class ParserInitializer {
1230
+ /**
1231
+ * CTOR
1232
+ */
1233
+ constructor(injector) {
1234
+ this.injector = injector;
1235
+ }
1236
+ appInitializer() {
1237
+ const res = this.parser.load(this.routes);
1238
+ return res.then(() => {
1239
+ const localize = this.injector.get(LocalizeRouterService);
1240
+ const router = this.injector.get(Router);
1241
+ const settings = this.injector.get(LocalizeRouterSettings);
1242
+ localize.init();
1243
+ if (settings.initialNavigation) {
1244
+ return new Promise(resolve => {
1245
+ // @ts-ignore
1246
+ const oldAfterPreactivation = router.navigationTransitions.afterPreactivation;
1247
+ let firstInit = true;
1248
+ // @ts-ignore
1249
+ router.navigationTransitions.afterPreactivation = () => {
1250
+ if (firstInit) {
1251
+ resolve();
1252
+ firstInit = false;
1253
+ localize.hooks._initializedSubject.next(true);
1254
+ localize.hooks._initializedSubject.complete();
1255
+ }
1256
+ return oldAfterPreactivation();
1257
+ };
1258
+ });
1259
+ }
1260
+ else {
1261
+ localize.hooks._initializedSubject.next(true);
1262
+ localize.hooks._initializedSubject.complete();
1263
+ }
1264
+ });
1265
+ }
1266
+ generateInitializer(parser, routes) {
1267
+ this.parser = parser;
1268
+ this.routes = routes.reduce((a, b) => a.concat(b));
1269
+ return this.appInitializer;
1270
+ }
1271
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ParserInitializer, deps: [{ token: i0.Injector }], target: i0.ɵɵFactoryTarget.Injectable }); }
1272
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ParserInitializer }); }
1273
+ }
1274
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: ParserInitializer, decorators: [{
1275
+ type: Injectable
1276
+ }], ctorParameters: () => [{ type: i0.Injector }] });
1277
+ function getAppInitializer(p, parser, routes) {
1278
+ // DeepCopy needed to prevent RAW_ROUTES mutation
1279
+ const routesCopy = deepCopy(routes);
1280
+ return p.generateInitializer(parser, routesCopy).bind(p);
1281
+ }
1282
+ function createLocalizeRouterProviders(routes, config) {
1283
+ return [
1284
+ {
1285
+ provide: Router,
1286
+ useClass: LocalizedRouter
1287
+ },
1288
+ {
1289
+ provide: LOCALIZE_ROUTER_FORROOT_GUARD,
1290
+ useFactory: provideForRootGuard,
1291
+ deps: [[LocalizeRouterModule, new Optional(), new SkipSelf()]]
1292
+ },
1293
+ { provide: USE_CACHED_LANG, useValue: config.useCachedLang },
1294
+ { provide: ALWAYS_SET_PREFIX, useValue: config.alwaysSetPrefix },
1295
+ { provide: CACHE_NAME, useValue: config.cacheName },
1296
+ { provide: CACHE_MECHANISM, useValue: config.cacheMechanism },
1297
+ { provide: DEFAULT_LANG_FUNCTION, useValue: config.defaultLangFunction },
1298
+ { provide: COOKIE_FORMAT, useValue: config.cookieFormat },
1299
+ { provide: INITIAL_NAVIGATION, useValue: config.initialNavigation },
1300
+ LocalizeRouterSettings,
1301
+ config.parser || { provide: LocalizeParser, useClass: DummyLocalizeParser },
1302
+ {
1303
+ provide: RAW_ROUTES,
1304
+ multi: true,
1305
+ useValue: routes
1306
+ },
1307
+ LocalizeRouterService,
1308
+ ParserInitializer,
1309
+ {
1310
+ provide: APP_INITIALIZER,
1311
+ multi: true,
1312
+ useFactory: getAppInitializer,
1313
+ deps: [ParserInitializer, LocalizeParser, RAW_ROUTES]
1314
+ },
1315
+ {
1316
+ provide: RouteReuseStrategy,
1317
+ useClass: GilsdavReuseStrategy
1318
+ }
1319
+ ];
1320
+ }
1321
+ class LocalizeRouterModule {
1322
+ static forRoot(routes, config = {}) {
1323
+ return {
1324
+ ngModule: LocalizeRouterModule,
1325
+ providers: createLocalizeRouterProviders(routes, config)
1326
+ };
1327
+ }
1328
+ static forChild(routes) {
1329
+ return {
1330
+ ngModule: LocalizeRouterModule,
1331
+ providers: [
1332
+ {
1333
+ provide: RAW_ROUTES,
1334
+ multi: true,
1335
+ useValue: routes
1336
+ }
1337
+ ]
1338
+ };
1339
+ }
1340
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizeRouterModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
1341
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "21.2.17", ngImport: i0, type: LocalizeRouterModule, imports: [CommonModule, RouterModule, TranslateModule, LocalizeRouterPipe], exports: [LocalizeRouterPipe] }); }
1342
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizeRouterModule, imports: [CommonModule, RouterModule, TranslateModule] }); }
1343
+ }
1344
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.17", ngImport: i0, type: LocalizeRouterModule, decorators: [{
1345
+ type: NgModule,
1346
+ args: [{
1347
+ imports: [CommonModule, RouterModule, TranslateModule, LocalizeRouterPipe],
1348
+ exports: [LocalizeRouterPipe]
1349
+ }]
1350
+ }] });
1351
+ function provideForRootGuard(localizeRouterModule) {
1352
+ if (localizeRouterModule) {
1353
+ throw new Error(`LocalizeRouterModule.forRoot() called twice. Lazy loaded modules should use LocalizeRouterModule.forChild() instead.`);
1354
+ }
1355
+ return 'guarded';
1356
+ }
1357
+ function withLocalizeRouter(routes, config = {}) {
1358
+ return {
1359
+ ɵkind: 'LocalizeRouter',
1360
+ ɵproviders: createLocalizeRouterProviders(routes, config)
1361
+ };
1362
+ }
1363
+
1364
+ class LocalizeNgModuleFactory extends NgModuleFactory {
1365
+ constructor(moduleType) {
1366
+ super();
1367
+ this.moduleType = moduleType;
1368
+ this.create = (parentInjector) => {
1369
+ const compiler = parentInjector.get(Compiler);
1370
+ const localize = parentInjector.get(LocalizeParser);
1371
+ const compiled = compiler.compileModuleAndAllComponentsSync(this.moduleType);
1372
+ const moduleRef = compiled.ngModuleFactory.create(parentInjector);
1373
+ const getMethod = moduleRef.injector.get.bind(moduleRef.injector);
1374
+ moduleRef.injector['get'] = (token, notFoundValue) => {
1375
+ const getResult = getMethod(token, notFoundValue);
1376
+ if (token === ROUTES) {
1377
+ // translate lazy routes
1378
+ return localize.initChildRoutes([].concat(...getResult));
1379
+ }
1380
+ else {
1381
+ return getResult;
1382
+ }
1383
+ };
1384
+ return moduleRef;
1385
+ };
1386
+ }
1387
+ }
1388
+ function translateModule(moduleType) {
1389
+ return new LocalizeNgModuleFactory(moduleType);
1390
+ }
1391
+
1392
+ /*
1393
+ * Public API Surface of ngx-translate-router
1394
+ */
1395
+
1396
+ /**
1397
+ * Generated bundle index. Do not edit.
1398
+ */
1399
+
1400
+ export { ALWAYS_SET_PREFIX, CACHE_MECHANISM, CACHE_NAME, COOKIE_FORMAT, CacheMechanism, DEFAULT_LANG_FUNCTION, DummyLocalizeParser, GilsdavReuseStrategy, INITIAL_NAVIGATION, LOCALIZE_ROUTER_FORROOT_GUARD, LocalizeNgModuleFactory, LocalizeParser, LocalizeRouterModule, LocalizeRouterPipe, LocalizeRouterService, LocalizeRouterSettings, LocalizedRouter, ManualParserLoader, ParserInitializer, RAW_ROUTES, USE_CACHED_LANG, getAppInitializer, provideForRootGuard, standardizeConfig, translateModule, withLocalizeRouter, wrapIntoPromise };
1401
+ //# sourceMappingURL=derschmidtler-ngx-translate-router.mjs.map