ngx-kit 8.5.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 +612 -0
- package/fesm2022/ngx-kit-lazy-i18n.mjs.map +1 -0
- package/lazy-i18n/README.md +521 -0
- package/package.json +45 -2
- package/types/ngx-kit-lazy-i18n.d.ts +281 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ngx-kit",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.1",
|
|
4
4
|
"description": "Angular input components",
|
|
5
5
|
"author": "Mohammadreza Samani <mr.samani1368@gmail.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -13,7 +13,43 @@
|
|
|
13
13
|
"url": "https://github.com/mr-samani/ngx-kit/issues"
|
|
14
14
|
},
|
|
15
15
|
"keywords": [
|
|
16
|
-
"angular"
|
|
16
|
+
"angular",
|
|
17
|
+
"i18n",
|
|
18
|
+
"translate",
|
|
19
|
+
"signals",
|
|
20
|
+
"lazy",
|
|
21
|
+
"scopes",
|
|
22
|
+
"input-color",
|
|
23
|
+
"color-picker",
|
|
24
|
+
"gradient-picker",
|
|
25
|
+
"select-angle",
|
|
26
|
+
"date-picker",
|
|
27
|
+
"calendar",
|
|
28
|
+
"file",
|
|
29
|
+
"drop-zone",
|
|
30
|
+
"image-viewer",
|
|
31
|
+
"draggable",
|
|
32
|
+
"resizable",
|
|
33
|
+
"kanban-view",
|
|
34
|
+
"drag-drop",
|
|
35
|
+
"shadow",
|
|
36
|
+
"message",
|
|
37
|
+
"notify",
|
|
38
|
+
"alert",
|
|
39
|
+
"dialog",
|
|
40
|
+
"modal",
|
|
41
|
+
"time-picker",
|
|
42
|
+
"tooltip",
|
|
43
|
+
"data-table",
|
|
44
|
+
"table",
|
|
45
|
+
"pagination",
|
|
46
|
+
"drop-zone",
|
|
47
|
+
"image-editor",
|
|
48
|
+
"media-control",
|
|
49
|
+
"media-player",
|
|
50
|
+
"audio-player",
|
|
51
|
+
"infinit-scroll",
|
|
52
|
+
"virtual-scroll"
|
|
17
53
|
],
|
|
18
54
|
"peerDependencies": {
|
|
19
55
|
"@angular/common": ">=22.0.0",
|
|
@@ -23,6 +59,9 @@
|
|
|
23
59
|
"tslib": "^2.3.0"
|
|
24
60
|
},
|
|
25
61
|
"sideEffects": false,
|
|
62
|
+
"bin": {
|
|
63
|
+
"ngx-lazy-i18n-extract": "./lazy-i18n/bin/extract.mjs"
|
|
64
|
+
},
|
|
26
65
|
"module": "fesm2022/ngx-kit.mjs",
|
|
27
66
|
"typings": "types/ngx-kit.d.ts",
|
|
28
67
|
"exports": {
|
|
@@ -93,6 +132,10 @@
|
|
|
93
132
|
"types": "./types/ngx-kit-infinite-scroll.d.ts",
|
|
94
133
|
"default": "./fesm2022/ngx-kit-infinite-scroll.mjs"
|
|
95
134
|
},
|
|
135
|
+
"./lazy-i18n": {
|
|
136
|
+
"types": "./types/ngx-kit-lazy-i18n.d.ts",
|
|
137
|
+
"default": "./fesm2022/ngx-kit-lazy-i18n.mjs"
|
|
138
|
+
},
|
|
96
139
|
"./media-control": {
|
|
97
140
|
"types": "./types/ngx-kit-media-control.d.ts",
|
|
98
141
|
"default": "./fesm2022/ngx-kit-media-control.mjs"
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, Type, PipeTransform, Signal, EnvironmentProviders } from '@angular/core';
|
|
3
|
+
import { Observable } from 'rxjs';
|
|
4
|
+
import { CanActivateChildFn } from '@angular/router';
|
|
5
|
+
|
|
6
|
+
/** A (possibly nested) translation file as returned by the server. */
|
|
7
|
+
interface Dictionary {
|
|
8
|
+
[key: string]: string | number | boolean | null | Dictionary | Array<unknown>;
|
|
9
|
+
}
|
|
10
|
+
type Params = Record<string, unknown>;
|
|
11
|
+
type TranslateFn = (key: string, params?: Params) => string;
|
|
12
|
+
type MissingHandler = (key: string, lang: string) => string;
|
|
13
|
+
type Transpiler = (value: string, params: Params | undefined, lang: string) => string;
|
|
14
|
+
interface I18nLoader {
|
|
15
|
+
/** `scope === undefined` means the global file. */
|
|
16
|
+
load(lang: string, scope?: string): Observable<Dictionary> | Promise<Dictionary>;
|
|
17
|
+
}
|
|
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;
|
|
28
|
+
/** Looked up (in order) when a key is empty/missing in the active language. */
|
|
29
|
+
fallbackLangs?: string[];
|
|
30
|
+
/** Default `'key'` (returns the key itself). */
|
|
31
|
+
missing?: 'key' | 'empty' | MissingHandler;
|
|
32
|
+
/** Log each missing key once. Default: `isDevMode()`. */
|
|
33
|
+
warnMissing?: boolean;
|
|
34
|
+
/** Replace the default `{{ name }}` interpolation (ICU, plurals, ...). */
|
|
35
|
+
transpile?: Transpiler;
|
|
36
|
+
/** Custom loader class (must be `@Injectable({providedIn:'root'})` or constructible without args). Default: HTTP. Provide-time only. */
|
|
37
|
+
loader?: Type<I18nLoader>;
|
|
38
|
+
/** Default HTTP loader: base URL. Default `'/i18n'` -> `/i18n/fa.json`, `/i18n/setting/fa.json`. Slashes are normalised. */
|
|
39
|
+
baseUrl?: string;
|
|
40
|
+
/** Default HTTP loader: file extension appended to the URL, e.g. `'.json'`. Default `'.json'`; use `''` for none. */
|
|
41
|
+
fileExtension?: string;
|
|
42
|
+
/** Default HTTP loader: full control over the URL. */
|
|
43
|
+
url?: (lang: string, scope?: string) => string;
|
|
44
|
+
/** Retries per failed request. Default `1`. */
|
|
45
|
+
retries?: number;
|
|
46
|
+
/** localStorage key used to remember the language. `null` disables. Default `'lazy-i18n.lang'`. */
|
|
47
|
+
storageKey?: string | null;
|
|
48
|
+
/** Pick the initial language from `navigator.languages` when nothing is stored. Default `false`. */
|
|
49
|
+
detectBrowserLang?: boolean;
|
|
50
|
+
/** Keep `<html lang dir>` in sync. Default `true`. */
|
|
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;
|
|
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;
|
|
69
|
+
declare const I18N_CONFIG: InjectionToken<I18nConfig>;
|
|
70
|
+
declare const I18N_LOADER: InjectionToken<I18nLoader>;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* `{{ 'orders.title' | tr }}` / `{{ 'hello' | tr: { name } }}`
|
|
74
|
+
*
|
|
75
|
+
* Impure on purpose (it must follow signals), but memoised: when key, params and the
|
|
76
|
+
* service revision are unchanged it returns the cached string in O(1) with no lookup.
|
|
77
|
+
*/
|
|
78
|
+
declare class TrPipe implements PipeTransform {
|
|
79
|
+
private readonly svc;
|
|
80
|
+
private k?;
|
|
81
|
+
private p?;
|
|
82
|
+
private rev;
|
|
83
|
+
private out;
|
|
84
|
+
transform(key: string | null | undefined, params?: Params): string;
|
|
85
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<TrPipe, never>;
|
|
86
|
+
static ɵpipe: i0.ɵɵPipeDeclaration<TrPipe, "tr", true>;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
interface TrContext {
|
|
90
|
+
$implicit: TranslateFn;
|
|
91
|
+
t: TranslateFn;
|
|
92
|
+
lang: Signal<string>;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Structural directive. The view is created ONCE; `t` reads signals, so only the bindings that use it update.
|
|
96
|
+
*
|
|
97
|
+
* <ng-container *tr="let t; prefix: 'orders'">
|
|
98
|
+
* <h1>{{ t('title') }}</h1> <!-- orders.title -->
|
|
99
|
+
* </ng-container>
|
|
100
|
+
*/
|
|
101
|
+
declare class TrDirective {
|
|
102
|
+
private readonly tpl;
|
|
103
|
+
private readonly vcr;
|
|
104
|
+
private readonly svc;
|
|
105
|
+
private readonly prefix;
|
|
106
|
+
/** `prefix: 'orders'` in the microsyntax. */
|
|
107
|
+
set trPrefix(v: string | undefined);
|
|
108
|
+
static ngTemplateContextGuard(_dir: TrDirective, _ctx: unknown): _ctx is TrContext;
|
|
109
|
+
constructor();
|
|
110
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<TrDirective, never>;
|
|
111
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<TrDirective, "[tr],[translate]", never, { "trPrefix": { "alias": "trPrefix"; "required": false; }; }, {}, never, never, true, never>;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Attribute directive.
|
|
115
|
+
* <span trKey="orders.title"></span>
|
|
116
|
+
* <input trKey="form.name" trAttr="placeholder" />
|
|
117
|
+
* <p trKey="hello" [trParams]="{ name: user.name }"></p>
|
|
118
|
+
*/
|
|
119
|
+
declare class TrKeyDirective {
|
|
120
|
+
private readonly key;
|
|
121
|
+
private readonly params;
|
|
122
|
+
private readonly attr;
|
|
123
|
+
private readonly prefix;
|
|
124
|
+
set trKey(v: string | undefined);
|
|
125
|
+
set trParams(v: Params | undefined);
|
|
126
|
+
set trAttr(v: string | undefined);
|
|
127
|
+
set trPrefix(v: string | undefined);
|
|
128
|
+
constructor();
|
|
129
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<TrKeyDirective, never>;
|
|
130
|
+
static ɵdir: i0.ɵɵDirectiveDeclaration<TrKeyDirective, "[trKey]", never, { "trKey": { "alias": "trKey"; "required": false; }; "trParams": { "alias": "trParams"; "required": false; }; "trAttr": { "alias": "trAttr"; "required": false; }; "trPrefix": { "alias": "trPrefix"; "required": false; }; }, {}, never, never, true, never>;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/** Flatten a nested dictionary into `target` (dot-separated keys). Non-string leaves are stringified. */
|
|
134
|
+
declare function flatten(dict: Dictionary, target?: Map<string, string>, onConflict?: (key: string) => void, prefix?: string): Map<string, string>;
|
|
135
|
+
/** `Hello {{ name }}` + `{name:'Ali'}` -> `Hello Ali`. Unknown placeholders are left untouched. */
|
|
136
|
+
declare function interpolate(text: string, params?: Params): string;
|
|
137
|
+
/** Identity function the extractor recognises: `const k = marker('orders.title')`. */
|
|
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;
|
|
147
|
+
|
|
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
|
+
*/
|
|
154
|
+
declare class HttpI18nLoader implements I18nLoader {
|
|
155
|
+
private readonly http;
|
|
156
|
+
private readonly cfg;
|
|
157
|
+
load(lang: string, scope?: string): Observable<Dictionary>;
|
|
158
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<HttpI18nLoader, never>;
|
|
159
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<HttpI18nLoader>;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
declare class I18nService {
|
|
163
|
+
private readonly cfg;
|
|
164
|
+
private readonly loader;
|
|
165
|
+
private readonly doc;
|
|
166
|
+
private readonly _lang;
|
|
167
|
+
private readonly _rev;
|
|
168
|
+
private readonly _langs;
|
|
169
|
+
private readonly _ready;
|
|
170
|
+
/** Active language (reactive). */
|
|
171
|
+
readonly lang: Signal<string>;
|
|
172
|
+
/** Bumps whenever the active language or any dictionary changes. */
|
|
173
|
+
readonly revision: Signal<number>;
|
|
174
|
+
readonly dir: Signal<"rtl" | "ltr">;
|
|
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();
|
|
181
|
+
private readonly dicts;
|
|
182
|
+
private readonly loaded;
|
|
183
|
+
private readonly inflight;
|
|
184
|
+
private readonly scopes;
|
|
185
|
+
private readonly pending;
|
|
186
|
+
private readonly warned;
|
|
187
|
+
private useToken;
|
|
188
|
+
/** Translate. Reads signals, so it is reactive inside `computed`, `effect` and templates. */
|
|
189
|
+
t(key: string, params?: Params): string;
|
|
190
|
+
/** Same as `t`, but never creates a reactive dependency. */
|
|
191
|
+
instant(key: string, params?: Params): string;
|
|
192
|
+
/** `computed` signal for one key. `params` may be a function reading other signals. */
|
|
193
|
+
select(key: string, params?: Params | (() => Params)): Signal<string>;
|
|
194
|
+
/** True when the key has a non-empty value in the active language or a fallback. */
|
|
195
|
+
has(key: string): boolean;
|
|
196
|
+
/** Returns every key below `prefix` as a nested object (values are raw, not interpolated). */
|
|
197
|
+
getObject(prefix: string): Dictionary;
|
|
198
|
+
private lookup;
|
|
199
|
+
private resolve;
|
|
200
|
+
private onMissing;
|
|
201
|
+
/**
|
|
202
|
+
* Load global + all registered scopes (and fallback languages) for `lang`, THEN activate it.
|
|
203
|
+
* Nothing renders half-translated and no "missing" flash occurs.
|
|
204
|
+
*/
|
|
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;
|
|
224
|
+
/** Download a language without activating it. */
|
|
225
|
+
preload(lang: string): Promise<void>;
|
|
226
|
+
/** Language to start with: stored > browser (opt-in) > default. */
|
|
227
|
+
detectInitialLang(): string;
|
|
228
|
+
/** Register a scope and start loading it for the active language. Idempotent. Used by `provideI18nScope`. */
|
|
229
|
+
registerScope(scope: string): void;
|
|
230
|
+
/** Resolves once every scope registered so far is available. Never rejects. */
|
|
231
|
+
whenReady(): Promise<void>;
|
|
232
|
+
/** Manually set translations (tests, SSR transfer state, ...). */
|
|
233
|
+
setTranslation(lang: string, dict: Dictionary, merge?: boolean): void;
|
|
234
|
+
private merge;
|
|
235
|
+
private fetch;
|
|
236
|
+
private loadLang;
|
|
237
|
+
private isComplete;
|
|
238
|
+
private storageKey;
|
|
239
|
+
private persist;
|
|
240
|
+
private applyDocument;
|
|
241
|
+
static ɵfac: i0.ɵɵFactoryDeclaration<I18nService, never>;
|
|
242
|
+
static ɵprov: i0.ɵɵInjectableDeclaration<I18nService>;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Static configuration (everything known at build time):
|
|
247
|
+
* ```ts
|
|
248
|
+
* provideHttpClient(),
|
|
249
|
+
* provideI18n({ langs: ['fa', 'en'], defaultLang: 'fa', fallbackLangs: ['en'] })
|
|
250
|
+
* ```
|
|
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.
|
|
261
|
+
*/
|
|
262
|
+
declare function provideI18n(config?: I18nConfig): EnvironmentProviders;
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Put in `route.providers`. Runs when the route injector is created (before guards),
|
|
266
|
+
* so loading starts as early as possible. Child routes need nothing.
|
|
267
|
+
*/
|
|
268
|
+
declare function provideI18nScope(scope: string | string[]): EnvironmentProviders;
|
|
269
|
+
/** Put ONCE on the root path-less route: `canActivateChild: [i18nReadyGuard]`. Waits for every registered scope. */
|
|
270
|
+
declare const i18nReadyGuard: CanActivateChildFn;
|
|
271
|
+
|
|
272
|
+
/** `readonly title = translateSignal('orders.title')` (injection context) */
|
|
273
|
+
declare function translateSignal(key: string, params?: Params | (() => Params)): Signal<string>;
|
|
274
|
+
/** `const t = injectT(); computed(() => t('a.b'))` — reactive translate function. */
|
|
275
|
+
declare function injectT(): TranslateFn;
|
|
276
|
+
|
|
277
|
+
/** `imports: [TR]` brings the pipe and both directives into a standalone component. */
|
|
278
|
+
declare const TR: readonly [typeof TrPipe, typeof TrDirective, typeof TrKeyDirective];
|
|
279
|
+
|
|
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 };
|