@acorex/core 19.12.0 → 19.13.0-next.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.
Files changed (75) hide show
  1. package/date-time/index.d.ts +5 -5
  2. package/date-time/lib/calendar.service.d.ts +8 -3
  3. package/date-time/lib/dateTime.config.d.ts +1 -1
  4. package/date-time/lib/datetime.pipe.d.ts +2 -1
  5. package/date-time/lib/{datetime.class.d.ts → datetime.types.d.ts} +2 -2
  6. package/date-time/lib/formatters/date.formatter.d.ts +12 -0
  7. package/date-time/lib/formatters/{datetime-formatter.d.ts → datetime.formatter.d.ts} +3 -1
  8. package/date-time/lib/formatters/time.formatter.d.ts +12 -0
  9. package/date-time/lib/{georgian.calendar.d.ts → gregorian.calendar.d.ts} +2 -2
  10. package/date-time/lib/{jalali.calendar.d.ts → solar-hijri.calendar.d.ts} +8 -8
  11. package/events/index.d.ts +0 -1
  12. package/fesm2022/acorex-core-components.mjs +3 -3
  13. package/fesm2022/acorex-core-config.mjs +3 -3
  14. package/fesm2022/acorex-core-date-time.mjs +282 -119
  15. package/fesm2022/acorex-core-date-time.mjs.map +1 -1
  16. package/fesm2022/acorex-core-events.mjs +4 -10
  17. package/fesm2022/acorex-core-events.mjs.map +1 -1
  18. package/fesm2022/acorex-core-file.mjs +10 -10
  19. package/fesm2022/acorex-core-format.mjs +78 -63
  20. package/fesm2022/acorex-core-format.mjs.map +1 -1
  21. package/fesm2022/acorex-core-image.mjs +3 -3
  22. package/fesm2022/acorex-core-locale-en-AU.profile-BW-_9tgT.mjs +64 -0
  23. package/fesm2022/acorex-core-locale-en-AU.profile-BW-_9tgT.mjs.map +1 -0
  24. package/fesm2022/acorex-core-locale.mjs +341 -0
  25. package/fesm2022/acorex-core-locale.mjs.map +1 -0
  26. package/fesm2022/acorex-core-network.mjs +3 -3
  27. package/fesm2022/acorex-core-pipes.mjs +3 -3
  28. package/fesm2022/acorex-core-platform.mjs +3 -3
  29. package/fesm2022/acorex-core-storage.mjs +9 -9
  30. package/fesm2022/acorex-core-translation.mjs +246 -206
  31. package/fesm2022/acorex-core-translation.mjs.map +1 -1
  32. package/fesm2022/acorex-core-utils.mjs +143 -18
  33. package/fesm2022/acorex-core-utils.mjs.map +1 -1
  34. package/fesm2022/acorex-core-validation.mjs +40 -40
  35. package/format/index.d.ts +2 -2
  36. package/format/lib/format.directive.d.ts +1 -1
  37. package/format/lib/format.module.d.ts +3 -3
  38. package/format/lib/format.service.d.ts +8 -7
  39. package/locale/README.md +3 -0
  40. package/locale/index.d.ts +8 -0
  41. package/locale/lib/formatters/currency.formatter.d.ts +10 -0
  42. package/locale/lib/locale-profile-provider.service.d.ts +24 -0
  43. package/locale/lib/locale.config.d.ts +5 -0
  44. package/locale/lib/locale.module.d.ts +7 -0
  45. package/locale/lib/locale.service.d.ts +20 -0
  46. package/locale/lib/locale.types.d.ts +61 -0
  47. package/locale/lib/profiles/en-AU.profile.d.ts +2 -0
  48. package/locale/lib/profiles/en-US.profile.d.ts +2 -0
  49. package/locale/lib/profiles/fa-IR.profile.d.ts +2 -0
  50. package/package.json +5 -1
  51. package/translation/index.d.ts +1 -1
  52. package/translation/lib/translation-loader.service.d.ts +20 -0
  53. package/translation/lib/translation.config.d.ts +19 -6
  54. package/translation/lib/translation.loader.d.ts +2 -2
  55. package/translation/lib/translation.parser.d.ts +7 -0
  56. package/translation/lib/translation.resolver.d.ts +9 -0
  57. package/translation/lib/translation.service.d.ts +12 -31
  58. package/translation/lib/translation.types.d.ts +8 -2
  59. package/translation/lib/translator.pipe.d.ts +0 -2
  60. package/types/README.md +2 -2
  61. package/utils/index.d.ts +6 -5
  62. package/utils/lib/execution.utils.d.ts +33 -0
  63. package/utils/lib/string.utils.d.ts +4 -0
  64. package/events/lib/event.type.d.ts +0 -5
  65. package/i18n/en/common.json +0 -191
  66. package/i18n/fa/common.json +0 -190
  67. package/utils/lib/string-util.d.ts +0 -6
  68. /package/date-time/lib/formatters/{time-duration-formatter.d.ts → time-duration.formatter.d.ts} +0 -0
  69. /package/date-time/lib/formatters/{timeleft-formatter.d.ts → timeleft.formatter.d.ts} +0 -0
  70. /package/format/lib/formatters/{number-formatter.d.ts → number.formatter.d.ts} +0 -0
  71. /package/format/lib/formatters/{string-formatter.d.ts → string.formatter.d.ts} +0 -0
  72. /package/utils/lib/{color-util.d.ts → color.utils.d.ts} +0 -0
  73. /package/utils/lib/{drawing-util.d.ts → drawing.utils.d.ts} +0 -0
  74. /package/utils/lib/{html-util.d.ts → html-utils.d.ts} +0 -0
  75. /package/utils/lib/{auto-unsubscribe.d.ts → lifecycle-helpers.utils.d.ts} +0 -0
