ngx-kit 9.0.0 → 9.0.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.
- package/fesm2022/ngx-kit-lazy-i18n.mjs +141 -53
- package/fesm2022/ngx-kit-lazy-i18n.mjs.map +1 -1
- package/lazy-i18n/README.md +86 -20
- package/package.json +1 -1
- package/types/ngx-kit-lazy-i18n.d.ts +83 -15
|
@@ -4,35 +4,8 @@ import { DOCUMENT } from '@angular/common';
|
|
|
4
4
|
import { firstValueFrom, defer, from, retry } from 'rxjs';
|
|
5
5
|
import { HttpClient } from '@angular/common/http';
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
langs;
|
|
10
|
-
defaultLang;
|
|
11
|
-
/** Looked up (in order) when a key is empty/missing in the active language. */
|
|
12
|
-
fallbackLangs;
|
|
13
|
-
/** Default `'key'` (returns the key itself). */
|
|
14
|
-
missing;
|
|
15
|
-
/** Log each missing key once. Default: `isDevMode()`. */
|
|
16
|
-
warnMissing;
|
|
17
|
-
/** Replace the default `{{ name }}` interpolation (ICU, plurals, ...). */
|
|
18
|
-
transpile;
|
|
19
|
-
/** Custom loader class (must be `@Injectable({providedIn:'root'})` or constructible without args). Default: HTTP. */
|
|
20
|
-
loader = HttpI18nLoader;
|
|
21
|
-
/** Default HTTP loader: base URL. Default `'/i18n'` -> `/i18n/fa`, `/i18n/setting/fa`. */
|
|
22
|
-
baseUrl;
|
|
23
|
-
/** Default HTTP loader: file extension appended to the URL, e.g. `'.json'`. Default `'.json'`. */
|
|
24
|
-
fileExtension = '.json';
|
|
25
|
-
/** Default HTTP loader: full control over the URL. */
|
|
26
|
-
url;
|
|
27
|
-
/** Retries per failed request. Default `1`. */
|
|
28
|
-
retries;
|
|
29
|
-
/** localStorage key used to remember the language. `null` disables. Default `'lazy-i18n.lang'`. */
|
|
30
|
-
storageKey;
|
|
31
|
-
/** Pick the initial language from `navigator.languages` when nothing is stored. Default `false`. */
|
|
32
|
-
detectBrowserLang;
|
|
33
|
-
/** Keep `<html lang dir>` in sync. Default `true`. */
|
|
34
|
-
manageDocument;
|
|
35
|
-
}
|
|
7
|
+
/** Defaults applied by `provideI18n()`. */
|
|
8
|
+
const I18N_DEFAULTS = { fileExtension: '.json', retries: 1 };
|
|
36
9
|
const I18N_CONFIG = new InjectionToken('I18N_CONFIG');
|
|
37
10
|
const I18N_LOADER = new InjectionToken('I18N_LOADER');
|
|
38
11
|
|
|
@@ -95,25 +68,62 @@ const RTL_LANGS = new Set(['ar', 'fa', 'he', 'ur', 'ps', 'sd', 'ug', 'yi', 'ckb'
|
|
|
95
68
|
function marker(key) {
|
|
96
69
|
return key;
|
|
97
70
|
}
|
|
71
|
+
/** Copy only the properties of `patch` that are not `undefined` (so an omitted option never wipes a default). */
|
|
72
|
+
function assignDefined(target, patch) {
|
|
73
|
+
for (const k of Object.keys(patch)) {
|
|
74
|
+
if (patch[k] !== undefined)
|
|
75
|
+
target[k] = patch[k];
|
|
76
|
+
}
|
|
77
|
+
return target;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Build `{base}/{scope}/{lang}{ext}` without ever producing `undefined`, `//` or a trailing slash problem:
|
|
81
|
+
* ("/i18n", undefined, "fa", ".json") -> "/i18n/fa.json"
|
|
82
|
+
* ("https://x.com/", "setting", "fa", "json") -> "https://x.com/setting/fa.json"
|
|
83
|
+
* (undefined | "", undefined, "fa") -> "/fa"
|
|
84
|
+
* The protocol part of `base` ("https://") is left alone.
|
|
85
|
+
*/
|
|
86
|
+
function buildI18nUrl(base, scope, lang, ext = '') {
|
|
87
|
+
const trim = (s) => s.trim().replace(/^\/+|\/+$/g, '');
|
|
88
|
+
const e = ext ? (ext.startsWith('.') ? ext : '.' + ext) : '';
|
|
89
|
+
const path = [scope ? trim(scope) : '', trim(lang) + e]
|
|
90
|
+
.filter(Boolean)
|
|
91
|
+
.join('/')
|
|
92
|
+
.replace(/\/{2,}/g, '/');
|
|
93
|
+
return `${(base ?? '').trim().replace(/\/+$/, '')}/${path}`;
|
|
94
|
+
}
|
|
98
95
|
|
|
99
96
|
const TAG = '[lazy-i18n]';
|
|
97
|
+
const EMPTY = [];
|
|
100
98
|
class I18nService {
|
|
101
99
|
cfg = inject(I18N_CONFIG);
|
|
102
100
|
loader = inject(I18N_LOADER);
|
|
103
101
|
doc = inject(DOCUMENT);
|
|
104
|
-
_lang = signal(this.cfg.defaultLang, /* @ts-ignore */
|
|
102
|
+
_lang = signal(this.cfg.defaultLang ?? '', /* @ts-ignore */
|
|
105
103
|
...(ngDevMode ? [{ debugName: "_lang" }] : /* istanbul ignore next */ []));
|
|
106
104
|
_rev = signal(0, /* @ts-ignore */
|
|
107
105
|
...(ngDevMode ? [{ debugName: "_rev" }] : /* istanbul ignore next */ []));
|
|
106
|
+
_langs = signal(this.cfg.langs ?? EMPTY, /* @ts-ignore */
|
|
107
|
+
...(ngDevMode ? [{ debugName: "_langs" }] : /* istanbul ignore next */ []));
|
|
108
|
+
_ready = signal(false, /* @ts-ignore */
|
|
109
|
+
...(ngDevMode ? [{ debugName: "_ready" }] : /* istanbul ignore next */ []));
|
|
108
110
|
/** Active language (reactive). */
|
|
109
111
|
lang = this._lang.asReadonly();
|
|
110
112
|
/** Bumps whenever the active language or any dictionary changes. */
|
|
111
113
|
revision = this._rev.asReadonly();
|
|
112
114
|
dir = computed(() => RTL_LANGS.has(this._lang().split('-')[0]) ? 'rtl' : 'ltr', /* @ts-ignore */
|
|
113
115
|
...(ngDevMode ? [{ debugName: "dir" }] : /* istanbul ignore next */ []));
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
116
|
+
/** Supported languages (a signal: they may be supplied after startup via `init()` / `configure()`). */
|
|
117
|
+
langs = this._langs.asReadonly();
|
|
118
|
+
/** `true` once the first language has been activated. Handy for a splash screen. */
|
|
119
|
+
ready = this._ready.asReadonly();
|
|
120
|
+
// read live, so values supplied later through configure() are honoured
|
|
121
|
+
get fallbacks() {
|
|
122
|
+
return this.cfg.fallbackLangs ?? EMPTY;
|
|
123
|
+
}
|
|
124
|
+
get warn() {
|
|
125
|
+
return this.cfg.warnMissing ?? isDevMode();
|
|
126
|
+
}
|
|
117
127
|
dicts = new Map(); // lang -> flat map
|
|
118
128
|
loaded = new Set(); // `${lang}|${scope}`
|
|
119
129
|
inflight = new Map();
|
|
@@ -191,8 +201,55 @@ class I18nService {
|
|
|
191
201
|
* Load global + all registered scopes (and fallback languages) for `lang`, THEN activate it.
|
|
192
202
|
* Nothing renders half-translated and no "missing" flash occurs.
|
|
193
203
|
*/
|
|
194
|
-
|
|
195
|
-
|
|
204
|
+
use(lang) {
|
|
205
|
+
const p = this.activate(lang);
|
|
206
|
+
// tracked so whenReady() / i18nReadyGuard also wait for an in-flight language change
|
|
207
|
+
const tracked = p
|
|
208
|
+
.then(() => undefined, () => undefined)
|
|
209
|
+
.then(() => {
|
|
210
|
+
this.pending.delete(tracked);
|
|
211
|
+
});
|
|
212
|
+
this.pending.add(tracked);
|
|
213
|
+
return p;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Complete the configuration and activate the first language in one call. Use it from your own
|
|
217
|
+
* APP_INITIALIZER when the language list is only known at runtime (e.g. from the backend):
|
|
218
|
+
*
|
|
219
|
+
* ```ts
|
|
220
|
+
* await i18n.init({ langs: ['fa', 'en'], defaultLang: 'fa', baseUrl: apiUrl, lang: user.lang });
|
|
221
|
+
* ```
|
|
222
|
+
* `lang` is used when it is in `langs`; otherwise stored > browser (opt-in) > `defaultLang`.
|
|
223
|
+
* Rejects when the files cannot be loaded, so the initializer can show an error page.
|
|
224
|
+
*/
|
|
225
|
+
init(options = {}) {
|
|
226
|
+
const { lang, ...patch } = options;
|
|
227
|
+
this.configure(patch);
|
|
228
|
+
const wanted = lang && (this.cfg.langs ?? EMPTY).includes(lang) ? lang : this.detectInitialLang();
|
|
229
|
+
return this.use(wanted);
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Update options after startup (languages, baseUrl, fallbacks, ...). Already downloaded files stay cached.
|
|
233
|
+
* `loader` and `autoInit` can only be set in `provideI18n()`.
|
|
234
|
+
*/
|
|
235
|
+
configure(patch) {
|
|
236
|
+
assignDefined(this.cfg, patch);
|
|
237
|
+
this.syncLangs();
|
|
238
|
+
if (patch.defaultLang && !this._ready())
|
|
239
|
+
this._lang.set(patch.defaultLang);
|
|
240
|
+
}
|
|
241
|
+
syncLangs() {
|
|
242
|
+
const l = this.cfg.langs ?? EMPTY;
|
|
243
|
+
if (l !== this._langs())
|
|
244
|
+
this._langs.set(l);
|
|
245
|
+
}
|
|
246
|
+
async activate(lang) {
|
|
247
|
+
this.syncLangs();
|
|
248
|
+
const langs = this.cfg.langs ?? EMPTY;
|
|
249
|
+
if (!langs.length) {
|
|
250
|
+
throw new Error(`${TAG} No languages configured. Pass "langs" to provideI18n() or call I18nService.init({ langs, defaultLang }) first.`);
|
|
251
|
+
}
|
|
252
|
+
if (!langs.includes(lang))
|
|
196
253
|
throw new Error(`${TAG} Unsupported language "${lang}"`);
|
|
197
254
|
const token = ++this.useToken;
|
|
198
255
|
do {
|
|
@@ -202,6 +259,7 @@ class I18nService {
|
|
|
202
259
|
return; // a newer use() won
|
|
203
260
|
this._lang.set(lang);
|
|
204
261
|
this._rev.update((v) => v + 1);
|
|
262
|
+
this._ready.set(true);
|
|
205
263
|
this.persist(lang);
|
|
206
264
|
this.applyDocument(lang);
|
|
207
265
|
}
|
|
@@ -211,10 +269,11 @@ class I18nService {
|
|
|
211
269
|
}
|
|
212
270
|
/** Language to start with: stored > browser (opt-in) > default. */
|
|
213
271
|
detectInitialLang() {
|
|
272
|
+
const langs = this.cfg.langs ?? EMPTY;
|
|
214
273
|
const key = this.storageKey();
|
|
215
274
|
try {
|
|
216
275
|
const s = key ? localStorage.getItem(key) : null;
|
|
217
|
-
if (s &&
|
|
276
|
+
if (s && langs.includes(s))
|
|
218
277
|
return s;
|
|
219
278
|
}
|
|
220
279
|
catch {
|
|
@@ -222,19 +281,25 @@ class I18nService {
|
|
|
222
281
|
}
|
|
223
282
|
if (this.cfg.detectBrowserLang && typeof navigator !== 'undefined') {
|
|
224
283
|
for (const n of navigator.languages ?? [navigator.language]) {
|
|
225
|
-
if (
|
|
284
|
+
if (langs.includes(n))
|
|
226
285
|
return n;
|
|
227
286
|
const base = n.split('-')[0];
|
|
228
|
-
if (
|
|
287
|
+
if (langs.includes(base))
|
|
229
288
|
return base;
|
|
230
289
|
}
|
|
231
290
|
}
|
|
232
|
-
|
|
291
|
+
const def = this.cfg.defaultLang ?? '';
|
|
292
|
+
return langs.includes(def) ? def : (langs[0] ?? def);
|
|
233
293
|
}
|
|
234
294
|
// ───────────────────────── scopes ─────────────────────────
|
|
235
295
|
/** Register a scope and start loading it for the active language. Idempotent. Used by `provideI18nScope`. */
|
|
236
296
|
registerScope(scope) {
|
|
297
|
+
if (!scope)
|
|
298
|
+
return;
|
|
237
299
|
this.scopes.add(scope);
|
|
300
|
+
// before the first activation only remember it: init()/use() loads every registered scope
|
|
301
|
+
if (!this._ready())
|
|
302
|
+
return;
|
|
238
303
|
const lang = this._lang();
|
|
239
304
|
const p = Promise.all([lang, ...this.fallbacks.filter((f) => f !== lang)].map((l) => this.fetch(l, scope).catch((e) => {
|
|
240
305
|
console.error(`${TAG} Failed to load scope "${scope}" (${l})`, e);
|
|
@@ -265,6 +330,7 @@ class I18nService {
|
|
|
265
330
|
this._rev.update((v) => v + 1);
|
|
266
331
|
}
|
|
267
332
|
fetch(lang, scope) {
|
|
333
|
+
scope = scope || undefined; // never hand '' / null to a loader
|
|
268
334
|
const id = `${lang}|${scope ?? ''}`;
|
|
269
335
|
if (this.loaded.has(id))
|
|
270
336
|
return Promise.resolve();
|
|
@@ -444,13 +510,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
|
|
|
444
510
|
args: ['trPrefix']
|
|
445
511
|
}] } });
|
|
446
512
|
|
|
447
|
-
/**
|
|
513
|
+
/**
|
|
514
|
+
* Default loader: GET `{baseUrl}/{lang}{ext}` and `{baseUrl}/{scope}/{lang}{ext}`.
|
|
515
|
+
* Goes through your HttpClient interceptors.
|
|
516
|
+
* Config is read on every request, so values supplied later via `I18nService.init()` /
|
|
517
|
+
* `configure()` (for example `baseUrl`) are picked up.
|
|
518
|
+
*/
|
|
448
519
|
class HttpI18nLoader {
|
|
449
520
|
http = inject(HttpClient);
|
|
450
521
|
cfg = inject(I18N_CONFIG);
|
|
451
522
|
load(lang, scope) {
|
|
452
|
-
const { url, baseUrl
|
|
453
|
-
const u = url
|
|
523
|
+
const { url, baseUrl, fileExtension } = this.cfg;
|
|
524
|
+
const u = url
|
|
525
|
+
? url(lang, scope)
|
|
526
|
+
: buildI18nUrl(baseUrl ?? '/i18n', scope, lang, fileExtension ?? I18N_DEFAULTS.fileExtension);
|
|
454
527
|
return this.http.get(u);
|
|
455
528
|
}
|
|
456
529
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.4", ngImport: i0, type: HttpI18nLoader, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
|
|
@@ -462,22 +535,37 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.4", ngImpor
|
|
|
462
535
|
}] });
|
|
463
536
|
|
|
464
537
|
/**
|
|
538
|
+
* Static configuration (everything known at build time):
|
|
465
539
|
* ```ts
|
|
466
540
|
* provideHttpClient(),
|
|
467
541
|
* provideI18n({ langs: ['fa', 'en'], defaultLang: 'fa', fallbackLangs: ['en'] })
|
|
468
542
|
* ```
|
|
469
|
-
* The global dictionary of the initial language is loaded before the app starts.
|
|
543
|
+
* The global dictionary of the initial language is then loaded before the app starts.
|
|
544
|
+
*
|
|
545
|
+
* Deferred configuration (languages come from your backend): omit `langs`/`defaultLang`
|
|
546
|
+
* and call `await inject(I18nService).init({ langs, defaultLang, baseUrl, lang })`
|
|
547
|
+
* from your own APP_INITIALIZER once you know them:
|
|
548
|
+
* ```ts
|
|
549
|
+
* provideI18n({ fallbackLangs: ['fa'] }), // no automatic initializer
|
|
550
|
+
* provideAppInitializer(() => AppInitializer()), // calls i18n.init(...)
|
|
551
|
+
* ```
|
|
552
|
+
* Pass `autoInit: false` to force this mode even when `langs` is given.
|
|
470
553
|
*/
|
|
471
|
-
function provideI18n(config) {
|
|
472
|
-
const
|
|
554
|
+
function provideI18n(config = {}) {
|
|
555
|
+
const autoInit = config.autoInit ?? (!!config.langs?.length && !!config.defaultLang);
|
|
473
556
|
return makeEnvironmentProviders([
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
557
|
+
// a fresh, mutable object per injector (the service updates it through configure())
|
|
558
|
+
{ provide: I18N_CONFIG, useFactory: () => assignDefined({ ...I18N_DEFAULTS }, config) },
|
|
559
|
+
...(config.loader ? [config.loader] : []),
|
|
560
|
+
{ provide: I18N_LOADER, useFactory: () => inject(config.loader ?? HttpI18nLoader) },
|
|
561
|
+
...(autoInit
|
|
562
|
+
? [
|
|
563
|
+
provideAppInitializer(() => {
|
|
564
|
+
const svc = inject(I18nService);
|
|
565
|
+
return svc.use(svc.detectInitialLang());
|
|
566
|
+
}),
|
|
567
|
+
]
|
|
568
|
+
: []),
|
|
481
569
|
]);
|
|
482
570
|
}
|
|
483
571
|
|
|
@@ -520,5 +608,5 @@ const TR = [TrPipe, TrDirective, TrKeyDirective];
|
|
|
520
608
|
* Generated bundle index. Do not edit.
|
|
521
609
|
*/
|
|
522
610
|
|
|
523
|
-
export { HttpI18nLoader, I18N_CONFIG,
|
|
611
|
+
export { HttpI18nLoader, I18N_CONFIG, I18N_DEFAULTS, I18N_LOADER, I18nService, TR, TrDirective, TrKeyDirective, TrPipe, buildI18nUrl, flatten, i18nReadyGuard, injectT, interpolate, marker, provideI18n, provideI18nScope, translateSignal };
|
|
524
612
|
//# sourceMappingURL=ngx-kit-lazy-i18n.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ngx-kit-lazy-i18n.mjs","sources":["../../../projects/libs/lazy-i18n/types/types.ts","../../../projects/libs/lazy-i18n/utils/utils.ts","../../../projects/libs/lazy-i18n/services/i18n.service.ts","../../../projects/libs/lazy-i18n/pipes/pipe.ts","../../../projects/libs/lazy-i18n/directives/directives.ts","../../../projects/libs/lazy-i18n/loader/http-loader.ts","../../../projects/libs/lazy-i18n/providers/provide-i18n.ts","../../../projects/libs/lazy-i18n/providers/routing.ts","../../../projects/libs/lazy-i18n/signals/signals.ts","../../../projects/libs/lazy-i18n/public-api.ts","../../../projects/libs/lazy-i18n/ngx-kit-lazy-i18n.ts"],"sourcesContent":["import { InjectionToken, Type } from '@angular/core';\nimport type { Observable } from 'rxjs';\nimport { HttpI18nLoader } from '../public-api';\n\n/** A (possibly nested) translation file as returned by the server. */\nexport interface Dictionary {\n [key: string]: string | number | boolean | null | Dictionary | Array<unknown>;\n}\nexport type Params = Record<string, unknown>;\nexport type TranslateFn = (key: string, params?: Params) => string;\nexport type MissingHandler = (key: string, lang: string) => string;\nexport type Transpiler = (value: string, params: Params | undefined, lang: string) => string;\n\nexport interface I18nLoader {\n /** `scope === undefined` means the global file. */\n load(lang: string, scope?: string): Observable<Dictionary> | Promise<Dictionary>;\n}\n\nexport class I18nConfig {\n /** All supported languages. */\n langs!: string[];\n defaultLang!: string;\n /** Looked up (in order) when a key is empty/missing in the active language. */\n fallbackLangs?: string[];\n /** Default `'key'` (returns the key itself). */\n missing?: 'key' | 'empty' | MissingHandler;\n /** Log each missing key once. Default: `isDevMode()`. */\n warnMissing?: boolean;\n /** Replace the default `{{ name }}` interpolation (ICU, plurals, ...). */\n transpile?: Transpiler;\n /** Custom loader class (must be `@Injectable({providedIn:'root'})` or constructible without args). Default: HTTP. */\n loader?: Type<I18nLoader> = HttpI18nLoader;\n /** Default HTTP loader: base URL. Default `'/i18n'` -> `/i18n/fa`, `/i18n/setting/fa`. */\n baseUrl?: string;\n /** Default HTTP loader: file extension appended to the URL, e.g. `'.json'`. Default `'.json'`. */\n fileExtension?: string = '.json';\n /** Default HTTP loader: full control over the URL. */\n url?: (lang: string, scope?: string) => string;\n /** Retries per failed request. Default `1`. */\n retries?: number;\n /** localStorage key used to remember the language. `null` disables. Default `'lazy-i18n.lang'`. */\n storageKey?: string | null;\n /** Pick the initial language from `navigator.languages` when nothing is stored. Default `false`. */\n detectBrowserLang?: boolean;\n /** Keep `<html lang dir>` in sync. Default `true`. */\n manageDocument?: boolean;\n}\n\nexport const I18N_CONFIG = new InjectionToken<I18nConfig>('I18N_CONFIG');\nexport const I18N_LOADER = new InjectionToken<I18nLoader>('I18N_LOADER');\n","import type { Dictionary, Params } from '../types/types';\n\n/** Flatten a nested dictionary into `target` (dot-separated keys). Non-string leaves are stringified. */\nexport function flatten(\n dict: Dictionary,\n target: Map<string, string> = new Map(),\n onConflict?: (key: string) => void,\n prefix = '',\n): Map<string, string> {\n for (const k of Object.keys(dict)) {\n const v = (dict as Record<string, unknown>)[k];\n const key = prefix ? `${prefix}.${k}` : k;\n if (v !== null && typeof v === 'object') {\n flatten(v as Dictionary, target, onConflict, key);\n } else if (v !== null && v !== undefined) {\n const s = String(v);\n if (onConflict) {\n const prev = target.get(key);\n if (prev !== undefined && prev !== '' && s !== '' && prev !== s) onConflict(key);\n }\n target.set(key, s);\n }\n }\n return target;\n}\n\nconst RE = /\\{\\{\\s*([\\w$.-]+)\\s*\\}\\}/g;\n\nfunction getPath(obj: Params, path: string): unknown {\n if (path in obj) return obj[path];\n let cur: any = obj;\n for (const part of path.split('.')) {\n if (cur == null) return undefined;\n cur = cur[part];\n }\n return cur;\n}\n\n/** `Hello {{ name }}` + `{name:'Ali'}` -> `Hello Ali`. Unknown placeholders are left untouched. */\nexport function interpolate(text: string, params?: Params): string {\n if (!params || text.indexOf('{{') === -1) return text; // fast path: no regex\n return text.replace(RE, (m, k: string) => {\n const v = getPath(params, k);\n return v == null ? m : String(v);\n });\n}\n\nexport function shallowEqual(a?: Params, b?: Params): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n const ka = Object.keys(a);\n if (ka.length !== Object.keys(b).length) return false;\n for (const k of ka) if (a[k] !== b[k]) return false;\n return true;\n}\n\nexport const RTL_LANGS = new Set(['ar', 'fa', 'he', 'ur', 'ps', 'sd', 'ug', 'yi', 'ckb', 'dv']);\n\n/** Identity function the extractor recognises: `const k = marker('orders.title')`. */\nexport function marker<T extends string>(key: T): T {\n return key;\n}\n","import { DOCUMENT } from '@angular/common';\nimport { Injectable, Signal, computed, inject, isDevMode, signal } from '@angular/core';\nimport { defer, firstValueFrom, from, retry } from 'rxjs';\nimport { I18N_CONFIG, I18N_LOADER, type Dictionary, type Params } from '../types/types';\nimport { RTL_LANGS, flatten, interpolate } from '../utils/utils';\n\nconst TAG = '[lazy-i18n]';\n@Injectable({ providedIn: 'root' })\nexport class I18nService {\n private readonly cfg = inject(I18N_CONFIG);\n private readonly loader = inject(I18N_LOADER);\n private readonly doc = inject(DOCUMENT);\n\n private readonly _lang = signal(this.cfg.defaultLang);\n private readonly _rev = signal(0);\n /** Active language (reactive). */\n readonly lang = this._lang.asReadonly();\n /** Bumps whenever the active language or any dictionary changes. */\n readonly revision = this._rev.asReadonly();\n readonly dir = computed<'rtl' | 'ltr'>(() =>\n RTL_LANGS.has(this._lang().split('-')[0]) ? 'rtl' : 'ltr',\n );\n readonly langs: readonly string[] = this.cfg.langs;\n\n private readonly fallbacks = this.cfg.fallbackLangs ?? [];\n private readonly warn = this.cfg.warnMissing ?? isDevMode();\n private readonly dicts = new Map<string, Map<string, string>>(); // lang -> flat map\n private readonly loaded = new Set<string>(); // `${lang}|${scope}`\n private readonly inflight = new Map<string, Promise<void>>();\n private readonly scopes = new Set<string>();\n private readonly pending = new Set<Promise<void>>();\n private readonly warned = new Set<string>();\n private useToken = 0;\n\n // ───────────────────────── translate ─────────────────────────\n\n /** Translate. Reads signals, so it is reactive inside `computed`, `effect` and templates. */\n t(key: string, params?: Params): string {\n this._rev();\n return this.resolve(key, params, this._lang());\n }\n\n /** Same as `t`, but never creates a reactive dependency. */\n instant(key: string, params?: Params): string {\n return this.resolve(key, params, this._lang());\n }\n\n /** `computed` signal for one key. `params` may be a function reading other signals. */\n select(key: string, params?: Params | (() => Params)): Signal<string> {\n return computed(() => this.t(key, typeof params === 'function' ? params() : params));\n }\n\n /** True when the key has a non-empty value in the active language or a fallback. */\n has(key: string): boolean {\n this._rev();\n return this.lookup(key, this._lang()) !== undefined;\n }\n\n /** Returns every key below `prefix` as a nested object (values are raw, not interpolated). */\n getObject(prefix: string): Dictionary {\n this._rev();\n const out: Dictionary = {};\n const p = prefix + '.';\n for (const lang of [this._lang(), ...this.fallbacks]) {\n for (const [k, v] of this.dicts.get(lang) ?? []) {\n if (!k.startsWith(p) || !v) continue;\n const parts = k.slice(p.length).split('.');\n let cur: any = out;\n for (let i = 0; i < parts.length - 1; i++)\n cur = typeof cur[parts[i]] === 'object' ? cur[parts[i]] : (cur[parts[i]] = {});\n if (!(parts[parts.length - 1] in cur)) cur[parts[parts.length - 1]] = v;\n }\n }\n return out;\n }\n\n private lookup(key: string, lang: string): string | undefined {\n let raw = this.dicts.get(lang)?.get(key);\n if (raw) return raw;\n for (const fb of this.fallbacks) {\n if (fb === lang) continue;\n raw = this.dicts.get(fb)?.get(key);\n if (raw) return raw;\n }\n return undefined; // '' (extractor placeholder) counts as missing -> falls back\n }\n\n private resolve(key: string, params: Params | undefined, lang: string): string {\n const raw = this.lookup(key, lang);\n if (raw === undefined) return this.onMissing(key, lang);\n return this.cfg.transpile ? this.cfg.transpile(raw, params, lang) : interpolate(raw, params);\n }\n\n private onMissing(key: string, lang: string): string {\n if (this.warn && this.dicts.has(lang) && !this.warned.has(key)) {\n this.warned.add(key);\n console.warn(`${TAG} Missing translation \"${key}\" (lang: ${lang})`);\n }\n const h = this.cfg.missing ?? 'key';\n return typeof h === 'function' ? h(key, lang) : h === 'empty' ? '' : key;\n }\n\n // ───────────────────────── language ─────────────────────────\n\n /**\n * Load global + all registered scopes (and fallback languages) for `lang`, THEN activate it.\n * Nothing renders half-translated and no \"missing\" flash occurs.\n */\n async use(lang: string): Promise<void> {\n if (!this.cfg.langs.includes(lang)) throw new Error(`${TAG} Unsupported language \"${lang}\"`);\n const token = ++this.useToken;\n do {\n await this.loadLang(lang);\n } while (!this.isComplete(lang)); // a scope may have been registered meanwhile\n if (token !== this.useToken) return; // a newer use() won\n this._lang.set(lang);\n this._rev.update((v) => v + 1);\n this.persist(lang);\n this.applyDocument(lang);\n }\n\n /** Download a language without activating it. */\n preload(lang: string): Promise<void> {\n return this.loadLang(lang);\n }\n\n /** Language to start with: stored > browser (opt-in) > default. */\n detectInitialLang(): string {\n const key = this.storageKey();\n try {\n const s = key ? localStorage.getItem(key) : null;\n if (s && this.cfg.langs.includes(s)) return s;\n } catch {\n /* storage unavailable (SSR / privacy mode) */\n }\n if (this.cfg.detectBrowserLang && typeof navigator !== 'undefined') {\n for (const n of navigator.languages ?? [navigator.language]) {\n if (this.cfg.langs.includes(n)) return n;\n const base = n.split('-')[0];\n if (this.cfg.langs.includes(base)) return base;\n }\n }\n return this.cfg.defaultLang;\n }\n\n // ───────────────────────── scopes ─────────────────────────\n\n /** Register a scope and start loading it for the active language. Idempotent. Used by `provideI18nScope`. */\n registerScope(scope: string): void {\n this.scopes.add(scope);\n const lang = this._lang();\n const p: Promise<void> = Promise.all(\n [lang, ...this.fallbacks.filter((f) => f !== lang)].map((l) =>\n this.fetch(l, scope).catch((e) => {\n console.error(`${TAG} Failed to load scope \"${scope}\" (${l})`, e);\n }),\n ),\n ).then(() => {\n this.pending.delete(p);\n });\n this.pending.add(p);\n }\n\n /** Resolves once every scope registered so far is available. Never rejects. */\n async whenReady(): Promise<void> {\n while (this.pending.size) await Promise.all([...this.pending]);\n }\n\n // ───────────────────────── data ─────────────────────────\n\n /** Manually set translations (tests, SSR transfer state, ...). */\n setTranslation(lang: string, dict: Dictionary, merge = true): void {\n if (!merge) this.dicts.delete(lang);\n this.merge(lang, dict);\n }\n\n private merge(lang: string, dict: Dictionary): void {\n let m = this.dicts.get(lang);\n if (!m) this.dicts.set(lang, (m = new Map()));\n flatten(\n dict,\n m,\n this.warn\n ? (k) => console.warn(`${TAG} Key \"${k}\" is defined twice with different values (${lang})`)\n : undefined,\n );\n this._rev.update((v) => v + 1);\n }\n\n private fetch(lang: string, scope?: string): Promise<void> {\n const id = `${lang}|${scope ?? ''}`;\n if (this.loaded.has(id)) return Promise.resolve();\n let p = this.inflight.get(id);\n if (!p) {\n p = firstValueFrom(\n defer(() => from(this.loader.load(lang, scope))).pipe(\n retry({ count: this.cfg.retries ?? 1, delay: 250 }),\n ),\n )\n .then((d) => {\n this.merge(lang, d);\n this.loaded.add(id);\n })\n .finally(() => this.inflight.delete(id));\n this.inflight.set(id, p);\n }\n return p;\n }\n\n private loadLang(lang: string): Promise<void> {\n const scopes: Array<string | undefined> = [undefined, ...this.scopes];\n const main = scopes.map((s) => this.fetch(lang, s));\n const fb = this.fallbacks\n .filter((f) => f !== lang)\n .flatMap((f) =>\n scopes.map((s) =>\n this.fetch(f, s).catch((e) => console.error(`${TAG} Fallback \"${f}\" failed`, e)),\n ),\n );\n return Promise.all([...main, ...fb]).then(() => undefined);\n }\n\n private isComplete(lang: string): boolean {\n return (\n this.loaded.has(`${lang}|`) && [...this.scopes].every((s) => this.loaded.has(`${lang}|${s}`))\n );\n }\n\n private storageKey(): string | null {\n return this.cfg.storageKey === undefined ? 'lazy-i18n.lang' : this.cfg.storageKey;\n }\n\n private persist(lang: string): void {\n const key = this.storageKey();\n try {\n if (key) localStorage.setItem(key, lang);\n } catch {\n /* ignore */\n }\n }\n\n private applyDocument(lang: string): void {\n if (this.cfg.manageDocument === false) return;\n const el = this.doc?.documentElement;\n if (!el) return;\n el.setAttribute('lang', lang);\n el.setAttribute('dir', this.dir());\n }\n}\n","import { Pipe, PipeTransform, inject } from '@angular/core';\nimport { I18nService } from '../services/i18n.service';\nimport type { Params } from '../types/types';\nimport { shallowEqual } from '../utils/utils';\n\n/**\n * `{{ 'orders.title' | tr }}` / `{{ 'hello' | tr: { name } }}`\n *\n * Impure on purpose (it must follow signals), but memoised: when key, params and the\n * service revision are unchanged it returns the cached string in O(1) with no lookup.\n */\n@Pipe({ name: 'tr', pure: false })\nexport class TrPipe implements PipeTransform {\n private readonly svc = inject(I18nService);\n private k?: string;\n private p?: Params;\n private rev = -1;\n private out = '';\n\n transform(key: string | null | undefined, params?: Params): string {\n if (!key) return '';\n const rev = this.svc.revision(); // tracked: marks the view dirty on lang / dictionary change\n if (rev === this.rev && key === this.k && shallowEqual(params, this.p)) return this.out;\n this.k = key;\n this.p = params;\n this.rev = rev;\n return (this.out = this.svc.t(key, params));\n }\n}\n","import {\n Directive, ElementRef, Input, Renderer2, Signal, TemplateRef, ViewContainerRef, effect, inject, signal,\n} from '@angular/core';\nimport { I18nService } from '../services/i18n.service';\nimport type { Params, TranslateFn } from '../types/types';\n\nexport interface TrContext {\n $implicit: TranslateFn;\n t: TranslateFn;\n lang: Signal<string>;\n}\n\n/**\n * Structural directive. The view is created ONCE; `t` reads signals, so only the bindings that use it update.\n *\n * <ng-container *tr=\"let t; prefix: 'orders'\">\n * <h1>{{ t('title') }}</h1> <!-- orders.title -->\n * </ng-container>\n */\n@Directive({ selector: '[tr],[translate]' })\nexport class TrDirective {\n private readonly tpl = inject<TemplateRef<TrContext>>(TemplateRef);\n private readonly vcr = inject(ViewContainerRef);\n private readonly svc = inject(I18nService);\n private readonly prefix = signal<string | undefined>(undefined);\n /** `prefix: 'orders'` in the microsyntax. */\n @Input('trPrefix') set trPrefix(v: string | undefined) { this.prefix.set(v); }\n\n static ngTemplateContextGuard(_dir: TrDirective, _ctx: unknown): _ctx is TrContext {\n return true;\n }\n\n constructor() {\n const t: TranslateFn = (key, params) => {\n const p = this.prefix();\n return this.svc.t(p ? p + '.' + key : key, params);\n };\n this.vcr.createEmbeddedView(this.tpl, { $implicit: t, t, lang: this.svc.lang });\n }\n}\n\n/**\n * Attribute directive.\n * <span trKey=\"orders.title\"></span>\n * <input trKey=\"form.name\" trAttr=\"placeholder\" />\n * <p trKey=\"hello\" [trParams]=\"{ name: user.name }\"></p>\n */\n@Directive({ selector: '[trKey]' })\nexport class TrKeyDirective {\n // @Input setters feeding signals: works with JIT, AOT and every supported Angular version.\n private readonly key = signal<string | undefined>(undefined);\n private readonly params = signal<Params | undefined>(undefined);\n private readonly attr = signal<string | undefined>(undefined);\n private readonly prefix = signal<string | undefined>(undefined);\n @Input('trKey') set trKey(v: string | undefined) { this.key.set(v); }\n @Input('trParams') set trParams(v: Params | undefined) { this.params.set(v); }\n @Input('trAttr') set trAttr(v: string | undefined) { this.attr.set(v); }\n @Input('trPrefix') set trPrefix(v: string | undefined) { this.prefix.set(v); }\n\n constructor() {\n const svc = inject(I18nService);\n const el = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n const r = inject(Renderer2);\n effect(() => {\n const k = this.key();\n if (!k) return;\n const p = this.prefix();\n const text = svc.t(p ? p + '.' + k : k, this.params());\n const a = this.attr();\n if (a) r.setAttribute(el, a, text);\n else if (el.textContent !== text) el.textContent = text;\n });\n }\n}\n","import { HttpClient } from '@angular/common/http';\nimport { Injectable, inject } from '@angular/core';\nimport type { Observable } from 'rxjs';\nimport { I18N_CONFIG, type Dictionary, type I18nLoader } from '../types/types';\n\n/** Default loader: GET `{baseUrl}/{lang}` and `{baseUrl}/{scope}/{lang}`. Goes through your HttpClient interceptors. */\n@Injectable({ providedIn: 'root' })\nexport class HttpI18nLoader implements I18nLoader {\n private readonly http = inject(HttpClient);\n private readonly cfg = inject(I18N_CONFIG);\n\n load(lang: string, scope?: string): Observable<Dictionary> {\n const { url, baseUrl = '/i18n', fileExtension = '' } = this.cfg;\n const u = url ? url(lang, scope) : `${baseUrl}/${scope ? scope + '/' : ''}${lang}${fileExtension}`;\n return this.http.get<Dictionary>(u);\n }\n}\n","import {\n EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideAppInitializer,\n} from '@angular/core';\nimport { HttpI18nLoader } from '../loader/http-loader';\nimport { I18nService } from '../services/i18n.service';\nimport { I18N_CONFIG, I18N_LOADER, I18nConfig } from '../types/types';\n\n/**\n * ```ts\n * provideHttpClient(),\n * provideI18n({ langs: ['fa', 'en'], defaultLang: 'fa', fallbackLangs: ['en'] })\n * ```\n * The global dictionary of the initial language is loaded before the app starts.\n */\nexport function provideI18n(config: I18nConfig): EnvironmentProviders {\n const c = { ...new I18nConfig(), ...config };\n return makeEnvironmentProviders([\n { provide: I18N_CONFIG, useValue: c },\n ...(c.loader ? [c.loader] : []),\n { provide: I18N_LOADER, useFactory: () => inject(c.loader ?? HttpI18nLoader) },\n provideAppInitializer(() => {\n const svc = inject(I18nService);\n return svc.use(svc.detectInitialLang());\n }),\n ]);\n}\n","import { EnvironmentProviders, inject, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';\nimport type { CanActivateChildFn } from '@angular/router';\nimport { I18nService } from '../services/i18n.service';\n\n/**\n * Put in `route.providers`. Runs when the route injector is created (before guards),\n * so loading starts as early as possible. Child routes need nothing.\n */\nexport function provideI18nScope(scope: string | string[]): EnvironmentProviders {\n const scopes = Array.isArray(scope) ? scope : [scope];\n return makeEnvironmentProviders([\n provideEnvironmentInitializer(() => {\n const svc = inject(I18nService);\n for (const s of scopes) svc.registerScope(s);\n }),\n ]);\n}\n\n/** Put ONCE on the root path-less route: `canActivateChild: [i18nReadyGuard]`. Waits for every registered scope. */\nexport const i18nReadyGuard: CanActivateChildFn = async () => {\n await inject(I18nService).whenReady();\n return true;\n};\n","import { Signal, assertInInjectionContext, inject } from '@angular/core';\nimport { I18nService } from '../services/i18n.service';\nimport type { Params, TranslateFn } from '../types/types';\n\n/** `readonly title = translateSignal('orders.title')` (injection context) */\nexport function translateSignal(key: string, params?: Params | (() => Params)): Signal<string> {\n assertInInjectionContext(translateSignal);\n return inject(I18nService).select(key, params);\n}\n\n/** `const t = injectT(); computed(() => t('a.b'))` — reactive translate function. */\nexport function injectT(): TranslateFn {\n assertInInjectionContext(injectT);\n const svc = inject(I18nService);\n return (key, params) => svc.t(key, params);\n}\n","import { TrPipe } from './pipes/pipe';\nimport { TrDirective, TrKeyDirective } from './directives/directives';\n\nexport * from './types/types';\nexport { flatten, interpolate, marker } from './utils/utils';\nexport { HttpI18nLoader } from './loader/http-loader';\nexport { I18nService } from './services/i18n.service';\nexport { provideI18n } from './providers/provide-i18n';\nexport { provideI18nScope, i18nReadyGuard } from './providers/routing';\nexport { translateSignal, injectT } from './signals/signals';\nexport { TrPipe } from './pipes/pipe';\nexport { TrDirective, TrKeyDirective, type TrContext } from './directives/directives';\n\n/** `imports: [TR]` brings the pipe and both directives into a standalone component. */\nexport const TR = [TrPipe, TrDirective, TrKeyDirective] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ngx-kit-lazy-i18n.mjs","sources":["../../../projects/libs/lazy-i18n/types/types.ts","../../../projects/libs/lazy-i18n/utils/utils.ts","../../../projects/libs/lazy-i18n/services/i18n.service.ts","../../../projects/libs/lazy-i18n/pipes/pipe.ts","../../../projects/libs/lazy-i18n/directives/directives.ts","../../../projects/libs/lazy-i18n/loader/http-loader.ts","../../../projects/libs/lazy-i18n/providers/provide-i18n.ts","../../../projects/libs/lazy-i18n/providers/routing.ts","../../../projects/libs/lazy-i18n/signals/signals.ts","../../../projects/libs/lazy-i18n/public-api.ts","../../../projects/libs/lazy-i18n/ngx-kit-lazy-i18n.ts"],"sourcesContent":["import { InjectionToken, Type } from '@angular/core';\nimport type { Observable } from 'rxjs';\n\n/** A (possibly nested) translation file as returned by the server. */\nexport interface Dictionary {\n [key: string]: string | number | boolean | null | Dictionary | Array<unknown>;\n}\nexport type Params = Record<string, unknown>;\nexport type TranslateFn = (key: string, params?: Params) => string;\nexport type MissingHandler = (key: string, lang: string) => string;\nexport type Transpiler = (value: string, params: Params | undefined, lang: string) => string;\n\nexport interface I18nLoader {\n /** `scope === undefined` means the global file. */\n load(lang: string, scope?: string): Observable<Dictionary> | Promise<Dictionary>;\n}\n\n/**\n * Every option is optional so the library can be provided at startup and completed later\n * (for example after your own APP_INITIALIZER has fetched the language list from the backend):\n * see `I18nService.init()` / `I18nService.configure()`.\n */\nexport interface I18nConfig {\n /** All supported languages. Required before the first language is activated. */\n langs?: string[];\n /** Language used when nothing is stored or detected. Required before the first language is activated. */\n defaultLang?: string;\n /** Looked up (in order) when a key is empty/missing in the active language. */\n fallbackLangs?: string[];\n /** Default `'key'` (returns the key itself). */\n missing?: 'key' | 'empty' | MissingHandler;\n /** Log each missing key once. Default: `isDevMode()`. */\n warnMissing?: boolean;\n /** Replace the default `{{ name }}` interpolation (ICU, plurals, ...). */\n transpile?: Transpiler;\n /** Custom loader class (must be `@Injectable({providedIn:'root'})` or constructible without args). Default: HTTP. Provide-time only. */\n loader?: Type<I18nLoader>;\n /** Default HTTP loader: base URL. Default `'/i18n'` -> `/i18n/fa.json`, `/i18n/setting/fa.json`. Slashes are normalised. */\n baseUrl?: string;\n /** Default HTTP loader: file extension appended to the URL, e.g. `'.json'`. Default `'.json'`; use `''` for none. */\n fileExtension?: string;\n /** Default HTTP loader: full control over the URL. */\n url?: (lang: string, scope?: string) => string;\n /** Retries per failed request. Default `1`. */\n retries?: number;\n /** localStorage key used to remember the language. `null` disables. Default `'lazy-i18n.lang'`. */\n storageKey?: string | null;\n /** Pick the initial language from `navigator.languages` when nothing is stored. Default `false`. */\n detectBrowserLang?: boolean;\n /** Keep `<html lang dir>` in sync. Default `true`. */\n manageDocument?: boolean;\n /**\n * Activate the initial language in an APP_INITIALIZER created by `provideI18n()`.\n * Default: `true` when both `langs` and `defaultLang` are given to `provideI18n()`, otherwise `false`\n * (then you call `I18nService.init()` yourself, typically from your own initializer).\n * Provide-time only.\n */\n autoInit?: boolean;\n}\n\n/** Options that can be changed after the app has started. */\nexport type I18nRuntimeConfig = Omit<I18nConfig, 'loader' | 'autoInit'>;\n\n/** `I18nService.init()` options: runtime config + the language to start with. */\nexport interface I18nInitOptions extends I18nRuntimeConfig {\n /** Preferred starting language (e.g. the user's saved language from the backend). Ignored if not in `langs`. */\n lang?: string;\n}\n\n/** Defaults applied by `provideI18n()`. */\nexport const I18N_DEFAULTS: I18nConfig = { fileExtension: '.json', retries: 1 };\n\nexport const I18N_CONFIG = new InjectionToken<I18nConfig>('I18N_CONFIG');\nexport const I18N_LOADER = new InjectionToken<I18nLoader>('I18N_LOADER');\n","import type { Dictionary, Params } from '../types/types';\n\n/** Flatten a nested dictionary into `target` (dot-separated keys). Non-string leaves are stringified. */\nexport function flatten(\n dict: Dictionary,\n target: Map<string, string> = new Map(),\n onConflict?: (key: string) => void,\n prefix = '',\n): Map<string, string> {\n for (const k of Object.keys(dict)) {\n const v = (dict as Record<string, unknown>)[k];\n const key = prefix ? `${prefix}.${k}` : k;\n if (v !== null && typeof v === 'object') {\n flatten(v as Dictionary, target, onConflict, key);\n } else if (v !== null && v !== undefined) {\n const s = String(v);\n if (onConflict) {\n const prev = target.get(key);\n if (prev !== undefined && prev !== '' && s !== '' && prev !== s) onConflict(key);\n }\n target.set(key, s);\n }\n }\n return target;\n}\n\nconst RE = /\\{\\{\\s*([\\w$.-]+)\\s*\\}\\}/g;\n\nfunction getPath(obj: Params, path: string): unknown {\n if (path in obj) return obj[path];\n let cur: any = obj;\n for (const part of path.split('.')) {\n if (cur == null) return undefined;\n cur = cur[part];\n }\n return cur;\n}\n\n/** `Hello {{ name }}` + `{name:'Ali'}` -> `Hello Ali`. Unknown placeholders are left untouched. */\nexport function interpolate(text: string, params?: Params): string {\n if (!params || text.indexOf('{{') === -1) return text; // fast path: no regex\n return text.replace(RE, (m, k: string) => {\n const v = getPath(params, k);\n return v == null ? m : String(v);\n });\n}\n\nexport function shallowEqual(a?: Params, b?: Params): boolean {\n if (a === b) return true;\n if (!a || !b) return false;\n const ka = Object.keys(a);\n if (ka.length !== Object.keys(b).length) return false;\n for (const k of ka) if (a[k] !== b[k]) return false;\n return true;\n}\n\nexport const RTL_LANGS = new Set(['ar', 'fa', 'he', 'ur', 'ps', 'sd', 'ug', 'yi', 'ckb', 'dv']);\n\n/** Identity function the extractor recognises: `const k = marker('orders.title')`. */\nexport function marker<T extends string>(key: T): T {\n return key;\n}\n\n/** Copy only the properties of `patch` that are not `undefined` (so an omitted option never wipes a default). */\nexport function assignDefined<T extends object>(target: T, patch: Partial<T>): T {\n for (const k of Object.keys(patch) as Array<keyof T>) {\n if (patch[k] !== undefined) target[k] = patch[k] as T[keyof T];\n }\n return target;\n}\n\n/**\n * Build `{base}/{scope}/{lang}{ext}` without ever producing `undefined`, `//` or a trailing slash problem:\n * (\"/i18n\", undefined, \"fa\", \".json\") -> \"/i18n/fa.json\"\n * (\"https://x.com/\", \"setting\", \"fa\", \"json\") -> \"https://x.com/setting/fa.json\"\n * (undefined | \"\", undefined, \"fa\") -> \"/fa\"\n * The protocol part of `base` (\"https://\") is left alone.\n */\nexport function buildI18nUrl(\n base: string | null | undefined,\n scope: string | null | undefined,\n lang: string,\n ext: string | null | undefined = '',\n): string {\n const trim = (s: string) => s.trim().replace(/^\\/+|\\/+$/g, '');\n const e = ext ? (ext.startsWith('.') ? ext : '.' + ext) : '';\n const path = [scope ? trim(scope) : '', trim(lang) + e]\n .filter(Boolean)\n .join('/')\n .replace(/\\/{2,}/g, '/');\n return `${(base ?? '').trim().replace(/\\/+$/, '')}/${path}`;\n}\n","import { DOCUMENT } from '@angular/common';\nimport { Injectable, Signal, computed, inject, isDevMode, signal } from '@angular/core';\nimport { defer, firstValueFrom, from, retry } from 'rxjs';\nimport {\n I18N_CONFIG,\n I18N_LOADER,\n type Dictionary,\n type I18nInitOptions,\n type I18nRuntimeConfig,\n type Params,\n} from '../types/types';\nimport { RTL_LANGS, assignDefined, flatten, interpolate } from '../utils/utils';\n\nconst TAG = '[lazy-i18n]';\nconst EMPTY: readonly string[] = [];\n\n@Injectable({ providedIn: 'root' })\nexport class I18nService {\n private readonly cfg = inject(I18N_CONFIG);\n private readonly loader = inject(I18N_LOADER);\n private readonly doc = inject(DOCUMENT);\n\n private readonly _lang = signal(this.cfg.defaultLang ?? '');\n private readonly _rev = signal(0);\n private readonly _langs = signal<readonly string[]>(this.cfg.langs ?? EMPTY);\n private readonly _ready = signal(false);\n /** Active language (reactive). */\n readonly lang = this._lang.asReadonly();\n /** Bumps whenever the active language or any dictionary changes. */\n readonly revision = this._rev.asReadonly();\n readonly dir = computed<'rtl' | 'ltr'>(() =>\n RTL_LANGS.has(this._lang().split('-')[0]) ? 'rtl' : 'ltr',\n );\n /** Supported languages (a signal: they may be supplied after startup via `init()` / `configure()`). */\n readonly langs = this._langs.asReadonly();\n /** `true` once the first language has been activated. Handy for a splash screen. */\n readonly ready = this._ready.asReadonly();\n\n // read live, so values supplied later through configure() are honoured\n private get fallbacks(): readonly string[] {\n return this.cfg.fallbackLangs ?? EMPTY;\n }\n private get warn(): boolean {\n return this.cfg.warnMissing ?? isDevMode();\n }\n private readonly dicts = new Map<string, Map<string, string>>(); // lang -> flat map\n private readonly loaded = new Set<string>(); // `${lang}|${scope}`\n private readonly inflight = new Map<string, Promise<void>>();\n private readonly scopes = new Set<string>();\n private readonly pending = new Set<Promise<void>>();\n private readonly warned = new Set<string>();\n private useToken = 0;\n\n // ───────────────────────── translate ─────────────────────────\n\n /** Translate. Reads signals, so it is reactive inside `computed`, `effect` and templates. */\n t(key: string, params?: Params): string {\n this._rev();\n return this.resolve(key, params, this._lang());\n }\n\n /** Same as `t`, but never creates a reactive dependency. */\n instant(key: string, params?: Params): string {\n return this.resolve(key, params, this._lang());\n }\n\n /** `computed` signal for one key. `params` may be a function reading other signals. */\n select(key: string, params?: Params | (() => Params)): Signal<string> {\n return computed(() => this.t(key, typeof params === 'function' ? params() : params));\n }\n\n /** True when the key has a non-empty value in the active language or a fallback. */\n has(key: string): boolean {\n this._rev();\n return this.lookup(key, this._lang()) !== undefined;\n }\n\n /** Returns every key below `prefix` as a nested object (values are raw, not interpolated). */\n getObject(prefix: string): Dictionary {\n this._rev();\n const out: Dictionary = {};\n const p = prefix + '.';\n for (const lang of [this._lang(), ...this.fallbacks]) {\n for (const [k, v] of this.dicts.get(lang) ?? []) {\n if (!k.startsWith(p) || !v) continue;\n const parts = k.slice(p.length).split('.');\n let cur: any = out;\n for (let i = 0; i < parts.length - 1; i++)\n cur = typeof cur[parts[i]] === 'object' ? cur[parts[i]] : (cur[parts[i]] = {});\n if (!(parts[parts.length - 1] in cur)) cur[parts[parts.length - 1]] = v;\n }\n }\n return out;\n }\n\n private lookup(key: string, lang: string): string | undefined {\n let raw = this.dicts.get(lang)?.get(key);\n if (raw) return raw;\n for (const fb of this.fallbacks) {\n if (fb === lang) continue;\n raw = this.dicts.get(fb)?.get(key);\n if (raw) return raw;\n }\n return undefined; // '' (extractor placeholder) counts as missing -> falls back\n }\n\n private resolve(key: string, params: Params | undefined, lang: string): string {\n const raw = this.lookup(key, lang);\n if (raw === undefined) return this.onMissing(key, lang);\n return this.cfg.transpile ? this.cfg.transpile(raw, params, lang) : interpolate(raw, params);\n }\n\n private onMissing(key: string, lang: string): string {\n if (this.warn && this.dicts.has(lang) && !this.warned.has(key)) {\n this.warned.add(key);\n console.warn(`${TAG} Missing translation \"${key}\" (lang: ${lang})`);\n }\n const h = this.cfg.missing ?? 'key';\n return typeof h === 'function' ? h(key, lang) : h === 'empty' ? '' : key;\n }\n\n // ───────────────────────── language ─────────────────────────\n\n /**\n * Load global + all registered scopes (and fallback languages) for `lang`, THEN activate it.\n * Nothing renders half-translated and no \"missing\" flash occurs.\n */\n use(lang: string): Promise<void> {\n const p = this.activate(lang);\n // tracked so whenReady() / i18nReadyGuard also wait for an in-flight language change\n const tracked: Promise<void> = p\n .then(\n () => undefined,\n () => undefined,\n )\n .then(() => {\n this.pending.delete(tracked);\n });\n this.pending.add(tracked);\n return p;\n }\n\n /**\n * Complete the configuration and activate the first language in one call. Use it from your own\n * APP_INITIALIZER when the language list is only known at runtime (e.g. from the backend):\n *\n * ```ts\n * await i18n.init({ langs: ['fa', 'en'], defaultLang: 'fa', baseUrl: apiUrl, lang: user.lang });\n * ```\n * `lang` is used when it is in `langs`; otherwise stored > browser (opt-in) > `defaultLang`.\n * Rejects when the files cannot be loaded, so the initializer can show an error page.\n */\n init(options: I18nInitOptions = {}): Promise<void> {\n const { lang, ...patch } = options;\n this.configure(patch);\n const wanted =\n lang && (this.cfg.langs ?? EMPTY).includes(lang) ? lang : this.detectInitialLang();\n return this.use(wanted);\n }\n\n /**\n * Update options after startup (languages, baseUrl, fallbacks, ...). Already downloaded files stay cached.\n * `loader` and `autoInit` can only be set in `provideI18n()`.\n */\n configure(patch: I18nRuntimeConfig): void {\n assignDefined(this.cfg, patch);\n this.syncLangs();\n if (patch.defaultLang && !this._ready()) this._lang.set(patch.defaultLang);\n }\n\n private syncLangs(): void {\n const l = this.cfg.langs ?? EMPTY;\n if (l !== this._langs()) this._langs.set(l);\n }\n\n private async activate(lang: string): Promise<void> {\n this.syncLangs();\n const langs = this.cfg.langs ?? EMPTY;\n if (!langs.length) {\n throw new Error(\n `${TAG} No languages configured. Pass \"langs\" to provideI18n() or call I18nService.init({ langs, defaultLang }) first.`,\n );\n }\n if (!langs.includes(lang)) throw new Error(`${TAG} Unsupported language \"${lang}\"`);\n const token = ++this.useToken;\n do {\n await this.loadLang(lang);\n } while (!this.isComplete(lang)); // a scope may have been registered meanwhile\n if (token !== this.useToken) return; // a newer use() won\n this._lang.set(lang);\n this._rev.update((v) => v + 1);\n this._ready.set(true);\n this.persist(lang);\n this.applyDocument(lang);\n }\n\n /** Download a language without activating it. */\n preload(lang: string): Promise<void> {\n return this.loadLang(lang);\n }\n\n /** Language to start with: stored > browser (opt-in) > default. */\n detectInitialLang(): string {\n const langs = this.cfg.langs ?? EMPTY;\n const key = this.storageKey();\n try {\n const s = key ? localStorage.getItem(key) : null;\n if (s && langs.includes(s)) return s;\n } catch {\n /* storage unavailable (SSR / privacy mode) */\n }\n if (this.cfg.detectBrowserLang && typeof navigator !== 'undefined') {\n for (const n of navigator.languages ?? [navigator.language]) {\n if (langs.includes(n)) return n;\n const base = n.split('-')[0];\n if (langs.includes(base)) return base;\n }\n }\n const def = this.cfg.defaultLang ?? '';\n return langs.includes(def) ? def : (langs[0] ?? def);\n }\n\n // ───────────────────────── scopes ─────────────────────────\n\n /** Register a scope and start loading it for the active language. Idempotent. Used by `provideI18nScope`. */\n registerScope(scope: string): void {\n if (!scope) return;\n this.scopes.add(scope);\n // before the first activation only remember it: init()/use() loads every registered scope\n if (!this._ready()) return;\n const lang = this._lang();\n const p: Promise<void> = Promise.all(\n [lang, ...this.fallbacks.filter((f) => f !== lang)].map((l) =>\n this.fetch(l, scope).catch((e) => {\n console.error(`${TAG} Failed to load scope \"${scope}\" (${l})`, e);\n }),\n ),\n ).then(() => {\n this.pending.delete(p);\n });\n this.pending.add(p);\n }\n\n /** Resolves once every scope registered so far is available. Never rejects. */\n async whenReady(): Promise<void> {\n while (this.pending.size) await Promise.all([...this.pending]);\n }\n\n // ───────────────────────── data ─────────────────────────\n\n /** Manually set translations (tests, SSR transfer state, ...). */\n setTranslation(lang: string, dict: Dictionary, merge = true): void {\n if (!merge) this.dicts.delete(lang);\n this.merge(lang, dict);\n }\n\n private merge(lang: string, dict: Dictionary): void {\n let m = this.dicts.get(lang);\n if (!m) this.dicts.set(lang, (m = new Map()));\n flatten(\n dict,\n m,\n this.warn\n ? (k) => console.warn(`${TAG} Key \"${k}\" is defined twice with different values (${lang})`)\n : undefined,\n );\n this._rev.update((v) => v + 1);\n }\n\n private fetch(lang: string, scope?: string): Promise<void> {\n scope = scope || undefined; // never hand '' / null to a loader\n const id = `${lang}|${scope ?? ''}`;\n if (this.loaded.has(id)) return Promise.resolve();\n let p = this.inflight.get(id);\n if (!p) {\n p = firstValueFrom(\n defer(() => from(this.loader.load(lang, scope))).pipe(\n retry({ count: this.cfg.retries ?? 1, delay: 250 }),\n ),\n )\n .then((d) => {\n this.merge(lang, d);\n this.loaded.add(id);\n })\n .finally(() => this.inflight.delete(id));\n this.inflight.set(id, p);\n }\n return p;\n }\n\n private loadLang(lang: string): Promise<void> {\n const scopes: Array<string | undefined> = [undefined, ...this.scopes];\n const main = scopes.map((s) => this.fetch(lang, s));\n const fb = this.fallbacks\n .filter((f) => f !== lang)\n .flatMap((f) =>\n scopes.map((s) =>\n this.fetch(f, s).catch((e) => console.error(`${TAG} Fallback \"${f}\" failed`, e)),\n ),\n );\n return Promise.all([...main, ...fb]).then(() => undefined);\n }\n\n private isComplete(lang: string): boolean {\n return (\n this.loaded.has(`${lang}|`) && [...this.scopes].every((s) => this.loaded.has(`${lang}|${s}`))\n );\n }\n\n private storageKey(): string | null {\n return this.cfg.storageKey === undefined ? 'lazy-i18n.lang' : this.cfg.storageKey;\n }\n\n private persist(lang: string): void {\n const key = this.storageKey();\n try {\n if (key) localStorage.setItem(key, lang);\n } catch {\n /* ignore */\n }\n }\n\n private applyDocument(lang: string): void {\n if (this.cfg.manageDocument === false) return;\n const el = this.doc?.documentElement;\n if (!el) return;\n el.setAttribute('lang', lang);\n el.setAttribute('dir', this.dir());\n }\n}\n","import { Pipe, PipeTransform, inject } from '@angular/core';\nimport { I18nService } from '../services/i18n.service';\nimport type { Params } from '../types/types';\nimport { shallowEqual } from '../utils/utils';\n\n/**\n * `{{ 'orders.title' | tr }}` / `{{ 'hello' | tr: { name } }}`\n *\n * Impure on purpose (it must follow signals), but memoised: when key, params and the\n * service revision are unchanged it returns the cached string in O(1) with no lookup.\n */\n@Pipe({ name: 'tr', pure: false })\nexport class TrPipe implements PipeTransform {\n private readonly svc = inject(I18nService);\n private k?: string;\n private p?: Params;\n private rev = -1;\n private out = '';\n\n transform(key: string | null | undefined, params?: Params): string {\n if (!key) return '';\n const rev = this.svc.revision(); // tracked: marks the view dirty on lang / dictionary change\n if (rev === this.rev && key === this.k && shallowEqual(params, this.p)) return this.out;\n this.k = key;\n this.p = params;\n this.rev = rev;\n return (this.out = this.svc.t(key, params));\n }\n}\n","import {\n Directive, ElementRef, Input, Renderer2, Signal, TemplateRef, ViewContainerRef, effect, inject, signal,\n} from '@angular/core';\nimport { I18nService } from '../services/i18n.service';\nimport type { Params, TranslateFn } from '../types/types';\n\nexport interface TrContext {\n $implicit: TranslateFn;\n t: TranslateFn;\n lang: Signal<string>;\n}\n\n/**\n * Structural directive. The view is created ONCE; `t` reads signals, so only the bindings that use it update.\n *\n * <ng-container *tr=\"let t; prefix: 'orders'\">\n * <h1>{{ t('title') }}</h1> <!-- orders.title -->\n * </ng-container>\n */\n@Directive({ selector: '[tr],[translate]' })\nexport class TrDirective {\n private readonly tpl = inject<TemplateRef<TrContext>>(TemplateRef);\n private readonly vcr = inject(ViewContainerRef);\n private readonly svc = inject(I18nService);\n private readonly prefix = signal<string | undefined>(undefined);\n /** `prefix: 'orders'` in the microsyntax. */\n @Input('trPrefix') set trPrefix(v: string | undefined) { this.prefix.set(v); }\n\n static ngTemplateContextGuard(_dir: TrDirective, _ctx: unknown): _ctx is TrContext {\n return true;\n }\n\n constructor() {\n const t: TranslateFn = (key, params) => {\n const p = this.prefix();\n return this.svc.t(p ? p + '.' + key : key, params);\n };\n this.vcr.createEmbeddedView(this.tpl, { $implicit: t, t, lang: this.svc.lang });\n }\n}\n\n/**\n * Attribute directive.\n * <span trKey=\"orders.title\"></span>\n * <input trKey=\"form.name\" trAttr=\"placeholder\" />\n * <p trKey=\"hello\" [trParams]=\"{ name: user.name }\"></p>\n */\n@Directive({ selector: '[trKey]' })\nexport class TrKeyDirective {\n // @Input setters feeding signals: works with JIT, AOT and every supported Angular version.\n private readonly key = signal<string | undefined>(undefined);\n private readonly params = signal<Params | undefined>(undefined);\n private readonly attr = signal<string | undefined>(undefined);\n private readonly prefix = signal<string | undefined>(undefined);\n @Input('trKey') set trKey(v: string | undefined) { this.key.set(v); }\n @Input('trParams') set trParams(v: Params | undefined) { this.params.set(v); }\n @Input('trAttr') set trAttr(v: string | undefined) { this.attr.set(v); }\n @Input('trPrefix') set trPrefix(v: string | undefined) { this.prefix.set(v); }\n\n constructor() {\n const svc = inject(I18nService);\n const el = inject<ElementRef<HTMLElement>>(ElementRef).nativeElement;\n const r = inject(Renderer2);\n effect(() => {\n const k = this.key();\n if (!k) return;\n const p = this.prefix();\n const text = svc.t(p ? p + '.' + k : k, this.params());\n const a = this.attr();\n if (a) r.setAttribute(el, a, text);\n else if (el.textContent !== text) el.textContent = text;\n });\n }\n}\n","import { HttpClient } from '@angular/common/http';\nimport { Injectable, inject } from '@angular/core';\nimport type { Observable } from 'rxjs';\nimport { I18N_CONFIG, I18N_DEFAULTS, type Dictionary, type I18nLoader } from '../types/types';\nimport { buildI18nUrl } from '../utils/utils';\n\n/**\n * Default loader: GET `{baseUrl}/{lang}{ext}` and `{baseUrl}/{scope}/{lang}{ext}`.\n * Goes through your HttpClient interceptors.\n * Config is read on every request, so values supplied later via `I18nService.init()` /\n * `configure()` (for example `baseUrl`) are picked up.\n */\n@Injectable({ providedIn: 'root' })\nexport class HttpI18nLoader implements I18nLoader {\n private readonly http = inject(HttpClient);\n private readonly cfg = inject(I18N_CONFIG);\n\n load(lang: string, scope?: string): Observable<Dictionary> {\n const { url, baseUrl, fileExtension } = this.cfg;\n const u = url\n ? url(lang, scope)\n : buildI18nUrl(baseUrl ?? '/i18n', scope, lang, fileExtension ?? I18N_DEFAULTS.fileExtension);\n return this.http.get<Dictionary>(u);\n }\n}\n","import {\n EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideAppInitializer,\n} from '@angular/core';\nimport { HttpI18nLoader } from '../loader/http-loader';\nimport { I18nService } from '../services/i18n.service';\nimport { I18N_CONFIG, I18N_DEFAULTS, I18N_LOADER, I18nConfig } from '../types/types';\nimport { assignDefined } from '../utils/utils';\n\n/**\n * Static configuration (everything known at build time):\n * ```ts\n * provideHttpClient(),\n * provideI18n({ langs: ['fa', 'en'], defaultLang: 'fa', fallbackLangs: ['en'] })\n * ```\n * The global dictionary of the initial language is then loaded before the app starts.\n *\n * Deferred configuration (languages come from your backend): omit `langs`/`defaultLang`\n * and call `await inject(I18nService).init({ langs, defaultLang, baseUrl, lang })`\n * from your own APP_INITIALIZER once you know them:\n * ```ts\n * provideI18n({ fallbackLangs: ['fa'] }), // no automatic initializer\n * provideAppInitializer(() => AppInitializer()), // calls i18n.init(...)\n * ```\n * Pass `autoInit: false` to force this mode even when `langs` is given.\n */\nexport function provideI18n(config: I18nConfig = {}): EnvironmentProviders {\n const autoInit = config.autoInit ?? (!!config.langs?.length && !!config.defaultLang);\n return makeEnvironmentProviders([\n // a fresh, mutable object per injector (the service updates it through configure())\n { provide: I18N_CONFIG, useFactory: () => assignDefined({ ...I18N_DEFAULTS }, config) },\n ...(config.loader ? [config.loader] : []),\n { provide: I18N_LOADER, useFactory: () => inject(config.loader ?? HttpI18nLoader) },\n ...(autoInit\n ? [\n provideAppInitializer(() => {\n const svc = inject(I18nService);\n return svc.use(svc.detectInitialLang());\n }),\n ]\n : []),\n ]);\n}\n","import { EnvironmentProviders, inject, makeEnvironmentProviders, provideEnvironmentInitializer } from '@angular/core';\nimport type { CanActivateChildFn } from '@angular/router';\nimport { I18nService } from '../services/i18n.service';\n\n/**\n * Put in `route.providers`. Runs when the route injector is created (before guards),\n * so loading starts as early as possible. Child routes need nothing.\n */\nexport function provideI18nScope(scope: string | string[]): EnvironmentProviders {\n const scopes = Array.isArray(scope) ? scope : [scope];\n return makeEnvironmentProviders([\n provideEnvironmentInitializer(() => {\n const svc = inject(I18nService);\n for (const s of scopes) svc.registerScope(s);\n }),\n ]);\n}\n\n/** Put ONCE on the root path-less route: `canActivateChild: [i18nReadyGuard]`. Waits for every registered scope. */\nexport const i18nReadyGuard: CanActivateChildFn = async () => {\n await inject(I18nService).whenReady();\n return true;\n};\n","import { Signal, assertInInjectionContext, inject } from '@angular/core';\nimport { I18nService } from '../services/i18n.service';\nimport type { Params, TranslateFn } from '../types/types';\n\n/** `readonly title = translateSignal('orders.title')` (injection context) */\nexport function translateSignal(key: string, params?: Params | (() => Params)): Signal<string> {\n assertInInjectionContext(translateSignal);\n return inject(I18nService).select(key, params);\n}\n\n/** `const t = injectT(); computed(() => t('a.b'))` — reactive translate function. */\nexport function injectT(): TranslateFn {\n assertInInjectionContext(injectT);\n const svc = inject(I18nService);\n return (key, params) => svc.t(key, params);\n}\n","import { TrPipe } from './pipes/pipe';\nimport { TrDirective, TrKeyDirective } from './directives/directives';\n\nexport * from './types/types';\nexport { buildI18nUrl, flatten, interpolate, marker } from './utils/utils';\nexport { HttpI18nLoader } from './loader/http-loader';\nexport { I18nService } from './services/i18n.service';\nexport { provideI18n } from './providers/provide-i18n';\nexport { provideI18nScope, i18nReadyGuard } from './providers/routing';\nexport { translateSignal, injectT } from './signals/signals';\nexport { TrPipe } from './pipes/pipe';\nexport { TrDirective, TrKeyDirective, type TrContext } from './directives/directives';\n\n/** `imports: [TR]` brings the pipe and both directives into a standalone component. */\nexport const TR = [TrPipe, TrDirective, TrKeyDirective] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/lazy-i18n/README.md
CHANGED
|
@@ -17,21 +17,22 @@ Flat keys, route-level scopes, pipe, directives, fallbacks and a key extractor C
|
|
|
17
17
|
|
|
18
18
|
1. [Install](#install)
|
|
19
19
|
2. [Quick start](#quick-start)
|
|
20
|
-
3. [
|
|
21
|
-
4. [
|
|
22
|
-
5. [
|
|
23
|
-
6. [
|
|
24
|
-
7. [
|
|
25
|
-
8. [
|
|
26
|
-
9. [
|
|
27
|
-
10. [
|
|
28
|
-
11. [
|
|
29
|
-
12. [
|
|
30
|
-
13. [
|
|
31
|
-
14. [
|
|
32
|
-
15. [
|
|
33
|
-
16. [
|
|
34
|
-
17. [
|
|
20
|
+
3. [Deferred configuration (languages from the backend)](#deferred-configuration-languages-from-the-backend)
|
|
21
|
+
4. [Concepts](#concepts)
|
|
22
|
+
5. [Using translations](#using-translations)
|
|
23
|
+
6. [Scopes and routing](#scopes-and-routing)
|
|
24
|
+
7. [Language switching](#language-switching)
|
|
25
|
+
8. [Fallbacks and missing keys](#fallbacks-and-missing-keys)
|
|
26
|
+
9. [Interpolation, plurals and ICU](#interpolation-plurals-and-icu)
|
|
27
|
+
10. [Loaders](#loaders)
|
|
28
|
+
11. [Key extractor CLI](#key-extractor-cli)
|
|
29
|
+
12. [Configuration reference](#configuration-reference)
|
|
30
|
+
13. [API reference](#api-reference)
|
|
31
|
+
14. [Testing your app](#testing-your-app)
|
|
32
|
+
15. [Performance notes](#performance-notes)
|
|
33
|
+
16. [Migrating from Transloco](#migrating-from-transloco)
|
|
34
|
+
17. [Limitations](#limitations)
|
|
35
|
+
18. [Developing this library](#developing-this-library)
|
|
35
36
|
|
|
36
37
|
---
|
|
37
38
|
|
|
@@ -116,6 +117,62 @@ export class RequestsComponent {
|
|
|
116
117
|
}
|
|
117
118
|
```
|
|
118
119
|
|
|
120
|
+
## Deferred configuration (languages from the backend)
|
|
121
|
+
|
|
122
|
+
Often the list of languages, the default language and the user's saved language only exist **after** your own
|
|
123
|
+
`APP_INITIALIZER` has talked to the backend. Do not try to build the config earlier (no `setTimeout`, no mutating
|
|
124
|
+
the config token, no async `useFactory`): provide the library **without** languages and finish the setup with
|
|
125
|
+
`I18nService.init()` when you know them.
|
|
126
|
+
|
|
127
|
+
```ts
|
|
128
|
+
// app.config.ts
|
|
129
|
+
provideHttpClient(),
|
|
130
|
+
provideI18n({ fallbackLangs: ['fa'] }), // no langs/defaultLang -> no automatic initializer
|
|
131
|
+
provideAppInitializer(() => AppInitializer()),
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
```ts
|
|
135
|
+
// app-initializer.ts
|
|
136
|
+
export async function AppInitializer(): Promise<void> {
|
|
137
|
+
const http = inject(HttpClient);
|
|
138
|
+
const platformLocation = inject(PlatformLocation);
|
|
139
|
+
const i18n = inject(I18nService);
|
|
140
|
+
inject(ColorSchemaService).load();
|
|
141
|
+
|
|
142
|
+
// 1) appconfig.json 2) /api/Common/InitialApp (as you do today)
|
|
143
|
+
const config = await firstValueFrom(http.get<any>(configUrl(platformLocation)));
|
|
144
|
+
AppConst.apiUrl = config.apiUrl; /* … */
|
|
145
|
+
const resp = await firstValueFrom(http.get<InitialAppDto>(AppConst.apiUrl + '/api/Common/InitialApp'));
|
|
146
|
+
AppConst.languageList = resp.languages ?? [];
|
|
147
|
+
AppConst.defaultLang = resp.defaultLang ?? 'fa';
|
|
148
|
+
|
|
149
|
+
// 3) finish the i18n setup and load the first language. AWAIT it: the app starts when translations are ready.
|
|
150
|
+
await i18n.init({
|
|
151
|
+
langs: AppConst.languageList.map((l) => l.culture),
|
|
152
|
+
defaultLang: AppConst.defaultLang,
|
|
153
|
+
baseUrl: AppConst.apiUrl, // read at request time, so a value known only now is fine
|
|
154
|
+
lang: AppConst.currentLanguage, // optional: user's saved language; ignored if not in `langs`
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
What `init()` does: merges the options into the live config (`configure()`), picks the language
|
|
160
|
+
(`lang` if supported, otherwise stored > browser (opt-in) > `defaultLang`), downloads the global file, every scope
|
|
161
|
+
registered so far and the fallback languages, and only then activates the language. It rejects if loading fails, so
|
|
162
|
+
you can catch it in the initializer and show an error page.
|
|
163
|
+
|
|
164
|
+
Notes:
|
|
165
|
+
|
|
166
|
+
- `loader` and `autoInit` can only be set in `provideI18n()`. Everything else can be changed later with
|
|
167
|
+
`i18n.configure({...})`; files that are already downloaded stay cached.
|
|
168
|
+
- When `langs` **and** `defaultLang` are given to `provideI18n()`, an initializer is added automatically
|
|
169
|
+
(`autoInit` defaults to `true`). Use `autoInit: false` if you prefer to call `init()` yourself.
|
|
170
|
+
- `i18n.langs` is a **signal** (`i18n.langs()`) because the list can arrive after startup. `i18n.ready()` becomes
|
|
171
|
+
`true` after the first language is active.
|
|
172
|
+
- Scopes registered before `init()` (for example by an early service) are remembered and loaded by `init()`.
|
|
173
|
+
`whenReady()` and `i18nReadyGuard` also wait for an in-flight `init()` / `use()`.
|
|
174
|
+
- Do not call `use()` without awaiting it inside an initializer: the app would start before the files arrive.
|
|
175
|
+
|
|
119
176
|
## Concepts
|
|
120
177
|
|
|
121
178
|
| Term | Meaning |
|
|
@@ -250,6 +307,11 @@ Use a real ICU library (for example `intl-messageformat`) inside `transpile` for
|
|
|
250
307
|
|
|
251
308
|
The default `HttpI18nLoader` uses `HttpClient`, so your interceptors (auth, base URL, retry) apply.
|
|
252
309
|
|
|
310
|
+
URLs are built as `{baseUrl}/{scope}/{lang}{fileExtension}` (global file: `{baseUrl}/{lang}{fileExtension}`).
|
|
311
|
+
The builder is safe by construction: a missing scope never becomes `undefined`, repeated or trailing slashes
|
|
312
|
+
are collapsed (`'https://api.x.com/'` + no scope -> `https://api.x.com/fa.json`), and the extension may be written
|
|
313
|
+
with or without the dot. `buildI18nUrl()` is exported if you need the same logic in a custom loader.
|
|
314
|
+
|
|
253
315
|
```ts
|
|
254
316
|
provideI18n({
|
|
255
317
|
…,
|
|
@@ -333,15 +395,16 @@ Add to `package.json` scripts: `"i18n": "ngx-lazy-i18n-extract"`, `"i18n:check":
|
|
|
333
395
|
|
|
334
396
|
| Option | Type | Default | Description |
|
|
335
397
|
|---|---|---|---|
|
|
336
|
-
| `langs` | `string[]` | required | Supported languages. |
|
|
337
|
-
| `defaultLang` | `string` | required | Used when nothing is stored or detected. |
|
|
398
|
+
| `langs` | `string[]` | required before first activation | Supported languages. May be supplied later with `init()`. |
|
|
399
|
+
| `defaultLang` | `string` | required before first activation | Used when nothing is stored or detected. May be supplied later. |
|
|
400
|
+
| `autoInit` | `boolean` | `true` if `langs` and `defaultLang` are given | Add an APP_INITIALIZER that activates the initial language. Provide-time only. |
|
|
338
401
|
| `fallbackLangs` | `string[]` | `[]` | Languages tried, in order, for missing/empty keys. |
|
|
339
402
|
| `missing` | `'key' \| 'empty' \| (key, lang) => string` | `'key'` | What to render for a missing key. |
|
|
340
403
|
| `warnMissing` | `boolean` | `isDevMode()` | Log each missing key once. |
|
|
341
404
|
| `transpile` | `(value, params, lang) => string` | built-in `{{ }}` | Custom message formatter. |
|
|
342
405
|
| `loader` | `Type<I18nLoader>` | `HttpI18nLoader` | Custom loader class. |
|
|
343
406
|
| `baseUrl` | `string` | `'/i18n'` | HTTP loader base. |
|
|
344
|
-
| `fileExtension` | `string` | `''` | HTTP loader extension
|
|
407
|
+
| `fileExtension` | `string` | `'.json'` | HTTP loader extension; `''` for none. |
|
|
345
408
|
| `url` | `(lang, scope?) => string` | none | HTTP loader full URL control. |
|
|
346
409
|
| `retries` | `number` | `1` | Retries per failed request (250 ms delay). |
|
|
347
410
|
| `storageKey` | `string \| null` | `'lazy-i18n.lang'` | Persist language; `null` disables. |
|
|
@@ -363,7 +426,10 @@ Add to `package.json` scripts: `"i18n": "ngx-lazy-i18n-extract"`, `"i18n:check":
|
|
|
363
426
|
| `lang: Signal<string>` | Active language. |
|
|
364
427
|
| `dir: Signal<'rtl' \| 'ltr'>` | Direction for the active language. |
|
|
365
428
|
| `revision: Signal<number>` | Changes on every language/dictionary change. |
|
|
366
|
-
| `langs: readonly string[]
|
|
429
|
+
| `langs: Signal<readonly string[]>` | Supported languages (reactive; may arrive after startup). |
|
|
430
|
+
| `ready: Signal<boolean>` | `true` after the first language is active. |
|
|
431
|
+
| `init({ langs, defaultLang, baseUrl, lang, … })` | Complete the config and activate the first language. Use in your own initializer. |
|
|
432
|
+
| `configure(patch)` | Change options after startup (not `loader`/`autoInit`). |
|
|
367
433
|
| `t(key, params?)` | Translate (reactive). |
|
|
368
434
|
| `instant(key, params?)` | Translate (non-reactive). |
|
|
369
435
|
| `select(key, params?)` | `Signal<string>`; `params` may be a function. |
|
|
@@ -371,7 +437,7 @@ Add to `package.json` scripts: `"i18n": "ngx-lazy-i18n-extract"`, `"i18n:check":
|
|
|
371
437
|
| `use(lang)` | Load everything, then activate. |
|
|
372
438
|
| `preload(lang)` | Download without activating. |
|
|
373
439
|
| `registerScope(scope)` | Register and start loading a scope. |
|
|
374
|
-
| `whenReady()` | Resolves when all registered scopes are
|
|
440
|
+
| `whenReady()` | Resolves when all registered scopes and any in-flight `init()`/`use()` are done. Never rejects. |
|
|
375
441
|
| `setTranslation(lang, dict, merge = true)` | Inject translations manually. |
|
|
376
442
|
| `detectInitialLang()` | Stored, browser (opt-in) or default. |
|
|
377
443
|
|
package/package.json
CHANGED
|
@@ -15,10 +15,16 @@ interface I18nLoader {
|
|
|
15
15
|
/** `scope === undefined` means the global file. */
|
|
16
16
|
load(lang: string, scope?: string): Observable<Dictionary> | Promise<Dictionary>;
|
|
17
17
|
}
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Every option is optional so the library can be provided at startup and completed later
|
|
20
|
+
* (for example after your own APP_INITIALIZER has fetched the language list from the backend):
|
|
21
|
+
* see `I18nService.init()` / `I18nService.configure()`.
|
|
22
|
+
*/
|
|
23
|
+
interface I18nConfig {
|
|
24
|
+
/** All supported languages. Required before the first language is activated. */
|
|
25
|
+
langs?: string[];
|
|
26
|
+
/** Language used when nothing is stored or detected. Required before the first language is activated. */
|
|
27
|
+
defaultLang?: string;
|
|
22
28
|
/** Looked up (in order) when a key is empty/missing in the active language. */
|
|
23
29
|
fallbackLangs?: string[];
|
|
24
30
|
/** Default `'key'` (returns the key itself). */
|
|
@@ -27,11 +33,11 @@ declare class I18nConfig {
|
|
|
27
33
|
warnMissing?: boolean;
|
|
28
34
|
/** Replace the default `{{ name }}` interpolation (ICU, plurals, ...). */
|
|
29
35
|
transpile?: Transpiler;
|
|
30
|
-
/** Custom loader class (must be `@Injectable({providedIn:'root'})` or constructible without args). Default: HTTP. */
|
|
36
|
+
/** Custom loader class (must be `@Injectable({providedIn:'root'})` or constructible without args). Default: HTTP. Provide-time only. */
|
|
31
37
|
loader?: Type<I18nLoader>;
|
|
32
|
-
/** Default HTTP loader: base URL. Default `'/i18n'` -> `/i18n/fa`, `/i18n/setting/fa`. */
|
|
38
|
+
/** Default HTTP loader: base URL. Default `'/i18n'` -> `/i18n/fa.json`, `/i18n/setting/fa.json`. Slashes are normalised. */
|
|
33
39
|
baseUrl?: string;
|
|
34
|
-
/** Default HTTP loader: file extension appended to the URL, e.g. `'.json'`. Default `'.json'
|
|
40
|
+
/** Default HTTP loader: file extension appended to the URL, e.g. `'.json'`. Default `'.json'`; use `''` for none. */
|
|
35
41
|
fileExtension?: string;
|
|
36
42
|
/** Default HTTP loader: full control over the URL. */
|
|
37
43
|
url?: (lang: string, scope?: string) => string;
|
|
@@ -43,7 +49,23 @@ declare class I18nConfig {
|
|
|
43
49
|
detectBrowserLang?: boolean;
|
|
44
50
|
/** Keep `<html lang dir>` in sync. Default `true`. */
|
|
45
51
|
manageDocument?: boolean;
|
|
52
|
+
/**
|
|
53
|
+
* Activate the initial language in an APP_INITIALIZER created by `provideI18n()`.
|
|
54
|
+
* Default: `true` when both `langs` and `defaultLang` are given to `provideI18n()`, otherwise `false`
|
|
55
|
+
* (then you call `I18nService.init()` yourself, typically from your own initializer).
|
|
56
|
+
* Provide-time only.
|
|
57
|
+
*/
|
|
58
|
+
autoInit?: boolean;
|
|
46
59
|
}
|
|
60
|
+
/** Options that can be changed after the app has started. */
|
|
61
|
+
type I18nRuntimeConfig = Omit<I18nConfig, 'loader' | 'autoInit'>;
|
|
62
|
+
/** `I18nService.init()` options: runtime config + the language to start with. */
|
|
63
|
+
interface I18nInitOptions extends I18nRuntimeConfig {
|
|
64
|
+
/** Preferred starting language (e.g. the user's saved language from the backend). Ignored if not in `langs`. */
|
|
65
|
+
lang?: string;
|
|
66
|
+
}
|
|
67
|
+
/** Defaults applied by `provideI18n()`. */
|
|
68
|
+
declare const I18N_DEFAULTS: I18nConfig;
|
|
47
69
|
declare const I18N_CONFIG: InjectionToken<I18nConfig>;
|
|
48
70
|
declare const I18N_LOADER: InjectionToken<I18nLoader>;
|
|
49
71
|
|
|
@@ -114,8 +136,21 @@ declare function flatten(dict: Dictionary, target?: Map<string, string>, onConfl
|
|
|
114
136
|
declare function interpolate(text: string, params?: Params): string;
|
|
115
137
|
/** Identity function the extractor recognises: `const k = marker('orders.title')`. */
|
|
116
138
|
declare function marker<T extends string>(key: T): T;
|
|
139
|
+
/**
|
|
140
|
+
* Build `{base}/{scope}/{lang}{ext}` without ever producing `undefined`, `//` or a trailing slash problem:
|
|
141
|
+
* ("/i18n", undefined, "fa", ".json") -> "/i18n/fa.json"
|
|
142
|
+
* ("https://x.com/", "setting", "fa", "json") -> "https://x.com/setting/fa.json"
|
|
143
|
+
* (undefined | "", undefined, "fa") -> "/fa"
|
|
144
|
+
* The protocol part of `base` ("https://") is left alone.
|
|
145
|
+
*/
|
|
146
|
+
declare function buildI18nUrl(base: string | null | undefined, scope: string | null | undefined, lang: string, ext?: string | null | undefined): string;
|
|
117
147
|
|
|
118
|
-
/**
|
|
148
|
+
/**
|
|
149
|
+
* Default loader: GET `{baseUrl}/{lang}{ext}` and `{baseUrl}/{scope}/{lang}{ext}`.
|
|
150
|
+
* Goes through your HttpClient interceptors.
|
|
151
|
+
* Config is read on every request, so values supplied later via `I18nService.init()` /
|
|
152
|
+
* `configure()` (for example `baseUrl`) are picked up.
|
|
153
|
+
*/
|
|
119
154
|
declare class HttpI18nLoader implements I18nLoader {
|
|
120
155
|
private readonly http;
|
|
121
156
|
private readonly cfg;
|
|
@@ -130,14 +165,19 @@ declare class I18nService {
|
|
|
130
165
|
private readonly doc;
|
|
131
166
|
private readonly _lang;
|
|
132
167
|
private readonly _rev;
|
|
168
|
+
private readonly _langs;
|
|
169
|
+
private readonly _ready;
|
|
133
170
|
/** Active language (reactive). */
|
|
134
171
|
readonly lang: Signal<string>;
|
|
135
172
|
/** Bumps whenever the active language or any dictionary changes. */
|
|
136
173
|
readonly revision: Signal<number>;
|
|
137
174
|
readonly dir: Signal<"rtl" | "ltr">;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
175
|
+
/** Supported languages (a signal: they may be supplied after startup via `init()` / `configure()`). */
|
|
176
|
+
readonly langs: Signal<readonly string[]>;
|
|
177
|
+
/** `true` once the first language has been activated. Handy for a splash screen. */
|
|
178
|
+
readonly ready: Signal<boolean>;
|
|
179
|
+
private get fallbacks();
|
|
180
|
+
private get warn();
|
|
141
181
|
private readonly dicts;
|
|
142
182
|
private readonly loaded;
|
|
143
183
|
private readonly inflight;
|
|
@@ -163,6 +203,24 @@ declare class I18nService {
|
|
|
163
203
|
* Nothing renders half-translated and no "missing" flash occurs.
|
|
164
204
|
*/
|
|
165
205
|
use(lang: string): Promise<void>;
|
|
206
|
+
/**
|
|
207
|
+
* Complete the configuration and activate the first language in one call. Use it from your own
|
|
208
|
+
* APP_INITIALIZER when the language list is only known at runtime (e.g. from the backend):
|
|
209
|
+
*
|
|
210
|
+
* ```ts
|
|
211
|
+
* await i18n.init({ langs: ['fa', 'en'], defaultLang: 'fa', baseUrl: apiUrl, lang: user.lang });
|
|
212
|
+
* ```
|
|
213
|
+
* `lang` is used when it is in `langs`; otherwise stored > browser (opt-in) > `defaultLang`.
|
|
214
|
+
* Rejects when the files cannot be loaded, so the initializer can show an error page.
|
|
215
|
+
*/
|
|
216
|
+
init(options?: I18nInitOptions): Promise<void>;
|
|
217
|
+
/**
|
|
218
|
+
* Update options after startup (languages, baseUrl, fallbacks, ...). Already downloaded files stay cached.
|
|
219
|
+
* `loader` and `autoInit` can only be set in `provideI18n()`.
|
|
220
|
+
*/
|
|
221
|
+
configure(patch: I18nRuntimeConfig): void;
|
|
222
|
+
private syncLangs;
|
|
223
|
+
private activate;
|
|
166
224
|
/** Download a language without activating it. */
|
|
167
225
|
preload(lang: string): Promise<void>;
|
|
168
226
|
/** Language to start with: stored > browser (opt-in) > default. */
|
|
@@ -185,13 +243,23 @@ declare class I18nService {
|
|
|
185
243
|
}
|
|
186
244
|
|
|
187
245
|
/**
|
|
246
|
+
* Static configuration (everything known at build time):
|
|
188
247
|
* ```ts
|
|
189
248
|
* provideHttpClient(),
|
|
190
249
|
* provideI18n({ langs: ['fa', 'en'], defaultLang: 'fa', fallbackLangs: ['en'] })
|
|
191
250
|
* ```
|
|
192
|
-
* The global dictionary of the initial language is loaded before the app starts.
|
|
251
|
+
* The global dictionary of the initial language is then loaded before the app starts.
|
|
252
|
+
*
|
|
253
|
+
* Deferred configuration (languages come from your backend): omit `langs`/`defaultLang`
|
|
254
|
+
* and call `await inject(I18nService).init({ langs, defaultLang, baseUrl, lang })`
|
|
255
|
+
* from your own APP_INITIALIZER once you know them:
|
|
256
|
+
* ```ts
|
|
257
|
+
* provideI18n({ fallbackLangs: ['fa'] }), // no automatic initializer
|
|
258
|
+
* provideAppInitializer(() => AppInitializer()), // calls i18n.init(...)
|
|
259
|
+
* ```
|
|
260
|
+
* Pass `autoInit: false` to force this mode even when `langs` is given.
|
|
193
261
|
*/
|
|
194
|
-
declare function provideI18n(config
|
|
262
|
+
declare function provideI18n(config?: I18nConfig): EnvironmentProviders;
|
|
195
263
|
|
|
196
264
|
/**
|
|
197
265
|
* Put in `route.providers`. Runs when the route injector is created (before guards),
|
|
@@ -209,5 +277,5 @@ declare function injectT(): TranslateFn;
|
|
|
209
277
|
/** `imports: [TR]` brings the pipe and both directives into a standalone component. */
|
|
210
278
|
declare const TR: readonly [typeof TrPipe, typeof TrDirective, typeof TrKeyDirective];
|
|
211
279
|
|
|
212
|
-
export { HttpI18nLoader, I18N_CONFIG,
|
|
213
|
-
export type { Dictionary, I18nLoader, MissingHandler, Params, TrContext, TranslateFn, Transpiler };
|
|
280
|
+
export { HttpI18nLoader, I18N_CONFIG, I18N_DEFAULTS, I18N_LOADER, I18nService, TR, TrDirective, TrKeyDirective, TrPipe, buildI18nUrl, flatten, i18nReadyGuard, injectT, interpolate, marker, provideI18n, provideI18nScope, translateSignal };
|
|
281
|
+
export type { Dictionary, I18nConfig, I18nInitOptions, I18nLoader, I18nRuntimeConfig, MissingHandler, Params, TrContext, TranslateFn, Transpiler };
|