@ng-linguo/linguo 0.9.3 → 0.9.5

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.
@@ -72,7 +72,13 @@ const CONTEXT_GLUE = String.fromCharCode(4);
72
72
  */
73
73
  function contextKey(key, context) {
74
74
  const normalizedKey = normalizeKey(key);
75
- const normalizedContext = context?.trim() ?? '';
75
+ // Normalize the context exactly like the key (trim + collapse whitespace).
76
+ // Angular collapses whitespace inside an interpolation before the pipe runs,
77
+ // so a multi-line `context: '…'` reaches here already single-spaced; the
78
+ // extractor must collapse it the same way or the contextual key won't match
79
+ // (the §5.2 parity contract — and it must also hold for the directive's
80
+ // tContext, whose whitespace Angular does NOT collapse).
81
+ const normalizedContext = normalizeKey(context ?? '');
76
82
  return normalizedContext === ''
77
83
  ? normalizedKey
78
84
  : `${normalizedContext}${CONTEXT_GLUE}${normalizedKey}`;
@@ -1 +1 @@
1
- {"version":3,"file":"ng-linguo-linguo.mjs","sources":["../../../../packages/linguo/src/lang-resolution.ts","../../../../packages/linguo/src/normalize.ts","../../../../packages/linguo/src/translate.store.ts","../../../../packages/linguo/src/provide-translate.ts","../../../../packages/linguo/src/slot-parser.ts","../../../../packages/linguo/src/message-formatter.ts","../../../../packages/linguo/src/translate-fn.ts","../../../../packages/linguo/src/translate.pipe.ts","../../../../packages/linguo/src/mark.ts","../../../../packages/linguo/src/slot-renderer.ts","../../../../packages/linguo/src/translate.directive.ts","../../../../packages/linguo/src/translate-slot-outlet.directive.ts","../../../../packages/linguo/src/ng-linguo-linguo.ts"],"sourcesContent":["/** Inputs for {@link resolveInitialLang}, gathered by the store (SSR-safe). */\nexport interface LangResolutionInput {\n /** The persisted language (e.g. from `localStorage`), or `null`/`''` if none\n * — already gated to `null` by the store when persistence is disabled. */\n readonly persisted: string | null;\n /** The browser's preferred languages (`navigator.languages`), most-preferred\n * first — already empty when detection is disabled or off the browser. */\n readonly browserLangs: readonly string[];\n /** The languages the app ships. Required to match a browser/persisted value\n * to one that actually exists; when omitted, browser matching is skipped. */\n readonly supportedLangs: readonly string[] | undefined;\n /** The configured fallback language. */\n readonly defaultLang: string;\n}\n\nfunction isSupported(lang: string, supported: readonly string[] | undefined): boolean {\n return supported === undefined || supported.some((l) => l.toLowerCase() === lang.toLowerCase());\n}\n\n/** Match a BCP-47 tag (e.g. `pl-PL`) to a supported language: exact first, then\n * its base subtag (`pl`). Returns the supported language in its original case. */\nfunction matchSupported(tag: string, supported: readonly string[]): string | undefined {\n const lower = tag.toLowerCase();\n const base = lower.split('-')[0] ?? lower;\n return (\n supported.find((l) => l.toLowerCase() === lower) ??\n supported.find((l) => l.toLowerCase() === base)\n );\n}\n\n/**\n * Resolve the language to load on startup, in priority order:\n * **persisted → browser-preferred → default**.\n *\n * Pure: the store gathers the (SSR-safe) inputs and passes them in. A persisted\n * value is honored only if it's supported; browser preferences are matched\n * against `supportedLangs` (so an unshipped language is never selected), and if\n * nothing matches the `defaultLang` is used.\n */\nexport function resolveInitialLang(input: LangResolutionInput): string {\n const { persisted, browserLangs, supportedLangs, defaultLang } = input;\n\n if (persisted && persisted.trim() !== '' && isSupported(persisted, supportedLangs)) {\n return persisted;\n }\n\n if (supportedLangs !== undefined) {\n for (const tag of browserLangs) {\n const match = matchSupported(tag, supportedLangs);\n if (match !== undefined) {\n return match;\n }\n }\n }\n\n return defaultLang;\n}\n","/**\n * Normalize a source message to its canonical translation key.\n *\n * Trims the message and collapses every internal whitespace run (including\n * newlines from multi-line templates) to a single space. This MUST stay\n * byte-for-byte identical to the extractor's normalizer\n * (`@ng-linguo/extract`'s `normalizeMessage`) so that a key extracted at build\n * time resolves at runtime — the parity contract in CLAUDE.md §5.2, verified by\n * both packages against `tests/fixtures/normalization-cases.json`.\n *\n * Duplicated rather than imported because `core` must not depend on `extract`\n * (CLAUDE.md §2.1).\n */\nexport function normalizeKey(source: string): string {\n return source.trim().replace(/\\s+/g, ' ');\n}\n\n/**\n * Separator joining a context to a key in the compiled dictionary. This is the\n * gettext `pgettext` convention — the EOT control character (U+0004) — chosen\n * because it never occurs in real translator text, so contextual keys cannot\n * collide with plain ones. MUST stay identical to the extractor's\n * `compileEntries`, so a contextual key produced at build time resolves at\n * runtime.\n */\nconst CONTEXT_GLUE = String.fromCharCode(4);\n\n/**\n * Build the dictionary lookup key for a message, optionally disambiguated by a\n * context label. With a context the key is `context<glue>normalizedKey`;\n * without one it is just the normalized key — which is also the fallback the\n * runtime tries when a contextual entry is absent.\n */\nexport function contextKey(key: string, context?: string): string {\n const normalizedKey = normalizeKey(key);\n const normalizedContext = context?.trim() ?? '';\n return normalizedContext === ''\n ? normalizedKey\n : `${normalizedContext}${CONTEXT_GLUE}${normalizedKey}`;\n}\n","import { DOCUMENT } from '@angular/common';\nimport { InjectionToken, inject } from '@angular/core';\nimport { patchState, signalStore, withHooks, withMethods, withState } from '@ngrx/signals';\n\nimport { resolveInitialLang } from './lang-resolution';\nimport { contextKey, normalizeKey } from './normalize';\nimport type { TranslateConfig, TranslationLoader, Translations } from './types';\n\n/**\n * DI token carrying the {@link TranslateConfig} supplied via `provideTranslate`.\n * Internal: consumers configure the runtime through `provideTranslate`, not by\n * providing this token directly.\n */\nexport const TRANSLATE_CONFIG = new InjectionToken<TranslateConfig>('ng-linguo.config');\n\n/**\n * DI token carrying the resolved {@link TranslationLoader}. `provideTranslate`\n * registers it from the config's `loader` — either directly (a ready-made\n * loader) or via a factory run inside the injection context (so loaders that\n * inject Angular services, like `createHttpLoader`, can be used). Internal.\n */\nexport const TRANSLATION_LOADER = new InjectionToken<TranslationLoader>('ng-linguo.loader');\n\ninterface TranslateState {\n readonly currentLang: string;\n readonly isReady: boolean;\n readonly translations: Translations;\n}\n\nconst initialState: TranslateState = {\n currentLang: '',\n isReady: false,\n translations: {},\n};\n\n/**\n * The reactive translation runtime, implemented as an `@ngrx/signals`\n * `signalStore`. Consumers inject it and read state through signals; there are\n * no `Observable` getters on the public surface.\n *\n * State is not loaded implicitly. Call {@link TranslateStore.restoreLang} once\n * at startup to pick the language (persisted → browser → default) and load it,\n * or {@link TranslateStore.setLang} to switch to a specific one.\n * {@link TranslateStore.isReady} stays `false` until a language has loaded, so\n * UI can gate on it to avoid a flash of untranslated content.\n *\n * @example\n * ```ts\n * const store = inject(TranslateStore);\n * store.currentLang(); // Signal<string>\n * store.isReady(); // Signal<boolean>\n * await store.restoreLang(); // startup: persisted ?? browser ?? default\n * await store.setLang('pl'); // explicit switch\n * store.translate('greeting');\n * ```\n */\nexport const TranslateStore = signalStore(\n { providedIn: 'root' },\n withState(initialState),\n withMethods((store) => {\n const config = inject(TRANSLATE_CONFIG);\n const loader = inject(TRANSLATION_LOADER);\n // `defaultView` is the Window in a browser, and null under SSR — so all\n // localStorage/navigator access below is automatically a no-op on the server.\n const win = inject(DOCUMENT).defaultView;\n const persistEnabled = config.persistSelectedLanguage ?? true;\n // Restore (read on startup) defaults to the persist setting, so disabling\n // persistence alone still disables restore (the pre-decoupling behavior);\n // set restoreSelectedLanguage explicitly to control the two independently.\n const restoreEnabled = config.restoreSelectedLanguage ?? persistEnabled;\n const detectEnabled = config.detectBrowserLanguage ?? true;\n const persistKey = config.persistKey ?? 'ng-linguo.lang';\n\n function readPersisted(): string | null {\n if (!restoreEnabled || !win) return null;\n try {\n return win.localStorage.getItem(persistKey);\n } catch {\n return null; // storage disabled (private mode, blocked cookies, …)\n }\n }\n function writePersisted(lang: string): void {\n if (!persistEnabled || !win) return;\n try {\n win.localStorage.setItem(persistKey, lang);\n } catch {\n // ignore — persistence is best-effort\n }\n }\n function browserLangs(): readonly string[] {\n const nav = win?.navigator;\n if (!detectEnabled || !nav) return [];\n return nav.languages?.length ? nav.languages : nav.language ? [nav.language] : [];\n }\n\n async function load(lang: string): Promise<void> {\n const translations = await loader.load(lang);\n patchState(store, { currentLang: lang, translations, isReady: true });\n writePersisted(lang);\n }\n\n return {\n /**\n * Load the translations for `lang`, make them current, and (when\n * `persistSelectedLanguage` is on) persist the choice. Resolves once the\n * loader has returned and the store is ready.\n */\n setLang(lang: string): Promise<void> {\n return load(lang);\n },\n /**\n * Resolve the startup language — **persisted → browser-preferred →\n * `defaultLang`** — and load it. Call this once at startup instead of\n * `setLang(defaultLang)`. Persistence and browser detection are on by\n * default and can be turned off via `provideTranslate` config; both are\n * SSR-safe (they fall through to the default on the server).\n */\n restoreLang(): Promise<void> {\n return load(\n resolveInitialLang({\n persisted: readPersisted(),\n browserLangs: browserLangs(),\n supportedLangs: config.supportedLangs,\n defaultLang: config.defaultLang,\n }),\n );\n },\n /**\n * Resolve a translation key against the currently loaded language.\n *\n * An optional `context` disambiguates keys that share the same source\n * text (for example `Play` in a game versus a music player). Lookup tries\n * the contextual key first, then falls back to the plain key, then to the\n * key itself — so a missing translation is visible rather than blank, and\n * omitting the context always resolves to the default entry.\n */\n translate(key: string, context?: string): string {\n const normalized = normalizeKey(key);\n const translations = store.translations();\n if (context !== undefined && context.trim() !== '') {\n const contextual = translations[contextKey(key, context)];\n if (contextual !== undefined) {\n return contextual;\n }\n }\n return translations[normalized] ?? normalized;\n },\n };\n }),\n withHooks({\n onInit(store): void {\n // Report the configured default language immediately, without loading.\n patchState(store, { currentLang: inject(TRANSLATE_CONFIG).defaultLang });\n },\n }),\n);\n","import { type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\n\nimport { TRANSLATE_CONFIG, TRANSLATION_LOADER } from './translate.store';\nimport type { TranslateConfig } from './types';\n\n/**\n * Register the translation runtime for an application or a feature scope.\n *\n * Add the returned providers to `bootstrapApplication`'s `providers` (or a\n * route's `providers`). This only wires up configuration — it never starts a\n * load, so no HTTP is fired during DI initialization. Trigger the first load\n * explicitly via `TranslateStore.setLang`.\n *\n * The `loader` may be a ready-made loader object, or a factory `() => loader`\n * that is run inside the injection context — use the factory form for loaders\n * that inject Angular services (e.g. `createHttpLoader()`, which needs\n * `HttpClient`).\n *\n * @example\n * ```ts\n * // a static dictionary or fetch-based loader\n * provideTranslate({ defaultLang: 'en', loader: myLoader });\n *\n * // an HttpClient-backed loader (factory form)\n * provideTranslate({ defaultLang: 'en', loader: () => createHttpLoader() });\n * ```\n */\nexport function provideTranslate(config: TranslateConfig): EnvironmentProviders {\n const { loader } = config;\n return makeEnvironmentProviders([\n { provide: TRANSLATE_CONFIG, useValue: config },\n typeof loader === 'function'\n ? { provide: TRANSLATION_LOADER, useFactory: loader }\n : { provide: TRANSLATION_LOADER, useValue: loader },\n ]);\n}\n","/**\n * A node in a parsed slot tree produced by {@link parseSlots}.\n *\n * - `text` nodes carry literal, translator-authored text. They are rendered as\n * DOM text nodes — never as HTML — which is how the library keeps its XSS\n * surface at zero by construction.\n * - `slot` nodes correspond to a `[name]...[/name]` region that the developer\n * fills with an `<ng-template>`. The bracket syntax resembles BBCode, but the\n * names are arbitrary and author-chosen and carry no predefined rendering —\n * each is a named slot bound to a template. Slots may nest.\n */\nexport type SlotNode =\n | { readonly kind: 'text'; readonly value: string }\n | {\n readonly kind: 'slot';\n readonly name: string;\n readonly children: readonly SlotNode[];\n };\n\n/**\n * Matches a single slot tag at the start of a string: an optional leading slash\n * (closing tag) followed by a name. The name grammar\n * (`[a-zA-Z_][a-zA-Z0-9_-]*`) is part of the public contract — see CLAUDE.md\n * §5.1. Changing it is a major version bump.\n *\n * A literal `[` is written as `[[`; that escape is handled in {@link tokenize}\n * before this pattern is tried, so a tag is never matched across an escape.\n */\nconst TAG_PATTERN = /^\\[(\\/?)([a-zA-Z_][a-zA-Z0-9_-]*)\\]/;\n\ntype Token =\n | { readonly kind: 'text'; readonly value: string }\n | { readonly kind: 'open'; readonly name: string }\n | { readonly kind: 'close'; readonly name: string };\n\nfunction tokenize(input: string): readonly Token[] {\n const tokens: Token[] = [];\n let buffer = '';\n\n const flush = (): void => {\n if (buffer.length > 0) {\n tokens.push({ kind: 'text', value: buffer });\n buffer = '';\n }\n };\n\n let index = 0;\n while (index < input.length) {\n if (input[index] === '[') {\n // `[[` is an escaped literal `[` — the one way a translatable string can\n // contain text that looks like a slot tag (e.g. `[note]`) without it being\n // parsed as one. Checked before TAG_PATTERN so the escape always wins; a\n // lone `]` needs no escape, since it is never significant on its own.\n if (input[index + 1] === '[') {\n buffer += '[';\n index += 2;\n continue;\n }\n const match = TAG_PATTERN.exec(input.slice(index));\n if (match) {\n // Defaults satisfy `noUncheckedIndexedAccess` without non-null `!`.\n const [whole, slash = '', name = ''] = match;\n flush();\n tokens.push(slash === '/' ? { kind: 'close', name } : { kind: 'open', name });\n index += whole.length;\n continue;\n }\n }\n // A `[` that does not start a valid tag is ordinary text — malformed input\n // is tolerated rather than throwing, so a stray bracket never breaks a page.\n buffer += input[index];\n index += 1;\n }\n flush();\n return tokens;\n}\n\ninterface OpenFrame {\n readonly name: string;\n readonly children: SlotNode[];\n}\n\nfunction appendText(into: SlotNode[], value: string): void {\n const last = into[into.length - 1];\n if (last && last.kind === 'text') {\n into[into.length - 1] = { kind: 'text', value: last.value + value };\n } else {\n into.push({ kind: 'text', value });\n }\n}\n\n/**\n * Parse a translator-authored string into a tree of {@link SlotNode}s.\n *\n * The parser is total: every input produces a tree, and any malformed\n * construct (a stray `[`, an unclosed tag, a mismatched closing tag) degrades\n * gracefully to literal text rather than throwing. Well-formed\n * `[name]...[/name]` regions — including nested ones — become `slot` nodes. To\n * include a literal `[` (so text such as `[note]` is not read as a tag), double\n * it: `[[` parses to a single `[`.\n *\n * @example\n * ```ts\n * parseSlots('Hello [b]world[/b]!');\n * // [\n * // { kind: 'text', value: 'Hello ' },\n * // { kind: 'slot', name: 'b', children: [{ kind: 'text', value: 'world' }] },\n * // { kind: 'text', value: '!' },\n * // ]\n *\n * parseSlots('See [[b] for bold');\n * // [{ kind: 'text', value: 'See [b] for bold' }]\n * ```\n */\nexport function parseSlots(input: string): readonly SlotNode[] {\n const root: SlotNode[] = [];\n const stack: OpenFrame[] = [];\n\n const current = (): SlotNode[] => {\n const top = stack.at(-1);\n return top ? top.children : root;\n };\n\n for (const token of tokenize(input)) {\n if (token.kind === 'text') {\n appendText(current(), token.value);\n continue;\n }\n if (token.kind === 'open') {\n stack.push({ name: token.name, children: [] });\n continue;\n }\n // Closing tag.\n const top = stack.at(-1);\n if (top && top.name === token.name) {\n stack.pop();\n current().push({ kind: 'slot', name: top.name, children: top.children });\n } else {\n // No matching open tag: treat the closing tag as literal text.\n appendText(current(), `[/${token.name}]`);\n }\n }\n\n // Unwind any tags left open at end of input, back into literal text so their\n // contents are still rendered.\n for (let frame = stack.pop(); frame !== undefined; frame = stack.pop()) {\n const parent = current();\n appendText(parent, `[${frame.name}]`);\n for (const child of frame.children) {\n if (child.kind === 'text') {\n appendText(parent, child.value);\n } else {\n parent.push(child);\n }\n }\n }\n\n return root;\n}\n\nfunction collectText(nodes: readonly SlotNode[]): string {\n let text = '';\n for (const node of nodes) {\n text += node.kind === 'text' ? node.value : collectText(node.children);\n }\n return text;\n}\n\n/**\n * Flatten a slot string to plain text: slot tags are dropped and their inner\n * text is kept. This is how the `t` pipe and {@link injectTranslate} render a\n * message containing `[name]...[/name]` slots — they return a string, so the\n * markup degrades to text (the `[t]` directive renders the slots into templates\n * instead).\n *\n * @example\n * ```ts\n * slotsToText('Read the [docs]documentation[/docs] now'); // 'Read the documentation now'\n * slotsToText('Press [[Enter] to send'); // 'Press [Enter] to send'\n * ```\n */\nexport function slotsToText(input: string): string {\n return collectText(parseSlots(input));\n}\n","import { InjectionToken } from '@angular/core';\n\n/**\n * Formats a resolved message against runtime arguments — the seam that lets the\n * `translate` pipe render ICU messages without `core` depending on any ICU\n * library. `@ng-linguo/linguo/icu`'s `provideIcu` supplies an implementation; when none\n * is provided the pipe simply returns the message unformatted.\n */\nexport interface MessageFormatter {\n /**\n * @param message The resolved (translated) message, possibly containing ICU\n * placeholders.\n * @param args Named arguments to substitute (counts, names, dates…).\n * @param locale BCP-47 locale for plural rules and number/date formatting.\n */\n format(message: string, args: Record<string, unknown>, locale: string): string;\n}\n\n/**\n * DI token for an optional {@link MessageFormatter}. Provided by\n * `@ng-linguo/linguo/icu`'s `provideIcu`; absent by default.\n */\nexport const MESSAGE_FORMATTER = new InjectionToken<MessageFormatter>(\n 'ng-linguo.message-formatter',\n);\n","import { inject } from '@angular/core';\n\nimport { slotsToText } from './slot-parser';\nimport { MESSAGE_FORMATTER } from './message-formatter';\nimport { TranslateStore } from './translate.store';\nimport type { TranslateOptions } from './types';\n\n/**\n * Resolves a key to its translation for the current language, with optional ICU\n * `params` and `context` — the function form of the `t` pipe.\n */\nexport type TranslateFn = (key: string, options?: TranslateOptions) => string;\n\n/**\n * Obtain a {@link TranslateFn} for use in component code — the TypeScript\n * counterpart of the `t` pipe.\n *\n * Call it in an injection context (a field initializer or constructor); the\n * returned `t` reads the store's signals each time it runs, so it stays reactive\n * when used in a template binding or inside a `computed`.\n *\n * @example\n * ```ts\n * export class Greeting {\n * private readonly t = injectTranslate();\n * protected readonly name = signal('Ada');\n * protected readonly text = computed(() =>\n * this.t('Hello {$name}!', { params: { name: this.name() } }),\n * );\n * }\n * ```\n */\nexport function injectTranslate(): TranslateFn {\n const store = inject(TranslateStore);\n const formatter = inject(MESSAGE_FORMATTER, { optional: true });\n return (key, options) => {\n let message = store.translate(key, options?.context);\n if (options?.params && formatter) {\n message = formatter.format(message, options.params, store.currentLang() || 'en');\n }\n // Returns a string, so slot tags degrade to their inner text; the `[t]`\n // directive renders them into templates instead.\n return slotsToText(message);\n };\n}\n","import { Pipe, inject, type PipeTransform } from '@angular/core';\n\nimport { injectTranslate, type TranslateFn } from './translate-fn';\nimport { TranslateStore } from './translate.store';\nimport type { TranslateOptions, Translations } from './types';\n\n/** Shallow value-equality for `params`: same keys, each value strictly equal. */\nfunction paramsEqual(\n a: Record<string, unknown> | undefined,\n b: Record<string, unknown> | undefined,\n): boolean {\n if (a === b) return true;\n if (a === undefined || b === undefined) return false;\n const keys = Object.keys(a);\n return keys.length === Object.keys(b).length && keys.every((k) => a[k] === b[k]);\n}\n\ninterface PipeCache {\n readonly key: string;\n readonly context: string | undefined;\n readonly params: Record<string, unknown> | undefined;\n readonly version: Translations;\n readonly result: string;\n}\n\n/**\n * Resolve a translation key to its string for the current language, with options\n * passed as a single object: ICU `params` and a disambiguating `context`.\n *\n * @example\n * ```html\n * {{ 'Play' | t }}\n * {{ 'Play' | t: { context: 'game' } }}\n * {{ 'Hello {$name}!' | t: { params: { name } } }}\n * ```\n *\n * The key is the source text; a missing key renders as itself. ICU `params` are\n * applied by the formatter from `@ng-linguo/linguo/icu` (`provideIcu`); without\n * it the message is returned unformatted.\n *\n * Impure so it re-evaluates against the store's signals each change detection,\n * re-rendering when the language changes. To keep that cheap it **memoizes** by\n * value: the lookup/format only re-runs when the key, `context`, `params`\n * contents, or the active language actually change — so a fresh `{ params: … }`\n * literal on every change-detection pass costs only an equality check. For hot\n * or looped bindings, prefer `injectTranslate()` inside a `computed()`, which\n * does zero work per change-detection pass.\n */\n@Pipe({ name: 't', pure: false })\nexport class TranslatePipe implements PipeTransform {\n private readonly t: TranslateFn = injectTranslate();\n // The translations signal's value reference changes on every `setLang`, so it\n // doubles as the \"language version\" for cache invalidation.\n private readonly translations = inject(TranslateStore).translations;\n private cache: PipeCache | undefined;\n\n transform(key: string, options?: TranslateOptions): string {\n const version = this.translations();\n const context = options?.context;\n const params = options?.params;\n\n const cached = this.cache;\n if (\n cached !== undefined &&\n cached.key === key &&\n cached.context === context &&\n cached.version === version &&\n paramsEqual(cached.params, params)\n ) {\n return cached.result;\n }\n\n const result = this.t(key, options);\n this.cache = { key, context, params, version, result };\n return result;\n }\n}\n","/**\n * Options for {@link mark}. Only `context` is read (by `@ng-linguo/extract`);\n * `params` is accepted so a single options object can be shared with the `t`\n * pipe / directive call that ultimately renders the message.\n */\nexport interface MarkOptions {\n /**\n * Disambiguating/contextual text recorded as the catalog `msgctxt`. It is part\n * of the key, so it must match the `context` passed at the consuming\n * pipe/directive for the runtime lookup to resolve. It doubles as a note for\n * translators (e.g. `'file = a document on disk'`).\n */\n readonly context?: string;\n}\n\n/**\n * Mark a string as a translatable message so `@ng-linguo/extract` collects it,\n * returning the string unchanged (it does not translate at runtime).\n *\n * Use it for messages that are not written inline at a `t` pipe or `[t]`\n * directive — for example an ICU message kept in a component field (an MF2\n * pattern contains `{{ … }}`, which collides with Angular's `{{ }}` binding).\n * The marked string is still translated at render time by the pipe/directive\n * that consumes it.\n *\n * Pass `context` to record a `msgctxt`/translator note for the entry. Because\n * context is part of the key, the **same** `context` must be supplied where the\n * marked string is rendered (`| t: { context }` or `tContext`), or the runtime\n * lookup will not find the contextual entry.\n *\n * @example\n * ```ts\n * readonly fileCount = mark(\n * '.input {$count :number} .match $count one {{{$count} file}} * {{{$count} files}}',\n * { context: 'file = a document on disk' },\n * );\n * // template: {{ fileCount | t: { params: { count: count() }, context: 'file = a document on disk' } }}\n * ```\n */\nexport function mark(message: string, options?: MarkOptions): string {\n // `options` is read only by the extractor (statically); at runtime it is a\n // no-op, so the parameter is intentionally unused here.\n void options;\n return message;\n}\n","import type { EmbeddedViewRef, Renderer2, TemplateRef, ViewContainerRef } from '@angular/core';\n\nimport type { SlotNode } from './slot-parser';\n\n/**\n * Context handed to a slot's `<ng-template tFor>`:\n *\n * - `$implicit` — the slot's inner text, flattened to a string. `let-text` binds\n * it, so `{{ text }}` renders a plain-text slot (the common case).\n * - `children` — the slot's parsed child nodes, for binding *nested* slots to\n * their own templates with `*tRender` instead of flattening them to text. Bind\n * it with `let-kids=\"children\"`. See `TranslateSlotOutlet`.\n */\nexport interface TranslateSlotContext {\n readonly $implicit: string;\n readonly children: readonly SlotNode[];\n}\n\n/** Map from a slot name to the `<ng-template>` that renders it. */\nexport type SlotTemplates = ReadonlyMap<string, TemplateRef<TranslateSlotContext>>;\n\n/** Flatten a slot subtree to its concatenated text, dropping the tags. */\nfunction collectText(nodes: readonly SlotNode[]): string {\n let text = '';\n for (const node of nodes) {\n text += node.kind === 'text' ? node.value : collectText(node.children);\n }\n return text;\n}\n\n/**\n * Renders a parsed slot tree into the DOM as text nodes and embedded\n * `<ng-template>` views — never as HTML (CLAUDE.md §5.1).\n *\n * Owns the views and host-level text nodes it creates so a re-render (a language\n * or params change) can tear them down. A single instance is shared by a `[t]`\n * element and every `*tRender` outlet nested inside it: views are tracked flat\n * and destroyed together, so a re-render is a clean teardown-then-rebuild\n * regardless of nesting depth.\n */\nexport class SlotRenderer {\n private views: EmbeddedViewRef<TranslateSlotContext>[] = [];\n private hostNodes: Node[] = [];\n\n constructor(\n private readonly renderer: Renderer2,\n private readonly viewContainer: ViewContainerRef,\n ) {}\n\n /** Destroy every view and remove every host-level text node from the last render. */\n clear(): void {\n for (const view of this.views) {\n view.destroy();\n }\n this.views = [];\n for (const node of this.hostNodes) {\n // Nodes placed inside an embedded view are gone with their view; only the\n // ones appended straight onto the persistent host element remain here.\n if (node.parentNode) {\n this.renderer.removeChild(node.parentNode, node);\n }\n }\n this.hostNodes = [];\n }\n\n /**\n * Render `nodes` into `parent`, before `before` (or appended when `before` is\n * `null`). `trackHost` is `true` only for nodes placed directly on the `[t]`\n * host element — those text nodes outlive any single view and must be removed\n * explicitly on the next render; nodes inside an embedded view ride along when\n * that view is destroyed, so they are not tracked.\n */\n render(\n nodes: readonly SlotNode[],\n parent: Node,\n before: Node | null,\n templates: SlotTemplates,\n trackHost: boolean,\n ): void {\n for (const node of nodes) {\n if (node.kind === 'text') {\n const text = this.renderer.createText(node.value);\n this.insert(parent, text, before);\n if (trackHost) {\n this.hostNodes.push(text);\n }\n continue;\n }\n\n const template = templates.get(node.name);\n if (!template) {\n // No matching template: render the slot's children inline (transparent),\n // so a nested *matched* slot still renders and text-only content reads\n // the same as a plain flatten.\n this.render(node.children, parent, before, templates, trackHost);\n continue;\n }\n\n const view = this.viewContainer.createEmbeddedView(template, {\n $implicit: collectText(node.children),\n children: node.children,\n });\n this.views.push(view);\n // Running the view's bindings instantiates any `*tRender` outlet inside it,\n // which calls back to render this slot's children into that outlet — the\n // nesting recursion happens here, on the call stack.\n view.detectChanges();\n for (const rootNode of view.rootNodes as Node[]) {\n this.insert(parent, rootNode, before);\n }\n }\n }\n\n private insert(parent: Node, node: Node, before: Node | null): void {\n if (before) {\n this.renderer.insertBefore(parent, node, before);\n } else {\n this.renderer.appendChild(parent, node);\n }\n }\n}\n","import {\n Directive,\n ElementRef,\n Renderer2,\n TemplateRef,\n ViewContainerRef,\n contentChildren,\n effect,\n inject,\n input,\n type OnInit,\n} from '@angular/core';\n\nimport { MESSAGE_FORMATTER } from './message-formatter';\nimport { parseSlots, type SlotNode } from './slot-parser';\nimport { SlotRenderer, type SlotTemplates, type TranslateSlotContext } from './slot-renderer';\nimport { TranslateStore } from './translate.store';\n\nexport type { TranslateSlotContext } from './slot-renderer';\n\n/**\n * Provides an `<ng-template>` as the renderer for a named slot of the enclosing\n * `[t]` element. The `[name]...[/name]` bracket syntax resembles BBCode, but the\n * name is arbitrary and author-chosen — it identifies a slot to fill, not a tag\n * with predefined HTML. Declared as a child of the `[t]` element (it renders as\n * an invisible anchor), so the name is scoped to that element. The template\n * receives the slot's inner text (`let-text`) and parsed children\n * (`let-kids=\"children\"`, for nesting via `*tRender`) as its context.\n *\n * @example\n * ```html\n * <ng-template tFor=\"docs\" let-text>\n * <a routerLink=\"/docs\">{{ text }}</a>\n * </ng-template>\n * ```\n */\n@Directive({ selector: 'ng-template[tFor]' })\nexport class TranslateSlot {\n /** Slot name, matching `[name]...[/name]` in the translation. */\n readonly name = input.required<string>({ alias: 'tFor' });\n readonly templateRef = inject<TemplateRef<TranslateSlotContext>>(TemplateRef);\n}\n\n/**\n * Translate the `t` message and render it into the element, applying ICU `tParams`\n * and binding any `[name]...[/name]` slot regions to `<ng-template tFor>`\n * children.\n *\n * The message is the `t` attribute (a string expression), so it may contain both\n * ICU (`{$name}`) and slot tags (`[name]`) safely. Rendered text is emitted as DOM\n * text nodes, never HTML (CLAUDE.md §5.1); a slot with no matching template\n * degrades to its inner text. Nested slots bind to their own templates when the\n * parent template marks where they go with `*tRender` (see\n * `TranslateSlotOutlet`); otherwise a nested slot renders as text. Re-renders\n * when the language, params, or a slot template changes.\n *\n * @example\n * ```html\n * <p t=\"Hello {$name}!\" [tParams]=\"{ name }\"></p>\n *\n * <p t=\"Read the [docs]documentation[/docs] to get started\">\n * <ng-template tFor=\"docs\" let-text>\n * <a routerLink=\"/docs\">{{ text }}</a>\n * </ng-template>\n * </p>\n * ```\n */\n@Directive({ selector: '[t]' })\nexport class TranslateDirective implements OnInit {\n private readonly store = inject(TranslateStore);\n private readonly host = inject<ElementRef<Element>>(ElementRef).nativeElement;\n private readonly renderer = inject(Renderer2);\n private readonly formatter = inject(MESSAGE_FORMATTER, { optional: true });\n private readonly slotRenderer = new SlotRenderer(this.renderer, inject(ViewContainerRef));\n\n /** Source message (the translation key); may contain ICU and slot tags. */\n readonly message = input.required<string>({ alias: 't' });\n /** ICU arguments, formatted via `@ng-linguo/linguo/icu` when provided. */\n readonly tParams = input<Record<string, unknown> | undefined>(undefined);\n /** Optional disambiguating/contextual text (part of the key). */\n readonly tContext = input<string>('');\n\n private readonly slots = contentChildren(TranslateSlot, { descendants: true });\n private templates: SlotTemplates = new Map();\n\n constructor() {\n effect(() => {\n const templates = new Map<string, TemplateRef<TranslateSlotContext>>();\n for (const slot of this.slots()) {\n templates.set(slot.name(), slot.templateRef);\n }\n this.templates = templates;\n\n let text = this.store.translate(this.message(), this.tContext());\n const params = this.tParams();\n if (params && this.formatter) {\n text = this.formatter.format(text, params, this.store.currentLang() || 'en');\n }\n\n this.slotRenderer.clear();\n this.slotRenderer.render(parseSlots(text), this.host, null, templates, true);\n });\n }\n\n ngOnInit(): void {\n // Remove any authored whitespace text so the rendered translation is not\n // preceded by stray spacing. Slot `<ng-template>` anchors (comments) are\n // left intact for the content query.\n for (const node of Array.from(this.host.childNodes)) {\n if (node.nodeType === 3 /* text node */) {\n this.renderer.removeChild(this.host, node);\n }\n }\n }\n\n /**\n * Render a nested slot's `nodes` into `parent` before `before`, reusing the\n * current templates and the shared renderer. Internal: called by\n * `TranslateSlotOutlet` (`*tRender`); not part of the consumer-facing API.\n */\n renderChildren(parent: Node, before: Node, nodes: readonly SlotNode[]): void {\n this.slotRenderer.render(nodes, parent, before, this.templates, false);\n }\n}\n","import { Directive, ViewContainerRef, inject, input, type OnInit } from '@angular/core';\n\nimport type { SlotNode } from './slot-parser';\nimport { TranslateDirective } from './translate.directive';\n\n/**\n * Renders the *children* of a slot at this position, so a nested slot binds to\n * its own `<ng-template>` instead of flattening to text. Without it, a slot\n * inside a templated slot renders as plain text (its `$implicit` value); with\n * it, each nested slot finds its own `tFor` template.\n *\n * Bind the `children` from the enclosing slot's context and place the outlet\n * where the nested content should appear:\n *\n * @example\n * ```html\n * <p t=\"Agree to our [link]Terms and [b]Conditions[/b][/link]\">\n * <ng-template tFor=\"link\" let-kids=\"children\">\n * <a href=\"/terms\"><ng-container *tRender=\"kids\" /></a>\n * </ng-template>\n * <ng-template tFor=\"b\" let-text><strong>{{ text }}</strong></ng-template>\n * </p>\n * ```\n *\n * Valid only inside a `[t]` element's slot template — it resolves the enclosing\n * {@link TranslateDirective} and renders through its shared engine, so nested\n * views are torn down with the rest on a language or params change.\n */\n@Directive({ selector: '[tRender]' })\nexport class TranslateSlotOutlet implements OnInit {\n /** The nodes to render — the `children` value from the slot context. */\n readonly nodes = input.required<readonly SlotNode[]>({ alias: 'tRender' });\n\n private readonly host = inject(TranslateDirective);\n // The outlet sits on an `<ng-container>`, whose anchor is a comment node; its\n // parent is the element the children belong in, and we render before it.\n private readonly anchor = inject(ViewContainerRef).element.nativeElement as Node;\n\n ngOnInit(): void {\n const parent = this.anchor.parentNode;\n if (parent) {\n this.host.renderChildren(parent, this.anchor, this.nodes());\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["collectText"],"mappings":"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"}
1
+ {"version":3,"file":"ng-linguo-linguo.mjs","sources":["../../../../packages/linguo/src/lang-resolution.ts","../../../../packages/linguo/src/normalize.ts","../../../../packages/linguo/src/translate.store.ts","../../../../packages/linguo/src/provide-translate.ts","../../../../packages/linguo/src/slot-parser.ts","../../../../packages/linguo/src/message-formatter.ts","../../../../packages/linguo/src/translate-fn.ts","../../../../packages/linguo/src/translate.pipe.ts","../../../../packages/linguo/src/mark.ts","../../../../packages/linguo/src/slot-renderer.ts","../../../../packages/linguo/src/translate.directive.ts","../../../../packages/linguo/src/translate-slot-outlet.directive.ts","../../../../packages/linguo/src/ng-linguo-linguo.ts"],"sourcesContent":["/** Inputs for {@link resolveInitialLang}, gathered by the store (SSR-safe). */\nexport interface LangResolutionInput {\n /** The persisted language (e.g. from `localStorage`), or `null`/`''` if none\n * — already gated to `null` by the store when persistence is disabled. */\n readonly persisted: string | null;\n /** The browser's preferred languages (`navigator.languages`), most-preferred\n * first — already empty when detection is disabled or off the browser. */\n readonly browserLangs: readonly string[];\n /** The languages the app ships. Required to match a browser/persisted value\n * to one that actually exists; when omitted, browser matching is skipped. */\n readonly supportedLangs: readonly string[] | undefined;\n /** The configured fallback language. */\n readonly defaultLang: string;\n}\n\nfunction isSupported(lang: string, supported: readonly string[] | undefined): boolean {\n return supported === undefined || supported.some((l) => l.toLowerCase() === lang.toLowerCase());\n}\n\n/** Match a BCP-47 tag (e.g. `pl-PL`) to a supported language: exact first, then\n * its base subtag (`pl`). Returns the supported language in its original case. */\nfunction matchSupported(tag: string, supported: readonly string[]): string | undefined {\n const lower = tag.toLowerCase();\n const base = lower.split('-')[0] ?? lower;\n return (\n supported.find((l) => l.toLowerCase() === lower) ??\n supported.find((l) => l.toLowerCase() === base)\n );\n}\n\n/**\n * Resolve the language to load on startup, in priority order:\n * **persisted → browser-preferred → default**.\n *\n * Pure: the store gathers the (SSR-safe) inputs and passes them in. A persisted\n * value is honored only if it's supported; browser preferences are matched\n * against `supportedLangs` (so an unshipped language is never selected), and if\n * nothing matches the `defaultLang` is used.\n */\nexport function resolveInitialLang(input: LangResolutionInput): string {\n const { persisted, browserLangs, supportedLangs, defaultLang } = input;\n\n if (persisted && persisted.trim() !== '' && isSupported(persisted, supportedLangs)) {\n return persisted;\n }\n\n if (supportedLangs !== undefined) {\n for (const tag of browserLangs) {\n const match = matchSupported(tag, supportedLangs);\n if (match !== undefined) {\n return match;\n }\n }\n }\n\n return defaultLang;\n}\n","/**\n * Normalize a source message to its canonical translation key.\n *\n * Trims the message and collapses every internal whitespace run (including\n * newlines from multi-line templates) to a single space. This MUST stay\n * byte-for-byte identical to the extractor's normalizer\n * (`@ng-linguo/extract`'s `normalizeMessage`) so that a key extracted at build\n * time resolves at runtime — the parity contract in CLAUDE.md §5.2, verified by\n * both packages against `tests/fixtures/normalization-cases.json`.\n *\n * Duplicated rather than imported because `core` must not depend on `extract`\n * (CLAUDE.md §2.1).\n */\nexport function normalizeKey(source: string): string {\n return source.trim().replace(/\\s+/g, ' ');\n}\n\n/**\n * Separator joining a context to a key in the compiled dictionary. This is the\n * gettext `pgettext` convention — the EOT control character (U+0004) — chosen\n * because it never occurs in real translator text, so contextual keys cannot\n * collide with plain ones. MUST stay identical to the extractor's\n * `compileEntries`, so a contextual key produced at build time resolves at\n * runtime.\n */\nconst CONTEXT_GLUE = String.fromCharCode(4);\n\n/**\n * Build the dictionary lookup key for a message, optionally disambiguated by a\n * context label. With a context the key is `context<glue>normalizedKey`;\n * without one it is just the normalized key — which is also the fallback the\n * runtime tries when a contextual entry is absent.\n */\nexport function contextKey(key: string, context?: string): string {\n const normalizedKey = normalizeKey(key);\n // Normalize the context exactly like the key (trim + collapse whitespace).\n // Angular collapses whitespace inside an interpolation before the pipe runs,\n // so a multi-line `context: '…'` reaches here already single-spaced; the\n // extractor must collapse it the same way or the contextual key won't match\n // (the §5.2 parity contract — and it must also hold for the directive's\n // tContext, whose whitespace Angular does NOT collapse).\n const normalizedContext = normalizeKey(context ?? '');\n return normalizedContext === ''\n ? normalizedKey\n : `${normalizedContext}${CONTEXT_GLUE}${normalizedKey}`;\n}\n","import { DOCUMENT } from '@angular/common';\nimport { InjectionToken, inject } from '@angular/core';\nimport { patchState, signalStore, withHooks, withMethods, withState } from '@ngrx/signals';\n\nimport { resolveInitialLang } from './lang-resolution';\nimport { contextKey, normalizeKey } from './normalize';\nimport type { TranslateConfig, TranslationLoader, Translations } from './types';\n\n/**\n * DI token carrying the {@link TranslateConfig} supplied via `provideTranslate`.\n * Internal: consumers configure the runtime through `provideTranslate`, not by\n * providing this token directly.\n */\nexport const TRANSLATE_CONFIG = new InjectionToken<TranslateConfig>('ng-linguo.config');\n\n/**\n * DI token carrying the resolved {@link TranslationLoader}. `provideTranslate`\n * registers it from the config's `loader` — either directly (a ready-made\n * loader) or via a factory run inside the injection context (so loaders that\n * inject Angular services, like `createHttpLoader`, can be used). Internal.\n */\nexport const TRANSLATION_LOADER = new InjectionToken<TranslationLoader>('ng-linguo.loader');\n\ninterface TranslateState {\n readonly currentLang: string;\n readonly isReady: boolean;\n readonly translations: Translations;\n}\n\nconst initialState: TranslateState = {\n currentLang: '',\n isReady: false,\n translations: {},\n};\n\n/**\n * The reactive translation runtime, implemented as an `@ngrx/signals`\n * `signalStore`. Consumers inject it and read state through signals; there are\n * no `Observable` getters on the public surface.\n *\n * State is not loaded implicitly. Call {@link TranslateStore.restoreLang} once\n * at startup to pick the language (persisted → browser → default) and load it,\n * or {@link TranslateStore.setLang} to switch to a specific one.\n * {@link TranslateStore.isReady} stays `false` until a language has loaded, so\n * UI can gate on it to avoid a flash of untranslated content.\n *\n * @example\n * ```ts\n * const store = inject(TranslateStore);\n * store.currentLang(); // Signal<string>\n * store.isReady(); // Signal<boolean>\n * await store.restoreLang(); // startup: persisted ?? browser ?? default\n * await store.setLang('pl'); // explicit switch\n * store.translate('greeting');\n * ```\n */\nexport const TranslateStore = signalStore(\n { providedIn: 'root' },\n withState(initialState),\n withMethods((store) => {\n const config = inject(TRANSLATE_CONFIG);\n const loader = inject(TRANSLATION_LOADER);\n // `defaultView` is the Window in a browser, and null under SSR — so all\n // localStorage/navigator access below is automatically a no-op on the server.\n const win = inject(DOCUMENT).defaultView;\n const persistEnabled = config.persistSelectedLanguage ?? true;\n // Restore (read on startup) defaults to the persist setting, so disabling\n // persistence alone still disables restore (the pre-decoupling behavior);\n // set restoreSelectedLanguage explicitly to control the two independently.\n const restoreEnabled = config.restoreSelectedLanguage ?? persistEnabled;\n const detectEnabled = config.detectBrowserLanguage ?? true;\n const persistKey = config.persistKey ?? 'ng-linguo.lang';\n\n function readPersisted(): string | null {\n if (!restoreEnabled || !win) return null;\n try {\n return win.localStorage.getItem(persistKey);\n } catch {\n return null; // storage disabled (private mode, blocked cookies, …)\n }\n }\n function writePersisted(lang: string): void {\n if (!persistEnabled || !win) return;\n try {\n win.localStorage.setItem(persistKey, lang);\n } catch {\n // ignore — persistence is best-effort\n }\n }\n function browserLangs(): readonly string[] {\n const nav = win?.navigator;\n if (!detectEnabled || !nav) return [];\n return nav.languages?.length ? nav.languages : nav.language ? [nav.language] : [];\n }\n\n async function load(lang: string): Promise<void> {\n const translations = await loader.load(lang);\n patchState(store, { currentLang: lang, translations, isReady: true });\n writePersisted(lang);\n }\n\n return {\n /**\n * Load the translations for `lang`, make them current, and (when\n * `persistSelectedLanguage` is on) persist the choice. Resolves once the\n * loader has returned and the store is ready.\n */\n setLang(lang: string): Promise<void> {\n return load(lang);\n },\n /**\n * Resolve the startup language — **persisted → browser-preferred →\n * `defaultLang`** — and load it. Call this once at startup instead of\n * `setLang(defaultLang)`. Persistence and browser detection are on by\n * default and can be turned off via `provideTranslate` config; both are\n * SSR-safe (they fall through to the default on the server).\n */\n restoreLang(): Promise<void> {\n return load(\n resolveInitialLang({\n persisted: readPersisted(),\n browserLangs: browserLangs(),\n supportedLangs: config.supportedLangs,\n defaultLang: config.defaultLang,\n }),\n );\n },\n /**\n * Resolve a translation key against the currently loaded language.\n *\n * An optional `context` disambiguates keys that share the same source\n * text (for example `Play` in a game versus a music player). Lookup tries\n * the contextual key first, then falls back to the plain key, then to the\n * key itself — so a missing translation is visible rather than blank, and\n * omitting the context always resolves to the default entry.\n */\n translate(key: string, context?: string): string {\n const normalized = normalizeKey(key);\n const translations = store.translations();\n if (context !== undefined && context.trim() !== '') {\n const contextual = translations[contextKey(key, context)];\n if (contextual !== undefined) {\n return contextual;\n }\n }\n return translations[normalized] ?? normalized;\n },\n };\n }),\n withHooks({\n onInit(store): void {\n // Report the configured default language immediately, without loading.\n patchState(store, { currentLang: inject(TRANSLATE_CONFIG).defaultLang });\n },\n }),\n);\n","import { type EnvironmentProviders, makeEnvironmentProviders } from '@angular/core';\n\nimport { TRANSLATE_CONFIG, TRANSLATION_LOADER } from './translate.store';\nimport type { TranslateConfig } from './types';\n\n/**\n * Register the translation runtime for an application or a feature scope.\n *\n * Add the returned providers to `bootstrapApplication`'s `providers` (or a\n * route's `providers`). This only wires up configuration — it never starts a\n * load, so no HTTP is fired during DI initialization. Trigger the first load\n * explicitly via `TranslateStore.setLang`.\n *\n * The `loader` may be a ready-made loader object, or a factory `() => loader`\n * that is run inside the injection context — use the factory form for loaders\n * that inject Angular services (e.g. `createHttpLoader()`, which needs\n * `HttpClient`).\n *\n * @example\n * ```ts\n * // a static dictionary or fetch-based loader\n * provideTranslate({ defaultLang: 'en', loader: myLoader });\n *\n * // an HttpClient-backed loader (factory form)\n * provideTranslate({ defaultLang: 'en', loader: () => createHttpLoader() });\n * ```\n */\nexport function provideTranslate(config: TranslateConfig): EnvironmentProviders {\n const { loader } = config;\n return makeEnvironmentProviders([\n { provide: TRANSLATE_CONFIG, useValue: config },\n typeof loader === 'function'\n ? { provide: TRANSLATION_LOADER, useFactory: loader }\n : { provide: TRANSLATION_LOADER, useValue: loader },\n ]);\n}\n","/**\n * A node in a parsed slot tree produced by {@link parseSlots}.\n *\n * - `text` nodes carry literal, translator-authored text. They are rendered as\n * DOM text nodes — never as HTML — which is how the library keeps its XSS\n * surface at zero by construction.\n * - `slot` nodes correspond to a `[name]...[/name]` region that the developer\n * fills with an `<ng-template>`. The bracket syntax resembles BBCode, but the\n * names are arbitrary and author-chosen and carry no predefined rendering —\n * each is a named slot bound to a template. Slots may nest.\n */\nexport type SlotNode =\n | { readonly kind: 'text'; readonly value: string }\n | {\n readonly kind: 'slot';\n readonly name: string;\n readonly children: readonly SlotNode[];\n };\n\n/**\n * Matches a single slot tag at the start of a string: an optional leading slash\n * (closing tag) followed by a name. The name grammar\n * (`[a-zA-Z_][a-zA-Z0-9_-]*`) is part of the public contract — see CLAUDE.md\n * §5.1. Changing it is a major version bump.\n *\n * A literal `[` is written as `[[`; that escape is handled in {@link tokenize}\n * before this pattern is tried, so a tag is never matched across an escape.\n */\nconst TAG_PATTERN = /^\\[(\\/?)([a-zA-Z_][a-zA-Z0-9_-]*)\\]/;\n\ntype Token =\n | { readonly kind: 'text'; readonly value: string }\n | { readonly kind: 'open'; readonly name: string }\n | { readonly kind: 'close'; readonly name: string };\n\nfunction tokenize(input: string): readonly Token[] {\n const tokens: Token[] = [];\n let buffer = '';\n\n const flush = (): void => {\n if (buffer.length > 0) {\n tokens.push({ kind: 'text', value: buffer });\n buffer = '';\n }\n };\n\n let index = 0;\n while (index < input.length) {\n if (input[index] === '[') {\n // `[[` is an escaped literal `[` — the one way a translatable string can\n // contain text that looks like a slot tag (e.g. `[note]`) without it being\n // parsed as one. Checked before TAG_PATTERN so the escape always wins; a\n // lone `]` needs no escape, since it is never significant on its own.\n if (input[index + 1] === '[') {\n buffer += '[';\n index += 2;\n continue;\n }\n const match = TAG_PATTERN.exec(input.slice(index));\n if (match) {\n // Defaults satisfy `noUncheckedIndexedAccess` without non-null `!`.\n const [whole, slash = '', name = ''] = match;\n flush();\n tokens.push(slash === '/' ? { kind: 'close', name } : { kind: 'open', name });\n index += whole.length;\n continue;\n }\n }\n // A `[` that does not start a valid tag is ordinary text — malformed input\n // is tolerated rather than throwing, so a stray bracket never breaks a page.\n buffer += input[index];\n index += 1;\n }\n flush();\n return tokens;\n}\n\ninterface OpenFrame {\n readonly name: string;\n readonly children: SlotNode[];\n}\n\nfunction appendText(into: SlotNode[], value: string): void {\n const last = into[into.length - 1];\n if (last && last.kind === 'text') {\n into[into.length - 1] = { kind: 'text', value: last.value + value };\n } else {\n into.push({ kind: 'text', value });\n }\n}\n\n/**\n * Parse a translator-authored string into a tree of {@link SlotNode}s.\n *\n * The parser is total: every input produces a tree, and any malformed\n * construct (a stray `[`, an unclosed tag, a mismatched closing tag) degrades\n * gracefully to literal text rather than throwing. Well-formed\n * `[name]...[/name]` regions — including nested ones — become `slot` nodes. To\n * include a literal `[` (so text such as `[note]` is not read as a tag), double\n * it: `[[` parses to a single `[`.\n *\n * @example\n * ```ts\n * parseSlots('Hello [b]world[/b]!');\n * // [\n * // { kind: 'text', value: 'Hello ' },\n * // { kind: 'slot', name: 'b', children: [{ kind: 'text', value: 'world' }] },\n * // { kind: 'text', value: '!' },\n * // ]\n *\n * parseSlots('See [[b] for bold');\n * // [{ kind: 'text', value: 'See [b] for bold' }]\n * ```\n */\nexport function parseSlots(input: string): readonly SlotNode[] {\n const root: SlotNode[] = [];\n const stack: OpenFrame[] = [];\n\n const current = (): SlotNode[] => {\n const top = stack.at(-1);\n return top ? top.children : root;\n };\n\n for (const token of tokenize(input)) {\n if (token.kind === 'text') {\n appendText(current(), token.value);\n continue;\n }\n if (token.kind === 'open') {\n stack.push({ name: token.name, children: [] });\n continue;\n }\n // Closing tag.\n const top = stack.at(-1);\n if (top && top.name === token.name) {\n stack.pop();\n current().push({ kind: 'slot', name: top.name, children: top.children });\n } else {\n // No matching open tag: treat the closing tag as literal text.\n appendText(current(), `[/${token.name}]`);\n }\n }\n\n // Unwind any tags left open at end of input, back into literal text so their\n // contents are still rendered.\n for (let frame = stack.pop(); frame !== undefined; frame = stack.pop()) {\n const parent = current();\n appendText(parent, `[${frame.name}]`);\n for (const child of frame.children) {\n if (child.kind === 'text') {\n appendText(parent, child.value);\n } else {\n parent.push(child);\n }\n }\n }\n\n return root;\n}\n\nfunction collectText(nodes: readonly SlotNode[]): string {\n let text = '';\n for (const node of nodes) {\n text += node.kind === 'text' ? node.value : collectText(node.children);\n }\n return text;\n}\n\n/**\n * Flatten a slot string to plain text: slot tags are dropped and their inner\n * text is kept. This is how the `t` pipe and {@link injectTranslate} render a\n * message containing `[name]...[/name]` slots — they return a string, so the\n * markup degrades to text (the `[t]` directive renders the slots into templates\n * instead).\n *\n * @example\n * ```ts\n * slotsToText('Read the [docs]documentation[/docs] now'); // 'Read the documentation now'\n * slotsToText('Press [[Enter] to send'); // 'Press [Enter] to send'\n * ```\n */\nexport function slotsToText(input: string): string {\n return collectText(parseSlots(input));\n}\n","import { InjectionToken } from '@angular/core';\n\n/**\n * Formats a resolved message against runtime arguments — the seam that lets the\n * `translate` pipe render ICU messages without `core` depending on any ICU\n * library. `@ng-linguo/linguo/icu`'s `provideIcu` supplies an implementation; when none\n * is provided the pipe simply returns the message unformatted.\n */\nexport interface MessageFormatter {\n /**\n * @param message The resolved (translated) message, possibly containing ICU\n * placeholders.\n * @param args Named arguments to substitute (counts, names, dates…).\n * @param locale BCP-47 locale for plural rules and number/date formatting.\n */\n format(message: string, args: Record<string, unknown>, locale: string): string;\n}\n\n/**\n * DI token for an optional {@link MessageFormatter}. Provided by\n * `@ng-linguo/linguo/icu`'s `provideIcu`; absent by default.\n */\nexport const MESSAGE_FORMATTER = new InjectionToken<MessageFormatter>(\n 'ng-linguo.message-formatter',\n);\n","import { inject } from '@angular/core';\n\nimport { slotsToText } from './slot-parser';\nimport { MESSAGE_FORMATTER } from './message-formatter';\nimport { TranslateStore } from './translate.store';\nimport type { TranslateOptions } from './types';\n\n/**\n * Resolves a key to its translation for the current language, with optional ICU\n * `params` and `context` — the function form of the `t` pipe.\n */\nexport type TranslateFn = (key: string, options?: TranslateOptions) => string;\n\n/**\n * Obtain a {@link TranslateFn} for use in component code — the TypeScript\n * counterpart of the `t` pipe.\n *\n * Call it in an injection context (a field initializer or constructor); the\n * returned `t` reads the store's signals each time it runs, so it stays reactive\n * when used in a template binding or inside a `computed`.\n *\n * @example\n * ```ts\n * export class Greeting {\n * private readonly t = injectTranslate();\n * protected readonly name = signal('Ada');\n * protected readonly text = computed(() =>\n * this.t('Hello {$name}!', { params: { name: this.name() } }),\n * );\n * }\n * ```\n */\nexport function injectTranslate(): TranslateFn {\n const store = inject(TranslateStore);\n const formatter = inject(MESSAGE_FORMATTER, { optional: true });\n return (key, options) => {\n let message = store.translate(key, options?.context);\n if (options?.params && formatter) {\n message = formatter.format(message, options.params, store.currentLang() || 'en');\n }\n // Returns a string, so slot tags degrade to their inner text; the `[t]`\n // directive renders them into templates instead.\n return slotsToText(message);\n };\n}\n","import { Pipe, inject, type PipeTransform } from '@angular/core';\n\nimport { injectTranslate, type TranslateFn } from './translate-fn';\nimport { TranslateStore } from './translate.store';\nimport type { TranslateOptions, Translations } from './types';\n\n/** Shallow value-equality for `params`: same keys, each value strictly equal. */\nfunction paramsEqual(\n a: Record<string, unknown> | undefined,\n b: Record<string, unknown> | undefined,\n): boolean {\n if (a === b) return true;\n if (a === undefined || b === undefined) return false;\n const keys = Object.keys(a);\n return keys.length === Object.keys(b).length && keys.every((k) => a[k] === b[k]);\n}\n\ninterface PipeCache {\n readonly key: string;\n readonly context: string | undefined;\n readonly params: Record<string, unknown> | undefined;\n readonly version: Translations;\n readonly result: string;\n}\n\n/**\n * Resolve a translation key to its string for the current language, with options\n * passed as a single object: ICU `params` and a disambiguating `context`.\n *\n * @example\n * ```html\n * {{ 'Play' | t }}\n * {{ 'Play' | t: { context: 'game' } }}\n * {{ 'Hello {$name}!' | t: { params: { name } } }}\n * ```\n *\n * The key is the source text; a missing key renders as itself. ICU `params` are\n * applied by the formatter from `@ng-linguo/linguo/icu` (`provideIcu`); without\n * it the message is returned unformatted.\n *\n * Impure so it re-evaluates against the store's signals each change detection,\n * re-rendering when the language changes. To keep that cheap it **memoizes** by\n * value: the lookup/format only re-runs when the key, `context`, `params`\n * contents, or the active language actually change — so a fresh `{ params: … }`\n * literal on every change-detection pass costs only an equality check. For hot\n * or looped bindings, prefer `injectTranslate()` inside a `computed()`, which\n * does zero work per change-detection pass.\n */\n@Pipe({ name: 't', pure: false })\nexport class TranslatePipe implements PipeTransform {\n private readonly t: TranslateFn = injectTranslate();\n // The translations signal's value reference changes on every `setLang`, so it\n // doubles as the \"language version\" for cache invalidation.\n private readonly translations = inject(TranslateStore).translations;\n private cache: PipeCache | undefined;\n\n transform(key: string, options?: TranslateOptions): string {\n const version = this.translations();\n const context = options?.context;\n const params = options?.params;\n\n const cached = this.cache;\n if (\n cached !== undefined &&\n cached.key === key &&\n cached.context === context &&\n cached.version === version &&\n paramsEqual(cached.params, params)\n ) {\n return cached.result;\n }\n\n const result = this.t(key, options);\n this.cache = { key, context, params, version, result };\n return result;\n }\n}\n","/**\n * Options for {@link mark}. Only `context` is read (by `@ng-linguo/extract`);\n * `params` is accepted so a single options object can be shared with the `t`\n * pipe / directive call that ultimately renders the message.\n */\nexport interface MarkOptions {\n /**\n * Disambiguating/contextual text recorded as the catalog `msgctxt`. It is part\n * of the key, so it must match the `context` passed at the consuming\n * pipe/directive for the runtime lookup to resolve. It doubles as a note for\n * translators (e.g. `'file = a document on disk'`).\n */\n readonly context?: string;\n}\n\n/**\n * Mark a string as a translatable message so `@ng-linguo/extract` collects it,\n * returning the string unchanged (it does not translate at runtime).\n *\n * Use it for messages that are not written inline at a `t` pipe or `[t]`\n * directive — for example an ICU message kept in a component field (an MF2\n * pattern contains `{{ … }}`, which collides with Angular's `{{ }}` binding).\n * The marked string is still translated at render time by the pipe/directive\n * that consumes it.\n *\n * Pass `context` to record a `msgctxt`/translator note for the entry. Because\n * context is part of the key, the **same** `context` must be supplied where the\n * marked string is rendered (`| t: { context }` or `tContext`), or the runtime\n * lookup will not find the contextual entry.\n *\n * @example\n * ```ts\n * readonly fileCount = mark(\n * '.input {$count :number} .match $count one {{{$count} file}} * {{{$count} files}}',\n * { context: 'file = a document on disk' },\n * );\n * // template: {{ fileCount | t: { params: { count: count() }, context: 'file = a document on disk' } }}\n * ```\n */\nexport function mark(message: string, options?: MarkOptions): string {\n // `options` is read only by the extractor (statically); at runtime it is a\n // no-op, so the parameter is intentionally unused here.\n void options;\n return message;\n}\n","import type { EmbeddedViewRef, Renderer2, TemplateRef, ViewContainerRef } from '@angular/core';\n\nimport type { SlotNode } from './slot-parser';\n\n/**\n * Context handed to a slot's `<ng-template tFor>`:\n *\n * - `$implicit` — the slot's inner text, flattened to a string. `let-text` binds\n * it, so `{{ text }}` renders a plain-text slot (the common case).\n * - `children` — the slot's parsed child nodes, for binding *nested* slots to\n * their own templates with `*tRender` instead of flattening them to text. Bind\n * it with `let-kids=\"children\"`. See `TranslateSlotOutlet`.\n */\nexport interface TranslateSlotContext {\n readonly $implicit: string;\n readonly children: readonly SlotNode[];\n}\n\n/** Map from a slot name to the `<ng-template>` that renders it. */\nexport type SlotTemplates = ReadonlyMap<string, TemplateRef<TranslateSlotContext>>;\n\n/** Flatten a slot subtree to its concatenated text, dropping the tags. */\nfunction collectText(nodes: readonly SlotNode[]): string {\n let text = '';\n for (const node of nodes) {\n text += node.kind === 'text' ? node.value : collectText(node.children);\n }\n return text;\n}\n\n/**\n * Renders a parsed slot tree into the DOM as text nodes and embedded\n * `<ng-template>` views — never as HTML (CLAUDE.md §5.1).\n *\n * Owns the views and host-level text nodes it creates so a re-render (a language\n * or params change) can tear them down. A single instance is shared by a `[t]`\n * element and every `*tRender` outlet nested inside it: views are tracked flat\n * and destroyed together, so a re-render is a clean teardown-then-rebuild\n * regardless of nesting depth.\n */\nexport class SlotRenderer {\n private views: EmbeddedViewRef<TranslateSlotContext>[] = [];\n private hostNodes: Node[] = [];\n\n constructor(\n private readonly renderer: Renderer2,\n private readonly viewContainer: ViewContainerRef,\n ) {}\n\n /** Destroy every view and remove every host-level text node from the last render. */\n clear(): void {\n for (const view of this.views) {\n view.destroy();\n }\n this.views = [];\n for (const node of this.hostNodes) {\n // Nodes placed inside an embedded view are gone with their view; only the\n // ones appended straight onto the persistent host element remain here.\n if (node.parentNode) {\n this.renderer.removeChild(node.parentNode, node);\n }\n }\n this.hostNodes = [];\n }\n\n /**\n * Render `nodes` into `parent`, before `before` (or appended when `before` is\n * `null`). `trackHost` is `true` only for nodes placed directly on the `[t]`\n * host element — those text nodes outlive any single view and must be removed\n * explicitly on the next render; nodes inside an embedded view ride along when\n * that view is destroyed, so they are not tracked.\n */\n render(\n nodes: readonly SlotNode[],\n parent: Node,\n before: Node | null,\n templates: SlotTemplates,\n trackHost: boolean,\n ): void {\n for (const node of nodes) {\n if (node.kind === 'text') {\n const text = this.renderer.createText(node.value);\n this.insert(parent, text, before);\n if (trackHost) {\n this.hostNodes.push(text);\n }\n continue;\n }\n\n const template = templates.get(node.name);\n if (!template) {\n // No matching template: render the slot's children inline (transparent),\n // so a nested *matched* slot still renders and text-only content reads\n // the same as a plain flatten.\n this.render(node.children, parent, before, templates, trackHost);\n continue;\n }\n\n const view = this.viewContainer.createEmbeddedView(template, {\n $implicit: collectText(node.children),\n children: node.children,\n });\n this.views.push(view);\n // Running the view's bindings instantiates any `*tRender` outlet inside it,\n // which calls back to render this slot's children into that outlet — the\n // nesting recursion happens here, on the call stack.\n view.detectChanges();\n for (const rootNode of view.rootNodes as Node[]) {\n this.insert(parent, rootNode, before);\n }\n }\n }\n\n private insert(parent: Node, node: Node, before: Node | null): void {\n if (before) {\n this.renderer.insertBefore(parent, node, before);\n } else {\n this.renderer.appendChild(parent, node);\n }\n }\n}\n","import {\n Directive,\n ElementRef,\n Renderer2,\n TemplateRef,\n ViewContainerRef,\n contentChildren,\n effect,\n inject,\n input,\n type OnInit,\n} from '@angular/core';\n\nimport { MESSAGE_FORMATTER } from './message-formatter';\nimport { parseSlots, type SlotNode } from './slot-parser';\nimport { SlotRenderer, type SlotTemplates, type TranslateSlotContext } from './slot-renderer';\nimport { TranslateStore } from './translate.store';\n\nexport type { TranslateSlotContext } from './slot-renderer';\n\n/**\n * Provides an `<ng-template>` as the renderer for a named slot of the enclosing\n * `[t]` element. The `[name]...[/name]` bracket syntax resembles BBCode, but the\n * name is arbitrary and author-chosen — it identifies a slot to fill, not a tag\n * with predefined HTML. Declared as a child of the `[t]` element (it renders as\n * an invisible anchor), so the name is scoped to that element. The template\n * receives the slot's inner text (`let-text`) and parsed children\n * (`let-kids=\"children\"`, for nesting via `*tRender`) as its context.\n *\n * @example\n * ```html\n * <ng-template tFor=\"docs\" let-text>\n * <a routerLink=\"/docs\">{{ text }}</a>\n * </ng-template>\n * ```\n */\n@Directive({ selector: 'ng-template[tFor]' })\nexport class TranslateSlot {\n /** Slot name, matching `[name]...[/name]` in the translation. */\n readonly name = input.required<string>({ alias: 'tFor' });\n readonly templateRef = inject<TemplateRef<TranslateSlotContext>>(TemplateRef);\n}\n\n/**\n * Translate the `t` message and render it into the element, applying ICU `tParams`\n * and binding any `[name]...[/name]` slot regions to `<ng-template tFor>`\n * children.\n *\n * The message is the `t` attribute (a string expression), so it may contain both\n * ICU (`{$name}`) and slot tags (`[name]`) safely. Rendered text is emitted as DOM\n * text nodes, never HTML (CLAUDE.md §5.1); a slot with no matching template\n * degrades to its inner text. Nested slots bind to their own templates when the\n * parent template marks where they go with `*tRender` (see\n * `TranslateSlotOutlet`); otherwise a nested slot renders as text. Re-renders\n * when the language, params, or a slot template changes.\n *\n * @example\n * ```html\n * <p t=\"Hello {$name}!\" [tParams]=\"{ name }\"></p>\n *\n * <p t=\"Read the [docs]documentation[/docs] to get started\">\n * <ng-template tFor=\"docs\" let-text>\n * <a routerLink=\"/docs\">{{ text }}</a>\n * </ng-template>\n * </p>\n * ```\n */\n@Directive({ selector: '[t]' })\nexport class TranslateDirective implements OnInit {\n private readonly store = inject(TranslateStore);\n private readonly host = inject<ElementRef<Element>>(ElementRef).nativeElement;\n private readonly renderer = inject(Renderer2);\n private readonly formatter = inject(MESSAGE_FORMATTER, { optional: true });\n private readonly slotRenderer = new SlotRenderer(this.renderer, inject(ViewContainerRef));\n\n /** Source message (the translation key); may contain ICU and slot tags. */\n readonly message = input.required<string>({ alias: 't' });\n /** ICU arguments, formatted via `@ng-linguo/linguo/icu` when provided. */\n readonly tParams = input<Record<string, unknown> | undefined>(undefined);\n /** Optional disambiguating/contextual text (part of the key). */\n readonly tContext = input<string>('');\n\n private readonly slots = contentChildren(TranslateSlot, { descendants: true });\n private templates: SlotTemplates = new Map();\n\n constructor() {\n effect(() => {\n const templates = new Map<string, TemplateRef<TranslateSlotContext>>();\n for (const slot of this.slots()) {\n templates.set(slot.name(), slot.templateRef);\n }\n this.templates = templates;\n\n let text = this.store.translate(this.message(), this.tContext());\n const params = this.tParams();\n if (params && this.formatter) {\n text = this.formatter.format(text, params, this.store.currentLang() || 'en');\n }\n\n this.slotRenderer.clear();\n this.slotRenderer.render(parseSlots(text), this.host, null, templates, true);\n });\n }\n\n ngOnInit(): void {\n // Remove any authored whitespace text so the rendered translation is not\n // preceded by stray spacing. Slot `<ng-template>` anchors (comments) are\n // left intact for the content query.\n for (const node of Array.from(this.host.childNodes)) {\n if (node.nodeType === 3 /* text node */) {\n this.renderer.removeChild(this.host, node);\n }\n }\n }\n\n /**\n * Render a nested slot's `nodes` into `parent` before `before`, reusing the\n * current templates and the shared renderer. Internal: called by\n * `TranslateSlotOutlet` (`*tRender`); not part of the consumer-facing API.\n */\n renderChildren(parent: Node, before: Node, nodes: readonly SlotNode[]): void {\n this.slotRenderer.render(nodes, parent, before, this.templates, false);\n }\n}\n","import { Directive, ViewContainerRef, inject, input, type OnInit } from '@angular/core';\n\nimport type { SlotNode } from './slot-parser';\nimport { TranslateDirective } from './translate.directive';\n\n/**\n * Renders the *children* of a slot at this position, so a nested slot binds to\n * its own `<ng-template>` instead of flattening to text. Without it, a slot\n * inside a templated slot renders as plain text (its `$implicit` value); with\n * it, each nested slot finds its own `tFor` template.\n *\n * Bind the `children` from the enclosing slot's context and place the outlet\n * where the nested content should appear:\n *\n * @example\n * ```html\n * <p t=\"Agree to our [link]Terms and [b]Conditions[/b][/link]\">\n * <ng-template tFor=\"link\" let-kids=\"children\">\n * <a href=\"/terms\"><ng-container *tRender=\"kids\" /></a>\n * </ng-template>\n * <ng-template tFor=\"b\" let-text><strong>{{ text }}</strong></ng-template>\n * </p>\n * ```\n *\n * Valid only inside a `[t]` element's slot template — it resolves the enclosing\n * {@link TranslateDirective} and renders through its shared engine, so nested\n * views are torn down with the rest on a language or params change.\n */\n@Directive({ selector: '[tRender]' })\nexport class TranslateSlotOutlet implements OnInit {\n /** The nodes to render — the `children` value from the slot context. */\n readonly nodes = input.required<readonly SlotNode[]>({ alias: 'tRender' });\n\n private readonly host = inject(TranslateDirective);\n // The outlet sits on an `<ng-container>`, whose anchor is a comment node; its\n // parent is the element the children belong in, and we render before it.\n private readonly anchor = inject(ViewContainerRef).element.nativeElement as Node;\n\n ngOnInit(): void {\n const parent = this.anchor.parentNode;\n if (parent) {\n this.host.renderChildren(parent, this.anchor, this.nodes());\n }\n }\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":["collectText"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ng-linguo/linguo",
3
- "version": "0.9.3",
3
+ "version": "0.9.5",
4
4
  "description": "A modern, signal-first i18n runtime for Angular 18+ — built on SignalStore, with a translator-safe slot syntax, ICU, and tree-shakeable HTTP loading.",
5
5
  "license": "MIT",
6
6
  "author": "jmwierzbicki",