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.
@@ -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
- class I18nConfig {
8
- /** All supported languages. */
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
- langs = this.cfg.langs;
115
- fallbacks = this.cfg.fallbackLangs ?? [];
116
- warn = this.cfg.warnMissing ?? isDevMode();
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
- async use(lang) {
195
- if (!this.cfg.langs.includes(lang))
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 && this.cfg.langs.includes(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 (this.cfg.langs.includes(n))
284
+ if (langs.includes(n))
226
285
  return n;
227
286
  const base = n.split('-')[0];
228
- if (this.cfg.langs.includes(base))
287
+ if (langs.includes(base))
229
288
  return base;
230
289
  }
231
290
  }
232
- return this.cfg.defaultLang;
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
- /** Default loader: GET `{baseUrl}/{lang}` and `{baseUrl}/{scope}/{lang}`. Goes through your HttpClient interceptors. */
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 = '/i18n', fileExtension = '' } = this.cfg;
453
- const u = url ? url(lang, scope) : `${baseUrl}/${scope ? scope + '/' : ''}${lang}${fileExtension}`;
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 c = { ...new I18nConfig(), ...config };
554
+ function provideI18n(config = {}) {
555
+ const autoInit = config.autoInit ?? (!!config.langs?.length && !!config.defaultLang);
473
556
  return makeEnvironmentProviders([
474
- { provide: I18N_CONFIG, useValue: c },
475
- ...(c.loader ? [c.loader] : []),
476
- { provide: I18N_LOADER, useFactory: () => inject(c.loader ?? HttpI18nLoader) },
477
- provideAppInitializer(() => {
478
- const svc = inject(I18nService);
479
- return svc.use(svc.detectInitialLang());
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, I18N_LOADER, I18nConfig, I18nService, TR, TrDirective, TrKeyDirective, TrPipe, flatten, i18nReadyGuard, injectT, interpolate, marker, provideI18n, provideI18nScope, translateSignal };
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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"}
@@ -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. [Concepts](#concepts)
21
- 4. [Using translations](#using-translations)
22
- 5. [Scopes and routing](#scopes-and-routing)
23
- 6. [Language switching](#language-switching)
24
- 7. [Fallbacks and missing keys](#fallbacks-and-missing-keys)
25
- 8. [Interpolation, plurals and ICU](#interpolation-plurals-and-icu)
26
- 9. [Loaders](#loaders)
27
- 10. [Key extractor CLI](#key-extractor-cli)
28
- 11. [Configuration reference](#configuration-reference)
29
- 12. [API reference](#api-reference)
30
- 13. [Testing your app](#testing-your-app)
31
- 14. [Performance notes](#performance-notes)
32
- 15. [Migrating from Transloco](#migrating-from-transloco)
33
- 16. [Limitations](#limitations)
34
- 17. [Developing this library](#developing-this-library)
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 (e.g. `.json`). |
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[]` | Supported languages. |
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 loaded. Never rejects. |
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ngx-kit",
3
- "version": "9.0.0",
3
+ "version": "9.0.1",
4
4
  "description": "Angular input components",
5
5
  "author": "Mohammadreza Samani <mr.samani1368@gmail.com>",
6
6
  "license": "MIT",
@@ -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
- declare class I18nConfig {
19
- /** All supported languages. */
20
- langs: string[];
21
- defaultLang: string;
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
- /** Default loader: GET `{baseUrl}/{lang}` and `{baseUrl}/{scope}/{lang}`. Goes through your HttpClient interceptors. */
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
- readonly langs: readonly string[];
139
- private readonly fallbacks;
140
- private readonly warn;
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: I18nConfig): EnvironmentProviders;
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, I18N_LOADER, I18nConfig, I18nService, TR, TrDirective, TrKeyDirective, TrPipe, flatten, i18nReadyGuard, injectT, interpolate, marker, provideI18n, provideI18nScope, translateSignal };
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 };