@mk-kit/ui 0.55.0 → 0.56.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -76,12 +76,21 @@ class MkTranslate {
76
76
  overridesLoader = null;
77
77
  dictionaries = new Map();
78
78
  declared = new Set();
79
+ /** Languages holding only the keys a server render used (see `transfer`). */
80
+ partial = new Set();
81
+ /** Keys read on the server, per language — what `transfer: 'used'` ships. */
82
+ used = new Map();
79
83
  pending = new Map();
80
84
  /** Bumped whenever a dictionary changes, so readers recompute. */
81
85
  version = signal(0, /* @ts-ignore */
82
86
  ...(ngDevMode ? [{ debugName: "version" }] : /* istanbul ignore next */ []));
83
- missing = signal(new Set(), /* @ts-ignore */
84
- ...(ngDevMode ? [{ debugName: "missing" }] : /* istanbul ignore next */ []));
87
+ // Missing keys are noticed while templates render, where writing a signal
88
+ // is forbidden (NG0600) so the set is plain and a tick signal is bumped
89
+ // in a microtask, once per batch of misses.
90
+ missing = new Set();
91
+ missingTick = signal(0, /* @ts-ignore */
92
+ ...(ngDevMode ? [{ debugName: "missingTick" }] : /* istanbul ignore next */ []));
93
+ missingFlush = null;
85
94
  /** The active language. Read it in a `computed()` to follow switches. */
86
95
  lang = signal(this.config?.lang ?? 'en', /* @ts-ignore */
87
96
  ...(ngDevMode ? [{ debugName: "lang" }] : /* istanbul ignore next */ []));
@@ -94,7 +103,10 @@ class MkTranslate {
94
103
  /** `lang` as an observable, for code still written around streams. */
95
104
  langChange = toObservable(this.lang);
96
105
  /** Keys asked for that neither the active nor the fallback language has. */
97
- missingKeys = computed(() => [...this.missing()].sort(), /* @ts-ignore */
106
+ missingKeys = computed(() => {
107
+ this.missingTick();
108
+ return [...this.missing].sort();
109
+ }, /* @ts-ignore */
98
110
  ...(ngDevMode ? [{ debugName: "missingKeys" }] : /* istanbul ignore next */ []));
99
111
  constructor() {
100
112
  if (!this.config && isDevMode()) {
@@ -136,21 +148,31 @@ class MkTranslate {
136
148
  * when the strings are in memory; a failed load rejects and leaves the
137
149
  * previous language active.
138
150
  */
139
- async use(lang) {
140
- await this.load(lang);
141
- if (this.config?.fallbackLang && this.config.fallbackLang !== lang) {
142
- // Best effort: a missing fallback file must not block the switch.
143
- this.load(this.config.fallbackLang).catch(() => undefined);
151
+ use(lang) {
152
+ // A language already in memory switches synchronously — no microtask
153
+ // between the click and the re-render, and code that reads the service
154
+ // right after `use()` (tests, `computed()` setups) sees the new state.
155
+ const fallback = this.config?.fallbackLang;
156
+ const needsFallback = !!fallback && fallback !== lang && !this.dictionaries.has(fallback);
157
+ if (this.dictionaries.has(lang) && !needsFallback) {
158
+ this.activate(lang);
159
+ return Promise.resolve();
144
160
  }
161
+ // The fallback rides along so that, once `use()` resolves, per-key
162
+ // fallback works too. Its failure never blocks the switch.
163
+ const fallbackLoad = needsFallback ? this.load(fallback).catch(() => undefined) : Promise.resolve();
164
+ return Promise.all([this.load(lang), fallbackLoad]).then(() => this.activate(lang));
165
+ }
166
+ activate(lang) {
145
167
  this.lang.set(lang);
146
168
  if (this.config?.documentLang !== false) {
147
169
  this.document?.documentElement?.setAttribute('lang', lang);
148
170
  }
149
171
  }
150
172
  /** Load a language into memory without switching to it. */
151
- async load(lang) {
173
+ load(lang) {
152
174
  if (this.dictionaries.has(lang))
153
- return;
175
+ return Promise.resolve();
154
176
  let task = this.pending.get(lang);
155
177
  if (!task) {
156
178
  task = this.fetch(lang).finally(() => this.pending.delete(lang));
@@ -168,9 +190,14 @@ class MkTranslate {
168
190
  instant(key, params) {
169
191
  const lang = this.lang();
170
192
  this.version();
193
+ if (this.isServer)
194
+ this.noteUsed(lang, key);
171
195
  const found = this.lookup(key, lang);
172
196
  if (found === undefined) {
173
- this.recordMissing(key);
197
+ // A partial (hydration) dictionary is expected to lack keys until the
198
+ // background load merges — those are not misses.
199
+ if (!this.partial.has(lang))
200
+ this.recordMissing(key);
174
201
  const replacement = this.config?.onMissing?.(key, lang);
175
202
  return typeof replacement === 'string' ? replacement : key;
176
203
  }
@@ -240,11 +267,26 @@ class MkTranslate {
240
267
  }
241
268
  async fetch(lang) {
242
269
  const stateKey = makeStateKey(`mk-translate:${lang}`);
243
- // Hydration: the server already loaded and serialised this language.
270
+ // Hydration: the server already loaded (some of) this language.
244
271
  if (!this.isServer && this.transfer?.hasKey(stateKey)) {
245
- const flat = this.transfer.get(stateKey, {});
272
+ const payload = this.transfer.get(stateKey, { strings: {}, partial: false });
246
273
  this.transfer.remove(stateKey);
247
- this.dictionaries.set(lang, flat);
274
+ this.dictionaries.set(lang, payload.strings);
275
+ if (payload.partial) {
276
+ // Enough to hydrate without a flash; the rest arrives in the
277
+ // background and merges under the same keys.
278
+ this.partial.add(lang);
279
+ void this.fetchFull(lang)
280
+ .then((flat) => {
281
+ this.dictionaries.set(lang, { ...flat, ...this.dictionaries.get(lang) });
282
+ this.partial.delete(lang);
283
+ this.bump();
284
+ })
285
+ .catch((err) => {
286
+ if (isDevMode())
287
+ console.warn(`[mk-translate] background load of "${lang}" failed`, err);
288
+ });
289
+ }
248
290
  this.bump();
249
291
  return;
250
292
  }
@@ -253,6 +295,14 @@ class MkTranslate {
253
295
  this.bump();
254
296
  return;
255
297
  }
298
+ const flat = await this.fetchFull(lang);
299
+ this.dictionaries.set(lang, flat);
300
+ if (this.isServer && this.transfer)
301
+ this.serialize(lang, stateKey, flat);
302
+ this.bump();
303
+ }
304
+ /** Base + overrides for `lang`, flattened. */
305
+ async fetchFull(lang) {
256
306
  const base = mkFlattenTranslations(await this.baseLoader().load(lang));
257
307
  let overrides = {};
258
308
  const overridesLoader = this.overridesLoaderOrNull();
@@ -265,11 +315,46 @@ class MkTranslate {
265
315
  console.warn(`[mk-translate] overrides for "${lang}" failed to load`, err);
266
316
  }
267
317
  }
268
- const flat = { ...base, ...overrides };
269
- this.dictionaries.set(lang, flat);
270
- if (this.isServer && this.transfer)
271
- this.transfer.set(stateKey, flat);
272
- this.bump();
318
+ return { ...base, ...overrides };
319
+ }
320
+ /** Server: decide what rides to the browser (see `MkTranslateConfig.transfer`). */
321
+ serialize(lang, stateKey, flat) {
322
+ const mode = this.config?.transfer ?? 'used';
323
+ if (mode === 'none' || !this.transfer)
324
+ return;
325
+ if (mode === 'all') {
326
+ this.transfer.set(stateKey, { strings: flat, partial: false });
327
+ return;
328
+ }
329
+ // 'used': resolved lazily when the page serialises, after every
330
+ // template ran — only the keys this render actually read.
331
+ this.transfer.onSerialize(stateKey, () => {
332
+ const keys = this.used.get(lang);
333
+ const strings = {};
334
+ if (keys)
335
+ for (const k of keys)
336
+ if (flat[k] !== undefined)
337
+ strings[k] = flat[k];
338
+ return { strings, partial: true };
339
+ });
340
+ }
341
+ noteUsed(lang, key) {
342
+ let set = this.used.get(lang);
343
+ if (!set)
344
+ this.used.set(lang, (set = new Set()));
345
+ set.add(key);
346
+ const fallback = this.config?.fallbackLang;
347
+ if (fallback && fallback !== lang) {
348
+ let fb = this.used.get(fallback);
349
+ if (!fb)
350
+ this.used.set(fallback, (fb = new Set()));
351
+ fb.add(key);
352
+ }
353
+ }
354
+ /** `true` while `lang` holds only a server render's keys and the rest is still loading. */
355
+ isPartial(lang = this.lang()) {
356
+ this.version();
357
+ return this.partial.has(lang);
273
358
  }
274
359
  baseLoader() {
275
360
  if (!this.loader) {
@@ -290,11 +375,13 @@ class MkTranslate {
290
375
  this.version.update((v) => v + 1);
291
376
  }
292
377
  recordMissing(key) {
293
- if (this.missing().has(key))
378
+ if (this.missing.has(key))
294
379
  return;
295
- const next = new Set(this.missing());
296
- next.add(key);
297
- this.missing.set(next);
380
+ this.missing.add(key);
381
+ this.missingFlush ??= Promise.resolve().then(() => {
382
+ this.missingFlush = null;
383
+ this.missingTick.update((v) => v + 1);
384
+ });
298
385
  }
299
386
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkTranslate, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
300
387
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: MkTranslate, providedIn: 'root' });
@@ -1 +1 @@
1
- {"version":3,"file":"mk-kit-ui-translate.mjs","sources":["../../../projects/mk-kit/translate/translate.service.ts","../../../projects/mk-kit/translate/translate.pipe.ts","../../../projects/mk-kit/translate/translate.loaders.ts","../../../projects/mk-kit/translate/translate.providers.ts","../../../projects/mk-kit/translate/mk-kit-ui-translate.ts"],"sourcesContent":["import {\n computed,\n inject,\n Injectable,\n InjectionToken,\n Injector,\n isDevMode,\n makeStateKey,\n PLATFORM_ID,\n runInInjectionContext,\n signal,\n TransferState,\n} from '@angular/core';\nimport { DOCUMENT, isPlatformServer } from '@angular/common';\nimport { toObservable } from '@angular/core/rxjs-interop';\nimport type {\n MkFlatTranslations,\n MkTranslateConfig,\n MkTranslateLoader,\n MkTranslateParams,\n MkTranslationTree,\n} from './translate.types';\n\n/** Configuration token; set by {@link provideMkTranslate}. */\nexport const MK_TRANSLATE_CONFIG = new InjectionToken<MkTranslateConfig>('MK_TRANSLATE_CONFIG');\n\nconst PLACEHOLDER = /\\{\\{\\s*([\\w.-]+)\\s*\\}\\}/g;\n\n/** `{ a: { b: 'x' } }` → `{ 'a.b': 'x' }`; already-dotted keys pass through. */\nexport function mkFlattenTranslations(\n tree: MkTranslationTree | null | undefined,\n prefix = '',\n out: MkFlatTranslations = {},\n): MkFlatTranslations {\n if (!tree) return out;\n for (const [k, v] of Object.entries(tree)) {\n const key = prefix ? `${prefix}.${k}` : k;\n if (v && typeof v === 'object') mkFlattenTranslations(v, key, out);\n else if (typeof v === 'string') out[key] = v;\n }\n return out;\n}\n\n/** `{ 'a.b': 'x' }` → `{ a: { b: 'x' } }` — for editors that write files back. */\nexport function mkUnflattenTranslations(flat: MkFlatTranslations): MkTranslationTree {\n const tree: MkTranslationTree = {};\n for (const key of Object.keys(flat).sort()) {\n const parts = key.split('.');\n let node = tree;\n for (let i = 0; i < parts.length - 1; i++) {\n const next = node[parts[i]];\n if (!next || typeof next !== 'object') node[parts[i]] = {};\n node = node[parts[i]] as MkTranslationTree;\n }\n node[parts[parts.length - 1]] = flat[key];\n }\n return tree;\n}\n\n/** Replace `{{name}}` placeholders; unknown names are left in place. */\nexport function mkInterpolate(template: string, params?: MkTranslateParams): string {\n if (!params) return template;\n return template.replace(PLACEHOLDER, (match, name: string) => {\n const value = name.split('.').reduce<unknown>(\n (acc, part) => (acc && typeof acc === 'object' ? (acc as Record<string, unknown>)[part] : undefined),\n params,\n );\n return value === undefined || value === null ? match : String(value);\n });\n}\n\n/**\n * App translations as signals: a `lang` you switch with `use()`, `instant()`\n * for code, the `translate` pipe for templates, and `plural()` for CLDR\n * count forms. Dictionaries are the plain nested JSON you already have; an\n * optional overrides loader (a database of edits, say) is merged on top per\n * language. On the server the loaded strings ride to the browser through\n * `TransferState`, so hydration never refetches or flashes raw keys.\n *\n * ```ts\n * provideMkTranslate({\n * lang: 'pl',\n * fallbackLang: 'pl',\n * loader: mkHttpTranslateLoader({ prefix: '/assets/i18n/' }),\n * });\n * ```\n * ```html\n * {{ 'checkout.cart.total' | translate }}\n * {{ 'checkout.cart.freeDeliveryMissing' | translate: { amount: 12 } }}\n * ```\n */\n@Injectable({ providedIn: 'root' })\nexport class MkTranslate {\n private readonly config = inject(MK_TRANSLATE_CONFIG, { optional: true });\n private readonly injector = inject(Injector);\n private readonly transfer = inject(TransferState, { optional: true });\n private readonly isServer = isPlatformServer(inject(PLATFORM_ID));\n private readonly document = inject(DOCUMENT, { optional: true });\n\n private loader: MkTranslateLoader | null = null;\n private overridesLoader: MkTranslateLoader | null = null;\n private readonly dictionaries = new Map<string, MkFlatTranslations>();\n private readonly declared = new Set<string>();\n private readonly pending = new Map<string, Promise<void>>();\n /** Bumped whenever a dictionary changes, so readers recompute. */\n private readonly version = signal(0);\n private readonly missing = signal<ReadonlySet<string>>(new Set());\n\n /** The active language. Read it in a `computed()` to follow switches. */\n readonly lang = signal<string>(this.config?.lang ?? 'en');\n /** `true` once the active language's strings are in memory. */\n readonly ready = computed(() => {\n this.version();\n return this.dictionaries.has(this.lang());\n });\n /** `lang` as an observable, for code still written around streams. */\n readonly langChange = toObservable(this.lang);\n /** Keys asked for that neither the active nor the fallback language has. */\n readonly missingKeys = computed(() => [...this.missing()].sort());\n\n constructor() {\n if (!this.config && isDevMode()) {\n console.warn('[mk-translate] provideMkTranslate() is missing — every key renders as itself.');\n }\n }\n\n /** The active language as a plain string (for non-reactive call sites). */\n getCurrentLang(): string {\n return this.lang();\n }\n\n /** ngx-translate-compatible alias of {@link getCurrentLang}. */\n get currentLang(): string {\n return this.lang();\n }\n\n /** Languages known to the service: loaded ones plus any added with {@link addLangs}. */\n getLangs(): string[] {\n this.version();\n return [...new Set([...this.declared, ...this.dictionaries.keys()])];\n }\n\n /** Declare languages up front (a switcher's list); loading still happens on `use()`. */\n addLangs(langs: string[]): void {\n for (const lang of langs) this.declared.add(lang);\n this.bump();\n }\n\n /**\n * ngx-translate-compatible alias: `setTranslation(lang, strings, true)`\n * merges like {@link patch}, `false` (the default there) replaces like\n * {@link set}.\n */\n setTranslation(lang: string, strings: MkTranslationTree | MkFlatTranslations, shouldMerge = false): void {\n if (shouldMerge) this.patch(lang, strings);\n else this.set(lang, strings);\n }\n\n /**\n * Load `lang` (once — later calls are cached) and make it active. Resolves\n * when the strings are in memory; a failed load rejects and leaves the\n * previous language active.\n */\n async use(lang: string): Promise<void> {\n await this.load(lang);\n if (this.config?.fallbackLang && this.config.fallbackLang !== lang) {\n // Best effort: a missing fallback file must not block the switch.\n this.load(this.config.fallbackLang).catch(() => undefined);\n }\n this.lang.set(lang);\n if (this.config?.documentLang !== false) {\n this.document?.documentElement?.setAttribute('lang', lang);\n }\n }\n\n /** Load a language into memory without switching to it. */\n async load(lang: string): Promise<void> {\n if (this.dictionaries.has(lang)) return;\n let task = this.pending.get(lang);\n if (!task) {\n task = this.fetch(lang).finally(() => this.pending.delete(lang));\n this.pending.set(lang, task);\n }\n return task;\n }\n\n /**\n * Translate `key` in the active language, then the fallback; `{{name}}`\n * placeholders come from `params`. A missing key renders as the key\n * itself (or whatever `onMissing` returns) and is recorded in\n * {@link missingKeys}. Reactive: reading it inside a template or a\n * `computed()` re-runs on language switch and after `patch()`.\n */\n instant(key: string, params?: MkTranslateParams): string {\n const lang = this.lang();\n this.version();\n const found = this.lookup(key, lang);\n if (found === undefined) {\n this.recordMissing(key);\n const replacement = this.config?.onMissing?.(key, lang);\n return typeof replacement === 'string' ? replacement : key;\n }\n return mkInterpolate(found, params);\n }\n\n /** Whether `key` exists in `lang` (default: the active language) or its fallback. */\n has(key: string, lang = this.lang()): boolean {\n this.version();\n return this.lookup(key, lang) !== undefined;\n }\n\n /**\n * CLDR plural form: `keyBase.{zero|one|two|few|many|other}` picked with\n * `Intl.PluralRules` for the active language, `other` as the fallback,\n * interpolated with `{ count, ...params }`.\n *\n * ```json\n * { \"guests\": { \"one\": \"{{count}} osoba\", \"few\": \"{{count}} osoby\", \"many\": \"{{count}} osób\", \"other\": \"{{count}} osoby\" } }\n * ```\n */\n plural(keyBase: string, count: number, params?: MkTranslateParams): string {\n const n = Number(count) || 0;\n const lang = this.lang();\n let category: string = 'other';\n try {\n category = new Intl.PluralRules(lang).select(n);\n } catch {\n /* unknown locale → other */\n }\n const merged = { count: n, ...params };\n const key = `${keyBase}.${category}`;\n return this.has(key) ? this.instant(key, merged) : this.instant(`${keyBase}.other`, merged);\n }\n\n /** The flat dictionary of `lang` (default: active), `{}` before it loads. */\n translations(lang = this.lang()): MkFlatTranslations {\n this.version();\n return { ...(this.dictionaries.get(lang) ?? {}) };\n }\n\n /** Languages currently in memory. */\n loadedLangs(): string[] {\n this.version();\n return [...this.dictionaries.keys()];\n }\n\n /**\n * Merge strings into `lang` at runtime — a translation editor previewing\n * an edit, or a late-arriving overrides payload. Nested or flat.\n */\n patch(lang: string, strings: MkTranslationTree | MkFlatTranslations): void {\n const current = this.dictionaries.get(lang) ?? {};\n this.dictionaries.set(lang, { ...current, ...mkFlattenTranslations(strings as MkTranslationTree) });\n this.bump();\n }\n\n /** Replace `lang` entirely (tests, editors reloading from source). */\n set(lang: string, strings: MkTranslationTree | MkFlatTranslations): void {\n this.dictionaries.set(lang, mkFlattenTranslations(strings as MkTranslationTree));\n this.bump();\n }\n\n // ── internals ────────────────────────────────────────────────────────────\n\n private lookup(key: string, lang: string): string | undefined {\n const direct = this.dictionaries.get(lang)?.[key];\n if (direct !== undefined) return direct;\n const fallback = this.config?.fallbackLang;\n if (fallback && fallback !== lang) return this.dictionaries.get(fallback)?.[key];\n return undefined;\n }\n\n private async fetch(lang: string): Promise<void> {\n const stateKey = makeStateKey<MkFlatTranslations>(`mk-translate:${lang}`);\n // Hydration: the server already loaded and serialised this language.\n if (!this.isServer && this.transfer?.hasKey(stateKey)) {\n const flat = this.transfer.get(stateKey, {});\n this.transfer.remove(stateKey);\n this.dictionaries.set(lang, flat);\n this.bump();\n return;\n }\n if (!this.config) {\n this.dictionaries.set(lang, {});\n this.bump();\n return;\n }\n const base = mkFlattenTranslations(await this.baseLoader().load(lang));\n let overrides: MkFlatTranslations = {};\n const overridesLoader = this.overridesLoaderOrNull();\n if (overridesLoader) {\n try {\n overrides = mkFlattenTranslations(await overridesLoader.load(lang));\n } catch (err) {\n if (isDevMode()) console.warn(`[mk-translate] overrides for \"${lang}\" failed to load`, err);\n }\n }\n const flat = { ...base, ...overrides };\n this.dictionaries.set(lang, flat);\n if (this.isServer && this.transfer) this.transfer.set(stateKey, flat);\n this.bump();\n }\n\n private baseLoader(): MkTranslateLoader {\n if (!this.loader) {\n this.loader = runInInjectionContext(this.injector, () => this.config!.loader());\n }\n return this.loader;\n }\n\n private overridesLoaderOrNull(): MkTranslateLoader | null {\n const factory = this.config?.overrides;\n if (!factory) return null;\n if (!this.overridesLoader) {\n this.overridesLoader = runInInjectionContext(this.injector, factory);\n }\n return this.overridesLoader;\n }\n\n private bump(): void {\n this.version.update((v) => v + 1);\n }\n\n private recordMissing(key: string): void {\n if (this.missing().has(key)) return;\n const next = new Set(this.missing());\n next.add(key);\n this.missing.set(next);\n }\n}\n","import { inject, Pipe, type PipeTransform } from '@angular/core';\nimport { MkTranslate } from './translate.service';\nimport type { MkTranslateParams } from './translate.types';\n\n/**\n * `translate` — the key's string in the active language, with `{{name}}`\n * placeholders filled from `params`. Impure so a language switch (or a\n * `patch()`) re-renders every use; the signal reads inside `instant()` mark\n * the host view dirty, so this stays cheap under OnPush and zoneless.\n *\n * ```html\n * {{ 'menu.title' | translate }}\n * {{ 'cart.items' | translate: { count: 3 } }}\n * ```\n */\n@Pipe({ name: 'translate', pure: false })\nexport class MkTranslatePipe implements PipeTransform {\n private readonly translate = inject(MkTranslate);\n\n transform(key: string | null | undefined, params?: MkTranslateParams): string {\n if (!key) return '';\n return this.translate.instant(key, params);\n }\n}\n\n/**\n * `translatePlural` — the CLDR plural form under `keyBase` for a count\n * (see {@link MkTranslate.plural}), interpolated with `{ count, ...params }`.\n *\n * ```html\n * {{ guests | translatePlural: 'reservation.guests' }} <!-- 2 osoby / 5 osób -->\n * ```\n */\n@Pipe({ name: 'translatePlural', pure: false })\nexport class MkTranslatePluralPipe implements PipeTransform {\n private readonly translate = inject(MkTranslate);\n\n transform(count: number | string | null | undefined, keyBase: string, params?: MkTranslateParams): string {\n return this.translate.plural(keyBase, Number(count) || 0, params);\n }\n}\n\n/**\n * Everything a template needs, for `imports: [...MkTranslateImports]` — the\n * one-line replacement for an ngx-translate `TranslateModule` import.\n */\nexport const MkTranslateImports = [MkTranslatePipe, MkTranslatePluralPipe] as const;\n","import { inject } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { firstValueFrom } from 'rxjs';\nimport type { MkTranslateLoaderFactory, MkTranslationTree } from './translate.types';\n\n/** Options for {@link mkHttpTranslateLoader}. */\nexport interface MkHttpTranslateLoaderOptions {\n /** URL prefix; the language code is appended. Default `/assets/i18n/`. */\n prefix?: string;\n /** URL suffix after the language code. Default `.json`. */\n suffix?: string;\n}\n\n/**\n * Fetch `<prefix><lang><suffix>` with `HttpClient` — the bundled JSON files\n * in `assets/i18n/`. Needs `provideHttpClient()`; SSR apps that read the\n * files from disk can supply their own {@link MkTranslateLoader} instead.\n */\nexport function mkHttpTranslateLoader(\n options: MkHttpTranslateLoaderOptions = {},\n): MkTranslateLoaderFactory {\n const { prefix = '/assets/i18n/', suffix = '.json' } = options;\n return () => {\n const http = inject(HttpClient);\n return {\n load: (lang: string) => firstValueFrom(http.get<MkTranslationTree>(`${prefix}${lang}${suffix}`)),\n };\n };\n}\n\n/**\n * Strings given up front, keyed by language — tests, storybooks, tiny apps.\n * A language not in the map resolves to `{}`.\n */\nexport function mkStaticTranslateLoader(\n byLang: Record<string, MkTranslationTree>,\n): MkTranslateLoaderFactory {\n return () => ({ load: (lang: string) => byLang[lang] ?? {} });\n}\n","import {\n type EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideAppInitializer,\n} from '@angular/core';\nimport { MK_TRANSLATE_CONFIG, MkTranslate } from './translate.service';\nimport type { MkTranslateConfig } from './translate.types';\n\n/**\n * Register app translations. By default the initial language is loaded\n * before the first render (`preload`), so no view ever shows raw keys and\n * nothing caches them.\n *\n * ```ts\n * providers: [\n * provideHttpClient(withFetch()),\n * provideMkTranslate({\n * lang: 'pl',\n * fallbackLang: 'pl',\n * loader: mkHttpTranslateLoader(),\n * overrides: () => inject(TranslationOverridesApi), // optional\n * }),\n * ]\n * ```\n */\nexport function provideMkTranslate(config: MkTranslateConfig): EnvironmentProviders {\n const providers: Parameters<typeof makeEnvironmentProviders>[0] = [\n { provide: MK_TRANSLATE_CONFIG, useValue: config },\n ];\n if (config.preload !== false) {\n providers.push(\n provideAppInitializer(() =>\n inject(MkTranslate)\n .use(config.lang)\n .catch((err: unknown) => {\n // A broken file must not blank the app — keys render as themselves.\n console.error(`[mk-translate] \"${config.lang}\" failed to load before first render`, err);\n }),\n ),\n );\n }\n return makeEnvironmentProviders(providers);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"mk-kit-ui-translate.mjs","sources":["../../../projects/mk-kit/translate/translate.service.ts","../../../projects/mk-kit/translate/translate.pipe.ts","../../../projects/mk-kit/translate/translate.loaders.ts","../../../projects/mk-kit/translate/translate.providers.ts","../../../projects/mk-kit/translate/mk-kit-ui-translate.ts"],"sourcesContent":["import {\n computed,\n inject,\n Injectable,\n InjectionToken,\n Injector,\n isDevMode,\n makeStateKey,\n PLATFORM_ID,\n runInInjectionContext,\n signal,\n TransferState,\n} from '@angular/core';\nimport { DOCUMENT, isPlatformServer } from '@angular/common';\nimport { toObservable } from '@angular/core/rxjs-interop';\nimport type {\n MkFlatTranslations,\n MkTranslateConfig,\n MkTranslateLoader,\n MkTranslateParams,\n MkTranslationTree,\n} from './translate.types';\n\n/** Configuration token; set by {@link provideMkTranslate}. */\nexport const MK_TRANSLATE_CONFIG = new InjectionToken<MkTranslateConfig>('MK_TRANSLATE_CONFIG');\n\nconst PLACEHOLDER = /\\{\\{\\s*([\\w.-]+)\\s*\\}\\}/g;\n\n/** What a server render hands to the browser for one language. */\ninterface MkTransferPayload {\n strings: MkFlatTranslations;\n /** Only the keys the render used — the rest is loading in the background. */\n partial: boolean;\n}\n\n/** `{ a: { b: 'x' } }` → `{ 'a.b': 'x' }`; already-dotted keys pass through. */\nexport function mkFlattenTranslations(\n tree: MkTranslationTree | null | undefined,\n prefix = '',\n out: MkFlatTranslations = {},\n): MkFlatTranslations {\n if (!tree) return out;\n for (const [k, v] of Object.entries(tree)) {\n const key = prefix ? `${prefix}.${k}` : k;\n if (v && typeof v === 'object') mkFlattenTranslations(v, key, out);\n else if (typeof v === 'string') out[key] = v;\n }\n return out;\n}\n\n/** `{ 'a.b': 'x' }` → `{ a: { b: 'x' } }` — for editors that write files back. */\nexport function mkUnflattenTranslations(flat: MkFlatTranslations): MkTranslationTree {\n const tree: MkTranslationTree = {};\n for (const key of Object.keys(flat).sort()) {\n const parts = key.split('.');\n let node = tree;\n for (let i = 0; i < parts.length - 1; i++) {\n const next = node[parts[i]];\n if (!next || typeof next !== 'object') node[parts[i]] = {};\n node = node[parts[i]] as MkTranslationTree;\n }\n node[parts[parts.length - 1]] = flat[key];\n }\n return tree;\n}\n\n/** Replace `{{name}}` placeholders; unknown names are left in place. */\nexport function mkInterpolate(template: string, params?: MkTranslateParams): string {\n if (!params) return template;\n return template.replace(PLACEHOLDER, (match, name: string) => {\n const value = name.split('.').reduce<unknown>(\n (acc, part) => (acc && typeof acc === 'object' ? (acc as Record<string, unknown>)[part] : undefined),\n params,\n );\n return value === undefined || value === null ? match : String(value);\n });\n}\n\n/**\n * App translations as signals: a `lang` you switch with `use()`, `instant()`\n * for code, the `translate` pipe for templates, and `plural()` for CLDR\n * count forms. Dictionaries are the plain nested JSON you already have; an\n * optional overrides loader (a database of edits, say) is merged on top per\n * language. On the server the loaded strings ride to the browser through\n * `TransferState`, so hydration never refetches or flashes raw keys.\n *\n * ```ts\n * provideMkTranslate({\n * lang: 'pl',\n * fallbackLang: 'pl',\n * loader: mkHttpTranslateLoader({ prefix: '/assets/i18n/' }),\n * });\n * ```\n * ```html\n * {{ 'checkout.cart.total' | translate }}\n * {{ 'checkout.cart.freeDeliveryMissing' | translate: { amount: 12 } }}\n * ```\n */\n@Injectable({ providedIn: 'root' })\nexport class MkTranslate {\n private readonly config = inject(MK_TRANSLATE_CONFIG, { optional: true });\n private readonly injector = inject(Injector);\n private readonly transfer = inject(TransferState, { optional: true });\n private readonly isServer = isPlatformServer(inject(PLATFORM_ID));\n private readonly document = inject(DOCUMENT, { optional: true });\n\n private loader: MkTranslateLoader | null = null;\n private overridesLoader: MkTranslateLoader | null = null;\n private readonly dictionaries = new Map<string, MkFlatTranslations>();\n private readonly declared = new Set<string>();\n /** Languages holding only the keys a server render used (see `transfer`). */\n private readonly partial = new Set<string>();\n /** Keys read on the server, per language — what `transfer: 'used'` ships. */\n private readonly used = new Map<string, Set<string>>();\n private readonly pending = new Map<string, Promise<void>>();\n /** Bumped whenever a dictionary changes, so readers recompute. */\n private readonly version = signal(0);\n // Missing keys are noticed while templates render, where writing a signal\n // is forbidden (NG0600) — so the set is plain and a tick signal is bumped\n // in a microtask, once per batch of misses.\n private readonly missing = new Set<string>();\n private readonly missingTick = signal(0);\n private missingFlush: Promise<void> | null = null;\n\n /** The active language. Read it in a `computed()` to follow switches. */\n readonly lang = signal<string>(this.config?.lang ?? 'en');\n /** `true` once the active language's strings are in memory. */\n readonly ready = computed(() => {\n this.version();\n return this.dictionaries.has(this.lang());\n });\n /** `lang` as an observable, for code still written around streams. */\n readonly langChange = toObservable(this.lang);\n /** Keys asked for that neither the active nor the fallback language has. */\n readonly missingKeys = computed(() => {\n this.missingTick();\n return [...this.missing].sort();\n });\n\n constructor() {\n if (!this.config && isDevMode()) {\n console.warn('[mk-translate] provideMkTranslate() is missing — every key renders as itself.');\n }\n }\n\n /** The active language as a plain string (for non-reactive call sites). */\n getCurrentLang(): string {\n return this.lang();\n }\n\n /** ngx-translate-compatible alias of {@link getCurrentLang}. */\n get currentLang(): string {\n return this.lang();\n }\n\n /** Languages known to the service: loaded ones plus any added with {@link addLangs}. */\n getLangs(): string[] {\n this.version();\n return [...new Set([...this.declared, ...this.dictionaries.keys()])];\n }\n\n /** Declare languages up front (a switcher's list); loading still happens on `use()`. */\n addLangs(langs: string[]): void {\n for (const lang of langs) this.declared.add(lang);\n this.bump();\n }\n\n /**\n * ngx-translate-compatible alias: `setTranslation(lang, strings, true)`\n * merges like {@link patch}, `false` (the default there) replaces like\n * {@link set}.\n */\n setTranslation(lang: string, strings: MkTranslationTree | MkFlatTranslations, shouldMerge = false): void {\n if (shouldMerge) this.patch(lang, strings);\n else this.set(lang, strings);\n }\n\n /**\n * Load `lang` (once — later calls are cached) and make it active. Resolves\n * when the strings are in memory; a failed load rejects and leaves the\n * previous language active.\n */\n use(lang: string): Promise<void> {\n // A language already in memory switches synchronously — no microtask\n // between the click and the re-render, and code that reads the service\n // right after `use()` (tests, `computed()` setups) sees the new state.\n const fallback = this.config?.fallbackLang;\n const needsFallback = !!fallback && fallback !== lang && !this.dictionaries.has(fallback);\n if (this.dictionaries.has(lang) && !needsFallback) {\n this.activate(lang);\n return Promise.resolve();\n }\n // The fallback rides along so that, once `use()` resolves, per-key\n // fallback works too. Its failure never blocks the switch.\n const fallbackLoad = needsFallback ? this.load(fallback!).catch(() => undefined) : Promise.resolve();\n return Promise.all([this.load(lang), fallbackLoad]).then(() => this.activate(lang));\n }\n\n private activate(lang: string): void {\n this.lang.set(lang);\n if (this.config?.documentLang !== false) {\n this.document?.documentElement?.setAttribute('lang', lang);\n }\n }\n\n /** Load a language into memory without switching to it. */\n load(lang: string): Promise<void> {\n if (this.dictionaries.has(lang)) return Promise.resolve();\n let task = this.pending.get(lang);\n if (!task) {\n task = this.fetch(lang).finally(() => this.pending.delete(lang));\n this.pending.set(lang, task);\n }\n return task;\n }\n\n /**\n * Translate `key` in the active language, then the fallback; `{{name}}`\n * placeholders come from `params`. A missing key renders as the key\n * itself (or whatever `onMissing` returns) and is recorded in\n * {@link missingKeys}. Reactive: reading it inside a template or a\n * `computed()` re-runs on language switch and after `patch()`.\n */\n instant(key: string, params?: MkTranslateParams): string {\n const lang = this.lang();\n this.version();\n if (this.isServer) this.noteUsed(lang, key);\n const found = this.lookup(key, lang);\n if (found === undefined) {\n // A partial (hydration) dictionary is expected to lack keys until the\n // background load merges — those are not misses.\n if (!this.partial.has(lang)) this.recordMissing(key);\n const replacement = this.config?.onMissing?.(key, lang);\n return typeof replacement === 'string' ? replacement : key;\n }\n return mkInterpolate(found, params);\n }\n\n /** Whether `key` exists in `lang` (default: the active language) or its fallback. */\n has(key: string, lang = this.lang()): boolean {\n this.version();\n return this.lookup(key, lang) !== undefined;\n }\n\n /**\n * CLDR plural form: `keyBase.{zero|one|two|few|many|other}` picked with\n * `Intl.PluralRules` for the active language, `other` as the fallback,\n * interpolated with `{ count, ...params }`.\n *\n * ```json\n * { \"guests\": { \"one\": \"{{count}} osoba\", \"few\": \"{{count}} osoby\", \"many\": \"{{count}} osób\", \"other\": \"{{count}} osoby\" } }\n * ```\n */\n plural(keyBase: string, count: number, params?: MkTranslateParams): string {\n const n = Number(count) || 0;\n const lang = this.lang();\n let category: string = 'other';\n try {\n category = new Intl.PluralRules(lang).select(n);\n } catch {\n /* unknown locale → other */\n }\n const merged = { count: n, ...params };\n const key = `${keyBase}.${category}`;\n return this.has(key) ? this.instant(key, merged) : this.instant(`${keyBase}.other`, merged);\n }\n\n /** The flat dictionary of `lang` (default: active), `{}` before it loads. */\n translations(lang = this.lang()): MkFlatTranslations {\n this.version();\n return { ...(this.dictionaries.get(lang) ?? {}) };\n }\n\n /** Languages currently in memory. */\n loadedLangs(): string[] {\n this.version();\n return [...this.dictionaries.keys()];\n }\n\n /**\n * Merge strings into `lang` at runtime — a translation editor previewing\n * an edit, or a late-arriving overrides payload. Nested or flat.\n */\n patch(lang: string, strings: MkTranslationTree | MkFlatTranslations): void {\n const current = this.dictionaries.get(lang) ?? {};\n this.dictionaries.set(lang, { ...current, ...mkFlattenTranslations(strings as MkTranslationTree) });\n this.bump();\n }\n\n /** Replace `lang` entirely (tests, editors reloading from source). */\n set(lang: string, strings: MkTranslationTree | MkFlatTranslations): void {\n this.dictionaries.set(lang, mkFlattenTranslations(strings as MkTranslationTree));\n this.bump();\n }\n\n // ── internals ────────────────────────────────────────────────────────────\n\n private lookup(key: string, lang: string): string | undefined {\n const direct = this.dictionaries.get(lang)?.[key];\n if (direct !== undefined) return direct;\n const fallback = this.config?.fallbackLang;\n if (fallback && fallback !== lang) return this.dictionaries.get(fallback)?.[key];\n return undefined;\n }\n\n private async fetch(lang: string): Promise<void> {\n const stateKey = makeStateKey<MkTransferPayload>(`mk-translate:${lang}`);\n // Hydration: the server already loaded (some of) this language.\n if (!this.isServer && this.transfer?.hasKey(stateKey)) {\n const payload = this.transfer.get(stateKey, { strings: {}, partial: false });\n this.transfer.remove(stateKey);\n this.dictionaries.set(lang, payload.strings);\n if (payload.partial) {\n // Enough to hydrate without a flash; the rest arrives in the\n // background and merges under the same keys.\n this.partial.add(lang);\n void this.fetchFull(lang)\n .then((flat) => {\n this.dictionaries.set(lang, { ...flat, ...this.dictionaries.get(lang) });\n this.partial.delete(lang);\n this.bump();\n })\n .catch((err) => {\n if (isDevMode()) console.warn(`[mk-translate] background load of \"${lang}\" failed`, err);\n });\n }\n this.bump();\n return;\n }\n if (!this.config) {\n this.dictionaries.set(lang, {});\n this.bump();\n return;\n }\n const flat = await this.fetchFull(lang);\n this.dictionaries.set(lang, flat);\n if (this.isServer && this.transfer) this.serialize(lang, stateKey, flat);\n this.bump();\n }\n\n /** Base + overrides for `lang`, flattened. */\n private async fetchFull(lang: string): Promise<MkFlatTranslations> {\n const base = mkFlattenTranslations(await this.baseLoader().load(lang));\n let overrides: MkFlatTranslations = {};\n const overridesLoader = this.overridesLoaderOrNull();\n if (overridesLoader) {\n try {\n overrides = mkFlattenTranslations(await overridesLoader.load(lang));\n } catch (err) {\n if (isDevMode()) console.warn(`[mk-translate] overrides for \"${lang}\" failed to load`, err);\n }\n }\n return { ...base, ...overrides };\n }\n\n /** Server: decide what rides to the browser (see `MkTranslateConfig.transfer`). */\n private serialize(lang: string, stateKey: ReturnType<typeof makeStateKey<MkTransferPayload>>, flat: MkFlatTranslations): void {\n const mode = this.config?.transfer ?? 'used';\n if (mode === 'none' || !this.transfer) return;\n if (mode === 'all') {\n this.transfer.set(stateKey, { strings: flat, partial: false });\n return;\n }\n // 'used': resolved lazily when the page serialises, after every\n // template ran — only the keys this render actually read.\n this.transfer.onSerialize(stateKey, () => {\n const keys = this.used.get(lang);\n const strings: MkFlatTranslations = {};\n if (keys) for (const k of keys) if (flat[k] !== undefined) strings[k] = flat[k];\n return { strings, partial: true };\n });\n }\n\n private noteUsed(lang: string, key: string): void {\n let set = this.used.get(lang);\n if (!set) this.used.set(lang, (set = new Set()));\n set.add(key);\n const fallback = this.config?.fallbackLang;\n if (fallback && fallback !== lang) {\n let fb = this.used.get(fallback);\n if (!fb) this.used.set(fallback, (fb = new Set()));\n fb.add(key);\n }\n }\n\n /** `true` while `lang` holds only a server render's keys and the rest is still loading. */\n isPartial(lang = this.lang()): boolean {\n this.version();\n return this.partial.has(lang);\n }\n\n private baseLoader(): MkTranslateLoader {\n if (!this.loader) {\n this.loader = runInInjectionContext(this.injector, () => this.config!.loader());\n }\n return this.loader;\n }\n\n private overridesLoaderOrNull(): MkTranslateLoader | null {\n const factory = this.config?.overrides;\n if (!factory) return null;\n if (!this.overridesLoader) {\n this.overridesLoader = runInInjectionContext(this.injector, factory);\n }\n return this.overridesLoader;\n }\n\n private bump(): void {\n this.version.update((v) => v + 1);\n }\n\n private recordMissing(key: string): void {\n if (this.missing.has(key)) return;\n this.missing.add(key);\n this.missingFlush ??= Promise.resolve().then(() => {\n this.missingFlush = null;\n this.missingTick.update((v) => v + 1);\n });\n }\n}\n","import { inject, Pipe, type PipeTransform } from '@angular/core';\nimport { MkTranslate } from './translate.service';\nimport type { MkTranslateParams } from './translate.types';\n\n/**\n * `translate` — the key's string in the active language, with `{{name}}`\n * placeholders filled from `params`. Impure so a language switch (or a\n * `patch()`) re-renders every use; the signal reads inside `instant()` mark\n * the host view dirty, so this stays cheap under OnPush and zoneless.\n *\n * ```html\n * {{ 'menu.title' | translate }}\n * {{ 'cart.items' | translate: { count: 3 } }}\n * ```\n */\n@Pipe({ name: 'translate', pure: false })\nexport class MkTranslatePipe implements PipeTransform {\n private readonly translate = inject(MkTranslate);\n\n transform(key: string | null | undefined, params?: MkTranslateParams): string {\n if (!key) return '';\n return this.translate.instant(key, params);\n }\n}\n\n/**\n * `translatePlural` — the CLDR plural form under `keyBase` for a count\n * (see {@link MkTranslate.plural}), interpolated with `{ count, ...params }`.\n *\n * ```html\n * {{ guests | translatePlural: 'reservation.guests' }} <!-- 2 osoby / 5 osób -->\n * ```\n */\n@Pipe({ name: 'translatePlural', pure: false })\nexport class MkTranslatePluralPipe implements PipeTransform {\n private readonly translate = inject(MkTranslate);\n\n transform(count: number | string | null | undefined, keyBase: string, params?: MkTranslateParams): string {\n return this.translate.plural(keyBase, Number(count) || 0, params);\n }\n}\n\n/**\n * Everything a template needs, for `imports: [...MkTranslateImports]` — the\n * one-line replacement for an ngx-translate `TranslateModule` import.\n */\nexport const MkTranslateImports = [MkTranslatePipe, MkTranslatePluralPipe] as const;\n","import { inject } from '@angular/core';\nimport { HttpClient } from '@angular/common/http';\nimport { firstValueFrom } from 'rxjs';\nimport type { MkTranslateLoaderFactory, MkTranslationTree } from './translate.types';\n\n/** Options for {@link mkHttpTranslateLoader}. */\nexport interface MkHttpTranslateLoaderOptions {\n /** URL prefix; the language code is appended. Default `/assets/i18n/`. */\n prefix?: string;\n /** URL suffix after the language code. Default `.json`. */\n suffix?: string;\n}\n\n/**\n * Fetch `<prefix><lang><suffix>` with `HttpClient` — the bundled JSON files\n * in `assets/i18n/`. Needs `provideHttpClient()`; SSR apps that read the\n * files from disk can supply their own {@link MkTranslateLoader} instead.\n */\nexport function mkHttpTranslateLoader(\n options: MkHttpTranslateLoaderOptions = {},\n): MkTranslateLoaderFactory {\n const { prefix = '/assets/i18n/', suffix = '.json' } = options;\n return () => {\n const http = inject(HttpClient);\n return {\n load: (lang: string) => firstValueFrom(http.get<MkTranslationTree>(`${prefix}${lang}${suffix}`)),\n };\n };\n}\n\n/**\n * Strings given up front, keyed by language — tests, storybooks, tiny apps.\n * A language not in the map resolves to `{}`.\n */\nexport function mkStaticTranslateLoader(\n byLang: Record<string, MkTranslationTree>,\n): MkTranslateLoaderFactory {\n return () => ({ load: (lang: string) => byLang[lang] ?? {} });\n}\n","import {\n type EnvironmentProviders,\n inject,\n makeEnvironmentProviders,\n provideAppInitializer,\n} from '@angular/core';\nimport { MK_TRANSLATE_CONFIG, MkTranslate } from './translate.service';\nimport type { MkTranslateConfig } from './translate.types';\n\n/**\n * Register app translations. By default the initial language is loaded\n * before the first render (`preload`), so no view ever shows raw keys and\n * nothing caches them.\n *\n * ```ts\n * providers: [\n * provideHttpClient(withFetch()),\n * provideMkTranslate({\n * lang: 'pl',\n * fallbackLang: 'pl',\n * loader: mkHttpTranslateLoader(),\n * overrides: () => inject(TranslationOverridesApi), // optional\n * }),\n * ]\n * ```\n */\nexport function provideMkTranslate(config: MkTranslateConfig): EnvironmentProviders {\n const providers: Parameters<typeof makeEnvironmentProviders>[0] = [\n { provide: MK_TRANSLATE_CONFIG, useValue: config },\n ];\n if (config.preload !== false) {\n providers.push(\n provideAppInitializer(() =>\n inject(MkTranslate)\n .use(config.lang)\n .catch((err: unknown) => {\n // A broken file must not blank the app — keys render as themselves.\n console.error(`[mk-translate] \"${config.lang}\" failed to load before first render`, err);\n }),\n ),\n );\n }\n return makeEnvironmentProviders(providers);\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mk-kit/ui",
3
- "version": "0.55.0",
3
+ "version": "0.56.0",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -193,6 +193,10 @@
193
193
  "types": "./types/mk-kit-ui-translate.d.ts",
194
194
  "default": "./fesm2022/mk-kit-ui-translate.mjs"
195
195
  },
196
+ "./translate/editor": {
197
+ "types": "./types/mk-kit-ui-translate-editor.d.ts",
198
+ "default": "./fesm2022/mk-kit-ui-translate-editor.mjs"
199
+ },
196
200
  "./translate/server": {
197
201
  "types": "./types/mk-kit-ui-translate-server.d.ts",
198
202
  "default": "./fesm2022/mk-kit-ui-translate-server.mjs"
@@ -771,6 +771,15 @@ interface MkI18nStrings {
771
771
  noResults: string;
772
772
  /** Empty data table / list. */
773
773
  noData: string;
774
+ /** Translation editor (`@mk-kit/ui/translate/editor`). */
775
+ translationEditorSearch: string;
776
+ translationEditorAll: string;
777
+ translationEditorOverridden: string;
778
+ translationEditorMissing: string;
779
+ translationEditorKey: string;
780
+ translationEditorReset: string;
781
+ translationEditorExport: string;
782
+ translationEditorKeys: string;
774
783
  /** Announced when a filterable list updates (autocomplete, multi-select, command palette). */
775
784
  resultsCount: (count: number) => string;
776
785
  /** Pagination: previous page control. */
@@ -0,0 +1,76 @@
1
+ import * as _angular_core from '@angular/core';
2
+ import * as _mk_kit_ui_core from '@mk-kit/ui/core';
3
+ import { MkTableColumn } from '@mk-kit/ui/table';
4
+ import { MkFlatTranslations } from '@mk-kit/ui/translate';
5
+
6
+ /** One edit made in the editor. `value: null` clears the override. */
7
+ interface MkTranslationChange {
8
+ locale: string;
9
+ key: string;
10
+ value: string | null;
11
+ /** The text shown before the edit (override, or the base string). */
12
+ previous: string | null;
13
+ }
14
+ /** A row of the editor: the key plus the effective text per locale. */
15
+ interface MkTranslationRow {
16
+ key: string;
17
+ [locale: string]: string;
18
+ }
19
+ type Filter = 'all' | 'overridden' | `missing:${string}`;
20
+ /**
21
+ * Translation editor — keys as rows, locales as columns, click a cell to
22
+ * edit. The base strings are your bundled files; edits are **overrides**
23
+ * kept apart from them, so a rebuild never loses them and a cell can be
24
+ * restored to the file text. Search by key or text, filter to the keys a
25
+ * locale is missing or the ones already edited, export the grid as CSV.
26
+ * Persistence is yours: every edit is emitted as `changed`, feed the result
27
+ * back through `overrides`.
28
+ *
29
+ * ```html
30
+ * <mk-translation-editor
31
+ * [locales]="['pl', 'en', 'ru']"
32
+ * [base]="base()"
33
+ * [overrides]="overrides()"
34
+ * (changed)="save($event)" />
35
+ * ```
36
+ */
37
+ declare class MkTranslationEditor {
38
+ protected readonly i18n: _mk_kit_ui_core.MkI18nStrings;
39
+ /** Locale codes, in column order. The first one is the reference language. */
40
+ readonly locales: _angular_core.InputSignal<string[]>;
41
+ /** Bundled strings per locale, flat (`'a.b.c': 'text'`) or nested. */
42
+ readonly base: _angular_core.InputSignal<Record<string, MkFlatTranslations>>;
43
+ /** Edits per locale, flat. Missing locales are fine. */
44
+ readonly overrides: _angular_core.InputSignal<Record<string, MkFlatTranslations>>;
45
+ /** Show but do not edit. */
46
+ readonly readonly: _angular_core.InputSignal<boolean>;
47
+ /** File name of the CSV export. */
48
+ readonly exportFilename: _angular_core.InputSignal<string>;
49
+ /** An edit or a restore; persist it and update `overrides`. */
50
+ readonly changed: _angular_core.OutputEmitterRef<MkTranslationChange>;
51
+ protected readonly query: _angular_core.WritableSignal<string>;
52
+ protected readonly filter: _angular_core.WritableSignal<Filter>;
53
+ /** Every key any locale knows, sorted. */
54
+ protected readonly keys: _angular_core.Signal<string[]>;
55
+ /** Per-locale counts shown as filter chips. */
56
+ protected readonly stats: _angular_core.Signal<{
57
+ locale: string;
58
+ missing: number;
59
+ overridden: number;
60
+ }[]>;
61
+ protected readonly overriddenTotal: _angular_core.Signal<number>;
62
+ protected readonly rows: _angular_core.Signal<MkTranslationRow[]>;
63
+ protected readonly columns: _angular_core.Signal<MkTableColumn<MkTranslationRow>[]>;
64
+ protected effective(locale: string, key: string): string | null;
65
+ protected hasOverride(locale: string, key: string): boolean;
66
+ protected isMissing(locale: string, key: string): boolean;
67
+ protected onSaved(locale: string, key: string, value: string): void;
68
+ protected reset(locale: string, key: string): void;
69
+ protected setFilter(next: string): void;
70
+ protected exportCsv(): void;
71
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<MkTranslationEditor, never>;
72
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<MkTranslationEditor, "mk-translation-editor", never, { "locales": { "alias": "locales"; "required": true; "isSignal": true; }; "base": { "alias": "base"; "required": true; "isSignal": true; }; "overrides": { "alias": "overrides"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "exportFilename": { "alias": "exportFilename"; "required": false; "isSignal": true; }; }, { "changed": "changed"; }, never, never, true, never>;
73
+ }
74
+
75
+ export { MkTranslationEditor };
76
+ export type { MkTranslationChange, MkTranslationRow };
@@ -44,6 +44,16 @@ interface MkTranslateConfig {
44
44
  * `computed()` never cache them. Default `true`.
45
45
  */
46
46
  preload?: boolean;
47
+ /**
48
+ * What a server render hands to the browser through `TransferState`:
49
+ * - `'used'` (default): only the keys read while rendering this page —
50
+ * a few KB — so hydration never flashes raw keys, while the full
51
+ * dictionary loads in the background right after;
52
+ * - `'all'`: the whole dictionary (no second request, but every page
53
+ * carries it);
54
+ * - `'none'`: nothing; the browser loads the file itself before bootstrap.
55
+ */
56
+ transfer?: 'used' | 'all' | 'none';
47
57
  /**
48
58
  * Mirror the active language onto `<html lang>` (server and browser), so
49
59
  * screen readers, hyphenation and search engines follow `use()`. Default
@@ -96,10 +106,16 @@ declare class MkTranslate {
96
106
  private overridesLoader;
97
107
  private readonly dictionaries;
98
108
  private readonly declared;
109
+ /** Languages holding only the keys a server render used (see `transfer`). */
110
+ private readonly partial;
111
+ /** Keys read on the server, per language — what `transfer: 'used'` ships. */
112
+ private readonly used;
99
113
  private readonly pending;
100
114
  /** Bumped whenever a dictionary changes, so readers recompute. */
101
115
  private readonly version;
102
116
  private readonly missing;
117
+ private readonly missingTick;
118
+ private missingFlush;
103
119
  /** The active language. Read it in a `computed()` to follow switches. */
104
120
  readonly lang: i0.WritableSignal<string>;
105
121
  /** `true` once the active language's strings are in memory. */
@@ -129,6 +145,7 @@ declare class MkTranslate {
129
145
  * previous language active.
130
146
  */
131
147
  use(lang: string): Promise<void>;
148
+ private activate;
132
149
  /** Load a language into memory without switching to it. */
133
150
  load(lang: string): Promise<void>;
134
151
  /**
@@ -164,6 +181,13 @@ declare class MkTranslate {
164
181
  set(lang: string, strings: MkTranslationTree | MkFlatTranslations): void;
165
182
  private lookup;
166
183
  private fetch;
184
+ /** Base + overrides for `lang`, flattened. */
185
+ private fetchFull;
186
+ /** Server: decide what rides to the browser (see `MkTranslateConfig.transfer`). */
187
+ private serialize;
188
+ private noteUsed;
189
+ /** `true` while `lang` holds only a server render's keys and the rest is still loading. */
190
+ isPartial(lang?: string): boolean;
167
191
  private baseLoader;
168
192
  private overridesLoaderOrNull;
169
193
  private bump;