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