@@ -1,8 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { InjectionToken, inject, Injectable, Directive, Pipe, provideAppInitializer, NgModule } from '@angular/core';
3
- import { AXEventTypes, AXEventService } from '@acorex/core/events';
4
- import { set, get } from 'lodash-es';
5
- import { of, BehaviorSubject, forkJoin, tap, catchError, finalize, shareReplay, startWith, map, distinctUntilChanged, switchMap, firstValueFrom } from 'rxjs';
3
+ import { of, shareReplay, firstValueFrom, BehaviorSubject, switchMap, from } from 'rxjs';
4
+ import { get } from 'lodash-es';
5
+ import { AXLocaleService } from '@acorex/core/locale';
6
+ import { waitFor } from '@acorex/core/utils';
6
7
 
7
8
  const AX_TRANSLATION_CONFIG = new InjectionToken('AX_TRANSLATION_CONFIG', {
8
9
  providedIn: 'root',
@@ -11,9 +12,17 @@ const AX_TRANSLATION_CONFIG = new InjectionToken('AX_TRANSLATION_CONFIG', {
11
12
  },
12
13
  });
13
14
  const AXTranslationDefaultConfig = {
14
- defaultLang: 'en',
15
- defaultScope: 'common',
16
- scopeResolverKey: 'scope',
15
+ defaults: {
16
+ lang: 'en-US',
17
+ scope: 'common',
18
+ },
19
+ preload: {
20
+ langs: ['en-US'],
21
+ scopes: ['common'],
22
+ },
23
+ resolver: {
24
+ scopeKey: 'scope',
25
+ },
17
26
  };
18
27
  function translationConfig(config = {}) {
19
28
  const result = {
@@ -24,7 +33,7 @@ function translationConfig(config = {}) {
24
33
  }
25
34
 
26
35
  class AXTranslationLoaderDefault {
27
- getTranslation(options) {
36
+ load(options) {
28
37
  return of({});
29
38
  }
30
39
  }
@@ -35,225 +44,256 @@ const AX_TRANSLATION_LOADER = new InjectionToken('AX_TRANSLATION_LOADER', {
35
44
  },
36
45
  });
37
46
 
38
- let singletonInstance;
39
- function translateSync(key, options) {
40
- return singletonInstance.translateSync(key, options);
41
- }
42
- class AXTranslationService {
43
- getDefaultLang() {
44
- return this.config.defaultLang;
45
- }
46
- getActiveLang() {
47
- return this.activeLang.getValue();
48
- }
49
- setActiveLang(lang) {
50
- if (lang != this.getActiveLang()) {
51
- this.activeLang.next(lang);
52
- this.eventService.emitEvent({
53
- type: AXEventTypes.AXLanguageChanged,
54
- payload: lang,
55
- });
56
- }
57
- }
58
- /**
59
- * @ignore
60
- */
47
+ class AXTranslationLoaderService {
61
48
  constructor() {
62
49
  this.loader = inject(AX_TRANSLATION_LOADER);
63
50
  this.config = inject(AX_TRANSLATION_CONFIG);
64
- this.eventService = inject(AXEventService);
65
- this.translationCache = {};
66
- this.ongoingRequests = new Map();
67
- this.activeLang = new BehaviorSubject(this.getDefaultLang());
68
- this.langChanges$ = this.activeLang.asObservable();
69
- this.expressionCache = new Map();
70
- this.isExpression = (value) => value.includes('t(');
71
- singletonInstance = this;
51
+ this.cache = new Map();
52
+ this.inflight = new Map();
72
53
  }
73
- loadLanguagesAndScopes(languages, scopes) {
74
- const requests = languages.flatMap((lang) => scopes.map((scope) => {
75
- // Check if translations are already cached
76
- if (this.translationCache[lang]?.[scope]) {
77
- return of(this.translationCache[lang][scope]);
78
- }
79
- // Use the new method to handle ongoing requests and loading
80
- return this.fetchTranslationFiles(lang, scope);
81
- }));
82
- return forkJoin(requests);
83
- }
84
- fetchTranslationFiles(lang, scope) {
85
- const requestKey = `${lang}_${scope}`;
86
- // Return existing observable if the request is already in progress
87
- if (this.ongoingRequests.has(requestKey)) {
88
- return this.ongoingRequests.get(requestKey);
54
+ load(lang, scope) {
55
+ const key = `${lang}:${scope}`;
56
+ console.log('load', key);
57
+ if (this.cache.has(key)) {
58
+ return of(this.cache.get(key));
89
59
  }
90
- // Load translations if not in cache or ongoing requests
91
- const translationObservable = this.loader.getTranslation({ lang, scope }).pipe(tap((translations) => this.setTranslationCache(lang, scope, translations)), catchError((error) => {
92
- this.handleError(`Error loading translations for lang: ${lang}, scope: ${scope}`, error);
93
- return of(null);
94
- }), finalize(() => {
95
- this.eventService.emitEvent({
96
- type: AXEventTypes.AXLanguageLoaded,
97
- payload: lang,
60
+ if (!this.inflight.has(key)) {
61
+ const obs = this.loader.load({ lang, scope }).pipe(shareReplay(1));
62
+ this.inflight.set(key, obs);
63
+ obs.subscribe({
64
+ next: (data) => {
65
+ this.cache.set(key, data);
66
+ this.inflight.delete(key);
67
+ },
68
+ error: () => {
69
+ this.inflight.delete(key);
70
+ },
98
71
  });
99
- this.ongoingRequests.delete(requestKey);
100
- }), shareReplay(1));
101
- this.ongoingRequests.set(requestKey, translationObservable);
102
- return translationObservable;
72
+ }
73
+ return this.inflight.get(key);
103
74
  }
104
- //#region Helpers Methods
105
- /**
106
- * Set translation data into cache
107
- */
108
- setTranslationCache(lang, scope, translations) {
109
- set(this.translationCache, `${lang}.${scope}`, translations);
75
+ peek(lang, scope, key) {
76
+ const cacheKey = `${lang}:${scope}`;
77
+ const data = this.cache.get(cacheKey);
78
+ return get(data, key, null);
79
+ }
80
+ async preload(options) {
81
+ const langs = [...new Set(options?.langs ?? this.config.preload?.langs ?? [this.config.defaults.lang])];
82
+ const scopes = [...new Set(options?.scopes ?? this.config.preload?.scopes ?? [this.config.defaults.scope])];
83
+ const promises = [];
84
+ langs.forEach(lang => {
85
+ scopes.forEach(scope => {
86
+ const p = firstValueFrom(this.load(lang, scope));
87
+ promises.push(p);
88
+ });
89
+ });
90
+ await Promise.all(promises);
110
91
  }
111
- /**
112
- * Get the translation from the cache or fallback to loading
113
- */
114
- getTranslationFromCache(lang, scope, key) {
115
- return get(this.translationCache, `${lang}.${scope}.${key}`, key);
92
+ clear() {
93
+ this.cache.clear();
94
+ this.inflight.clear();
116
95
  }
117
- isLangAvailable(lang, scope = null) {
118
- return !this.translationCache[lang] && (!scope || !this.translationCache[lang][scope]);
96
+ getFallbackLangs(lang) {
97
+ return this.config.fallbacks?.langs?.[lang] ?? [];
119
98
  }
120
- translateKey(key, lang, scope, params) {
121
- // Trigger async preloading without blocking execution
122
- this.loadLanguagesAndScopes([lang], [scope]).pipe(startWith()).subscribe({
123
- error: (err) => {
124
- this.handleError(`Error preloading translations for ${lang}, ${scope}`, err);
125
- },
126
- });
127
- // Retrieve the translation from the cache or fallback to the key
128
- let translation = this.getTranslationFromCache(lang, scope, key);
129
- // Replace params like {{ name }}
130
- if (params && typeof translation === 'string') {
131
- Object.keys(params).forEach((paramKey) => {
132
- translation = translation.replace(new RegExp(`{{\\s*${paramKey}\\s*}}`, 'g'), params[paramKey]);
133
- });
134
- }
135
- return translation || key;
99
+ getFallbackScopes(scope) {
100
+ return this.config.fallbacks?.scopes?.[scope] ?? [];
136
101
  }
137
- decodeExpression(expression) {
138
- if (this.expressionCache.has(expression)) {
139
- return this.expressionCache.get(expression);
102
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AXTranslationLoaderService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
103
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AXTranslationLoaderService, providedIn: 'root' }); }
104
+ }
105
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AXTranslationLoaderService, decorators: [{
106
+ type: Injectable,
107
+ args: [{ providedIn: 'root' }]
108
+ }] });
109
+
110
+ class TranslationParserService {
111
+ constructor() {
112
+ this.legacyRegex = /t\(["']([^"']+)["']\s*(?:,\s*(\{.*?\}))?\)/g;
113
+ this.inlineRegex = /@([a-zA-Z\-]+:)?([a-zA-Z0-9_-]+):([a-zA-Z0-9_.:-]+)(?=[\\s.,;!?)]|$)/g;
114
+ }
115
+ isExpression(value) {
116
+ return typeof value === 'string' && (value.includes('t(') || value.includes('@'));
117
+ }
118
+ parse(expression, currentLang, currentScope) {
119
+ const tokens = [];
120
+ // Extract inline references like @lang:scope:key or @scope:key
121
+ let inlineMatch;
122
+ while ((inlineMatch = this.inlineRegex.exec(expression)) !== null) {
123
+ const langPart = inlineMatch[1]?.replace(':', '') ?? null;
124
+ const scope = inlineMatch[2] ?? currentScope;
125
+ const key = inlineMatch[3];
126
+ const fullMatch = inlineMatch[0];
127
+ tokens.push({ lang: langPart || currentLang, scope, key, fullMatch });
128
+ }
129
+ // Extract legacy expressions
130
+ let legacyMatch;
131
+ while ((legacyMatch = this.legacyRegex.exec(expression)) !== null) {
132
+ const key = legacyMatch[1];
133
+ const rawOptions = legacyMatch[2];
134
+ let scope = currentScope;
135
+ let lang = currentLang;
136
+ if (rawOptions) {
137
+ try {
138
+ const jsonString = rawOptions
139
+ .replace(/(['"])?([a-zA-Z0-9_]+)(['"])?\s*:/g, '"$2":')
140
+ .replace(/'/g, '"');
141
+ const options = JSON.parse(jsonString);
142
+ scope = options.scope || scope;
143
+ lang = options.lang || lang;
144
+ }
145
+ catch (err) {
146
+ console.error('Invalid translation expression options:', err);
147
+ }
148
+ }
149
+ tokens.push({ key, scope, lang, fullMatch: legacyMatch[0] });
140
150
  }
141
- const regex = /t\(["']([^"']+)["']\s*(?:,\s*(\{.*?\}))?\)/g;
142
- const matches = [];
143
- let match;
144
- while ((match = regex.exec(expression)) !== null) {
145
- const key = match[1];
146
- const rawOptions = match[2];
147
- if (!rawOptions) {
148
- matches.push({ key, scope: null, lang: null, fullMatch: match[0] });
149
- continue;
151
+ return tokens;
152
+ }
153
+ }
154
+
155
+ class TranslationResolverService {
156
+ constructor() {
157
+ this.loaderService = inject(AXTranslationLoaderService);
158
+ this.cache = new Map();
159
+ this.inflight = new Map();
160
+ this.parser = new TranslationParserService();
161
+ }
162
+ async resolve(lang, scope, key, params) {
163
+ const cacheKey = `${lang}:${scope}`;
164
+ // Load and cache if not present
165
+ if (!this.cache.has(cacheKey)) {
166
+ if (!this.inflight.has(cacheKey)) {
167
+ const fetchPromise = firstValueFrom(this.loaderService.load(lang, scope));
168
+ this.inflight.set(cacheKey, fetchPromise);
169
+ const data = await fetchPromise;
170
+ this.cache.set(cacheKey, data);
171
+ this.inflight.delete(cacheKey);
150
172
  }
151
- try {
152
- const jsonString = rawOptions
153
- .replace(/(['"])?([a-zA-Z0-9_]+)(['"])?\s*:/g, '"$2":') // Ensure keys are quoted
154
- .replace(/'/g, '"'); // Replace single quotes with double quotes
155
- const options = JSON.parse(jsonString);
156
- matches.push({
157
- key,
158
- scope: options.scope || null,
159
- lang: options.lang || null,
160
- fullMatch: match[0],
161
- });
173
+ else {
174
+ await this.inflight.get(cacheKey); // wait for the inflight fetch
162
175
  }
163
- catch (error) {
164
- this.handleError(`Failed to parse options for key "${key}":`, error);
165
- matches.push({ key, scope: null, lang: null, fullMatch: match[0] });
176
+ }
177
+ const translations = this.cache.get(cacheKey);
178
+ let result = get(translations, key, key);
179
+ // Recursively resolve references
180
+ if (typeof result === 'string' && this.parser.isExpression(result)) {
181
+ const tokens = this.parser.parse(result, lang, scope);
182
+ if (tokens.length > 0) {
183
+ const first = tokens[0];
184
+ result = await this.resolve(first.lang || lang, first.scope || scope, first.key, params);
166
185
  }
167
186
  }
168
- this.expressionCache.set(expression, matches);
169
- return matches;
187
+ return this.format(result, params);
188
+ }
189
+ format(text, params) {
190
+ if (!params || typeof text !== 'string')
191
+ return text;
192
+ return Object.keys(params).reduce((out, key) => {
193
+ const re = new RegExp(`{{\s*${key}\s*}}`, 'g');
194
+ return out.replace(re, params[key]);
195
+ }, text);
170
196
  }
171
- handleError(message, error) {
172
- console.error(message, error);
173
- this.eventService.emitEvent({
174
- type: 'error',
175
- payload: { message, error },
197
+ }
198
+
199
+ let singletonInstance;
200
+ function translateSync(key, options) {
201
+ return singletonInstance.translateSync(key, options);
202
+ }
203
+ class AXTranslationService {
204
+ constructor() {
205
+ this.localeService = inject(AXLocaleService);
206
+ this.config = inject(AX_TRANSLATION_CONFIG);
207
+ this.loader = inject(AXTranslationLoaderService);
208
+ this.parser = new TranslationParserService();
209
+ this.resolver = new TranslationResolverService();
210
+ this.activeLang = new BehaviorSubject('en-US');
211
+ this.langChanges$ = this.activeLang.asObservable();
212
+ singletonInstance = this;
213
+ this.localeService.profileChanged$.subscribe((locale) => {
214
+ if (locale?.localeInfo?.code) {
215
+ this.setActiveLang(locale.localeInfo.code);
216
+ }
176
217
  });
177
218
  }
178
- //#endregion
179
- //#region Async Translation Methods
180
- translateText(text, contextLang, contextScope, params) {
181
- if (!this.isExpression(text)) {
182
- return this.loadLanguagesAndScopes([contextLang], [contextScope]).pipe(map(() => this.translateKey(text, contextLang, contextScope, params)), catchError((error) => {
183
- this.handleError(`Error during translation:`, error);
184
- return of(text); // Fallback to the original text
185
- }));
219
+ setActiveLang(lang) {
220
+ if (lang && lang !== this.activeLang.getValue()) {
221
+ this.activeLang.next(lang);
186
222
  }
187
- const matches = this.decodeExpression(text);
188
- // Extract unique languages and scopes for batch loading
189
- const langScopeSet = new Set(matches.map(({ lang, scope }) => `${lang || contextLang}_${scope || contextScope}`));
190
- const langs = Array.from(new Set(Array.from(langScopeSet).map((pair) => pair.split('_')[0])));
191
- const scopes = Array.from(new Set(Array.from(langScopeSet).map((pair) => pair.split('_')[1])));
192
- // Load all required languages and scopes
193
- return this.loadLanguagesAndScopes(langs, scopes).pipe(map(() => {
194
- // Resolve translations after loading
195
- const translations = matches.reduce((acc, { key, scope, lang, fullMatch }) => {
196
- const resolvedScope = scope || contextScope;
197
- const resolvedLang = lang || contextLang;
198
- acc[fullMatch] = this.translateKey(key, resolvedLang, resolvedScope, params);
199
- return acc;
200
- }, {});
201
- // Replace all matches in the text with their resolved translations
202
- return matches.reduce((result, { fullMatch }) => result.replace(new RegExp(fullMatch.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'), 'g'), translations[fullMatch]), text);
203
- }), catchError((error) => {
204
- this.handleError(`Error during translation:`, error);
205
- return of(text); // Fallback to the original text
206
- }));
223
+ }
224
+ getActiveLang() {
225
+ return this.activeLang.getValue();
207
226
  }
208
227
  translate$(text, options) {
209
- if (options?.lang) {
210
- return this.translateText(text, options.lang, options?.scope ?? this.config.defaultScope, options?.params);
211
- }
212
- return this.langChanges$.pipe(startWith(this.getActiveLang()), distinctUntilChanged(), switchMap((lang) => {
213
- return this.translateText(text, lang, options?.scope ?? this.config.defaultScope, options?.params);
228
+ if (!text)
229
+ return of('');
230
+ const staticOptions = { ...options };
231
+ const staticScope = staticOptions.scope ?? this.config.defaults.scope;
232
+ return this.langChanges$.pipe(switchMap(lang => {
233
+ return from(this.translateAsync(text, {
234
+ ...staticOptions,
235
+ lang: staticOptions.lang ?? lang,
236
+ scope: staticScope
237
+ }));
214
238
  }));
215
239
  }
216
240
  async translateAsync(text, options) {
217
- return firstValueFrom(this.translate$(text, options));
241
+ const lang = options?.lang ?? this.getActiveLang() ?? this.config.defaults.lang;
242
+ const scope = options?.scope ?? this.config.defaults.scope;
243
+ const params = options?.params;
244
+ if (!this.parser.isExpression(text)) {
245
+ const resolved = await this.resolver.resolve(lang, scope, text, params);
246
+ return this.resolver.format(resolved, params);
247
+ }
248
+ const tokens = this.parser.parse(text, lang, scope);
249
+ const results = await Promise.all(tokens.map(token => this.resolver.resolve(token.lang ?? lang, token.scope ?? scope, token.key, params)
250
+ .then(translated => [token.fullMatch, this.resolver.format(translated, params)])));
251
+ return results.reduce((output, [match, translated]) => output.replace(match, translated), text);
218
252
  }
219
- //#endregion
220
- //#region Sync Translation Methods
221
253
  translateSync(text, options) {
222
- if (this.isExpression(text)) {
223
- return this.translateTextSync(text, options?.lang ?? this.getActiveLang(), options?.scope ?? this.config.defaultScope);
224
- }
225
- else {
226
- return this.translateKey(text, options?.lang ?? this.getActiveLang(), options?.scope ?? this.config.defaultScope);
254
+ const lang = options?.lang ?? this.getActiveLang() ?? this.config.defaults.lang;
255
+ const scope = options?.scope ?? this.config.defaults.scope;
256
+ const params = options?.params;
257
+ const waitForLoad = options?.waitForLoad ?? false;
258
+ const timeoutMs = options?.timeoutMs ?? 100;
259
+ if (!this.parser.isExpression(text)) {
260
+ let translated = this.loader.peek(lang, scope, text);
261
+ if (!translated && waitForLoad) {
262
+ translated = waitFor(() => this.loader.peek(lang, scope, text), () => this.loader.load(lang, scope).subscribe(), timeoutMs);
263
+ }
264
+ return this.resolver.format(translated ?? text, params);
227
265
  }
228
- }
229
- translateTextSync(text, contextLang, contextScope, params) {
230
- const matches = this.decodeExpression(text);
231
- const translations = matches.reduce((acc, { key, scope, lang, fullMatch }) => {
232
- const resolvedScope = scope || contextScope;
233
- const resolvedLang = lang || contextLang;
234
- // Cache translation to avoid redundant processing
235
- if (!acc[fullMatch]) {
236
- acc[fullMatch] = this.translateKey(key, resolvedLang, resolvedScope, params);
266
+ const tokens = this.parser.parse(text, lang, scope);
267
+ let result = text;
268
+ for (const token of tokens) {
269
+ const targetLang = token.lang ?? lang;
270
+ const targetScope = token.scope ?? scope;
271
+ const key = token.key;
272
+ let translation = this.loader.peek(targetLang, targetScope, key);
273
+ if (!translation) {
274
+ translation = waitFor(() => this.loader.peek(targetLang, targetScope, key), () => this.loader.load(targetLang, targetScope).subscribe(), waitForLoad ? timeoutMs : 0) ?? key;
237
275
  }
238
- return acc;
239
- }, {});
240
- // Replace all matches in one go
241
- return matches.reduce((result, { fullMatch }) => result.replace(fullMatch, translations[fullMatch]), text);
276
+ result = result.replace(token.fullMatch, this.resolver.format(translation, params));
277
+ }
278
+ return result;
242
279
  }
243
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: AXTranslationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
244
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: AXTranslationService, providedIn: 'root' }); }
280
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AXTranslationService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
281
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AXTranslationService, providedIn: 'root' }); }
245
282
  }
246
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: AXTranslationService, decorators: [{
283
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AXTranslationService, decorators: [{
247
284
  type: Injectable,
248
- args: [{ providedIn: 'root' }]
285
+ args: [{
286
+ providedIn: 'root'
287
+ }]
249
288
  }], ctorParameters: () => [] });
250
289
 
251
290
  const loadTranslationScope = (route, state) => {
252
- const translatorService = inject(AXTranslationService);
291
+ const translationLoaderService = inject(AXTranslationLoaderService);
292
+ const translationService = inject(AXTranslationService);
253
293
  const config = inject(AX_TRANSLATION_CONFIG);
254
- const scopeValue = route.data[config.scopeResolverKey];
294
+ const scopeValue = route.data[config.resolver?.scopeKey];
255
295
  const scopes = Array.isArray(scopeValue) ? scopeValue : [scopeValue];
256
- return translatorService.loadLanguagesAndScopes([translatorService.getActiveLang()], scopes);
296
+ return translationLoaderService.preload({ langs: [translationService.getActiveLang()], scopes });
257
297
  };
258
298
 
259
299
  class AXTranslatorDirective {
@@ -270,17 +310,17 @@ class AXTranslatorDirective {
270
310
  },
271
311
  });
272
312
  }
273
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: AXTranslatorDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }, { token: AXTranslationService }], target: i0.ɵɵFactoryTarget.Directive }); }
274
- static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.5", type: AXTranslatorDirective, isStandalone: true, selector: "[translate]", ngImport: i0 }); }
313
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AXTranslatorDirective, deps: [{ token: i0.TemplateRef }, { token: i0.ViewContainerRef }, { token: AXTranslationService }], target: i0.ɵɵFactoryTarget.Directive }); }
314
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "19.2.8", type: AXTranslatorDirective, isStandalone: true, selector: "[translate]", ngImport: i0 }); }
275
315
  }
276
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: AXTranslatorDirective, decorators: [{
316
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AXTranslatorDirective, decorators: [{
277
317
  type: Directive,
278
318
  args: [{ selector: '[translate]' }]
279
319
  }], ctorParameters: () => [{ type: i0.TemplateRef }, { type: i0.ViewContainerRef }, { type: AXTranslationService }] });
280
320
 
281
321
  class AXTranslatorPipe {
282
- constructor(service) {
283
- this.service = service;
322
+ constructor() {
323
+ this.service = inject(AXTranslationService);
284
324
  }
285
325
  transform(key, options) {
286
326
  if (!key) {
@@ -288,40 +328,40 @@ class AXTranslatorPipe {
288
328
  }
289
329
  return this.service.translate$(key, options);
290
330
  }
291
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: AXTranslatorPipe, deps: [{ token: AXTranslationService }], target: i0.ɵɵFactoryTarget.Pipe }); }
292
- static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.5", ngImport: i0, type: AXTranslatorPipe, isStandalone: true, name: "translate" }); }
331
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AXTranslatorPipe, deps: [], target: i0.ɵɵFactoryTarget.Pipe }); }
332
+ static { this.ɵpipe = i0.ɵɵngDeclarePipe({ minVersion: "14.0.0", version: "19.2.8", ngImport: i0, type: AXTranslatorPipe, isStandalone: true, name: "translate" }); }
293
333
  }
294
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: AXTranslatorPipe, decorators: [{
334
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AXTranslatorPipe, decorators: [{
295
335
  type: Pipe,
296
336
  args: [{
297
337
  name: 'translate',
298
338
  pure: true,
299
339
  }]
300
- }], ctorParameters: () => [{ type: AXTranslationService }] });
340
+ }] });
301
341
 
302
- function initializeApp(translatorService, config) {
342
+ function initializeApp(translatorService) {
303
343
  return () => {
304
- return translatorService.loadLanguagesAndScopes(config.preloadLangs ?? [config.defaultLang], config.preloadScopes ?? [config.defaultScope]);
344
+ return translatorService.preload();
305
345
  };
306
346
  }
307
347
  class AXTranslationModule {
308
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: AXTranslationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
309
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.5", ngImport: i0, type: AXTranslationModule, imports: [AXTranslatorPipe, AXTranslatorDirective], exports: [AXTranslatorPipe, AXTranslatorDirective] }); }
310
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: AXTranslationModule, providers: [
348
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AXTranslationModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
349
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "19.2.8", ngImport: i0, type: AXTranslationModule, imports: [AXTranslatorPipe, AXTranslatorDirective], exports: [AXTranslatorPipe, AXTranslatorDirective] }); }
350
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AXTranslationModule, providers: [
311
351
  provideAppInitializer(() => {
312
- const initializerFn = initializeApp(inject(AXTranslationService), inject(AX_TRANSLATION_CONFIG));
352
+ const initializerFn = initializeApp(inject(AXTranslationLoaderService));
313
353
  return initializerFn();
314
354
  }),
315
355
  ] }); }
316
356
  }
317
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.5", ngImport: i0, type: AXTranslationModule, decorators: [{
357
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "19.2.8", ngImport: i0, type: AXTranslationModule, decorators: [{
318
358
  type: NgModule,
319
359
  args: [{
320
360
  imports: [AXTranslatorPipe, AXTranslatorDirective],
321
361
  exports: [AXTranslatorPipe, AXTranslatorDirective],
322
362
  providers: [
323
363
  provideAppInitializer(() => {
324
- const initializerFn = initializeApp(inject(AXTranslationService), inject(AX_TRANSLATION_CONFIG));
364
+ const initializerFn = initializeApp(inject(AXTranslationLoaderService));
325
365
  return initializerFn();
326
366
  }),
327
367
  ],