@vielzeug/lingua 26.11.13 → 26.11.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/i18n.cjs CHANGED
@@ -1,2 +1,2 @@
1
- require("./_dev.cjs");const e=require("./_locale.cjs"),t=require("./_resources.cjs"),n=require("./errors.cjs"),r=require("./translator.cjs");function createI18n(i){let a=(Array.isArray(i.fallback)?i.fallback:i.fallback?[i.fallback]:[]).map(e.canonicalLocale),o=new AbortController,s=new Set,c=!1,l=e.canonicalLocale(i.locale??`en`),u=0,d=t.createCatalogStore({catalogs:i.catalogs,loadCatalog:i.loadCatalog}),loadTargets=async e=>{let t=[];for(let n of new Set([e,...a]))await d.load(n)&&t.push(n);return t},buildEngine=()=>{let t=e.localeChain(l,a).some(d.isLoaded)?i.missing:()=>{throw new n.LinguaMissingCatalogError(`No catalog loaded for locale "${l}". Call load() before translating.`)};return r.createTranslateEngine(d.catalogMap(),{...i,locale:l,missing:t})},assertLive=()=>{if(c)throw new n.LinguaDisposedError},dispose=()=>{c||(c=!0,s.clear(),o.abort())},notify=()=>{f=buildEngine();for(let e of[...s])try{e(l)}catch{}},relevant=t=>e.localeChain(l,a).includes(t),f=buildEngine();return{get disposalSignal(){return o.signal},dispose,get disposed(){return c},getSnapshot(){return l},isLoaded(e){return!c&&d.isLoaded(e?.locale??l)},async load(t){assertLive();let n=e.canonicalLocale(t?.locale??l),r=await loadTargets(n);assertLive(),r.some(relevant)&&notify()},get locale(){return l},serialize(){return assertLive(),d.serialized(l)},async setLocale(t){assertLive();let n=e.canonicalLocale(t),r=++u,i=await loadTargets(n);assertLive(),r===u&&(n===l?i.some(relevant)&&notify():(l=n,notify()))},subscribe(e,t){if(assertLive(),t?.signal?.aborted)return()=>{};let unsubscribe=()=>{s.delete(e),t?.signal?.removeEventListener(`abort`,unsubscribe)};if(t?.signal?.addEventListener(`abort`,unsubscribe,{once:!0}),s.add(e),t?.immediate)try{e(l)}catch{}return unsubscribe},translate:(e,t)=>f.translate(e,t),[Symbol.dispose]:dispose}}exports.createI18n=createI18n;
1
+ require("./_dev.cjs");const e=require("./_locale.cjs"),t=require("./_resources.cjs"),n=require("./errors.cjs"),r=require("./translator.cjs");function createI18n(i){let a=(Array.isArray(i.fallback)?i.fallback:i.fallback?[i.fallback]:[]).map(e.canonicalLocale),o=new AbortController,s=new Set,c=!1,l=e.canonicalLocale(i.locale??`en`),u=0,d=t.createCatalogStore({catalogs:i.catalogs,loadCatalog:i.loadCatalog}),loadTargets=async e=>{let t=[];for(let n of new Set([e,...a]))await d.load(n)&&t.push(n);return t},buildEngine=()=>{let t=e.localeChain(l,a).some(d.isLoaded)?i.missing:()=>{throw new n.LinguaMissingCatalogError(`No catalog loaded for locale "${l}". Call load() before translating.`)};return r.createTranslateEngine(d.catalogMap(),{...i,locale:l,missing:t})},assertLive=()=>{if(c)throw new n.LinguaDisposedError},dispose=()=>{c||(c=!0,s.clear(),o.abort())},notify=()=>{f=buildEngine();for(let e of[...s])try{e(l)}catch{}},relevant=t=>e.localeChain(l,a).includes(t),f=buildEngine();return{get disposalSignal(){return o.signal},dispose,get disposed(){return c},getSnapshot(){return l},isLoaded(e){return!c&&d.isLoaded(e?.locale??l)},async load(t){assertLive();let n=e.canonicalLocale(t?.locale??l),r=await loadTargets(n);assertLive(),r.some(relevant)&&notify()},get locale(){return l},serialize(){return assertLive(),d.serialized(l)},async setLocale(t){assertLive();let n=e.canonicalLocale(t),r=++u,i=await loadTargets(n);assertLive(),r===u&&(n===l?i.some(relevant)&&notify():(l=n,notify()))},subscribe(e,t){if(assertLive(),t?.signal?.aborted)return()=>{};let unsubscribe=()=>{s.delete(e),t?.signal?.removeEventListener(`abort`,unsubscribe)};if(t?.signal?.addEventListener(`abort`,unsubscribe,{once:!0}),s.add(e),t?.immediate)try{e(l)}catch{}return unsubscribe},translate:(e,t)=>f.translate(e,t),translateUnkeyed:(e,t)=>f.translate(e,t),[Symbol.dispose]:dispose}}exports.createI18n=createI18n;
2
2
  //# sourceMappingURL=i18n.cjs.map
package/dist/i18n.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.cjs","names":["canonicalLocale","createCatalogStore","localeChain","LinguaMissingCatalogError","createTranslateEngine","LinguaDisposedError","error","require__locale","require__resources","require_errors","require_translator","fallbackLocales","options","controller","subscribers","disposed","locale","transition","catalogs","changed","candidate","target","effectiveMissing","listener","engine","loadOptions","targetLocale","next","nextLocale","request","subscribeOptions","key","translateOptions"],"sources":["../src/i18n.ts"],"sourcesContent":["import { error as logError } from './_dev';\nimport { canonicalLocale, localeChain } from './_locale';\nimport { createCatalogStore } from './_resources';\nimport { LinguaDisposedError, LinguaMissingCatalogError } from './errors';\nimport { createTranslateEngine, type Translator } from './translator';\nimport type {\n Catalog,\n I18nOptions,\n Locale,\n PluralOptions,\n SerializedI18n,\n SubscribeOptions,\n TranslateOptions,\n} from './types';\n\nexport type I18n<C extends Catalog = Catalog> = Translator<C> & {\n [Symbol.dispose](): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n /** Current locale. Referentially stable until the locale changes, so frameworks can use it directly as a store snapshot. */\n getSnapshot(): Locale;\n isLoaded(options?: { locale?: Locale }): boolean;\n load(options?: { locale?: Locale }): Promise<void>;\n serialize(): SerializedI18n<C>;\n setLocale(locale: Locale): Promise<void>;\n subscribe(listener: (locale: Locale) => void, options?: SubscribeOptions): () => void;\n};\n\nexport function createI18n<C extends Catalog>(options: I18nOptions<C>): I18n<C> {\n const fallbackLocales = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const controller = new AbortController();\n const subscribers = new Set<(locale: Locale) => void>();\n let disposed = false;\n let locale = canonicalLocale(options.locale ?? 'en');\n let transition = 0;\n\n const catalogs = createCatalogStore<C>({ catalogs: options.catalogs, loadCatalog: options.loadCatalog });\n\n const loadTargets = async (target: Locale): Promise<readonly Locale[]> => {\n const changed: Locale[] = [];\n\n for (const candidate of new Set([target, ...fallbackLocales])) {\n if (await catalogs.load(candidate)) changed.push(candidate);\n }\n\n return changed;\n };\n\n const buildEngine = () => {\n const ready = localeChain(locale, fallbackLocales).some(catalogs.isLoaded);\n const effectiveMissing = ready\n ? options.missing\n : (): never => {\n throw new LinguaMissingCatalogError(\n `No catalog loaded for locale \"${locale}\". Call load() before translating.`,\n );\n };\n\n return createTranslateEngine(catalogs.catalogMap(), { ...options, locale, missing: effectiveMissing });\n };\n\n const assertLive = (): void => {\n if (disposed) throw new LinguaDisposedError();\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n subscribers.clear();\n controller.abort();\n };\n\n const notify = (): void => {\n engine = buildEngine();\n\n for (const listener of [...subscribers]) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n };\n\n const relevant = (candidate: Locale): boolean => localeChain(locale, fallbackLocales).includes(candidate);\n\n let engine = buildEngine();\n\n const instance: I18n<C> = {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n getSnapshot() {\n return locale;\n },\n isLoaded(loadOptions) {\n return !disposed && catalogs.isLoaded(loadOptions?.locale ?? locale);\n },\n async load(loadOptions) {\n assertLive();\n\n const targetLocale = canonicalLocale(loadOptions?.locale ?? locale);\n const changed = await loadTargets(targetLocale);\n\n assertLive();\n if (changed.some(relevant)) notify();\n },\n get locale() {\n return locale;\n },\n serialize() {\n assertLive();\n\n return catalogs.serialized(locale);\n },\n async setLocale(nextLocale) {\n assertLive();\n\n const next = canonicalLocale(nextLocale);\n const request = ++transition;\n const changed = await loadTargets(next);\n\n assertLive();\n if (request !== transition) return;\n\n if (next === locale) {\n if (changed.some(relevant)) notify();\n\n return;\n }\n\n locale = next;\n notify();\n },\n subscribe(listener, subscribeOptions) {\n assertLive();\n\n if (subscribeOptions?.signal?.aborted) return () => {};\n\n const unsubscribe = (): void => {\n subscribers.delete(listener);\n subscribeOptions?.signal?.removeEventListener('abort', unsubscribe);\n };\n\n subscribeOptions?.signal?.addEventListener('abort', unsubscribe, { once: true });\n subscribers.add(listener);\n\n if (subscribeOptions?.immediate) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n\n return unsubscribe;\n },\n translate: (key: string, translateOptions?: TranslateOptions | PluralOptions): string =>\n engine.translate(key, translateOptions),\n [Symbol.dispose]: dispose,\n };\n\n return instance;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"i18n.cjs","names":["canonicalLocale","createCatalogStore","localeChain","LinguaMissingCatalogError","createTranslateEngine","LinguaDisposedError","error","require__locale","require__resources","require_errors","require_translator","fallbackLocales","options","controller","subscribers","disposed","locale","transition","catalogs","changed","candidate","target","effectiveMissing","listener","engine","loadOptions","targetLocale","next","nextLocale","request","subscribeOptions","key","translateOptions"],"sources":["../src/i18n.ts"],"sourcesContent":["import { error as logError } from './_dev';\nimport { canonicalLocale, localeChain } from './_locale';\nimport { createCatalogStore } from './_resources';\nimport { LinguaDisposedError, LinguaMissingCatalogError } from './errors';\nimport { createTranslateEngine, type Translator } from './translator';\nimport type {\n Catalog,\n I18nOptions,\n Locale,\n PluralOptions,\n SerializedI18n,\n SubscribeOptions,\n TranslateOptions,\n} from './types';\n\nexport type I18n<C extends Catalog = Catalog> = Translator<C> & {\n [Symbol.dispose](): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n /** Current locale. Referentially stable until the locale changes, so frameworks can use it directly as a store snapshot. */\n getSnapshot(): Locale;\n isLoaded(options?: { locale?: Locale }): boolean;\n load(options?: { locale?: Locale }): Promise<void>;\n serialize(): SerializedI18n<C>;\n setLocale(locale: Locale): Promise<void>;\n subscribe(listener: (locale: Locale) => void, options?: SubscribeOptions): () => void;\n};\n\nexport function createI18n<C extends Catalog>(options: I18nOptions<C>): I18n<C> {\n const fallbackLocales = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const controller = new AbortController();\n const subscribers = new Set<(locale: Locale) => void>();\n let disposed = false;\n let locale = canonicalLocale(options.locale ?? 'en');\n let transition = 0;\n\n const catalogs = createCatalogStore<C>({ catalogs: options.catalogs, loadCatalog: options.loadCatalog });\n\n const loadTargets = async (target: Locale): Promise<readonly Locale[]> => {\n const changed: Locale[] = [];\n\n for (const candidate of new Set([target, ...fallbackLocales])) {\n if (await catalogs.load(candidate)) changed.push(candidate);\n }\n\n return changed;\n };\n\n const buildEngine = () => {\n const ready = localeChain(locale, fallbackLocales).some(catalogs.isLoaded);\n const effectiveMissing = ready\n ? options.missing\n : (): never => {\n throw new LinguaMissingCatalogError(\n `No catalog loaded for locale \"${locale}\". Call load() before translating.`,\n );\n };\n\n return createTranslateEngine(catalogs.catalogMap(), { ...options, locale, missing: effectiveMissing });\n };\n\n const assertLive = (): void => {\n if (disposed) throw new LinguaDisposedError();\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n subscribers.clear();\n controller.abort();\n };\n\n const notify = (): void => {\n engine = buildEngine();\n\n for (const listener of [...subscribers]) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n };\n\n const relevant = (candidate: Locale): boolean => localeChain(locale, fallbackLocales).includes(candidate);\n\n let engine = buildEngine();\n\n const instance: I18n<C> = {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n getSnapshot() {\n return locale;\n },\n isLoaded(loadOptions) {\n return !disposed && catalogs.isLoaded(loadOptions?.locale ?? locale);\n },\n async load(loadOptions) {\n assertLive();\n\n const targetLocale = canonicalLocale(loadOptions?.locale ?? locale);\n const changed = await loadTargets(targetLocale);\n\n assertLive();\n if (changed.some(relevant)) notify();\n },\n get locale() {\n return locale;\n },\n serialize() {\n assertLive();\n\n return catalogs.serialized(locale);\n },\n async setLocale(nextLocale) {\n assertLive();\n\n const next = canonicalLocale(nextLocale);\n const request = ++transition;\n const changed = await loadTargets(next);\n\n assertLive();\n if (request !== transition) return;\n\n if (next === locale) {\n if (changed.some(relevant)) notify();\n\n return;\n }\n\n locale = next;\n notify();\n },\n subscribe(listener, subscribeOptions) {\n assertLive();\n\n if (subscribeOptions?.signal?.aborted) return () => {};\n\n const unsubscribe = (): void => {\n subscribers.delete(listener);\n subscribeOptions?.signal?.removeEventListener('abort', unsubscribe);\n };\n\n subscribeOptions?.signal?.addEventListener('abort', unsubscribe, { once: true });\n subscribers.add(listener);\n\n if (subscribeOptions?.immediate) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n\n return unsubscribe;\n },\n translate: (key: string, translateOptions?: TranslateOptions | PluralOptions): string =>\n engine.translate(key, translateOptions),\n translateUnkeyed: (key: string, translateOptions?: TranslateOptions | PluralOptions): string =>\n engine.translate(key, translateOptions),\n [Symbol.dispose]: dispose,\n };\n\n return instance;\n}\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"AAIA,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,KAAK,EACV,OAAO,EACP,WAAW,EACX,MAAM,EAEN,cAAc,EACd,gBAAgB,EAEjB,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,IAAI,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,GAAG;IAC9D,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;IACzB,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC;IACrC,OAAO,IAAI,IAAI,CAAC;IAChB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,4HAA4H;IAC5H,WAAW,IAAI,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;IACjD,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,SAAS,IAAI,cAAc,CAAC,CAAC,CAAC,CAAC;IAC/B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,SAAS,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,IAAI,CAAC;CACvF,CAAC;AAEF,wBAAgB,UAAU,CAAC,CAAC,SAAS,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CA8I9E"}
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../src/i18n.ts"],"names":[],"mappings":"AAIA,OAAO,EAAyB,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,KAAK,EACV,OAAO,EACP,WAAW,EACX,MAAM,EAEN,cAAc,EACd,gBAAgB,EAEjB,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,IAAI,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI,UAAU,CAAC,CAAC,CAAC,GAAG;IAC9D,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;IACzB,QAAQ,CAAC,cAAc,EAAE,WAAW,CAAC;IACrC,OAAO,IAAI,IAAI,CAAC;IAChB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,4HAA4H;IAC5H,WAAW,IAAI,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC;IACjD,IAAI,CAAC,OAAO,CAAC,EAAE;QAAE,MAAM,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnD,SAAS,IAAI,cAAc,CAAC,CAAC,CAAC,CAAC;IAC/B,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,SAAS,CAAC,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,IAAI,CAAC;CACvF,CAAC;AAEF,wBAAgB,UAAU,CAAC,CAAC,SAAS,OAAO,EAAE,OAAO,EAAE,WAAW,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC,CAgJ9E"}
package/dist/i18n.js CHANGED
@@ -72,6 +72,7 @@ function createI18n(o) {
72
72
  return unsubscribe;
73
73
  },
74
74
  translate: (e, t) => m.translate(e, t),
75
+ translateUnkeyed: (e, t) => m.translate(e, t),
75
76
  [Symbol.dispose]: dispose
76
77
  };
77
78
  }
package/dist/i18n.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.js","names":["error","fallbackLocales","options","canonicalLocale","controller","subscribers","disposed","locale","transition","catalogs","createCatalogStore","changed","candidate","target","effectiveMissing","localeChain","LinguaMissingCatalogError","createTranslateEngine","LinguaDisposedError","listener","engine","loadOptions","targetLocale","next","nextLocale","request","subscribeOptions","key","translateOptions"],"sources":["../src/i18n.ts"],"sourcesContent":["import { error as logError } from './_dev';\nimport { canonicalLocale, localeChain } from './_locale';\nimport { createCatalogStore } from './_resources';\nimport { LinguaDisposedError, LinguaMissingCatalogError } from './errors';\nimport { createTranslateEngine, type Translator } from './translator';\nimport type {\n Catalog,\n I18nOptions,\n Locale,\n PluralOptions,\n SerializedI18n,\n SubscribeOptions,\n TranslateOptions,\n} from './types';\n\nexport type I18n<C extends Catalog = Catalog> = Translator<C> & {\n [Symbol.dispose](): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n /** Current locale. Referentially stable until the locale changes, so frameworks can use it directly as a store snapshot. */\n getSnapshot(): Locale;\n isLoaded(options?: { locale?: Locale }): boolean;\n load(options?: { locale?: Locale }): Promise<void>;\n serialize(): SerializedI18n<C>;\n setLocale(locale: Locale): Promise<void>;\n subscribe(listener: (locale: Locale) => void, options?: SubscribeOptions): () => void;\n};\n\nexport function createI18n<C extends Catalog>(options: I18nOptions<C>): I18n<C> {\n const fallbackLocales = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const controller = new AbortController();\n const subscribers = new Set<(locale: Locale) => void>();\n let disposed = false;\n let locale = canonicalLocale(options.locale ?? 'en');\n let transition = 0;\n\n const catalogs = createCatalogStore<C>({ catalogs: options.catalogs, loadCatalog: options.loadCatalog });\n\n const loadTargets = async (target: Locale): Promise<readonly Locale[]> => {\n const changed: Locale[] = [];\n\n for (const candidate of new Set([target, ...fallbackLocales])) {\n if (await catalogs.load(candidate)) changed.push(candidate);\n }\n\n return changed;\n };\n\n const buildEngine = () => {\n const ready = localeChain(locale, fallbackLocales).some(catalogs.isLoaded);\n const effectiveMissing = ready\n ? options.missing\n : (): never => {\n throw new LinguaMissingCatalogError(\n `No catalog loaded for locale \"${locale}\". Call load() before translating.`,\n );\n };\n\n return createTranslateEngine(catalogs.catalogMap(), { ...options, locale, missing: effectiveMissing });\n };\n\n const assertLive = (): void => {\n if (disposed) throw new LinguaDisposedError();\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n subscribers.clear();\n controller.abort();\n };\n\n const notify = (): void => {\n engine = buildEngine();\n\n for (const listener of [...subscribers]) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n };\n\n const relevant = (candidate: Locale): boolean => localeChain(locale, fallbackLocales).includes(candidate);\n\n let engine = buildEngine();\n\n const instance: I18n<C> = {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n getSnapshot() {\n return locale;\n },\n isLoaded(loadOptions) {\n return !disposed && catalogs.isLoaded(loadOptions?.locale ?? locale);\n },\n async load(loadOptions) {\n assertLive();\n\n const targetLocale = canonicalLocale(loadOptions?.locale ?? locale);\n const changed = await loadTargets(targetLocale);\n\n assertLive();\n if (changed.some(relevant)) notify();\n },\n get locale() {\n return locale;\n },\n serialize() {\n assertLive();\n\n return catalogs.serialized(locale);\n },\n async setLocale(nextLocale) {\n assertLive();\n\n const next = canonicalLocale(nextLocale);\n const request = ++transition;\n const changed = await loadTargets(next);\n\n assertLive();\n if (request !== transition) return;\n\n if (next === locale) {\n if (changed.some(relevant)) notify();\n\n return;\n }\n\n locale = next;\n notify();\n },\n subscribe(listener, subscribeOptions) {\n assertLive();\n\n if (subscribeOptions?.signal?.aborted) return () => {};\n\n const unsubscribe = (): void => {\n subscribers.delete(listener);\n subscribeOptions?.signal?.removeEventListener('abort', unsubscribe);\n };\n\n subscribeOptions?.signal?.addEventListener('abort', unsubscribe, { once: true });\n subscribers.add(listener);\n\n if (subscribeOptions?.immediate) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n\n return unsubscribe;\n },\n translate: (key: string, translateOptions?: TranslateOptions | PluralOptions): string =>\n engine.translate(key, translateOptions),\n [Symbol.dispose]: dispose,\n };\n\n return instance;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"i18n.js","names":["error","fallbackLocales","options","canonicalLocale","controller","subscribers","disposed","locale","transition","catalogs","createCatalogStore","changed","candidate","target","effectiveMissing","localeChain","LinguaMissingCatalogError","createTranslateEngine","LinguaDisposedError","listener","engine","loadOptions","targetLocale","next","nextLocale","request","subscribeOptions","key","translateOptions"],"sources":["../src/i18n.ts"],"sourcesContent":["import { error as logError } from './_dev';\nimport { canonicalLocale, localeChain } from './_locale';\nimport { createCatalogStore } from './_resources';\nimport { LinguaDisposedError, LinguaMissingCatalogError } from './errors';\nimport { createTranslateEngine, type Translator } from './translator';\nimport type {\n Catalog,\n I18nOptions,\n Locale,\n PluralOptions,\n SerializedI18n,\n SubscribeOptions,\n TranslateOptions,\n} from './types';\n\nexport type I18n<C extends Catalog = Catalog> = Translator<C> & {\n [Symbol.dispose](): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n /** Current locale. Referentially stable until the locale changes, so frameworks can use it directly as a store snapshot. */\n getSnapshot(): Locale;\n isLoaded(options?: { locale?: Locale }): boolean;\n load(options?: { locale?: Locale }): Promise<void>;\n serialize(): SerializedI18n<C>;\n setLocale(locale: Locale): Promise<void>;\n subscribe(listener: (locale: Locale) => void, options?: SubscribeOptions): () => void;\n};\n\nexport function createI18n<C extends Catalog>(options: I18nOptions<C>): I18n<C> {\n const fallbackLocales = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const controller = new AbortController();\n const subscribers = new Set<(locale: Locale) => void>();\n let disposed = false;\n let locale = canonicalLocale(options.locale ?? 'en');\n let transition = 0;\n\n const catalogs = createCatalogStore<C>({ catalogs: options.catalogs, loadCatalog: options.loadCatalog });\n\n const loadTargets = async (target: Locale): Promise<readonly Locale[]> => {\n const changed: Locale[] = [];\n\n for (const candidate of new Set([target, ...fallbackLocales])) {\n if (await catalogs.load(candidate)) changed.push(candidate);\n }\n\n return changed;\n };\n\n const buildEngine = () => {\n const ready = localeChain(locale, fallbackLocales).some(catalogs.isLoaded);\n const effectiveMissing = ready\n ? options.missing\n : (): never => {\n throw new LinguaMissingCatalogError(\n `No catalog loaded for locale \"${locale}\". Call load() before translating.`,\n );\n };\n\n return createTranslateEngine(catalogs.catalogMap(), { ...options, locale, missing: effectiveMissing });\n };\n\n const assertLive = (): void => {\n if (disposed) throw new LinguaDisposedError();\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n subscribers.clear();\n controller.abort();\n };\n\n const notify = (): void => {\n engine = buildEngine();\n\n for (const listener of [...subscribers]) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n };\n\n const relevant = (candidate: Locale): boolean => localeChain(locale, fallbackLocales).includes(candidate);\n\n let engine = buildEngine();\n\n const instance: I18n<C> = {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n getSnapshot() {\n return locale;\n },\n isLoaded(loadOptions) {\n return !disposed && catalogs.isLoaded(loadOptions?.locale ?? locale);\n },\n async load(loadOptions) {\n assertLive();\n\n const targetLocale = canonicalLocale(loadOptions?.locale ?? locale);\n const changed = await loadTargets(targetLocale);\n\n assertLive();\n if (changed.some(relevant)) notify();\n },\n get locale() {\n return locale;\n },\n serialize() {\n assertLive();\n\n return catalogs.serialized(locale);\n },\n async setLocale(nextLocale) {\n assertLive();\n\n const next = canonicalLocale(nextLocale);\n const request = ++transition;\n const changed = await loadTargets(next);\n\n assertLive();\n if (request !== transition) return;\n\n if (next === locale) {\n if (changed.some(relevant)) notify();\n\n return;\n }\n\n locale = next;\n notify();\n },\n subscribe(listener, subscribeOptions) {\n assertLive();\n\n if (subscribeOptions?.signal?.aborted) return () => {};\n\n const unsubscribe = (): void => {\n subscribers.delete(listener);\n subscribeOptions?.signal?.removeEventListener('abort', unsubscribe);\n };\n\n subscribeOptions?.signal?.addEventListener('abort', unsubscribe, { once: true });\n subscribers.add(listener);\n\n if (subscribeOptions?.immediate) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n\n return unsubscribe;\n },\n translate: (key: string, translateOptions?: TranslateOptions | PluralOptions): string =>\n engine.translate(key, translateOptions),\n translateUnkeyed: (key: string, translateOptions?: TranslateOptions | PluralOptions): string =>\n engine.translate(key, translateOptions),\n [Symbol.dispose]: dispose,\n };\n\n return instance;\n}\n"],"mappings":";;;;;;AA6BA,SAAgB,WAA8B,GAAkC;CAC9E,IAAMC,KACJ,MAAM,QAAQC,EAAQ,QAAQ,IAAIA,EAAQ,WAAWA,EAAQ,WAAW,CAACA,EAAQ,QAAQ,IAAI,CAAC,EAAA,CAC9F,IAAIC,CAAe,GACfC,IAAa,IAAI,gBAAgB,GACjCC,oBAAc,IAAI,IAA8B,GAClDC,IAAW,IACXC,IAASJ,EAAgBD,EAAQ,UAAU,IAAI,GAC/CM,IAAa,GAEXC,IAAWC,EAAsB;EAAE,UAAUR,EAAQ;EAAU,aAAaA,EAAQ;CAAY,CAAC,GAEjG,cAAc,OAAO,MAA+C;EACxE,IAAMS,IAAoB,CAAC;EAE3B,KAAK,IAAMC,qBAAa,IAAI,IAAI,CAACC,GAAQ,GAAGZ,CAAe,CAAC,GAC1D,AAAI,MAAMQ,EAAS,KAAKG,CAAS,KAAGD,EAAQ,KAAKC,CAAS;EAG5D,OAAOD;CACT,GAEM,oBAAoB;EAExB,IAAMG,IADQC,EAAYR,GAAQN,CAAe,CAAC,CAAC,KAAKQ,EAAS,QACxC,IACrBP,EAAQ,gBACK;GACX,MAAM,IAAIc,EACR,iCAAiCT,EAAO,mCAC1C;EACF;EAEJ,OAAOU,EAAsBR,EAAS,WAAW,GAAG;GAAE,GAAGP;GAAS;GAAQ,SAASY;EAAiB,CAAC;CACvG,GAEM,mBAAyB;EAC7B,IAAIR,GAAU,MAAM,IAAIY,EAAoB;CAC9C,GAEM,gBAAsB;EAC1B,AAAIZ,MAEJ,IAAW,IACXD,EAAY,MAAM,GAClBD,EAAW,MAAM;CACnB,GAEM,eAAqB;EACzB,IAAS,YAAY;EAErB,KAAK,IAAMe,KAAY,CAAC,GAAGd,CAAW,GACpC,IAAI;GACF,EAASE,CAAM;EACjB,QAAgB,CAEhB;CAEJ,GAEM,YAAY,MAA+BQ,EAAYR,GAAQN,CAAe,CAAC,CAAC,SAASW,CAAS,GAEpGQ,IAAS,YAAY;CAkFzB,OAAO;EA/EL,IAAI,iBAAiB;GACnB,OAAOhB,EAAW;EACpB;EACA;EACA,IAAI,WAAW;GACb,OAAOE;EACT;EACA,cAAc;GACZ,OAAOC;EACT;EACA,SAAS,GAAa;GACpB,OAAO,CAACD,KAAYG,EAAS,SAASY,GAAa,UAAUd,CAAM;EACrE;EACA,MAAM,KAAK,GAAa;GACtB,WAAW;GAEX,IAAMe,IAAenB,EAAgBkB,GAAa,UAAUd,CAAM,GAC5DI,IAAU,MAAM,YAAYW,CAAY;GAG9C,AADA,WAAW,GACPX,EAAQ,KAAK,QAAQ,KAAG,OAAO;EACrC;EACA,IAAI,SAAS;GACX,OAAOJ;EACT;EACA,YAAY;GAGV,OAFA,WAAW,GAEJE,EAAS,WAAWF,CAAM;EACnC;EACA,MAAM,UAAU,GAAY;GAC1B,WAAW;GAEX,IAAMgB,IAAOpB,EAAgBqB,CAAU,GACjCC,IAAU,EAAEjB,GACZG,IAAU,MAAM,YAAYY,CAAI;GAGtC,AADA,WAAW,GACPE,MAAYjB,MAEZe,MAAShB,IACPI,EAAQ,KAAK,QAAQ,KAAG,OAAO,KAKrC,IAASY,GACT,OAAO;EACT;EACA,UAAU,GAAU,GAAkB;GAGpC,IAFA,WAAW,GAEPG,GAAkB,QAAQ,SAAS,aAAa,CAAC;GAErD,IAAM,oBAA0B;IAE9B,AADArB,EAAY,OAAOc,CAAQ,GAC3BO,GAAkB,QAAQ,oBAAoB,SAAS,WAAW;GACpE;GAKA,IAHAA,GAAkB,QAAQ,iBAAiB,SAAS,aAAa,EAAE,MAAM,GAAK,CAAC,GAC/ErB,EAAY,IAAIc,CAAQ,GAEpBO,GAAkB,WACpB,IAAI;IACF,EAASnB,CAAM;GACjB,QAAgB,CAEhB;GAGF,OAAO;EACT;EACA,YAAY,GAAa,MACvBa,EAAO,UAAUO,GAAKC,CAAgB;EACxC,mBAAmB,GAAa,MAC9BR,EAAO,UAAUO,GAAKC,CAAgB;GACvC,OAAO,UAAU;CAGb;AACT"}
package/dist/lingua.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=/\{([\p{ID_Continue}-]+)\}/gu;function compileTemplate(t){let n=[],r=0;for(let i of t.matchAll(e)){let e=i.index??0;e>r&&n.push(t.slice(r,e)),n.push({value:i[1]}),r=e+i[0].length}return r<t.length&&n.push(t.slice(r)),n}function renderText(e,t,n){return e.map(e=>{if(typeof e==`string`)return e;let r=Object.hasOwn(t,e.value)?t[e.value]:void 0;return r==null?n(e.value):String(r)}).join(``)}var LinguaError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},LinguaDisposedError=class extends LinguaError{constructor(){super(`Operation called on a disposed i18n instance.`)}},LinguaInvalidCatalogError=class extends LinguaError{},LinguaInvalidLocaleError=class extends LinguaError{},LinguaInvalidPluralCountError=class extends LinguaError{},LinguaMissingCatalogError=class extends LinguaError{},LinguaMissingKeyError=class extends LinguaError{},LinguaMissingValueError=class extends LinguaError{},t=new Set([`__proto__`,`constructor`,`prototype`]);function isPluralMessage(e){return typeof e==`object`&&!!e&&Object.hasOwn(e,`plural`)}function compileCatalog(e){let n=new Map,visit=(e,r)=>{if(typeof e==`string`)n.set(r,{kind:`text`,template:compileTemplate(e)});else{if(typeof e!=`object`||!e||Array.isArray(e))throw new LinguaInvalidCatalogError(`Catalog node "${r}" must be a string, plural message, or object.`);if(isPluralMessage(e)){if(Reflect.ownKeys(e).some(e=>e!==`plural`))throw new LinguaInvalidCatalogError(`Plural message "${r}" must contain only a "plural" property.`);if(typeof e.plural!=`object`||e.plural===null||Array.isArray(e.plural))throw new LinguaInvalidCatalogError(`Plural message "${r}" must provide an object of string forms.`);let t=new Map;for(let[n,i]of Object.entries(e.plural)){if(typeof i!=`string`)throw new LinguaInvalidCatalogError(`Plural form "${r}.${n}" must be a string.`);t.set(n,compileTemplate(i))}n.set(r,{forms:t,kind:`plural`})}else for(let[n,i]of Object.entries(e)){if(t.has(n))throw new LinguaInvalidCatalogError(`Catalog key "${n}" is reserved.`);visit(i,r?`${r}.${n}`:n)}}};for(let[n,r]of Object.entries(e)){if(t.has(n))throw new LinguaInvalidCatalogError(`Catalog key "${n}" is reserved.`);visit(r,n)}return n}function catalogKeys(e){return[...compileCatalog(e).keys()]}var n=new Map;function canonicalLocale(e){try{let[t]=Intl.getCanonicalLocales(e);if(t)return t}catch{}throw new LinguaInvalidLocaleError(`Invalid BCP 47 locale tag: "${e}".`)}function localeChain(e,t){let n=new Set;for(let r of[e,...t]){let e=r.split(`-`);for(let t=e.length;t>0;t--)n.add(e.slice(0,t).join(`-`))}return[...n]}function pluralCategory(e,t,r){let i=`${e}:${r?`ordinal`:`cardinal`}`,a=n.get(i);return a||(a=new Intl.PluralRules(e,{type:r?`ordinal`:`cardinal`}),n.set(i,a)),a.select(t)}function createCatalogStore(e){let t=new Map,n=new Map,hydrate=(e,n)=>{t.set(canonicalLocale(e),{catalog:n,compiled:compileCatalog(n)})};for(let[t,n]of Object.entries(e.catalogs??{}))hydrate(t,n);let load=async r=>{let i=canonicalLocale(r);if(t.has(i))return!1;let a=n.get(i);if(a)return await a,!1;if(!e.loadCatalog)throw new LinguaMissingCatalogError(`No catalog source provided for locale "${i}".`);let o=Promise.resolve(e.loadCatalog(i)).then(e=>{hydrate(i,e),n.delete(i)},e=>{throw n.delete(i),e});return n.set(i,o),await o,!0};return{catalogMap(){return new Map([...t].map(([e,{compiled:t}])=>[e,t]))},isLoaded(e){return t.has(canonicalLocale(e))},load,serialized(e){let n={};for(let[e,{catalog:r}]of t)n[e]=r;return{catalogs:n,locale:e}}}}function resolveMissing(e,t){if(e===`throw`)throw t.name?new LinguaMissingValueError(`Missing interpolation value "${t.name}" for key "${t.key}" in locale "${t.locale}".`):new LinguaMissingKeyError(`Missing translation key "${t.key}" in locale "${t.locale}".`);return typeof e==`function`?e(t):t.name?`{${t.name}}`:t.key}function createTranslateEngine(e,t={}){let n=canonicalLocale(t.locale??`en`),r=localeChain(n,(Array.isArray(t.fallback)?t.fallback:t.fallback?[t.fallback]:[]).map(canonicalLocale)),i=t.missing,missingKey=e=>resolveMissing(i,{key:e,locale:n}),missingValue=(e,t)=>resolveMissing(i,{key:t,locale:n,name:e}),resolve=t=>{for(let n of r){let r=e.get(n)?.get(t);if(r)return{locale:n,message:r}}},templateFor=(e,t)=>{let n=resolve(e);if(!n)return;if(n.message.kind===`text`)return`count`in t?void 0:n.message.template;if(!(`count`in t)||!Number.isFinite(t.count)){if(`count`in t)throw new LinguaInvalidPluralCountError("`count` must be a finite number.");return}let r=pluralCategory(n.locale,t.count,t.ordinal??!1);return n.message.forms.get(r)??n.message.forms.get(`other`)},translate=(e,t={})=>{let n=templateFor(e,t);return n?renderText(n,`count`in t?{count:t.count,...t.values}:t.values??{},t=>missingValue(t,e)):missingKey(e)};return{locale:n,translate}}function createTranslator(e,t){let n=createTranslateEngine(new Map([[canonicalLocale(t?.locale??`en`),compileCatalog(e)]]),t);return{locale:n.locale,translate:n.translate}}function createI18n(e){let t=(Array.isArray(e.fallback)?e.fallback:e.fallback?[e.fallback]:[]).map(canonicalLocale),n=new AbortController,r=new Set,i=!1,a=canonicalLocale(e.locale??`en`),o=0,s=createCatalogStore({catalogs:e.catalogs,loadCatalog:e.loadCatalog}),loadTargets=async e=>{let n=[];for(let r of new Set([e,...t]))await s.load(r)&&n.push(r);return n},buildEngine=()=>{let n=localeChain(a,t).some(s.isLoaded)?e.missing:()=>{throw new LinguaMissingCatalogError(`No catalog loaded for locale "${a}". Call load() before translating.`)};return createTranslateEngine(s.catalogMap(),{...e,locale:a,missing:n})},assertLive=()=>{if(i)throw new LinguaDisposedError},dispose=()=>{i||(i=!0,r.clear(),n.abort())},notify=()=>{c=buildEngine();for(let e of[...r])try{e(a)}catch{}},relevant=e=>localeChain(a,t).includes(e),c=buildEngine();return{get disposalSignal(){return n.signal},dispose,get disposed(){return i},getSnapshot(){return a},isLoaded(e){return!i&&s.isLoaded(e?.locale??a)},async load(e){assertLive();let t=canonicalLocale(e?.locale??a),n=await loadTargets(t);assertLive(),n.some(relevant)&&notify()},get locale(){return a},serialize(){return assertLive(),s.serialized(a)},async setLocale(e){assertLive();let t=canonicalLocale(e),n=++o,r=await loadTargets(t);assertLive(),n===o&&(t===a?r.some(relevant)&&notify():(a=t,notify()))},subscribe(e,t){if(assertLive(),t?.signal?.aborted)return()=>{};let unsubscribe=()=>{r.delete(e),t?.signal?.removeEventListener(`abort`,unsubscribe)};if(t?.signal?.addEventListener(`abort`,unsubscribe,{once:!0}),r.add(e),t?.immediate)try{e(a)}catch{}return unsubscribe},translate:(e,t)=>c.translate(e,t),[Symbol.dispose]:dispose}}exports.LinguaDisposedError=LinguaDisposedError,exports.LinguaError=LinguaError,exports.LinguaInvalidCatalogError=LinguaInvalidCatalogError,exports.LinguaInvalidLocaleError=LinguaInvalidLocaleError,exports.LinguaInvalidPluralCountError=LinguaInvalidPluralCountError,exports.LinguaMissingCatalogError=LinguaMissingCatalogError,exports.LinguaMissingKeyError=LinguaMissingKeyError,exports.LinguaMissingValueError=LinguaMissingValueError,exports.catalogKeys=catalogKeys,exports.createI18n=createI18n,exports.createTranslator=createTranslator;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});var e=/\{([\p{ID_Continue}-]+)\}/gu;function compileTemplate(t){let n=[],r=0;for(let i of t.matchAll(e)){let e=i.index??0;e>r&&n.push(t.slice(r,e)),n.push({value:i[1]}),r=e+i[0].length}return r<t.length&&n.push(t.slice(r)),n}function renderText(e,t,n){return e.map(e=>{if(typeof e==`string`)return e;let r=Object.hasOwn(t,e.value)?t[e.value]:void 0;return r==null?n(e.value):String(r)}).join(``)}var LinguaError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},LinguaDisposedError=class extends LinguaError{constructor(){super(`Operation called on a disposed i18n instance.`)}},LinguaInvalidCatalogError=class extends LinguaError{},LinguaInvalidLocaleError=class extends LinguaError{},LinguaInvalidPluralCountError=class extends LinguaError{},LinguaMissingCatalogError=class extends LinguaError{},LinguaMissingKeyError=class extends LinguaError{},LinguaMissingValueError=class extends LinguaError{},t=new Set([`__proto__`,`constructor`,`prototype`]);function isPluralMessage(e){return typeof e==`object`&&!!e&&Object.hasOwn(e,`plural`)}function compileCatalog(e){let n=new Map,visit=(e,r)=>{if(typeof e==`string`)n.set(r,{kind:`text`,template:compileTemplate(e)});else{if(typeof e!=`object`||!e||Array.isArray(e))throw new LinguaInvalidCatalogError(`Catalog node "${r}" must be a string, plural message, or object.`);if(isPluralMessage(e)){if(Reflect.ownKeys(e).some(e=>e!==`plural`))throw new LinguaInvalidCatalogError(`Plural message "${r}" must contain only a "plural" property.`);if(typeof e.plural!=`object`||e.plural===null||Array.isArray(e.plural))throw new LinguaInvalidCatalogError(`Plural message "${r}" must provide an object of string forms.`);let t=new Map;for(let[n,i]of Object.entries(e.plural)){if(typeof i!=`string`)throw new LinguaInvalidCatalogError(`Plural form "${r}.${n}" must be a string.`);t.set(n,compileTemplate(i))}n.set(r,{forms:t,kind:`plural`})}else for(let[n,i]of Object.entries(e)){if(t.has(n))throw new LinguaInvalidCatalogError(`Catalog key "${n}" is reserved.`);visit(i,r?`${r}.${n}`:n)}}};for(let[n,r]of Object.entries(e)){if(t.has(n))throw new LinguaInvalidCatalogError(`Catalog key "${n}" is reserved.`);visit(r,n)}return n}function catalogKeys(e){return[...compileCatalog(e).keys()]}var n=new Map;function canonicalLocale(e){try{let[t]=Intl.getCanonicalLocales(e);if(t)return t}catch{}throw new LinguaInvalidLocaleError(`Invalid BCP 47 locale tag: "${e}".`)}function localeChain(e,t){let n=new Set;for(let r of[e,...t]){let e=r.split(`-`);for(let t=e.length;t>0;t--)n.add(e.slice(0,t).join(`-`))}return[...n]}function pluralCategory(e,t,r){let i=`${e}:${r?`ordinal`:`cardinal`}`,a=n.get(i);return a||(a=new Intl.PluralRules(e,{type:r?`ordinal`:`cardinal`}),n.set(i,a)),a.select(t)}function createCatalogStore(e){let t=new Map,n=new Map,hydrate=(e,n)=>{t.set(canonicalLocale(e),{catalog:n,compiled:compileCatalog(n)})};for(let[t,n]of Object.entries(e.catalogs??{}))hydrate(t,n);let load=async r=>{let i=canonicalLocale(r);if(t.has(i))return!1;let a=n.get(i);if(a)return await a,!1;if(!e.loadCatalog)throw new LinguaMissingCatalogError(`No catalog source provided for locale "${i}".`);let o=Promise.resolve(e.loadCatalog(i)).then(e=>{hydrate(i,e),n.delete(i)},e=>{throw n.delete(i),e});return n.set(i,o),await o,!0};return{catalogMap(){return new Map([...t].map(([e,{compiled:t}])=>[e,t]))},isLoaded(e){return t.has(canonicalLocale(e))},load,serialized(e){let n={};for(let[e,{catalog:r}]of t)n[e]=r;return{catalogs:n,locale:e}}}}function resolveMissing(e,t){if(e===`throw`)throw t.name?new LinguaMissingValueError(`Missing interpolation value "${t.name}" for key "${t.key}" in locale "${t.locale}".`):new LinguaMissingKeyError(`Missing translation key "${t.key}" in locale "${t.locale}".`);return typeof e==`function`?e(t):t.name?`{${t.name}}`:t.key}function createTranslateEngine(e,t={}){let n=canonicalLocale(t.locale??`en`),r=localeChain(n,(Array.isArray(t.fallback)?t.fallback:t.fallback?[t.fallback]:[]).map(canonicalLocale)),i=t.missing,missingKey=e=>resolveMissing(i,{key:e,locale:n}),missingValue=(e,t)=>resolveMissing(i,{key:t,locale:n,name:e}),resolve=t=>{for(let n of r){let r=e.get(n)?.get(t);if(r)return{locale:n,message:r}}},templateFor=(e,t)=>{let n=resolve(e);if(!n)return;if(n.message.kind===`text`)return`count`in t?void 0:n.message.template;if(!(`count`in t)||!Number.isFinite(t.count)){if(`count`in t)throw new LinguaInvalidPluralCountError("`count` must be a finite number.");return}let r=pluralCategory(n.locale,t.count,t.ordinal??!1);return n.message.forms.get(r)??n.message.forms.get(`other`)},translate=(e,t={})=>{let n=templateFor(e,t);return n?renderText(n,`count`in t?{count:t.count,...t.values}:t.values??{},t=>missingValue(t,e)):missingKey(e)};return{locale:n,translate}}function createTranslator(e,t){let n=createTranslateEngine(new Map([[canonicalLocale(t?.locale??`en`),compileCatalog(e)]]),t);return{locale:n.locale,translate:n.translate,translateUnkeyed:n.translate}}function createI18n(e){let t=(Array.isArray(e.fallback)?e.fallback:e.fallback?[e.fallback]:[]).map(canonicalLocale),n=new AbortController,r=new Set,i=!1,a=canonicalLocale(e.locale??`en`),o=0,s=createCatalogStore({catalogs:e.catalogs,loadCatalog:e.loadCatalog}),loadTargets=async e=>{let n=[];for(let r of new Set([e,...t]))await s.load(r)&&n.push(r);return n},buildEngine=()=>{let n=localeChain(a,t).some(s.isLoaded)?e.missing:()=>{throw new LinguaMissingCatalogError(`No catalog loaded for locale "${a}". Call load() before translating.`)};return createTranslateEngine(s.catalogMap(),{...e,locale:a,missing:n})},assertLive=()=>{if(i)throw new LinguaDisposedError},dispose=()=>{i||(i=!0,r.clear(),n.abort())},notify=()=>{c=buildEngine();for(let e of[...r])try{e(a)}catch{}},relevant=e=>localeChain(a,t).includes(e),c=buildEngine();return{get disposalSignal(){return n.signal},dispose,get disposed(){return i},getSnapshot(){return a},isLoaded(e){return!i&&s.isLoaded(e?.locale??a)},async load(e){assertLive();let t=canonicalLocale(e?.locale??a),n=await loadTargets(t);assertLive(),n.some(relevant)&&notify()},get locale(){return a},serialize(){return assertLive(),s.serialized(a)},async setLocale(e){assertLive();let t=canonicalLocale(e),n=++o,r=await loadTargets(t);assertLive(),n===o&&(t===a?r.some(relevant)&&notify():(a=t,notify()))},subscribe(e,t){if(assertLive(),t?.signal?.aborted)return()=>{};let unsubscribe=()=>{r.delete(e),t?.signal?.removeEventListener(`abort`,unsubscribe)};if(t?.signal?.addEventListener(`abort`,unsubscribe,{once:!0}),r.add(e),t?.immediate)try{e(a)}catch{}return unsubscribe},translate:(e,t)=>c.translate(e,t),translateUnkeyed:(e,t)=>c.translate(e,t),[Symbol.dispose]:dispose}}exports.LinguaDisposedError=LinguaDisposedError,exports.LinguaError=LinguaError,exports.LinguaInvalidCatalogError=LinguaInvalidCatalogError,exports.LinguaInvalidLocaleError=LinguaInvalidLocaleError,exports.LinguaInvalidPluralCountError=LinguaInvalidPluralCountError,exports.LinguaMissingCatalogError=LinguaMissingCatalogError,exports.LinguaMissingKeyError=LinguaMissingKeyError,exports.LinguaMissingValueError=LinguaMissingValueError,exports.catalogKeys=catalogKeys,exports.createI18n=createI18n,exports.createTranslator=createTranslator;
2
2
  //# sourceMappingURL=lingua.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"lingua.cjs","names":["error","interpolation","parts","offset","match","value","index","part","values","missing","message","opts","unsafeKeys","node","messages","prefix","key","forms","form","template","catalog","pluralRules","canonical","locale","chain","candidate","fallback","length","ordinal","rules","count","loaded","tasks","options","requestedLocale","existing","task","error","compiled","catalogs","loadedLocale","strategy","info","found","category","name","engine","fallbackLocales","controller","subscribers","disposed","transition","changed","target","effectiveMissing","listener","loadOptions","targetLocale","next","nextLocale","request","subscribeOptions","translateOptions"],"sources":["../src/_template.ts","../src/errors.ts","../src/_catalog.ts","../src/catalog.ts","../src/_dev.ts","../src/_locale.ts","../src/_resources.ts","../src/translator.ts","../src/i18n.ts"],"sourcesContent":["export type TemplatePart = string | { readonly value: string };\nexport type Template = readonly TemplatePart[];\n\nconst interpolation = /\\{([\\p{ID_Continue}-]+)\\}/gu;\n\nexport function compileTemplate(value: string): Template {\n const parts: TemplatePart[] = [];\n let offset = 0;\n\n for (const match of value.matchAll(interpolation)) {\n const index = match.index ?? 0;\n\n if (index > offset) parts.push(value.slice(offset, index));\n\n parts.push({ value: match[1] });\n offset = index + match[0].length;\n }\n\n if (offset < value.length) parts.push(value.slice(offset));\n\n return parts;\n}\n\nexport function renderText(\n parts: Template,\n values: Record<string, unknown>,\n missing: (name: string) => string,\n): string {\n return parts\n .map((part) => {\n if (typeof part === 'string') return part;\n\n const value = Object.hasOwn(values, part.value) ? values[part.value] : undefined;\n\n return value == null ? missing(part.value) : String(value);\n })\n .join('');\n}\n","export class LinguaError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class LinguaDisposedError extends LinguaError {\n constructor() {\n super('Operation called on a disposed i18n instance.');\n }\n}\n\nexport class LinguaInvalidCatalogError extends LinguaError {}\nexport class LinguaInvalidLocaleError extends LinguaError {}\nexport class LinguaInvalidPluralCountError extends LinguaError {}\nexport class LinguaMissingCatalogError extends LinguaError {}\nexport class LinguaMissingKeyError extends LinguaError {}\nexport class LinguaMissingValueError extends LinguaError {}\n","import { compileTemplate, type Template } from './_template';\nimport { LinguaInvalidCatalogError } from './errors';\nimport type { Catalog, CatalogNode, PluralMessage } from './types';\n\nconst unsafeKeys = new Set(['__proto__', 'constructor', 'prototype']);\n\nexport type CompiledText = { readonly kind: 'text'; readonly template: Template };\nexport type CompiledPlural = { readonly forms: ReadonlyMap<string, Template>; readonly kind: 'plural' };\nexport type CompiledMessage = CompiledPlural | CompiledText;\nexport type CompiledCatalog = ReadonlyMap<string, CompiledMessage>;\n\nexport function isPluralMessage(node: CatalogNode): node is PluralMessage {\n return typeof node === 'object' && node !== null && Object.hasOwn(node, 'plural');\n}\n\n/** Compile explicit catalog nodes once. Nested objects only group keys; plural intent is never inferred. */\nexport function compileCatalog(catalog: Catalog): CompiledCatalog {\n const messages = new Map<string, CompiledMessage>();\n\n const visit = (node: CatalogNode, prefix: string): void => {\n if (typeof node === 'string') {\n messages.set(prefix, { kind: 'text', template: compileTemplate(node) });\n\n return;\n }\n\n if (typeof node !== 'object' || node === null || Array.isArray(node)) {\n throw new LinguaInvalidCatalogError(`Catalog node \"${prefix}\" must be a string, plural message, or object.`);\n }\n\n if (isPluralMessage(node)) {\n if (Reflect.ownKeys(node).some((key) => key !== 'plural')) {\n throw new LinguaInvalidCatalogError(`Plural message \"${prefix}\" must contain only a \"plural\" property.`);\n }\n\n if (typeof node.plural !== 'object' || node.plural === null || Array.isArray(node.plural)) {\n throw new LinguaInvalidCatalogError(`Plural message \"${prefix}\" must provide an object of string forms.`);\n }\n\n const forms = new Map<string, Template>();\n\n for (const [form, template] of Object.entries(node.plural)) {\n if (typeof template !== 'string') {\n throw new LinguaInvalidCatalogError(`Plural form \"${prefix}.${form}\" must be a string.`);\n }\n\n forms.set(form, compileTemplate(template));\n }\n\n messages.set(prefix, { forms, kind: 'plural' });\n\n return;\n }\n\n for (const [key, value] of Object.entries(node)) {\n if (unsafeKeys.has(key)) {\n throw new LinguaInvalidCatalogError(`Catalog key \"${key}\" is reserved.`);\n }\n\n visit(value, prefix ? `${prefix}.${key}` : key);\n }\n };\n\n for (const [key, value] of Object.entries(catalog)) {\n if (unsafeKeys.has(key)) throw new LinguaInvalidCatalogError(`Catalog key \"${key}\" is reserved.`);\n\n visit(value, key);\n }\n\n return messages;\n}\n","import { compileCatalog } from './_catalog';\nimport type { Catalog, MessageKey } from './types';\n\n/** Enumerate every message key in a catalog as a dotted path.\n *\n * Traverses nested grouping objects and explicit `{ plural: ... }` messages,\n * producing the same dotted paths that `MessageKey<C>` represents at the type\n * level. Use this to derive key arrays from the catalog itself instead of\n * maintaining a parallel list that can go stale.\n *\n * Pass any catalog subtree to enumerate group-scoped keys. For an `I18n`\n * instance, pass its current locale catalog explicitly.\n *\n * @example\n * ```ts\n * const messages = {\n * nav: { home: '...', settings: '...' },\n * };\n * const keys = catalogKeys(messages); // ['nav.home', 'nav.settings']\n * const navKeys = catalogKeys(messages.nav); // ['home', 'settings']\n *\n * // From an i18n instance: read the current locale's catalog first\n * const i18n = createI18n({ catalogs: { en: messages }, locale: 'en' });\n * const state = i18n.serialize();\n * const allKeys = catalogKeys(state.catalogs[state.locale]);\n * ```\n */\nexport function catalogKeys<C extends Catalog>(catalog: C): ReadonlyArray<MessageKey<C>> {\n return [...compileCatalog(catalog).keys()] as unknown as ReadonlyArray<MessageKey<C>>;\n}\n","export const isDev = !(globalThis as { __LINGUA_PROD__?: boolean }).__LINGUA_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/lingua] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/lingua] ${msg}`, ...args);\n}\n","import { LinguaInvalidLocaleError } from './errors';\nimport type { Locale } from './types';\n\nconst pluralRules = new Map<string, Intl.PluralRules>();\n\nexport function canonicalLocale(locale: string): Locale {\n try {\n const [canonical] = Intl.getCanonicalLocales(locale);\n\n if (canonical) return canonical;\n } catch {\n // Error below names the failed public input.\n }\n\n throw new LinguaInvalidLocaleError(`Invalid BCP 47 locale tag: \"${locale}\".`);\n}\n\nexport function localeChain(locale: Locale, fallback: readonly Locale[]): readonly Locale[] {\n const chain = new Set<Locale>();\n\n for (const candidate of [locale, ...fallback]) {\n const parts = candidate.split('-');\n\n for (let length = parts.length; length > 0; length--) {\n chain.add(parts.slice(0, length).join('-'));\n }\n }\n\n return [...chain];\n}\n\nexport function pluralCategory(locale: Locale, count: number, ordinal: boolean): Intl.LDMLPluralRule {\n const key = `${locale}:${ordinal ? 'ordinal' : 'cardinal'}`;\n let rules = pluralRules.get(key);\n\n if (!rules) {\n rules = new Intl.PluralRules(locale, { type: ordinal ? 'ordinal' : 'cardinal' });\n pluralRules.set(key, rules);\n }\n\n return rules.select(count);\n}\n","import { type CompiledCatalog, compileCatalog } from './_catalog';\nimport { canonicalLocale } from './_locale';\nimport { LinguaMissingCatalogError } from './errors';\nimport type { Catalog, Catalogs, Locale, SerializedI18n } from './types';\n\ntype CatalogStoreOptions<C extends Catalog> = {\n readonly catalogs?: Catalogs<C>;\n readonly loadCatalog?: (locale: Locale) => Promise<C> | C;\n};\n\n/** One catalog state machine owns loaded catalogs and in-flight loading work. */\nexport function createCatalogStore<C extends Catalog>(options: CatalogStoreOptions<C>) {\n const loaded = new Map<Locale, { readonly catalog: C; readonly compiled: CompiledCatalog }>();\n const tasks = new Map<Locale, Promise<void>>();\n\n const hydrate = (locale: Locale, catalog: C): void => {\n loaded.set(canonicalLocale(locale), { catalog, compiled: compileCatalog(catalog) });\n };\n\n for (const [locale, catalog] of Object.entries(options.catalogs ?? {})) hydrate(locale, catalog);\n\n const load = async (requestedLocale: Locale): Promise<boolean> => {\n const locale = canonicalLocale(requestedLocale);\n\n if (loaded.has(locale)) return false;\n\n const existing = tasks.get(locale);\n\n if (existing) {\n await existing;\n\n return false;\n }\n\n if (!options.loadCatalog) {\n throw new LinguaMissingCatalogError(`No catalog source provided for locale \"${locale}\".`);\n }\n\n const task = Promise.resolve(options.loadCatalog(locale)).then(\n (catalog) => {\n hydrate(locale, catalog);\n tasks.delete(locale);\n },\n (error: unknown) => {\n tasks.delete(locale);\n throw error;\n },\n );\n\n tasks.set(locale, task);\n await task;\n\n return true;\n };\n\n return {\n catalogMap(): ReadonlyMap<Locale, CompiledCatalog> {\n return new Map([...loaded].map(([locale, { compiled }]) => [locale, compiled]));\n },\n isLoaded(locale: Locale): boolean {\n return loaded.has(canonicalLocale(locale));\n },\n load,\n serialized(locale: Locale): SerializedI18n<C> {\n const catalogs: Catalogs<C> = {};\n\n for (const [loadedLocale, { catalog }] of loaded) catalogs[loadedLocale] = catalog;\n\n return { catalogs, locale };\n },\n };\n}\n","import { type CompiledCatalog, type CompiledMessage, compileCatalog } from './_catalog';\nimport { isDev, warn } from './_dev';\nimport { canonicalLocale, localeChain, pluralCategory } from './_locale';\nimport { renderText, type Template } from './_template';\nimport { LinguaInvalidPluralCountError, LinguaMissingKeyError, LinguaMissingValueError } from './errors';\nimport type {\n Catalog,\n Locale,\n MissingInfo,\n MissingStrategy,\n PluralKey,\n PluralOptions,\n TextKey,\n TranslateOptions,\n TranslatorOptions,\n} from './types';\n\nexport type Translator<C extends Catalog = Catalog> = {\n readonly locale: Locale;\n translate(key: TextKey<C>, options?: TranslateOptions): string;\n translate(key: PluralKey<C>, options: PluralOptions): string;\n};\n\n/** Loose translate signature implemented by the engine; public overloads restrict keys per catalog type. */\nexport type LooseTranslate = (key: string, options?: TranslateOptions | PluralOptions) => string;\n\nfunction resolveMissing(strategy: MissingStrategy | undefined, info: MissingInfo): string {\n if (strategy === 'throw') {\n throw info.name\n ? new LinguaMissingValueError(\n `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`,\n )\n : new LinguaMissingKeyError(`Missing translation key \"${info.key}\" in locale \"${info.locale}\".`);\n }\n\n if (typeof strategy === 'function') return strategy(info);\n\n // 'key' or undefined (default)\n if (strategy === undefined && isDev) {\n warn(\n info.name\n ? `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`\n : `Missing translation key \"${info.key}\" in locale \"${info.locale}\".`,\n );\n }\n\n return info.name ? `{${info.name}}` : info.key;\n}\n\n/** Internal engine: resolves and renders any string key against compiled catalogs for a fixed locale. */\nexport function createTranslateEngine(\n catalogs: ReadonlyMap<Locale, CompiledCatalog>,\n options: InternalOptions = {},\n): { readonly locale: Locale; readonly translate: LooseTranslate } {\n const locale = canonicalLocale(options.locale ?? 'en');\n const fallback = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const chain = localeChain(locale, fallback);\n const missing = options.missing;\n\n const missingKey = (key: string): string => resolveMissing(missing, { key, locale });\n const missingValue = (name: string, key: string): string => resolveMissing(missing, { key, locale, name });\n\n const resolve = (key: string): { readonly locale: Locale; readonly message: CompiledMessage } | undefined => {\n for (const candidate of chain) {\n const message = catalogs.get(candidate)?.get(key);\n\n if (message) return { locale: candidate, message };\n }\n\n return undefined;\n };\n\n const templateFor = (key: string, options: TranslateOptions | PluralOptions): Template | undefined => {\n const found = resolve(key);\n\n if (!found) return undefined;\n\n if (found.message.kind === 'text') {\n if ('count' in options) return undefined;\n\n return found.message.template;\n }\n\n if (!('count' in options) || !Number.isFinite(options.count)) {\n if ('count' in options) throw new LinguaInvalidPluralCountError('`count` must be a finite number.');\n\n return undefined;\n }\n\n const category = pluralCategory(found.locale, options.count, options.ordinal ?? false);\n\n return found.message.forms.get(category) ?? found.message.forms.get('other');\n };\n\n const translate = (key: string, options: TranslateOptions | PluralOptions = {}): string => {\n const template = templateFor(key, options);\n\n if (!template) return missingKey(key);\n\n const values: Record<string, unknown> =\n 'count' in options ? { count: options.count, ...options.values } : (options.values ?? {});\n\n return renderText(template, values, (name) => missingValue(name, key));\n };\n\n return { locale, translate };\n}\n\ntype InternalOptions = {\n readonly fallback?: Locale | readonly Locale[];\n readonly locale?: Locale;\n readonly missing?: MissingStrategy;\n};\n\n/** Creates a fixed-locale translator from one catalog. Lingua snapshots catalog messages during construction. */\nexport function createTranslator<C extends Catalog>(catalog: C, options?: TranslatorOptions): Translator<C> {\n const engine = createTranslateEngine(\n new Map([[canonicalLocale(options?.locale ?? 'en'), compileCatalog(catalog)]]),\n options,\n );\n\n return { locale: engine.locale, translate: engine.translate };\n}\n","import { error as logError } from './_dev';\nimport { canonicalLocale, localeChain } from './_locale';\nimport { createCatalogStore } from './_resources';\nimport { LinguaDisposedError, LinguaMissingCatalogError } from './errors';\nimport { createTranslateEngine, type Translator } from './translator';\nimport type {\n Catalog,\n I18nOptions,\n Locale,\n PluralOptions,\n SerializedI18n,\n SubscribeOptions,\n TranslateOptions,\n} from './types';\n\nexport type I18n<C extends Catalog = Catalog> = Translator<C> & {\n [Symbol.dispose](): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n /** Current locale. Referentially stable until the locale changes, so frameworks can use it directly as a store snapshot. */\n getSnapshot(): Locale;\n isLoaded(options?: { locale?: Locale }): boolean;\n load(options?: { locale?: Locale }): Promise<void>;\n serialize(): SerializedI18n<C>;\n setLocale(locale: Locale): Promise<void>;\n subscribe(listener: (locale: Locale) => void, options?: SubscribeOptions): () => void;\n};\n\nexport function createI18n<C extends Catalog>(options: I18nOptions<C>): I18n<C> {\n const fallbackLocales = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const controller = new AbortController();\n const subscribers = new Set<(locale: Locale) => void>();\n let disposed = false;\n let locale = canonicalLocale(options.locale ?? 'en');\n let transition = 0;\n\n const catalogs = createCatalogStore<C>({ catalogs: options.catalogs, loadCatalog: options.loadCatalog });\n\n const loadTargets = async (target: Locale): Promise<readonly Locale[]> => {\n const changed: Locale[] = [];\n\n for (const candidate of new Set([target, ...fallbackLocales])) {\n if (await catalogs.load(candidate)) changed.push(candidate);\n }\n\n return changed;\n };\n\n const buildEngine = () => {\n const ready = localeChain(locale, fallbackLocales).some(catalogs.isLoaded);\n const effectiveMissing = ready\n ? options.missing\n : (): never => {\n throw new LinguaMissingCatalogError(\n `No catalog loaded for locale \"${locale}\". Call load() before translating.`,\n );\n };\n\n return createTranslateEngine(catalogs.catalogMap(), { ...options, locale, missing: effectiveMissing });\n };\n\n const assertLive = (): void => {\n if (disposed) throw new LinguaDisposedError();\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n subscribers.clear();\n controller.abort();\n };\n\n const notify = (): void => {\n engine = buildEngine();\n\n for (const listener of [...subscribers]) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n };\n\n const relevant = (candidate: Locale): boolean => localeChain(locale, fallbackLocales).includes(candidate);\n\n let engine = buildEngine();\n\n const instance: I18n<C> = {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n getSnapshot() {\n return locale;\n },\n isLoaded(loadOptions) {\n return !disposed && catalogs.isLoaded(loadOptions?.locale ?? locale);\n },\n async load(loadOptions) {\n assertLive();\n\n const targetLocale = canonicalLocale(loadOptions?.locale ?? locale);\n const changed = await loadTargets(targetLocale);\n\n assertLive();\n if (changed.some(relevant)) notify();\n },\n get locale() {\n return locale;\n },\n serialize() {\n assertLive();\n\n return catalogs.serialized(locale);\n },\n async setLocale(nextLocale) {\n assertLive();\n\n const next = canonicalLocale(nextLocale);\n const request = ++transition;\n const changed = await loadTargets(next);\n\n assertLive();\n if (request !== transition) return;\n\n if (next === locale) {\n if (changed.some(relevant)) notify();\n\n return;\n }\n\n locale = next;\n notify();\n },\n subscribe(listener, subscribeOptions) {\n assertLive();\n\n if (subscribeOptions?.signal?.aborted) return () => {};\n\n const unsubscribe = (): void => {\n subscribers.delete(listener);\n subscribeOptions?.signal?.removeEventListener('abort', unsubscribe);\n };\n\n subscribeOptions?.signal?.addEventListener('abort', unsubscribe, { once: true });\n subscribers.add(listener);\n\n if (subscribeOptions?.immediate) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n\n return unsubscribe;\n },\n translate: (key: string, translateOptions?: TranslateOptions | PluralOptions): string =>\n engine.translate(key, translateOptions),\n [Symbol.dispose]: dispose,\n };\n\n return instance;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lingua.cjs","names":["error","interpolation","parts","offset","match","value","index","part","values","missing","message","opts","unsafeKeys","node","messages","prefix","key","forms","form","template","catalog","pluralRules","canonical","locale","chain","candidate","fallback","length","ordinal","rules","count","loaded","tasks","options","requestedLocale","existing","task","error","compiled","catalogs","loadedLocale","strategy","info","found","category","name","engine","fallbackLocales","controller","subscribers","disposed","transition","changed","target","effectiveMissing","listener","loadOptions","targetLocale","next","nextLocale","request","subscribeOptions","translateOptions"],"sources":["../src/_template.ts","../src/errors.ts","../src/_catalog.ts","../src/catalog.ts","../src/_dev.ts","../src/_locale.ts","../src/_resources.ts","../src/translator.ts","../src/i18n.ts"],"sourcesContent":["export type TemplatePart = string | { readonly value: string };\nexport type Template = readonly TemplatePart[];\n\nconst interpolation = /\\{([\\p{ID_Continue}-]+)\\}/gu;\n\nexport function compileTemplate(value: string): Template {\n const parts: TemplatePart[] = [];\n let offset = 0;\n\n for (const match of value.matchAll(interpolation)) {\n const index = match.index ?? 0;\n\n if (index > offset) parts.push(value.slice(offset, index));\n\n parts.push({ value: match[1] });\n offset = index + match[0].length;\n }\n\n if (offset < value.length) parts.push(value.slice(offset));\n\n return parts;\n}\n\nexport function renderText(\n parts: Template,\n values: Record<string, unknown>,\n missing: (name: string) => string,\n): string {\n return parts\n .map((part) => {\n if (typeof part === 'string') return part;\n\n const value = Object.hasOwn(values, part.value) ? values[part.value] : undefined;\n\n return value == null ? missing(part.value) : String(value);\n })\n .join('');\n}\n","export class LinguaError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class LinguaDisposedError extends LinguaError {\n constructor() {\n super('Operation called on a disposed i18n instance.');\n }\n}\n\nexport class LinguaInvalidCatalogError extends LinguaError {}\nexport class LinguaInvalidLocaleError extends LinguaError {}\nexport class LinguaInvalidPluralCountError extends LinguaError {}\nexport class LinguaMissingCatalogError extends LinguaError {}\nexport class LinguaMissingKeyError extends LinguaError {}\nexport class LinguaMissingValueError extends LinguaError {}\n","import { compileTemplate, type Template } from './_template';\nimport { LinguaInvalidCatalogError } from './errors';\nimport type { Catalog, CatalogNode, PluralMessage } from './types';\n\nconst unsafeKeys = new Set(['__proto__', 'constructor', 'prototype']);\n\nexport type CompiledText = { readonly kind: 'text'; readonly template: Template };\nexport type CompiledPlural = { readonly forms: ReadonlyMap<string, Template>; readonly kind: 'plural' };\nexport type CompiledMessage = CompiledPlural | CompiledText;\nexport type CompiledCatalog = ReadonlyMap<string, CompiledMessage>;\n\nexport function isPluralMessage(node: CatalogNode): node is PluralMessage {\n return typeof node === 'object' && node !== null && Object.hasOwn(node, 'plural');\n}\n\n/** Compile explicit catalog nodes once. Nested objects only group keys; plural intent is never inferred. */\nexport function compileCatalog(catalog: Catalog): CompiledCatalog {\n const messages = new Map<string, CompiledMessage>();\n\n const visit = (node: CatalogNode, prefix: string): void => {\n if (typeof node === 'string') {\n messages.set(prefix, { kind: 'text', template: compileTemplate(node) });\n\n return;\n }\n\n if (typeof node !== 'object' || node === null || Array.isArray(node)) {\n throw new LinguaInvalidCatalogError(`Catalog node \"${prefix}\" must be a string, plural message, or object.`);\n }\n\n if (isPluralMessage(node)) {\n if (Reflect.ownKeys(node).some((key) => key !== 'plural')) {\n throw new LinguaInvalidCatalogError(`Plural message \"${prefix}\" must contain only a \"plural\" property.`);\n }\n\n if (typeof node.plural !== 'object' || node.plural === null || Array.isArray(node.plural)) {\n throw new LinguaInvalidCatalogError(`Plural message \"${prefix}\" must provide an object of string forms.`);\n }\n\n const forms = new Map<string, Template>();\n\n for (const [form, template] of Object.entries(node.plural)) {\n if (typeof template !== 'string') {\n throw new LinguaInvalidCatalogError(`Plural form \"${prefix}.${form}\" must be a string.`);\n }\n\n forms.set(form, compileTemplate(template));\n }\n\n messages.set(prefix, { forms, kind: 'plural' });\n\n return;\n }\n\n for (const [key, value] of Object.entries(node)) {\n if (unsafeKeys.has(key)) {\n throw new LinguaInvalidCatalogError(`Catalog key \"${key}\" is reserved.`);\n }\n\n visit(value, prefix ? `${prefix}.${key}` : key);\n }\n };\n\n for (const [key, value] of Object.entries(catalog)) {\n if (unsafeKeys.has(key)) throw new LinguaInvalidCatalogError(`Catalog key \"${key}\" is reserved.`);\n\n visit(value, key);\n }\n\n return messages;\n}\n","import { compileCatalog } from './_catalog';\nimport type { Catalog, MessageKey } from './types';\n\n/** Enumerate every message key in a catalog as a dotted path.\n *\n * Traverses nested grouping objects and explicit `{ plural: ... }` messages,\n * producing the same dotted paths that `MessageKey<C>` represents at the type\n * level. Use this to derive key arrays from the catalog itself instead of\n * maintaining a parallel list that can go stale.\n *\n * Pass any catalog subtree to enumerate group-scoped keys. For an `I18n`\n * instance, pass its current locale catalog explicitly.\n *\n * @example\n * ```ts\n * const messages = {\n * nav: { home: '...', settings: '...' },\n * };\n * const keys = catalogKeys(messages); // ['nav.home', 'nav.settings']\n * const navKeys = catalogKeys(messages.nav); // ['home', 'settings']\n *\n * // From an i18n instance: read the current locale's catalog first\n * const i18n = createI18n({ catalogs: { en: messages }, locale: 'en' });\n * const state = i18n.serialize();\n * const allKeys = catalogKeys(state.catalogs[state.locale]);\n * ```\n */\nexport function catalogKeys<C extends Catalog>(catalog: C): ReadonlyArray<MessageKey<C>> {\n return [...compileCatalog(catalog).keys()] as unknown as ReadonlyArray<MessageKey<C>>;\n}\n","export const isDev = !(globalThis as { __LINGUA_PROD__?: boolean }).__LINGUA_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/lingua] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/lingua] ${msg}`, ...args);\n}\n","import { LinguaInvalidLocaleError } from './errors';\nimport type { Locale } from './types';\n\nconst pluralRules = new Map<string, Intl.PluralRules>();\n\nexport function canonicalLocale(locale: string): Locale {\n try {\n const [canonical] = Intl.getCanonicalLocales(locale);\n\n if (canonical) return canonical;\n } catch {\n // Error below names the failed public input.\n }\n\n throw new LinguaInvalidLocaleError(`Invalid BCP 47 locale tag: \"${locale}\".`);\n}\n\nexport function localeChain(locale: Locale, fallback: readonly Locale[]): readonly Locale[] {\n const chain = new Set<Locale>();\n\n for (const candidate of [locale, ...fallback]) {\n const parts = candidate.split('-');\n\n for (let length = parts.length; length > 0; length--) {\n chain.add(parts.slice(0, length).join('-'));\n }\n }\n\n return [...chain];\n}\n\nexport function pluralCategory(locale: Locale, count: number, ordinal: boolean): Intl.LDMLPluralRule {\n const key = `${locale}:${ordinal ? 'ordinal' : 'cardinal'}`;\n let rules = pluralRules.get(key);\n\n if (!rules) {\n rules = new Intl.PluralRules(locale, { type: ordinal ? 'ordinal' : 'cardinal' });\n pluralRules.set(key, rules);\n }\n\n return rules.select(count);\n}\n","import { type CompiledCatalog, compileCatalog } from './_catalog';\nimport { canonicalLocale } from './_locale';\nimport { LinguaMissingCatalogError } from './errors';\nimport type { Catalog, Catalogs, Locale, SerializedI18n } from './types';\n\ntype CatalogStoreOptions<C extends Catalog> = {\n readonly catalogs?: Catalogs<C>;\n readonly loadCatalog?: (locale: Locale) => Promise<C> | C;\n};\n\n/** One catalog state machine owns loaded catalogs and in-flight loading work. */\nexport function createCatalogStore<C extends Catalog>(options: CatalogStoreOptions<C>) {\n const loaded = new Map<Locale, { readonly catalog: C; readonly compiled: CompiledCatalog }>();\n const tasks = new Map<Locale, Promise<void>>();\n\n const hydrate = (locale: Locale, catalog: C): void => {\n loaded.set(canonicalLocale(locale), { catalog, compiled: compileCatalog(catalog) });\n };\n\n for (const [locale, catalog] of Object.entries(options.catalogs ?? {})) hydrate(locale, catalog);\n\n const load = async (requestedLocale: Locale): Promise<boolean> => {\n const locale = canonicalLocale(requestedLocale);\n\n if (loaded.has(locale)) return false;\n\n const existing = tasks.get(locale);\n\n if (existing) {\n await existing;\n\n return false;\n }\n\n if (!options.loadCatalog) {\n throw new LinguaMissingCatalogError(`No catalog source provided for locale \"${locale}\".`);\n }\n\n const task = Promise.resolve(options.loadCatalog(locale)).then(\n (catalog) => {\n hydrate(locale, catalog);\n tasks.delete(locale);\n },\n (error: unknown) => {\n tasks.delete(locale);\n throw error;\n },\n );\n\n tasks.set(locale, task);\n await task;\n\n return true;\n };\n\n return {\n catalogMap(): ReadonlyMap<Locale, CompiledCatalog> {\n return new Map([...loaded].map(([locale, { compiled }]) => [locale, compiled]));\n },\n isLoaded(locale: Locale): boolean {\n return loaded.has(canonicalLocale(locale));\n },\n load,\n serialized(locale: Locale): SerializedI18n<C> {\n const catalogs: Catalogs<C> = {};\n\n for (const [loadedLocale, { catalog }] of loaded) catalogs[loadedLocale] = catalog;\n\n return { catalogs, locale };\n },\n };\n}\n","import { type CompiledCatalog, type CompiledMessage, compileCatalog } from './_catalog';\nimport { isDev, warn } from './_dev';\nimport { canonicalLocale, localeChain, pluralCategory } from './_locale';\nimport { renderText, type Template } from './_template';\nimport { LinguaInvalidPluralCountError, LinguaMissingKeyError, LinguaMissingValueError } from './errors';\nimport type {\n Catalog,\n Locale,\n MissingInfo,\n MissingStrategy,\n PluralKey,\n PluralOptions,\n TextKey,\n TranslateOptions,\n TranslatorOptions,\n} from './types';\n\nexport type Translator<C extends Catalog = Catalog> = {\n readonly locale: Locale;\n translate(key: TextKey<C>, options?: TranslateOptions): string;\n translate(key: PluralKey<C>, options: PluralOptions): string;\n /**\n * Translates a runtime key that is not in the catalog's typed key set. Identical to\n * `translate` at runtime: the key is looked up the same way and a missing key follows\n * the configured `missing` strategy. The one typed escape hatch for keys that arrive\n * as strings, so consumers never cast around the typed surface.\n */\n translateUnkeyed(key: string, options?: TranslateOptions | PluralOptions): string;\n};\n\n/** Loose translate signature implemented by the engine; public overloads restrict keys per catalog type. */\nexport type LooseTranslate = (key: string, options?: TranslateOptions | PluralOptions) => string;\n\nfunction resolveMissing(strategy: MissingStrategy | undefined, info: MissingInfo): string {\n if (strategy === 'throw') {\n throw info.name\n ? new LinguaMissingValueError(\n `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`,\n )\n : new LinguaMissingKeyError(`Missing translation key \"${info.key}\" in locale \"${info.locale}\".`);\n }\n\n if (typeof strategy === 'function') return strategy(info);\n\n // 'key' or undefined (default)\n if (strategy === undefined && isDev) {\n warn(\n info.name\n ? `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`\n : `Missing translation key \"${info.key}\" in locale \"${info.locale}\".`,\n );\n }\n\n return info.name ? `{${info.name}}` : info.key;\n}\n\n/** Internal engine: resolves and renders any string key against compiled catalogs for a fixed locale. */\nexport function createTranslateEngine(\n catalogs: ReadonlyMap<Locale, CompiledCatalog>,\n options: InternalOptions = {},\n): { readonly locale: Locale; readonly translate: LooseTranslate } {\n const locale = canonicalLocale(options.locale ?? 'en');\n const fallback = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const chain = localeChain(locale, fallback);\n const missing = options.missing;\n\n const missingKey = (key: string): string => resolveMissing(missing, { key, locale });\n const missingValue = (name: string, key: string): string => resolveMissing(missing, { key, locale, name });\n\n const resolve = (key: string): { readonly locale: Locale; readonly message: CompiledMessage } | undefined => {\n for (const candidate of chain) {\n const message = catalogs.get(candidate)?.get(key);\n\n if (message) return { locale: candidate, message };\n }\n\n return undefined;\n };\n\n const templateFor = (key: string, options: TranslateOptions | PluralOptions): Template | undefined => {\n const found = resolve(key);\n\n if (!found) return undefined;\n\n if (found.message.kind === 'text') {\n if ('count' in options) return undefined;\n\n return found.message.template;\n }\n\n if (!('count' in options) || !Number.isFinite(options.count)) {\n if ('count' in options) throw new LinguaInvalidPluralCountError('`count` must be a finite number.');\n\n return undefined;\n }\n\n const category = pluralCategory(found.locale, options.count, options.ordinal ?? false);\n\n return found.message.forms.get(category) ?? found.message.forms.get('other');\n };\n\n const translate = (key: string, options: TranslateOptions | PluralOptions = {}): string => {\n const template = templateFor(key, options);\n\n if (!template) return missingKey(key);\n\n const values: Record<string, unknown> =\n 'count' in options ? { count: options.count, ...options.values } : (options.values ?? {});\n\n return renderText(template, values, (name) => missingValue(name, key));\n };\n\n return { locale, translate };\n}\n\ntype InternalOptions = {\n readonly fallback?: Locale | readonly Locale[];\n readonly locale?: Locale;\n readonly missing?: MissingStrategy;\n};\n\n/** Creates a fixed-locale translator from one catalog. Lingua snapshots catalog messages during construction. */\nexport function createTranslator<C extends Catalog>(catalog: C, options?: TranslatorOptions): Translator<C> {\n const engine = createTranslateEngine(\n new Map([[canonicalLocale(options?.locale ?? 'en'), compileCatalog(catalog)]]),\n options,\n );\n\n return { locale: engine.locale, translate: engine.translate, translateUnkeyed: engine.translate };\n}\n","import { error as logError } from './_dev';\nimport { canonicalLocale, localeChain } from './_locale';\nimport { createCatalogStore } from './_resources';\nimport { LinguaDisposedError, LinguaMissingCatalogError } from './errors';\nimport { createTranslateEngine, type Translator } from './translator';\nimport type {\n Catalog,\n I18nOptions,\n Locale,\n PluralOptions,\n SerializedI18n,\n SubscribeOptions,\n TranslateOptions,\n} from './types';\n\nexport type I18n<C extends Catalog = Catalog> = Translator<C> & {\n [Symbol.dispose](): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n /** Current locale. Referentially stable until the locale changes, so frameworks can use it directly as a store snapshot. */\n getSnapshot(): Locale;\n isLoaded(options?: { locale?: Locale }): boolean;\n load(options?: { locale?: Locale }): Promise<void>;\n serialize(): SerializedI18n<C>;\n setLocale(locale: Locale): Promise<void>;\n subscribe(listener: (locale: Locale) => void, options?: SubscribeOptions): () => void;\n};\n\nexport function createI18n<C extends Catalog>(options: I18nOptions<C>): I18n<C> {\n const fallbackLocales = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const controller = new AbortController();\n const subscribers = new Set<(locale: Locale) => void>();\n let disposed = false;\n let locale = canonicalLocale(options.locale ?? 'en');\n let transition = 0;\n\n const catalogs = createCatalogStore<C>({ catalogs: options.catalogs, loadCatalog: options.loadCatalog });\n\n const loadTargets = async (target: Locale): Promise<readonly Locale[]> => {\n const changed: Locale[] = [];\n\n for (const candidate of new Set([target, ...fallbackLocales])) {\n if (await catalogs.load(candidate)) changed.push(candidate);\n }\n\n return changed;\n };\n\n const buildEngine = () => {\n const ready = localeChain(locale, fallbackLocales).some(catalogs.isLoaded);\n const effectiveMissing = ready\n ? options.missing\n : (): never => {\n throw new LinguaMissingCatalogError(\n `No catalog loaded for locale \"${locale}\". Call load() before translating.`,\n );\n };\n\n return createTranslateEngine(catalogs.catalogMap(), { ...options, locale, missing: effectiveMissing });\n };\n\n const assertLive = (): void => {\n if (disposed) throw new LinguaDisposedError();\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n subscribers.clear();\n controller.abort();\n };\n\n const notify = (): void => {\n engine = buildEngine();\n\n for (const listener of [...subscribers]) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n };\n\n const relevant = (candidate: Locale): boolean => localeChain(locale, fallbackLocales).includes(candidate);\n\n let engine = buildEngine();\n\n const instance: I18n<C> = {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n getSnapshot() {\n return locale;\n },\n isLoaded(loadOptions) {\n return !disposed && catalogs.isLoaded(loadOptions?.locale ?? locale);\n },\n async load(loadOptions) {\n assertLive();\n\n const targetLocale = canonicalLocale(loadOptions?.locale ?? locale);\n const changed = await loadTargets(targetLocale);\n\n assertLive();\n if (changed.some(relevant)) notify();\n },\n get locale() {\n return locale;\n },\n serialize() {\n assertLive();\n\n return catalogs.serialized(locale);\n },\n async setLocale(nextLocale) {\n assertLive();\n\n const next = canonicalLocale(nextLocale);\n const request = ++transition;\n const changed = await loadTargets(next);\n\n assertLive();\n if (request !== transition) return;\n\n if (next === locale) {\n if (changed.some(relevant)) notify();\n\n return;\n }\n\n locale = next;\n notify();\n },\n subscribe(listener, subscribeOptions) {\n assertLive();\n\n if (subscribeOptions?.signal?.aborted) return () => {};\n\n const unsubscribe = (): void => {\n subscribers.delete(listener);\n subscribeOptions?.signal?.removeEventListener('abort', unsubscribe);\n };\n\n subscribeOptions?.signal?.addEventListener('abort', unsubscribe, { once: true });\n subscribers.add(listener);\n\n if (subscribeOptions?.immediate) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n\n return unsubscribe;\n },\n translate: (key: string, translateOptions?: TranslateOptions | PluralOptions): string =>\n engine.translate(key, translateOptions),\n translateUnkeyed: (key: string, translateOptions?: TranslateOptions | PluralOptions): string =>\n engine.translate(key, translateOptions),\n [Symbol.dispose]: dispose,\n };\n\n return instance;\n}\n"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- var Lingua=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=/\{([\p{ID_Continue}-]+)\}/gu;function compileTemplate(e){let n=[],r=0;for(let i of e.matchAll(t)){let t=i.index??0;t>r&&n.push(e.slice(r,t)),n.push({value:i[1]}),r=t+i[0].length}return r<e.length&&n.push(e.slice(r)),n}function renderText(e,t,n){return e.map(e=>{if(typeof e==`string`)return e;let r=Object.hasOwn(t,e.value)?t[e.value]:void 0;return r==null?n(e.value):String(r)}).join(``)}var LinguaError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},LinguaDisposedError=class extends LinguaError{constructor(){super(`Operation called on a disposed i18n instance.`)}},LinguaInvalidCatalogError=class extends LinguaError{},LinguaInvalidLocaleError=class extends LinguaError{},LinguaInvalidPluralCountError=class extends LinguaError{},LinguaMissingCatalogError=class extends LinguaError{},LinguaMissingKeyError=class extends LinguaError{},LinguaMissingValueError=class extends LinguaError{},n=new Set([`__proto__`,`constructor`,`prototype`]);function isPluralMessage(e){return typeof e==`object`&&!!e&&Object.hasOwn(e,`plural`)}function compileCatalog(e){let t=new Map,visit=(e,r)=>{if(typeof e==`string`)t.set(r,{kind:`text`,template:compileTemplate(e)});else{if(typeof e!=`object`||!e||Array.isArray(e))throw new LinguaInvalidCatalogError(`Catalog node "${r}" must be a string, plural message, or object.`);if(isPluralMessage(e)){if(Reflect.ownKeys(e).some(e=>e!==`plural`))throw new LinguaInvalidCatalogError(`Plural message "${r}" must contain only a "plural" property.`);if(typeof e.plural!=`object`||e.plural===null||Array.isArray(e.plural))throw new LinguaInvalidCatalogError(`Plural message "${r}" must provide an object of string forms.`);let n=new Map;for(let[t,i]of Object.entries(e.plural)){if(typeof i!=`string`)throw new LinguaInvalidCatalogError(`Plural form "${r}.${t}" must be a string.`);n.set(t,compileTemplate(i))}t.set(r,{forms:n,kind:`plural`})}else for(let[t,i]of Object.entries(e)){if(n.has(t))throw new LinguaInvalidCatalogError(`Catalog key "${t}" is reserved.`);visit(i,r?`${r}.${t}`:t)}}};for(let[t,r]of Object.entries(e)){if(n.has(t))throw new LinguaInvalidCatalogError(`Catalog key "${t}" is reserved.`);visit(r,t)}return t}function catalogKeys(e){return[...compileCatalog(e).keys()]}var r=new Map;function canonicalLocale(e){try{let[t]=Intl.getCanonicalLocales(e);if(t)return t}catch{}throw new LinguaInvalidLocaleError(`Invalid BCP 47 locale tag: "${e}".`)}function localeChain(e,t){let n=new Set;for(let r of[e,...t]){let e=r.split(`-`);for(let t=e.length;t>0;t--)n.add(e.slice(0,t).join(`-`))}return[...n]}function pluralCategory(e,t,n){let i=`${e}:${n?`ordinal`:`cardinal`}`,a=r.get(i);return a||(a=new Intl.PluralRules(e,{type:n?`ordinal`:`cardinal`}),r.set(i,a)),a.select(t)}function createCatalogStore(e){let t=new Map,n=new Map,hydrate=(e,n)=>{t.set(canonicalLocale(e),{catalog:n,compiled:compileCatalog(n)})};for(let[t,n]of Object.entries(e.catalogs??{}))hydrate(t,n);let load=async r=>{let i=canonicalLocale(r);if(t.has(i))return!1;let a=n.get(i);if(a)return await a,!1;if(!e.loadCatalog)throw new LinguaMissingCatalogError(`No catalog source provided for locale "${i}".`);let o=Promise.resolve(e.loadCatalog(i)).then(e=>{hydrate(i,e),n.delete(i)},e=>{throw n.delete(i),e});return n.set(i,o),await o,!0};return{catalogMap(){return new Map([...t].map(([e,{compiled:t}])=>[e,t]))},isLoaded(e){return t.has(canonicalLocale(e))},load,serialized(e){let n={};for(let[e,{catalog:r}]of t)n[e]=r;return{catalogs:n,locale:e}}}}function resolveMissing(e,t){if(e===`throw`)throw t.name?new LinguaMissingValueError(`Missing interpolation value "${t.name}" for key "${t.key}" in locale "${t.locale}".`):new LinguaMissingKeyError(`Missing translation key "${t.key}" in locale "${t.locale}".`);return typeof e==`function`?e(t):t.name?`{${t.name}}`:t.key}function createTranslateEngine(e,t={}){let n=canonicalLocale(t.locale??`en`),r=localeChain(n,(Array.isArray(t.fallback)?t.fallback:t.fallback?[t.fallback]:[]).map(canonicalLocale)),i=t.missing,missingKey=e=>resolveMissing(i,{key:e,locale:n}),missingValue=(e,t)=>resolveMissing(i,{key:t,locale:n,name:e}),resolve=t=>{for(let n of r){let r=e.get(n)?.get(t);if(r)return{locale:n,message:r}}},templateFor=(e,t)=>{let n=resolve(e);if(!n)return;if(n.message.kind===`text`)return`count`in t?void 0:n.message.template;if(!(`count`in t)||!Number.isFinite(t.count)){if(`count`in t)throw new LinguaInvalidPluralCountError("`count` must be a finite number.");return}let r=pluralCategory(n.locale,t.count,t.ordinal??!1);return n.message.forms.get(r)??n.message.forms.get(`other`)},translate=(e,t={})=>{let n=templateFor(e,t);return n?renderText(n,`count`in t?{count:t.count,...t.values}:t.values??{},t=>missingValue(t,e)):missingKey(e)};return{locale:n,translate}}function createTranslator(e,t){let n=createTranslateEngine(new Map([[canonicalLocale(t?.locale??`en`),compileCatalog(e)]]),t);return{locale:n.locale,translate:n.translate}}function createI18n(e){let t=(Array.isArray(e.fallback)?e.fallback:e.fallback?[e.fallback]:[]).map(canonicalLocale),n=new AbortController,r=new Set,i=!1,a=canonicalLocale(e.locale??`en`),o=0,s=createCatalogStore({catalogs:e.catalogs,loadCatalog:e.loadCatalog}),loadTargets=async e=>{let n=[];for(let r of new Set([e,...t]))await s.load(r)&&n.push(r);return n},buildEngine=()=>{let n=localeChain(a,t).some(s.isLoaded)?e.missing:()=>{throw new LinguaMissingCatalogError(`No catalog loaded for locale "${a}". Call load() before translating.`)};return createTranslateEngine(s.catalogMap(),{...e,locale:a,missing:n})},assertLive=()=>{if(i)throw new LinguaDisposedError},dispose=()=>{i||(i=!0,r.clear(),n.abort())},notify=()=>{c=buildEngine();for(let e of[...r])try{e(a)}catch{}},relevant=e=>localeChain(a,t).includes(e),c=buildEngine();return{get disposalSignal(){return n.signal},dispose,get disposed(){return i},getSnapshot(){return a},isLoaded(e){return!i&&s.isLoaded(e?.locale??a)},async load(e){assertLive();let t=canonicalLocale(e?.locale??a),n=await loadTargets(t);assertLive(),n.some(relevant)&&notify()},get locale(){return a},serialize(){return assertLive(),s.serialized(a)},async setLocale(e){assertLive();let t=canonicalLocale(e),n=++o,r=await loadTargets(t);assertLive(),n===o&&(t===a?r.some(relevant)&&notify():(a=t,notify()))},subscribe(e,t){if(assertLive(),t?.signal?.aborted)return()=>{};let unsubscribe=()=>{r.delete(e),t?.signal?.removeEventListener(`abort`,unsubscribe)};if(t?.signal?.addEventListener(`abort`,unsubscribe,{once:!0}),r.add(e),t?.immediate)try{e(a)}catch{}return unsubscribe},translate:(e,t)=>c.translate(e,t),[Symbol.dispose]:dispose}}return e.LinguaDisposedError=LinguaDisposedError,e.LinguaError=LinguaError,e.LinguaInvalidCatalogError=LinguaInvalidCatalogError,e.LinguaInvalidLocaleError=LinguaInvalidLocaleError,e.LinguaInvalidPluralCountError=LinguaInvalidPluralCountError,e.LinguaMissingCatalogError=LinguaMissingCatalogError,e.LinguaMissingKeyError=LinguaMissingKeyError,e.LinguaMissingValueError=LinguaMissingValueError,e.catalogKeys=catalogKeys,e.createI18n=createI18n,e.createTranslator=createTranslator,e})({});
1
+ var Lingua=(function(e){Object.defineProperty(e,Symbol.toStringTag,{value:`Module`});var t=/\{([\p{ID_Continue}-]+)\}/gu;function compileTemplate(e){let n=[],r=0;for(let i of e.matchAll(t)){let t=i.index??0;t>r&&n.push(e.slice(r,t)),n.push({value:i[1]}),r=t+i[0].length}return r<e.length&&n.push(e.slice(r)),n}function renderText(e,t,n){return e.map(e=>{if(typeof e==`string`)return e;let r=Object.hasOwn(t,e.value)?t[e.value]:void 0;return r==null?n(e.value):String(r)}).join(``)}var LinguaError=class extends Error{constructor(e,t){super(e,t),this.name=new.target.name,Object.setPrototypeOf(this,new.target.prototype)}},LinguaDisposedError=class extends LinguaError{constructor(){super(`Operation called on a disposed i18n instance.`)}},LinguaInvalidCatalogError=class extends LinguaError{},LinguaInvalidLocaleError=class extends LinguaError{},LinguaInvalidPluralCountError=class extends LinguaError{},LinguaMissingCatalogError=class extends LinguaError{},LinguaMissingKeyError=class extends LinguaError{},LinguaMissingValueError=class extends LinguaError{},n=new Set([`__proto__`,`constructor`,`prototype`]);function isPluralMessage(e){return typeof e==`object`&&!!e&&Object.hasOwn(e,`plural`)}function compileCatalog(e){let t=new Map,visit=(e,r)=>{if(typeof e==`string`)t.set(r,{kind:`text`,template:compileTemplate(e)});else{if(typeof e!=`object`||!e||Array.isArray(e))throw new LinguaInvalidCatalogError(`Catalog node "${r}" must be a string, plural message, or object.`);if(isPluralMessage(e)){if(Reflect.ownKeys(e).some(e=>e!==`plural`))throw new LinguaInvalidCatalogError(`Plural message "${r}" must contain only a "plural" property.`);if(typeof e.plural!=`object`||e.plural===null||Array.isArray(e.plural))throw new LinguaInvalidCatalogError(`Plural message "${r}" must provide an object of string forms.`);let n=new Map;for(let[t,i]of Object.entries(e.plural)){if(typeof i!=`string`)throw new LinguaInvalidCatalogError(`Plural form "${r}.${t}" must be a string.`);n.set(t,compileTemplate(i))}t.set(r,{forms:n,kind:`plural`})}else for(let[t,i]of Object.entries(e)){if(n.has(t))throw new LinguaInvalidCatalogError(`Catalog key "${t}" is reserved.`);visit(i,r?`${r}.${t}`:t)}}};for(let[t,r]of Object.entries(e)){if(n.has(t))throw new LinguaInvalidCatalogError(`Catalog key "${t}" is reserved.`);visit(r,t)}return t}function catalogKeys(e){return[...compileCatalog(e).keys()]}var r=new Map;function canonicalLocale(e){try{let[t]=Intl.getCanonicalLocales(e);if(t)return t}catch{}throw new LinguaInvalidLocaleError(`Invalid BCP 47 locale tag: "${e}".`)}function localeChain(e,t){let n=new Set;for(let r of[e,...t]){let e=r.split(`-`);for(let t=e.length;t>0;t--)n.add(e.slice(0,t).join(`-`))}return[...n]}function pluralCategory(e,t,n){let i=`${e}:${n?`ordinal`:`cardinal`}`,a=r.get(i);return a||(a=new Intl.PluralRules(e,{type:n?`ordinal`:`cardinal`}),r.set(i,a)),a.select(t)}function createCatalogStore(e){let t=new Map,n=new Map,hydrate=(e,n)=>{t.set(canonicalLocale(e),{catalog:n,compiled:compileCatalog(n)})};for(let[t,n]of Object.entries(e.catalogs??{}))hydrate(t,n);let load=async r=>{let i=canonicalLocale(r);if(t.has(i))return!1;let a=n.get(i);if(a)return await a,!1;if(!e.loadCatalog)throw new LinguaMissingCatalogError(`No catalog source provided for locale "${i}".`);let o=Promise.resolve(e.loadCatalog(i)).then(e=>{hydrate(i,e),n.delete(i)},e=>{throw n.delete(i),e});return n.set(i,o),await o,!0};return{catalogMap(){return new Map([...t].map(([e,{compiled:t}])=>[e,t]))},isLoaded(e){return t.has(canonicalLocale(e))},load,serialized(e){let n={};for(let[e,{catalog:r}]of t)n[e]=r;return{catalogs:n,locale:e}}}}function resolveMissing(e,t){if(e===`throw`)throw t.name?new LinguaMissingValueError(`Missing interpolation value "${t.name}" for key "${t.key}" in locale "${t.locale}".`):new LinguaMissingKeyError(`Missing translation key "${t.key}" in locale "${t.locale}".`);return typeof e==`function`?e(t):t.name?`{${t.name}}`:t.key}function createTranslateEngine(e,t={}){let n=canonicalLocale(t.locale??`en`),r=localeChain(n,(Array.isArray(t.fallback)?t.fallback:t.fallback?[t.fallback]:[]).map(canonicalLocale)),i=t.missing,missingKey=e=>resolveMissing(i,{key:e,locale:n}),missingValue=(e,t)=>resolveMissing(i,{key:t,locale:n,name:e}),resolve=t=>{for(let n of r){let r=e.get(n)?.get(t);if(r)return{locale:n,message:r}}},templateFor=(e,t)=>{let n=resolve(e);if(!n)return;if(n.message.kind===`text`)return`count`in t?void 0:n.message.template;if(!(`count`in t)||!Number.isFinite(t.count)){if(`count`in t)throw new LinguaInvalidPluralCountError("`count` must be a finite number.");return}let r=pluralCategory(n.locale,t.count,t.ordinal??!1);return n.message.forms.get(r)??n.message.forms.get(`other`)},translate=(e,t={})=>{let n=templateFor(e,t);return n?renderText(n,`count`in t?{count:t.count,...t.values}:t.values??{},t=>missingValue(t,e)):missingKey(e)};return{locale:n,translate}}function createTranslator(e,t){let n=createTranslateEngine(new Map([[canonicalLocale(t?.locale??`en`),compileCatalog(e)]]),t);return{locale:n.locale,translate:n.translate,translateUnkeyed:n.translate}}function createI18n(e){let t=(Array.isArray(e.fallback)?e.fallback:e.fallback?[e.fallback]:[]).map(canonicalLocale),n=new AbortController,r=new Set,i=!1,a=canonicalLocale(e.locale??`en`),o=0,s=createCatalogStore({catalogs:e.catalogs,loadCatalog:e.loadCatalog}),loadTargets=async e=>{let n=[];for(let r of new Set([e,...t]))await s.load(r)&&n.push(r);return n},buildEngine=()=>{let n=localeChain(a,t).some(s.isLoaded)?e.missing:()=>{throw new LinguaMissingCatalogError(`No catalog loaded for locale "${a}". Call load() before translating.`)};return createTranslateEngine(s.catalogMap(),{...e,locale:a,missing:n})},assertLive=()=>{if(i)throw new LinguaDisposedError},dispose=()=>{i||(i=!0,r.clear(),n.abort())},notify=()=>{c=buildEngine();for(let e of[...r])try{e(a)}catch{}},relevant=e=>localeChain(a,t).includes(e),c=buildEngine();return{get disposalSignal(){return n.signal},dispose,get disposed(){return i},getSnapshot(){return a},isLoaded(e){return!i&&s.isLoaded(e?.locale??a)},async load(e){assertLive();let t=canonicalLocale(e?.locale??a),n=await loadTargets(t);assertLive(),n.some(relevant)&&notify()},get locale(){return a},serialize(){return assertLive(),s.serialized(a)},async setLocale(e){assertLive();let t=canonicalLocale(e),n=++o,r=await loadTargets(t);assertLive(),n===o&&(t===a?r.some(relevant)&&notify():(a=t,notify()))},subscribe(e,t){if(assertLive(),t?.signal?.aborted)return()=>{};let unsubscribe=()=>{r.delete(e),t?.signal?.removeEventListener(`abort`,unsubscribe)};if(t?.signal?.addEventListener(`abort`,unsubscribe,{once:!0}),r.add(e),t?.immediate)try{e(a)}catch{}return unsubscribe},translate:(e,t)=>c.translate(e,t),translateUnkeyed:(e,t)=>c.translate(e,t),[Symbol.dispose]:dispose}}return e.LinguaDisposedError=LinguaDisposedError,e.LinguaError=LinguaError,e.LinguaInvalidCatalogError=LinguaInvalidCatalogError,e.LinguaInvalidLocaleError=LinguaInvalidLocaleError,e.LinguaInvalidPluralCountError=LinguaInvalidPluralCountError,e.LinguaMissingCatalogError=LinguaMissingCatalogError,e.LinguaMissingKeyError=LinguaMissingKeyError,e.LinguaMissingValueError=LinguaMissingValueError,e.catalogKeys=catalogKeys,e.createI18n=createI18n,e.createTranslator=createTranslator,e})({});
2
2
  //# sourceMappingURL=lingua.iife.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"lingua.iife.js","names":["error","exports","interpolation","parts","offset","match","value","index","part","values","missing","message","opts","unsafeKeys","node","messages","prefix","key","forms","form","template","catalog","pluralRules","canonical","locale","chain","candidate","fallback","length","ordinal","rules","count","loaded","tasks","options","requestedLocale","existing","task","error","compiled","catalogs","loadedLocale","strategy","info","found","category","name","engine","fallbackLocales","controller","subscribers","disposed","transition","changed","target","effectiveMissing","listener","loadOptions","targetLocale","next","nextLocale","request","subscribeOptions","translateOptions"],"sources":["../src/_template.ts","../src/errors.ts","../src/_catalog.ts","../src/catalog.ts","../src/_dev.ts","../src/_locale.ts","../src/_resources.ts","../src/translator.ts","../src/i18n.ts"],"sourcesContent":["export type TemplatePart = string | { readonly value: string };\nexport type Template = readonly TemplatePart[];\n\nconst interpolation = /\\{([\\p{ID_Continue}-]+)\\}/gu;\n\nexport function compileTemplate(value: string): Template {\n const parts: TemplatePart[] = [];\n let offset = 0;\n\n for (const match of value.matchAll(interpolation)) {\n const index = match.index ?? 0;\n\n if (index > offset) parts.push(value.slice(offset, index));\n\n parts.push({ value: match[1] });\n offset = index + match[0].length;\n }\n\n if (offset < value.length) parts.push(value.slice(offset));\n\n return parts;\n}\n\nexport function renderText(\n parts: Template,\n values: Record<string, unknown>,\n missing: (name: string) => string,\n): string {\n return parts\n .map((part) => {\n if (typeof part === 'string') return part;\n\n const value = Object.hasOwn(values, part.value) ? values[part.value] : undefined;\n\n return value == null ? missing(part.value) : String(value);\n })\n .join('');\n}\n","export class LinguaError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class LinguaDisposedError extends LinguaError {\n constructor() {\n super('Operation called on a disposed i18n instance.');\n }\n}\n\nexport class LinguaInvalidCatalogError extends LinguaError {}\nexport class LinguaInvalidLocaleError extends LinguaError {}\nexport class LinguaInvalidPluralCountError extends LinguaError {}\nexport class LinguaMissingCatalogError extends LinguaError {}\nexport class LinguaMissingKeyError extends LinguaError {}\nexport class LinguaMissingValueError extends LinguaError {}\n","import { compileTemplate, type Template } from './_template';\nimport { LinguaInvalidCatalogError } from './errors';\nimport type { Catalog, CatalogNode, PluralMessage } from './types';\n\nconst unsafeKeys = new Set(['__proto__', 'constructor', 'prototype']);\n\nexport type CompiledText = { readonly kind: 'text'; readonly template: Template };\nexport type CompiledPlural = { readonly forms: ReadonlyMap<string, Template>; readonly kind: 'plural' };\nexport type CompiledMessage = CompiledPlural | CompiledText;\nexport type CompiledCatalog = ReadonlyMap<string, CompiledMessage>;\n\nexport function isPluralMessage(node: CatalogNode): node is PluralMessage {\n return typeof node === 'object' && node !== null && Object.hasOwn(node, 'plural');\n}\n\n/** Compile explicit catalog nodes once. Nested objects only group keys; plural intent is never inferred. */\nexport function compileCatalog(catalog: Catalog): CompiledCatalog {\n const messages = new Map<string, CompiledMessage>();\n\n const visit = (node: CatalogNode, prefix: string): void => {\n if (typeof node === 'string') {\n messages.set(prefix, { kind: 'text', template: compileTemplate(node) });\n\n return;\n }\n\n if (typeof node !== 'object' || node === null || Array.isArray(node)) {\n throw new LinguaInvalidCatalogError(`Catalog node \"${prefix}\" must be a string, plural message, or object.`);\n }\n\n if (isPluralMessage(node)) {\n if (Reflect.ownKeys(node).some((key) => key !== 'plural')) {\n throw new LinguaInvalidCatalogError(`Plural message \"${prefix}\" must contain only a \"plural\" property.`);\n }\n\n if (typeof node.plural !== 'object' || node.plural === null || Array.isArray(node.plural)) {\n throw new LinguaInvalidCatalogError(`Plural message \"${prefix}\" must provide an object of string forms.`);\n }\n\n const forms = new Map<string, Template>();\n\n for (const [form, template] of Object.entries(node.plural)) {\n if (typeof template !== 'string') {\n throw new LinguaInvalidCatalogError(`Plural form \"${prefix}.${form}\" must be a string.`);\n }\n\n forms.set(form, compileTemplate(template));\n }\n\n messages.set(prefix, { forms, kind: 'plural' });\n\n return;\n }\n\n for (const [key, value] of Object.entries(node)) {\n if (unsafeKeys.has(key)) {\n throw new LinguaInvalidCatalogError(`Catalog key \"${key}\" is reserved.`);\n }\n\n visit(value, prefix ? `${prefix}.${key}` : key);\n }\n };\n\n for (const [key, value] of Object.entries(catalog)) {\n if (unsafeKeys.has(key)) throw new LinguaInvalidCatalogError(`Catalog key \"${key}\" is reserved.`);\n\n visit(value, key);\n }\n\n return messages;\n}\n","import { compileCatalog } from './_catalog';\nimport type { Catalog, MessageKey } from './types';\n\n/** Enumerate every message key in a catalog as a dotted path.\n *\n * Traverses nested grouping objects and explicit `{ plural: ... }` messages,\n * producing the same dotted paths that `MessageKey<C>` represents at the type\n * level. Use this to derive key arrays from the catalog itself instead of\n * maintaining a parallel list that can go stale.\n *\n * Pass any catalog subtree to enumerate group-scoped keys. For an `I18n`\n * instance, pass its current locale catalog explicitly.\n *\n * @example\n * ```ts\n * const messages = {\n * nav: { home: '...', settings: '...' },\n * };\n * const keys = catalogKeys(messages); // ['nav.home', 'nav.settings']\n * const navKeys = catalogKeys(messages.nav); // ['home', 'settings']\n *\n * // From an i18n instance: read the current locale's catalog first\n * const i18n = createI18n({ catalogs: { en: messages }, locale: 'en' });\n * const state = i18n.serialize();\n * const allKeys = catalogKeys(state.catalogs[state.locale]);\n * ```\n */\nexport function catalogKeys<C extends Catalog>(catalog: C): ReadonlyArray<MessageKey<C>> {\n return [...compileCatalog(catalog).keys()] as unknown as ReadonlyArray<MessageKey<C>>;\n}\n","export const isDev = !(globalThis as { __LINGUA_PROD__?: boolean }).__LINGUA_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/lingua] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/lingua] ${msg}`, ...args);\n}\n","import { LinguaInvalidLocaleError } from './errors';\nimport type { Locale } from './types';\n\nconst pluralRules = new Map<string, Intl.PluralRules>();\n\nexport function canonicalLocale(locale: string): Locale {\n try {\n const [canonical] = Intl.getCanonicalLocales(locale);\n\n if (canonical) return canonical;\n } catch {\n // Error below names the failed public input.\n }\n\n throw new LinguaInvalidLocaleError(`Invalid BCP 47 locale tag: \"${locale}\".`);\n}\n\nexport function localeChain(locale: Locale, fallback: readonly Locale[]): readonly Locale[] {\n const chain = new Set<Locale>();\n\n for (const candidate of [locale, ...fallback]) {\n const parts = candidate.split('-');\n\n for (let length = parts.length; length > 0; length--) {\n chain.add(parts.slice(0, length).join('-'));\n }\n }\n\n return [...chain];\n}\n\nexport function pluralCategory(locale: Locale, count: number, ordinal: boolean): Intl.LDMLPluralRule {\n const key = `${locale}:${ordinal ? 'ordinal' : 'cardinal'}`;\n let rules = pluralRules.get(key);\n\n if (!rules) {\n rules = new Intl.PluralRules(locale, { type: ordinal ? 'ordinal' : 'cardinal' });\n pluralRules.set(key, rules);\n }\n\n return rules.select(count);\n}\n","import { type CompiledCatalog, compileCatalog } from './_catalog';\nimport { canonicalLocale } from './_locale';\nimport { LinguaMissingCatalogError } from './errors';\nimport type { Catalog, Catalogs, Locale, SerializedI18n } from './types';\n\ntype CatalogStoreOptions<C extends Catalog> = {\n readonly catalogs?: Catalogs<C>;\n readonly loadCatalog?: (locale: Locale) => Promise<C> | C;\n};\n\n/** One catalog state machine owns loaded catalogs and in-flight loading work. */\nexport function createCatalogStore<C extends Catalog>(options: CatalogStoreOptions<C>) {\n const loaded = new Map<Locale, { readonly catalog: C; readonly compiled: CompiledCatalog }>();\n const tasks = new Map<Locale, Promise<void>>();\n\n const hydrate = (locale: Locale, catalog: C): void => {\n loaded.set(canonicalLocale(locale), { catalog, compiled: compileCatalog(catalog) });\n };\n\n for (const [locale, catalog] of Object.entries(options.catalogs ?? {})) hydrate(locale, catalog);\n\n const load = async (requestedLocale: Locale): Promise<boolean> => {\n const locale = canonicalLocale(requestedLocale);\n\n if (loaded.has(locale)) return false;\n\n const existing = tasks.get(locale);\n\n if (existing) {\n await existing;\n\n return false;\n }\n\n if (!options.loadCatalog) {\n throw new LinguaMissingCatalogError(`No catalog source provided for locale \"${locale}\".`);\n }\n\n const task = Promise.resolve(options.loadCatalog(locale)).then(\n (catalog) => {\n hydrate(locale, catalog);\n tasks.delete(locale);\n },\n (error: unknown) => {\n tasks.delete(locale);\n throw error;\n },\n );\n\n tasks.set(locale, task);\n await task;\n\n return true;\n };\n\n return {\n catalogMap(): ReadonlyMap<Locale, CompiledCatalog> {\n return new Map([...loaded].map(([locale, { compiled }]) => [locale, compiled]));\n },\n isLoaded(locale: Locale): boolean {\n return loaded.has(canonicalLocale(locale));\n },\n load,\n serialized(locale: Locale): SerializedI18n<C> {\n const catalogs: Catalogs<C> = {};\n\n for (const [loadedLocale, { catalog }] of loaded) catalogs[loadedLocale] = catalog;\n\n return { catalogs, locale };\n },\n };\n}\n","import { type CompiledCatalog, type CompiledMessage, compileCatalog } from './_catalog';\nimport { isDev, warn } from './_dev';\nimport { canonicalLocale, localeChain, pluralCategory } from './_locale';\nimport { renderText, type Template } from './_template';\nimport { LinguaInvalidPluralCountError, LinguaMissingKeyError, LinguaMissingValueError } from './errors';\nimport type {\n Catalog,\n Locale,\n MissingInfo,\n MissingStrategy,\n PluralKey,\n PluralOptions,\n TextKey,\n TranslateOptions,\n TranslatorOptions,\n} from './types';\n\nexport type Translator<C extends Catalog = Catalog> = {\n readonly locale: Locale;\n translate(key: TextKey<C>, options?: TranslateOptions): string;\n translate(key: PluralKey<C>, options: PluralOptions): string;\n};\n\n/** Loose translate signature implemented by the engine; public overloads restrict keys per catalog type. */\nexport type LooseTranslate = (key: string, options?: TranslateOptions | PluralOptions) => string;\n\nfunction resolveMissing(strategy: MissingStrategy | undefined, info: MissingInfo): string {\n if (strategy === 'throw') {\n throw info.name\n ? new LinguaMissingValueError(\n `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`,\n )\n : new LinguaMissingKeyError(`Missing translation key \"${info.key}\" in locale \"${info.locale}\".`);\n }\n\n if (typeof strategy === 'function') return strategy(info);\n\n // 'key' or undefined (default)\n if (strategy === undefined && isDev) {\n warn(\n info.name\n ? `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`\n : `Missing translation key \"${info.key}\" in locale \"${info.locale}\".`,\n );\n }\n\n return info.name ? `{${info.name}}` : info.key;\n}\n\n/** Internal engine: resolves and renders any string key against compiled catalogs for a fixed locale. */\nexport function createTranslateEngine(\n catalogs: ReadonlyMap<Locale, CompiledCatalog>,\n options: InternalOptions = {},\n): { readonly locale: Locale; readonly translate: LooseTranslate } {\n const locale = canonicalLocale(options.locale ?? 'en');\n const fallback = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const chain = localeChain(locale, fallback);\n const missing = options.missing;\n\n const missingKey = (key: string): string => resolveMissing(missing, { key, locale });\n const missingValue = (name: string, key: string): string => resolveMissing(missing, { key, locale, name });\n\n const resolve = (key: string): { readonly locale: Locale; readonly message: CompiledMessage } | undefined => {\n for (const candidate of chain) {\n const message = catalogs.get(candidate)?.get(key);\n\n if (message) return { locale: candidate, message };\n }\n\n return undefined;\n };\n\n const templateFor = (key: string, options: TranslateOptions | PluralOptions): Template | undefined => {\n const found = resolve(key);\n\n if (!found) return undefined;\n\n if (found.message.kind === 'text') {\n if ('count' in options) return undefined;\n\n return found.message.template;\n }\n\n if (!('count' in options) || !Number.isFinite(options.count)) {\n if ('count' in options) throw new LinguaInvalidPluralCountError('`count` must be a finite number.');\n\n return undefined;\n }\n\n const category = pluralCategory(found.locale, options.count, options.ordinal ?? false);\n\n return found.message.forms.get(category) ?? found.message.forms.get('other');\n };\n\n const translate = (key: string, options: TranslateOptions | PluralOptions = {}): string => {\n const template = templateFor(key, options);\n\n if (!template) return missingKey(key);\n\n const values: Record<string, unknown> =\n 'count' in options ? { count: options.count, ...options.values } : (options.values ?? {});\n\n return renderText(template, values, (name) => missingValue(name, key));\n };\n\n return { locale, translate };\n}\n\ntype InternalOptions = {\n readonly fallback?: Locale | readonly Locale[];\n readonly locale?: Locale;\n readonly missing?: MissingStrategy;\n};\n\n/** Creates a fixed-locale translator from one catalog. Lingua snapshots catalog messages during construction. */\nexport function createTranslator<C extends Catalog>(catalog: C, options?: TranslatorOptions): Translator<C> {\n const engine = createTranslateEngine(\n new Map([[canonicalLocale(options?.locale ?? 'en'), compileCatalog(catalog)]]),\n options,\n );\n\n return { locale: engine.locale, translate: engine.translate };\n}\n","import { error as logError } from './_dev';\nimport { canonicalLocale, localeChain } from './_locale';\nimport { createCatalogStore } from './_resources';\nimport { LinguaDisposedError, LinguaMissingCatalogError } from './errors';\nimport { createTranslateEngine, type Translator } from './translator';\nimport type {\n Catalog,\n I18nOptions,\n Locale,\n PluralOptions,\n SerializedI18n,\n SubscribeOptions,\n TranslateOptions,\n} from './types';\n\nexport type I18n<C extends Catalog = Catalog> = Translator<C> & {\n [Symbol.dispose](): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n /** Current locale. Referentially stable until the locale changes, so frameworks can use it directly as a store snapshot. */\n getSnapshot(): Locale;\n isLoaded(options?: { locale?: Locale }): boolean;\n load(options?: { locale?: Locale }): Promise<void>;\n serialize(): SerializedI18n<C>;\n setLocale(locale: Locale): Promise<void>;\n subscribe(listener: (locale: Locale) => void, options?: SubscribeOptions): () => void;\n};\n\nexport function createI18n<C extends Catalog>(options: I18nOptions<C>): I18n<C> {\n const fallbackLocales = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const controller = new AbortController();\n const subscribers = new Set<(locale: Locale) => void>();\n let disposed = false;\n let locale = canonicalLocale(options.locale ?? 'en');\n let transition = 0;\n\n const catalogs = createCatalogStore<C>({ catalogs: options.catalogs, loadCatalog: options.loadCatalog });\n\n const loadTargets = async (target: Locale): Promise<readonly Locale[]> => {\n const changed: Locale[] = [];\n\n for (const candidate of new Set([target, ...fallbackLocales])) {\n if (await catalogs.load(candidate)) changed.push(candidate);\n }\n\n return changed;\n };\n\n const buildEngine = () => {\n const ready = localeChain(locale, fallbackLocales).some(catalogs.isLoaded);\n const effectiveMissing = ready\n ? options.missing\n : (): never => {\n throw new LinguaMissingCatalogError(\n `No catalog loaded for locale \"${locale}\". Call load() before translating.`,\n );\n };\n\n return createTranslateEngine(catalogs.catalogMap(), { ...options, locale, missing: effectiveMissing });\n };\n\n const assertLive = (): void => {\n if (disposed) throw new LinguaDisposedError();\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n subscribers.clear();\n controller.abort();\n };\n\n const notify = (): void => {\n engine = buildEngine();\n\n for (const listener of [...subscribers]) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n };\n\n const relevant = (candidate: Locale): boolean => localeChain(locale, fallbackLocales).includes(candidate);\n\n let engine = buildEngine();\n\n const instance: I18n<C> = {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n getSnapshot() {\n return locale;\n },\n isLoaded(loadOptions) {\n return !disposed && catalogs.isLoaded(loadOptions?.locale ?? locale);\n },\n async load(loadOptions) {\n assertLive();\n\n const targetLocale = canonicalLocale(loadOptions?.locale ?? locale);\n const changed = await loadTargets(targetLocale);\n\n assertLive();\n if (changed.some(relevant)) notify();\n },\n get locale() {\n return locale;\n },\n serialize() {\n assertLive();\n\n return catalogs.serialized(locale);\n },\n async setLocale(nextLocale) {\n assertLive();\n\n const next = canonicalLocale(nextLocale);\n const request = ++transition;\n const changed = await loadTargets(next);\n\n assertLive();\n if (request !== transition) return;\n\n if (next === locale) {\n if (changed.some(relevant)) notify();\n\n return;\n }\n\n locale = next;\n notify();\n },\n subscribe(listener, subscribeOptions) {\n assertLive();\n\n if (subscribeOptions?.signal?.aborted) return () => {};\n\n const unsubscribe = (): void => {\n subscribers.delete(listener);\n subscribeOptions?.signal?.removeEventListener('abort', unsubscribe);\n };\n\n subscribeOptions?.signal?.addEventListener('abort', unsubscribe, { once: true });\n subscribers.add(listener);\n\n if (subscribeOptions?.immediate) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n\n return unsubscribe;\n },\n translate: (key: string, translateOptions?: TranslateOptions | PluralOptions): string =>\n engine.translate(key, translateOptions),\n [Symbol.dispose]: dispose,\n };\n\n return instance;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lingua.iife.js","names":["error","exports","interpolation","parts","offset","match","value","index","part","values","missing","message","opts","unsafeKeys","node","messages","prefix","key","forms","form","template","catalog","pluralRules","canonical","locale","chain","candidate","fallback","length","ordinal","rules","count","loaded","tasks","options","requestedLocale","existing","task","error","compiled","catalogs","loadedLocale","strategy","info","found","category","name","engine","fallbackLocales","controller","subscribers","disposed","transition","changed","target","effectiveMissing","listener","loadOptions","targetLocale","next","nextLocale","request","subscribeOptions","translateOptions"],"sources":["../src/_template.ts","../src/errors.ts","../src/_catalog.ts","../src/catalog.ts","../src/_dev.ts","../src/_locale.ts","../src/_resources.ts","../src/translator.ts","../src/i18n.ts"],"sourcesContent":["export type TemplatePart = string | { readonly value: string };\nexport type Template = readonly TemplatePart[];\n\nconst interpolation = /\\{([\\p{ID_Continue}-]+)\\}/gu;\n\nexport function compileTemplate(value: string): Template {\n const parts: TemplatePart[] = [];\n let offset = 0;\n\n for (const match of value.matchAll(interpolation)) {\n const index = match.index ?? 0;\n\n if (index > offset) parts.push(value.slice(offset, index));\n\n parts.push({ value: match[1] });\n offset = index + match[0].length;\n }\n\n if (offset < value.length) parts.push(value.slice(offset));\n\n return parts;\n}\n\nexport function renderText(\n parts: Template,\n values: Record<string, unknown>,\n missing: (name: string) => string,\n): string {\n return parts\n .map((part) => {\n if (typeof part === 'string') return part;\n\n const value = Object.hasOwn(values, part.value) ? values[part.value] : undefined;\n\n return value == null ? missing(part.value) : String(value);\n })\n .join('');\n}\n","export class LinguaError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class LinguaDisposedError extends LinguaError {\n constructor() {\n super('Operation called on a disposed i18n instance.');\n }\n}\n\nexport class LinguaInvalidCatalogError extends LinguaError {}\nexport class LinguaInvalidLocaleError extends LinguaError {}\nexport class LinguaInvalidPluralCountError extends LinguaError {}\nexport class LinguaMissingCatalogError extends LinguaError {}\nexport class LinguaMissingKeyError extends LinguaError {}\nexport class LinguaMissingValueError extends LinguaError {}\n","import { compileTemplate, type Template } from './_template';\nimport { LinguaInvalidCatalogError } from './errors';\nimport type { Catalog, CatalogNode, PluralMessage } from './types';\n\nconst unsafeKeys = new Set(['__proto__', 'constructor', 'prototype']);\n\nexport type CompiledText = { readonly kind: 'text'; readonly template: Template };\nexport type CompiledPlural = { readonly forms: ReadonlyMap<string, Template>; readonly kind: 'plural' };\nexport type CompiledMessage = CompiledPlural | CompiledText;\nexport type CompiledCatalog = ReadonlyMap<string, CompiledMessage>;\n\nexport function isPluralMessage(node: CatalogNode): node is PluralMessage {\n return typeof node === 'object' && node !== null && Object.hasOwn(node, 'plural');\n}\n\n/** Compile explicit catalog nodes once. Nested objects only group keys; plural intent is never inferred. */\nexport function compileCatalog(catalog: Catalog): CompiledCatalog {\n const messages = new Map<string, CompiledMessage>();\n\n const visit = (node: CatalogNode, prefix: string): void => {\n if (typeof node === 'string') {\n messages.set(prefix, { kind: 'text', template: compileTemplate(node) });\n\n return;\n }\n\n if (typeof node !== 'object' || node === null || Array.isArray(node)) {\n throw new LinguaInvalidCatalogError(`Catalog node \"${prefix}\" must be a string, plural message, or object.`);\n }\n\n if (isPluralMessage(node)) {\n if (Reflect.ownKeys(node).some((key) => key !== 'plural')) {\n throw new LinguaInvalidCatalogError(`Plural message \"${prefix}\" must contain only a \"plural\" property.`);\n }\n\n if (typeof node.plural !== 'object' || node.plural === null || Array.isArray(node.plural)) {\n throw new LinguaInvalidCatalogError(`Plural message \"${prefix}\" must provide an object of string forms.`);\n }\n\n const forms = new Map<string, Template>();\n\n for (const [form, template] of Object.entries(node.plural)) {\n if (typeof template !== 'string') {\n throw new LinguaInvalidCatalogError(`Plural form \"${prefix}.${form}\" must be a string.`);\n }\n\n forms.set(form, compileTemplate(template));\n }\n\n messages.set(prefix, { forms, kind: 'plural' });\n\n return;\n }\n\n for (const [key, value] of Object.entries(node)) {\n if (unsafeKeys.has(key)) {\n throw new LinguaInvalidCatalogError(`Catalog key \"${key}\" is reserved.`);\n }\n\n visit(value, prefix ? `${prefix}.${key}` : key);\n }\n };\n\n for (const [key, value] of Object.entries(catalog)) {\n if (unsafeKeys.has(key)) throw new LinguaInvalidCatalogError(`Catalog key \"${key}\" is reserved.`);\n\n visit(value, key);\n }\n\n return messages;\n}\n","import { compileCatalog } from './_catalog';\nimport type { Catalog, MessageKey } from './types';\n\n/** Enumerate every message key in a catalog as a dotted path.\n *\n * Traverses nested grouping objects and explicit `{ plural: ... }` messages,\n * producing the same dotted paths that `MessageKey<C>` represents at the type\n * level. Use this to derive key arrays from the catalog itself instead of\n * maintaining a parallel list that can go stale.\n *\n * Pass any catalog subtree to enumerate group-scoped keys. For an `I18n`\n * instance, pass its current locale catalog explicitly.\n *\n * @example\n * ```ts\n * const messages = {\n * nav: { home: '...', settings: '...' },\n * };\n * const keys = catalogKeys(messages); // ['nav.home', 'nav.settings']\n * const navKeys = catalogKeys(messages.nav); // ['home', 'settings']\n *\n * // From an i18n instance: read the current locale's catalog first\n * const i18n = createI18n({ catalogs: { en: messages }, locale: 'en' });\n * const state = i18n.serialize();\n * const allKeys = catalogKeys(state.catalogs[state.locale]);\n * ```\n */\nexport function catalogKeys<C extends Catalog>(catalog: C): ReadonlyArray<MessageKey<C>> {\n return [...compileCatalog(catalog).keys()] as unknown as ReadonlyArray<MessageKey<C>>;\n}\n","export const isDev = !(globalThis as { __LINGUA_PROD__?: boolean }).__LINGUA_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/lingua] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/lingua] ${msg}`, ...args);\n}\n","import { LinguaInvalidLocaleError } from './errors';\nimport type { Locale } from './types';\n\nconst pluralRules = new Map<string, Intl.PluralRules>();\n\nexport function canonicalLocale(locale: string): Locale {\n try {\n const [canonical] = Intl.getCanonicalLocales(locale);\n\n if (canonical) return canonical;\n } catch {\n // Error below names the failed public input.\n }\n\n throw new LinguaInvalidLocaleError(`Invalid BCP 47 locale tag: \"${locale}\".`);\n}\n\nexport function localeChain(locale: Locale, fallback: readonly Locale[]): readonly Locale[] {\n const chain = new Set<Locale>();\n\n for (const candidate of [locale, ...fallback]) {\n const parts = candidate.split('-');\n\n for (let length = parts.length; length > 0; length--) {\n chain.add(parts.slice(0, length).join('-'));\n }\n }\n\n return [...chain];\n}\n\nexport function pluralCategory(locale: Locale, count: number, ordinal: boolean): Intl.LDMLPluralRule {\n const key = `${locale}:${ordinal ? 'ordinal' : 'cardinal'}`;\n let rules = pluralRules.get(key);\n\n if (!rules) {\n rules = new Intl.PluralRules(locale, { type: ordinal ? 'ordinal' : 'cardinal' });\n pluralRules.set(key, rules);\n }\n\n return rules.select(count);\n}\n","import { type CompiledCatalog, compileCatalog } from './_catalog';\nimport { canonicalLocale } from './_locale';\nimport { LinguaMissingCatalogError } from './errors';\nimport type { Catalog, Catalogs, Locale, SerializedI18n } from './types';\n\ntype CatalogStoreOptions<C extends Catalog> = {\n readonly catalogs?: Catalogs<C>;\n readonly loadCatalog?: (locale: Locale) => Promise<C> | C;\n};\n\n/** One catalog state machine owns loaded catalogs and in-flight loading work. */\nexport function createCatalogStore<C extends Catalog>(options: CatalogStoreOptions<C>) {\n const loaded = new Map<Locale, { readonly catalog: C; readonly compiled: CompiledCatalog }>();\n const tasks = new Map<Locale, Promise<void>>();\n\n const hydrate = (locale: Locale, catalog: C): void => {\n loaded.set(canonicalLocale(locale), { catalog, compiled: compileCatalog(catalog) });\n };\n\n for (const [locale, catalog] of Object.entries(options.catalogs ?? {})) hydrate(locale, catalog);\n\n const load = async (requestedLocale: Locale): Promise<boolean> => {\n const locale = canonicalLocale(requestedLocale);\n\n if (loaded.has(locale)) return false;\n\n const existing = tasks.get(locale);\n\n if (existing) {\n await existing;\n\n return false;\n }\n\n if (!options.loadCatalog) {\n throw new LinguaMissingCatalogError(`No catalog source provided for locale \"${locale}\".`);\n }\n\n const task = Promise.resolve(options.loadCatalog(locale)).then(\n (catalog) => {\n hydrate(locale, catalog);\n tasks.delete(locale);\n },\n (error: unknown) => {\n tasks.delete(locale);\n throw error;\n },\n );\n\n tasks.set(locale, task);\n await task;\n\n return true;\n };\n\n return {\n catalogMap(): ReadonlyMap<Locale, CompiledCatalog> {\n return new Map([...loaded].map(([locale, { compiled }]) => [locale, compiled]));\n },\n isLoaded(locale: Locale): boolean {\n return loaded.has(canonicalLocale(locale));\n },\n load,\n serialized(locale: Locale): SerializedI18n<C> {\n const catalogs: Catalogs<C> = {};\n\n for (const [loadedLocale, { catalog }] of loaded) catalogs[loadedLocale] = catalog;\n\n return { catalogs, locale };\n },\n };\n}\n","import { type CompiledCatalog, type CompiledMessage, compileCatalog } from './_catalog';\nimport { isDev, warn } from './_dev';\nimport { canonicalLocale, localeChain, pluralCategory } from './_locale';\nimport { renderText, type Template } from './_template';\nimport { LinguaInvalidPluralCountError, LinguaMissingKeyError, LinguaMissingValueError } from './errors';\nimport type {\n Catalog,\n Locale,\n MissingInfo,\n MissingStrategy,\n PluralKey,\n PluralOptions,\n TextKey,\n TranslateOptions,\n TranslatorOptions,\n} from './types';\n\nexport type Translator<C extends Catalog = Catalog> = {\n readonly locale: Locale;\n translate(key: TextKey<C>, options?: TranslateOptions): string;\n translate(key: PluralKey<C>, options: PluralOptions): string;\n /**\n * Translates a runtime key that is not in the catalog's typed key set. Identical to\n * `translate` at runtime: the key is looked up the same way and a missing key follows\n * the configured `missing` strategy. The one typed escape hatch for keys that arrive\n * as strings, so consumers never cast around the typed surface.\n */\n translateUnkeyed(key: string, options?: TranslateOptions | PluralOptions): string;\n};\n\n/** Loose translate signature implemented by the engine; public overloads restrict keys per catalog type. */\nexport type LooseTranslate = (key: string, options?: TranslateOptions | PluralOptions) => string;\n\nfunction resolveMissing(strategy: MissingStrategy | undefined, info: MissingInfo): string {\n if (strategy === 'throw') {\n throw info.name\n ? new LinguaMissingValueError(\n `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`,\n )\n : new LinguaMissingKeyError(`Missing translation key \"${info.key}\" in locale \"${info.locale}\".`);\n }\n\n if (typeof strategy === 'function') return strategy(info);\n\n // 'key' or undefined (default)\n if (strategy === undefined && isDev) {\n warn(\n info.name\n ? `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`\n : `Missing translation key \"${info.key}\" in locale \"${info.locale}\".`,\n );\n }\n\n return info.name ? `{${info.name}}` : info.key;\n}\n\n/** Internal engine: resolves and renders any string key against compiled catalogs for a fixed locale. */\nexport function createTranslateEngine(\n catalogs: ReadonlyMap<Locale, CompiledCatalog>,\n options: InternalOptions = {},\n): { readonly locale: Locale; readonly translate: LooseTranslate } {\n const locale = canonicalLocale(options.locale ?? 'en');\n const fallback = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const chain = localeChain(locale, fallback);\n const missing = options.missing;\n\n const missingKey = (key: string): string => resolveMissing(missing, { key, locale });\n const missingValue = (name: string, key: string): string => resolveMissing(missing, { key, locale, name });\n\n const resolve = (key: string): { readonly locale: Locale; readonly message: CompiledMessage } | undefined => {\n for (const candidate of chain) {\n const message = catalogs.get(candidate)?.get(key);\n\n if (message) return { locale: candidate, message };\n }\n\n return undefined;\n };\n\n const templateFor = (key: string, options: TranslateOptions | PluralOptions): Template | undefined => {\n const found = resolve(key);\n\n if (!found) return undefined;\n\n if (found.message.kind === 'text') {\n if ('count' in options) return undefined;\n\n return found.message.template;\n }\n\n if (!('count' in options) || !Number.isFinite(options.count)) {\n if ('count' in options) throw new LinguaInvalidPluralCountError('`count` must be a finite number.');\n\n return undefined;\n }\n\n const category = pluralCategory(found.locale, options.count, options.ordinal ?? false);\n\n return found.message.forms.get(category) ?? found.message.forms.get('other');\n };\n\n const translate = (key: string, options: TranslateOptions | PluralOptions = {}): string => {\n const template = templateFor(key, options);\n\n if (!template) return missingKey(key);\n\n const values: Record<string, unknown> =\n 'count' in options ? { count: options.count, ...options.values } : (options.values ?? {});\n\n return renderText(template, values, (name) => missingValue(name, key));\n };\n\n return { locale, translate };\n}\n\ntype InternalOptions = {\n readonly fallback?: Locale | readonly Locale[];\n readonly locale?: Locale;\n readonly missing?: MissingStrategy;\n};\n\n/** Creates a fixed-locale translator from one catalog. Lingua snapshots catalog messages during construction. */\nexport function createTranslator<C extends Catalog>(catalog: C, options?: TranslatorOptions): Translator<C> {\n const engine = createTranslateEngine(\n new Map([[canonicalLocale(options?.locale ?? 'en'), compileCatalog(catalog)]]),\n options,\n );\n\n return { locale: engine.locale, translate: engine.translate, translateUnkeyed: engine.translate };\n}\n","import { error as logError } from './_dev';\nimport { canonicalLocale, localeChain } from './_locale';\nimport { createCatalogStore } from './_resources';\nimport { LinguaDisposedError, LinguaMissingCatalogError } from './errors';\nimport { createTranslateEngine, type Translator } from './translator';\nimport type {\n Catalog,\n I18nOptions,\n Locale,\n PluralOptions,\n SerializedI18n,\n SubscribeOptions,\n TranslateOptions,\n} from './types';\n\nexport type I18n<C extends Catalog = Catalog> = Translator<C> & {\n [Symbol.dispose](): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n /** Current locale. Referentially stable until the locale changes, so frameworks can use it directly as a store snapshot. */\n getSnapshot(): Locale;\n isLoaded(options?: { locale?: Locale }): boolean;\n load(options?: { locale?: Locale }): Promise<void>;\n serialize(): SerializedI18n<C>;\n setLocale(locale: Locale): Promise<void>;\n subscribe(listener: (locale: Locale) => void, options?: SubscribeOptions): () => void;\n};\n\nexport function createI18n<C extends Catalog>(options: I18nOptions<C>): I18n<C> {\n const fallbackLocales = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const controller = new AbortController();\n const subscribers = new Set<(locale: Locale) => void>();\n let disposed = false;\n let locale = canonicalLocale(options.locale ?? 'en');\n let transition = 0;\n\n const catalogs = createCatalogStore<C>({ catalogs: options.catalogs, loadCatalog: options.loadCatalog });\n\n const loadTargets = async (target: Locale): Promise<readonly Locale[]> => {\n const changed: Locale[] = [];\n\n for (const candidate of new Set([target, ...fallbackLocales])) {\n if (await catalogs.load(candidate)) changed.push(candidate);\n }\n\n return changed;\n };\n\n const buildEngine = () => {\n const ready = localeChain(locale, fallbackLocales).some(catalogs.isLoaded);\n const effectiveMissing = ready\n ? options.missing\n : (): never => {\n throw new LinguaMissingCatalogError(\n `No catalog loaded for locale \"${locale}\". Call load() before translating.`,\n );\n };\n\n return createTranslateEngine(catalogs.catalogMap(), { ...options, locale, missing: effectiveMissing });\n };\n\n const assertLive = (): void => {\n if (disposed) throw new LinguaDisposedError();\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n subscribers.clear();\n controller.abort();\n };\n\n const notify = (): void => {\n engine = buildEngine();\n\n for (const listener of [...subscribers]) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n };\n\n const relevant = (candidate: Locale): boolean => localeChain(locale, fallbackLocales).includes(candidate);\n\n let engine = buildEngine();\n\n const instance: I18n<C> = {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n getSnapshot() {\n return locale;\n },\n isLoaded(loadOptions) {\n return !disposed && catalogs.isLoaded(loadOptions?.locale ?? locale);\n },\n async load(loadOptions) {\n assertLive();\n\n const targetLocale = canonicalLocale(loadOptions?.locale ?? locale);\n const changed = await loadTargets(targetLocale);\n\n assertLive();\n if (changed.some(relevant)) notify();\n },\n get locale() {\n return locale;\n },\n serialize() {\n assertLive();\n\n return catalogs.serialized(locale);\n },\n async setLocale(nextLocale) {\n assertLive();\n\n const next = canonicalLocale(nextLocale);\n const request = ++transition;\n const changed = await loadTargets(next);\n\n assertLive();\n if (request !== transition) return;\n\n if (next === locale) {\n if (changed.some(relevant)) notify();\n\n return;\n }\n\n locale = next;\n notify();\n },\n subscribe(listener, subscribeOptions) {\n assertLive();\n\n if (subscribeOptions?.signal?.aborted) return () => {};\n\n const unsubscribe = (): void => {\n subscribers.delete(listener);\n subscribeOptions?.signal?.removeEventListener('abort', unsubscribe);\n };\n\n subscribeOptions?.signal?.addEventListener('abort', unsubscribe, { once: true });\n subscribers.add(listener);\n\n if (subscribeOptions?.immediate) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n\n return unsubscribe;\n },\n translate: (key: string, translateOptions?: TranslateOptions | PluralOptions): string =>\n engine.translate(key, translateOptions),\n translateUnkeyed: (key: string, translateOptions?: TranslateOptions | PluralOptions): string =>\n engine.translate(key, translateOptions),\n [Symbol.dispose]: dispose,\n };\n\n return instance;\n}\n"],"mappings":"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"}
package/dist/lingua.js CHANGED
@@ -176,7 +176,8 @@ function createTranslator(e, t) {
176
176
  let n = createTranslateEngine(/* @__PURE__ */ new Map([[canonicalLocale(t?.locale ?? "en"), compileCatalog(e)]]), t);
177
177
  return {
178
178
  locale: n.locale,
179
- translate: n.translate
179
+ translate: n.translate,
180
+ translateUnkeyed: n.translate
180
181
  };
181
182
  }
182
183
  // src/i18n.ts
@@ -248,6 +249,7 @@ function createI18n(e) {
248
249
  return unsubscribe;
249
250
  },
250
251
  translate: (e, t) => c.translate(e, t),
252
+ translateUnkeyed: (e, t) => c.translate(e, t),
251
253
  [Symbol.dispose]: dispose
252
254
  };
253
255
  }
@@ -1 +1 @@
1
- {"version":3,"file":"lingua.js","names":["error","interpolation","parts","offset","match","value","index","part","values","missing","message","opts","unsafeKeys","node","messages","prefix","key","forms","form","template","catalog","pluralRules","canonical","locale","chain","candidate","fallback","length","ordinal","rules","count","loaded","tasks","options","requestedLocale","existing","task","error","compiled","catalogs","loadedLocale","strategy","info","found","category","name","engine","fallbackLocales","controller","subscribers","disposed","transition","changed","target","effectiveMissing","listener","loadOptions","targetLocale","next","nextLocale","request","subscribeOptions","translateOptions"],"sources":["../src/_template.ts","../src/errors.ts","../src/_catalog.ts","../src/catalog.ts","../src/_dev.ts","../src/_locale.ts","../src/_resources.ts","../src/translator.ts","../src/i18n.ts"],"sourcesContent":["export type TemplatePart = string | { readonly value: string };\nexport type Template = readonly TemplatePart[];\n\nconst interpolation = /\\{([\\p{ID_Continue}-]+)\\}/gu;\n\nexport function compileTemplate(value: string): Template {\n const parts: TemplatePart[] = [];\n let offset = 0;\n\n for (const match of value.matchAll(interpolation)) {\n const index = match.index ?? 0;\n\n if (index > offset) parts.push(value.slice(offset, index));\n\n parts.push({ value: match[1] });\n offset = index + match[0].length;\n }\n\n if (offset < value.length) parts.push(value.slice(offset));\n\n return parts;\n}\n\nexport function renderText(\n parts: Template,\n values: Record<string, unknown>,\n missing: (name: string) => string,\n): string {\n return parts\n .map((part) => {\n if (typeof part === 'string') return part;\n\n const value = Object.hasOwn(values, part.value) ? values[part.value] : undefined;\n\n return value == null ? missing(part.value) : String(value);\n })\n .join('');\n}\n","export class LinguaError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class LinguaDisposedError extends LinguaError {\n constructor() {\n super('Operation called on a disposed i18n instance.');\n }\n}\n\nexport class LinguaInvalidCatalogError extends LinguaError {}\nexport class LinguaInvalidLocaleError extends LinguaError {}\nexport class LinguaInvalidPluralCountError extends LinguaError {}\nexport class LinguaMissingCatalogError extends LinguaError {}\nexport class LinguaMissingKeyError extends LinguaError {}\nexport class LinguaMissingValueError extends LinguaError {}\n","import { compileTemplate, type Template } from './_template';\nimport { LinguaInvalidCatalogError } from './errors';\nimport type { Catalog, CatalogNode, PluralMessage } from './types';\n\nconst unsafeKeys = new Set(['__proto__', 'constructor', 'prototype']);\n\nexport type CompiledText = { readonly kind: 'text'; readonly template: Template };\nexport type CompiledPlural = { readonly forms: ReadonlyMap<string, Template>; readonly kind: 'plural' };\nexport type CompiledMessage = CompiledPlural | CompiledText;\nexport type CompiledCatalog = ReadonlyMap<string, CompiledMessage>;\n\nexport function isPluralMessage(node: CatalogNode): node is PluralMessage {\n return typeof node === 'object' && node !== null && Object.hasOwn(node, 'plural');\n}\n\n/** Compile explicit catalog nodes once. Nested objects only group keys; plural intent is never inferred. */\nexport function compileCatalog(catalog: Catalog): CompiledCatalog {\n const messages = new Map<string, CompiledMessage>();\n\n const visit = (node: CatalogNode, prefix: string): void => {\n if (typeof node === 'string') {\n messages.set(prefix, { kind: 'text', template: compileTemplate(node) });\n\n return;\n }\n\n if (typeof node !== 'object' || node === null || Array.isArray(node)) {\n throw new LinguaInvalidCatalogError(`Catalog node \"${prefix}\" must be a string, plural message, or object.`);\n }\n\n if (isPluralMessage(node)) {\n if (Reflect.ownKeys(node).some((key) => key !== 'plural')) {\n throw new LinguaInvalidCatalogError(`Plural message \"${prefix}\" must contain only a \"plural\" property.`);\n }\n\n if (typeof node.plural !== 'object' || node.plural === null || Array.isArray(node.plural)) {\n throw new LinguaInvalidCatalogError(`Plural message \"${prefix}\" must provide an object of string forms.`);\n }\n\n const forms = new Map<string, Template>();\n\n for (const [form, template] of Object.entries(node.plural)) {\n if (typeof template !== 'string') {\n throw new LinguaInvalidCatalogError(`Plural form \"${prefix}.${form}\" must be a string.`);\n }\n\n forms.set(form, compileTemplate(template));\n }\n\n messages.set(prefix, { forms, kind: 'plural' });\n\n return;\n }\n\n for (const [key, value] of Object.entries(node)) {\n if (unsafeKeys.has(key)) {\n throw new LinguaInvalidCatalogError(`Catalog key \"${key}\" is reserved.`);\n }\n\n visit(value, prefix ? `${prefix}.${key}` : key);\n }\n };\n\n for (const [key, value] of Object.entries(catalog)) {\n if (unsafeKeys.has(key)) throw new LinguaInvalidCatalogError(`Catalog key \"${key}\" is reserved.`);\n\n visit(value, key);\n }\n\n return messages;\n}\n","import { compileCatalog } from './_catalog';\nimport type { Catalog, MessageKey } from './types';\n\n/** Enumerate every message key in a catalog as a dotted path.\n *\n * Traverses nested grouping objects and explicit `{ plural: ... }` messages,\n * producing the same dotted paths that `MessageKey<C>` represents at the type\n * level. Use this to derive key arrays from the catalog itself instead of\n * maintaining a parallel list that can go stale.\n *\n * Pass any catalog subtree to enumerate group-scoped keys. For an `I18n`\n * instance, pass its current locale catalog explicitly.\n *\n * @example\n * ```ts\n * const messages = {\n * nav: { home: '...', settings: '...' },\n * };\n * const keys = catalogKeys(messages); // ['nav.home', 'nav.settings']\n * const navKeys = catalogKeys(messages.nav); // ['home', 'settings']\n *\n * // From an i18n instance: read the current locale's catalog first\n * const i18n = createI18n({ catalogs: { en: messages }, locale: 'en' });\n * const state = i18n.serialize();\n * const allKeys = catalogKeys(state.catalogs[state.locale]);\n * ```\n */\nexport function catalogKeys<C extends Catalog>(catalog: C): ReadonlyArray<MessageKey<C>> {\n return [...compileCatalog(catalog).keys()] as unknown as ReadonlyArray<MessageKey<C>>;\n}\n","export const isDev = !(globalThis as { __LINGUA_PROD__?: boolean }).__LINGUA_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/lingua] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/lingua] ${msg}`, ...args);\n}\n","import { LinguaInvalidLocaleError } from './errors';\nimport type { Locale } from './types';\n\nconst pluralRules = new Map<string, Intl.PluralRules>();\n\nexport function canonicalLocale(locale: string): Locale {\n try {\n const [canonical] = Intl.getCanonicalLocales(locale);\n\n if (canonical) return canonical;\n } catch {\n // Error below names the failed public input.\n }\n\n throw new LinguaInvalidLocaleError(`Invalid BCP 47 locale tag: \"${locale}\".`);\n}\n\nexport function localeChain(locale: Locale, fallback: readonly Locale[]): readonly Locale[] {\n const chain = new Set<Locale>();\n\n for (const candidate of [locale, ...fallback]) {\n const parts = candidate.split('-');\n\n for (let length = parts.length; length > 0; length--) {\n chain.add(parts.slice(0, length).join('-'));\n }\n }\n\n return [...chain];\n}\n\nexport function pluralCategory(locale: Locale, count: number, ordinal: boolean): Intl.LDMLPluralRule {\n const key = `${locale}:${ordinal ? 'ordinal' : 'cardinal'}`;\n let rules = pluralRules.get(key);\n\n if (!rules) {\n rules = new Intl.PluralRules(locale, { type: ordinal ? 'ordinal' : 'cardinal' });\n pluralRules.set(key, rules);\n }\n\n return rules.select(count);\n}\n","import { type CompiledCatalog, compileCatalog } from './_catalog';\nimport { canonicalLocale } from './_locale';\nimport { LinguaMissingCatalogError } from './errors';\nimport type { Catalog, Catalogs, Locale, SerializedI18n } from './types';\n\ntype CatalogStoreOptions<C extends Catalog> = {\n readonly catalogs?: Catalogs<C>;\n readonly loadCatalog?: (locale: Locale) => Promise<C> | C;\n};\n\n/** One catalog state machine owns loaded catalogs and in-flight loading work. */\nexport function createCatalogStore<C extends Catalog>(options: CatalogStoreOptions<C>) {\n const loaded = new Map<Locale, { readonly catalog: C; readonly compiled: CompiledCatalog }>();\n const tasks = new Map<Locale, Promise<void>>();\n\n const hydrate = (locale: Locale, catalog: C): void => {\n loaded.set(canonicalLocale(locale), { catalog, compiled: compileCatalog(catalog) });\n };\n\n for (const [locale, catalog] of Object.entries(options.catalogs ?? {})) hydrate(locale, catalog);\n\n const load = async (requestedLocale: Locale): Promise<boolean> => {\n const locale = canonicalLocale(requestedLocale);\n\n if (loaded.has(locale)) return false;\n\n const existing = tasks.get(locale);\n\n if (existing) {\n await existing;\n\n return false;\n }\n\n if (!options.loadCatalog) {\n throw new LinguaMissingCatalogError(`No catalog source provided for locale \"${locale}\".`);\n }\n\n const task = Promise.resolve(options.loadCatalog(locale)).then(\n (catalog) => {\n hydrate(locale, catalog);\n tasks.delete(locale);\n },\n (error: unknown) => {\n tasks.delete(locale);\n throw error;\n },\n );\n\n tasks.set(locale, task);\n await task;\n\n return true;\n };\n\n return {\n catalogMap(): ReadonlyMap<Locale, CompiledCatalog> {\n return new Map([...loaded].map(([locale, { compiled }]) => [locale, compiled]));\n },\n isLoaded(locale: Locale): boolean {\n return loaded.has(canonicalLocale(locale));\n },\n load,\n serialized(locale: Locale): SerializedI18n<C> {\n const catalogs: Catalogs<C> = {};\n\n for (const [loadedLocale, { catalog }] of loaded) catalogs[loadedLocale] = catalog;\n\n return { catalogs, locale };\n },\n };\n}\n","import { type CompiledCatalog, type CompiledMessage, compileCatalog } from './_catalog';\nimport { isDev, warn } from './_dev';\nimport { canonicalLocale, localeChain, pluralCategory } from './_locale';\nimport { renderText, type Template } from './_template';\nimport { LinguaInvalidPluralCountError, LinguaMissingKeyError, LinguaMissingValueError } from './errors';\nimport type {\n Catalog,\n Locale,\n MissingInfo,\n MissingStrategy,\n PluralKey,\n PluralOptions,\n TextKey,\n TranslateOptions,\n TranslatorOptions,\n} from './types';\n\nexport type Translator<C extends Catalog = Catalog> = {\n readonly locale: Locale;\n translate(key: TextKey<C>, options?: TranslateOptions): string;\n translate(key: PluralKey<C>, options: PluralOptions): string;\n};\n\n/** Loose translate signature implemented by the engine; public overloads restrict keys per catalog type. */\nexport type LooseTranslate = (key: string, options?: TranslateOptions | PluralOptions) => string;\n\nfunction resolveMissing(strategy: MissingStrategy | undefined, info: MissingInfo): string {\n if (strategy === 'throw') {\n throw info.name\n ? new LinguaMissingValueError(\n `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`,\n )\n : new LinguaMissingKeyError(`Missing translation key \"${info.key}\" in locale \"${info.locale}\".`);\n }\n\n if (typeof strategy === 'function') return strategy(info);\n\n // 'key' or undefined (default)\n if (strategy === undefined && isDev) {\n warn(\n info.name\n ? `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`\n : `Missing translation key \"${info.key}\" in locale \"${info.locale}\".`,\n );\n }\n\n return info.name ? `{${info.name}}` : info.key;\n}\n\n/** Internal engine: resolves and renders any string key against compiled catalogs for a fixed locale. */\nexport function createTranslateEngine(\n catalogs: ReadonlyMap<Locale, CompiledCatalog>,\n options: InternalOptions = {},\n): { readonly locale: Locale; readonly translate: LooseTranslate } {\n const locale = canonicalLocale(options.locale ?? 'en');\n const fallback = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const chain = localeChain(locale, fallback);\n const missing = options.missing;\n\n const missingKey = (key: string): string => resolveMissing(missing, { key, locale });\n const missingValue = (name: string, key: string): string => resolveMissing(missing, { key, locale, name });\n\n const resolve = (key: string): { readonly locale: Locale; readonly message: CompiledMessage } | undefined => {\n for (const candidate of chain) {\n const message = catalogs.get(candidate)?.get(key);\n\n if (message) return { locale: candidate, message };\n }\n\n return undefined;\n };\n\n const templateFor = (key: string, options: TranslateOptions | PluralOptions): Template | undefined => {\n const found = resolve(key);\n\n if (!found) return undefined;\n\n if (found.message.kind === 'text') {\n if ('count' in options) return undefined;\n\n return found.message.template;\n }\n\n if (!('count' in options) || !Number.isFinite(options.count)) {\n if ('count' in options) throw new LinguaInvalidPluralCountError('`count` must be a finite number.');\n\n return undefined;\n }\n\n const category = pluralCategory(found.locale, options.count, options.ordinal ?? false);\n\n return found.message.forms.get(category) ?? found.message.forms.get('other');\n };\n\n const translate = (key: string, options: TranslateOptions | PluralOptions = {}): string => {\n const template = templateFor(key, options);\n\n if (!template) return missingKey(key);\n\n const values: Record<string, unknown> =\n 'count' in options ? { count: options.count, ...options.values } : (options.values ?? {});\n\n return renderText(template, values, (name) => missingValue(name, key));\n };\n\n return { locale, translate };\n}\n\ntype InternalOptions = {\n readonly fallback?: Locale | readonly Locale[];\n readonly locale?: Locale;\n readonly missing?: MissingStrategy;\n};\n\n/** Creates a fixed-locale translator from one catalog. Lingua snapshots catalog messages during construction. */\nexport function createTranslator<C extends Catalog>(catalog: C, options?: TranslatorOptions): Translator<C> {\n const engine = createTranslateEngine(\n new Map([[canonicalLocale(options?.locale ?? 'en'), compileCatalog(catalog)]]),\n options,\n );\n\n return { locale: engine.locale, translate: engine.translate };\n}\n","import { error as logError } from './_dev';\nimport { canonicalLocale, localeChain } from './_locale';\nimport { createCatalogStore } from './_resources';\nimport { LinguaDisposedError, LinguaMissingCatalogError } from './errors';\nimport { createTranslateEngine, type Translator } from './translator';\nimport type {\n Catalog,\n I18nOptions,\n Locale,\n PluralOptions,\n SerializedI18n,\n SubscribeOptions,\n TranslateOptions,\n} from './types';\n\nexport type I18n<C extends Catalog = Catalog> = Translator<C> & {\n [Symbol.dispose](): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n /** Current locale. Referentially stable until the locale changes, so frameworks can use it directly as a store snapshot. */\n getSnapshot(): Locale;\n isLoaded(options?: { locale?: Locale }): boolean;\n load(options?: { locale?: Locale }): Promise<void>;\n serialize(): SerializedI18n<C>;\n setLocale(locale: Locale): Promise<void>;\n subscribe(listener: (locale: Locale) => void, options?: SubscribeOptions): () => void;\n};\n\nexport function createI18n<C extends Catalog>(options: I18nOptions<C>): I18n<C> {\n const fallbackLocales = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const controller = new AbortController();\n const subscribers = new Set<(locale: Locale) => void>();\n let disposed = false;\n let locale = canonicalLocale(options.locale ?? 'en');\n let transition = 0;\n\n const catalogs = createCatalogStore<C>({ catalogs: options.catalogs, loadCatalog: options.loadCatalog });\n\n const loadTargets = async (target: Locale): Promise<readonly Locale[]> => {\n const changed: Locale[] = [];\n\n for (const candidate of new Set([target, ...fallbackLocales])) {\n if (await catalogs.load(candidate)) changed.push(candidate);\n }\n\n return changed;\n };\n\n const buildEngine = () => {\n const ready = localeChain(locale, fallbackLocales).some(catalogs.isLoaded);\n const effectiveMissing = ready\n ? options.missing\n : (): never => {\n throw new LinguaMissingCatalogError(\n `No catalog loaded for locale \"${locale}\". Call load() before translating.`,\n );\n };\n\n return createTranslateEngine(catalogs.catalogMap(), { ...options, locale, missing: effectiveMissing });\n };\n\n const assertLive = (): void => {\n if (disposed) throw new LinguaDisposedError();\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n subscribers.clear();\n controller.abort();\n };\n\n const notify = (): void => {\n engine = buildEngine();\n\n for (const listener of [...subscribers]) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n };\n\n const relevant = (candidate: Locale): boolean => localeChain(locale, fallbackLocales).includes(candidate);\n\n let engine = buildEngine();\n\n const instance: I18n<C> = {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n getSnapshot() {\n return locale;\n },\n isLoaded(loadOptions) {\n return !disposed && catalogs.isLoaded(loadOptions?.locale ?? locale);\n },\n async load(loadOptions) {\n assertLive();\n\n const targetLocale = canonicalLocale(loadOptions?.locale ?? locale);\n const changed = await loadTargets(targetLocale);\n\n assertLive();\n if (changed.some(relevant)) notify();\n },\n get locale() {\n return locale;\n },\n serialize() {\n assertLive();\n\n return catalogs.serialized(locale);\n },\n async setLocale(nextLocale) {\n assertLive();\n\n const next = canonicalLocale(nextLocale);\n const request = ++transition;\n const changed = await loadTargets(next);\n\n assertLive();\n if (request !== transition) return;\n\n if (next === locale) {\n if (changed.some(relevant)) notify();\n\n return;\n }\n\n locale = next;\n notify();\n },\n subscribe(listener, subscribeOptions) {\n assertLive();\n\n if (subscribeOptions?.signal?.aborted) return () => {};\n\n const unsubscribe = (): void => {\n subscribers.delete(listener);\n subscribeOptions?.signal?.removeEventListener('abort', unsubscribe);\n };\n\n subscribeOptions?.signal?.addEventListener('abort', unsubscribe, { once: true });\n subscribers.add(listener);\n\n if (subscribeOptions?.immediate) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n\n return unsubscribe;\n },\n translate: (key: string, translateOptions?: TranslateOptions | PluralOptions): string =>\n engine.translate(key, translateOptions),\n [Symbol.dispose]: dispose,\n };\n\n return instance;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"lingua.js","names":["error","interpolation","parts","offset","match","value","index","part","values","missing","message","opts","unsafeKeys","node","messages","prefix","key","forms","form","template","catalog","pluralRules","canonical","locale","chain","candidate","fallback","length","ordinal","rules","count","loaded","tasks","options","requestedLocale","existing","task","error","compiled","catalogs","loadedLocale","strategy","info","found","category","name","engine","fallbackLocales","controller","subscribers","disposed","transition","changed","target","effectiveMissing","listener","loadOptions","targetLocale","next","nextLocale","request","subscribeOptions","translateOptions"],"sources":["../src/_template.ts","../src/errors.ts","../src/_catalog.ts","../src/catalog.ts","../src/_dev.ts","../src/_locale.ts","../src/_resources.ts","../src/translator.ts","../src/i18n.ts"],"sourcesContent":["export type TemplatePart = string | { readonly value: string };\nexport type Template = readonly TemplatePart[];\n\nconst interpolation = /\\{([\\p{ID_Continue}-]+)\\}/gu;\n\nexport function compileTemplate(value: string): Template {\n const parts: TemplatePart[] = [];\n let offset = 0;\n\n for (const match of value.matchAll(interpolation)) {\n const index = match.index ?? 0;\n\n if (index > offset) parts.push(value.slice(offset, index));\n\n parts.push({ value: match[1] });\n offset = index + match[0].length;\n }\n\n if (offset < value.length) parts.push(value.slice(offset));\n\n return parts;\n}\n\nexport function renderText(\n parts: Template,\n values: Record<string, unknown>,\n missing: (name: string) => string,\n): string {\n return parts\n .map((part) => {\n if (typeof part === 'string') return part;\n\n const value = Object.hasOwn(values, part.value) ? values[part.value] : undefined;\n\n return value == null ? missing(part.value) : String(value);\n })\n .join('');\n}\n","export class LinguaError extends Error {\n constructor(message: string, opts?: ErrorOptions) {\n super(message, opts);\n this.name = new.target.name;\n Object.setPrototypeOf(this, new.target.prototype);\n }\n}\n\nexport class LinguaDisposedError extends LinguaError {\n constructor() {\n super('Operation called on a disposed i18n instance.');\n }\n}\n\nexport class LinguaInvalidCatalogError extends LinguaError {}\nexport class LinguaInvalidLocaleError extends LinguaError {}\nexport class LinguaInvalidPluralCountError extends LinguaError {}\nexport class LinguaMissingCatalogError extends LinguaError {}\nexport class LinguaMissingKeyError extends LinguaError {}\nexport class LinguaMissingValueError extends LinguaError {}\n","import { compileTemplate, type Template } from './_template';\nimport { LinguaInvalidCatalogError } from './errors';\nimport type { Catalog, CatalogNode, PluralMessage } from './types';\n\nconst unsafeKeys = new Set(['__proto__', 'constructor', 'prototype']);\n\nexport type CompiledText = { readonly kind: 'text'; readonly template: Template };\nexport type CompiledPlural = { readonly forms: ReadonlyMap<string, Template>; readonly kind: 'plural' };\nexport type CompiledMessage = CompiledPlural | CompiledText;\nexport type CompiledCatalog = ReadonlyMap<string, CompiledMessage>;\n\nexport function isPluralMessage(node: CatalogNode): node is PluralMessage {\n return typeof node === 'object' && node !== null && Object.hasOwn(node, 'plural');\n}\n\n/** Compile explicit catalog nodes once. Nested objects only group keys; plural intent is never inferred. */\nexport function compileCatalog(catalog: Catalog): CompiledCatalog {\n const messages = new Map<string, CompiledMessage>();\n\n const visit = (node: CatalogNode, prefix: string): void => {\n if (typeof node === 'string') {\n messages.set(prefix, { kind: 'text', template: compileTemplate(node) });\n\n return;\n }\n\n if (typeof node !== 'object' || node === null || Array.isArray(node)) {\n throw new LinguaInvalidCatalogError(`Catalog node \"${prefix}\" must be a string, plural message, or object.`);\n }\n\n if (isPluralMessage(node)) {\n if (Reflect.ownKeys(node).some((key) => key !== 'plural')) {\n throw new LinguaInvalidCatalogError(`Plural message \"${prefix}\" must contain only a \"plural\" property.`);\n }\n\n if (typeof node.plural !== 'object' || node.plural === null || Array.isArray(node.plural)) {\n throw new LinguaInvalidCatalogError(`Plural message \"${prefix}\" must provide an object of string forms.`);\n }\n\n const forms = new Map<string, Template>();\n\n for (const [form, template] of Object.entries(node.plural)) {\n if (typeof template !== 'string') {\n throw new LinguaInvalidCatalogError(`Plural form \"${prefix}.${form}\" must be a string.`);\n }\n\n forms.set(form, compileTemplate(template));\n }\n\n messages.set(prefix, { forms, kind: 'plural' });\n\n return;\n }\n\n for (const [key, value] of Object.entries(node)) {\n if (unsafeKeys.has(key)) {\n throw new LinguaInvalidCatalogError(`Catalog key \"${key}\" is reserved.`);\n }\n\n visit(value, prefix ? `${prefix}.${key}` : key);\n }\n };\n\n for (const [key, value] of Object.entries(catalog)) {\n if (unsafeKeys.has(key)) throw new LinguaInvalidCatalogError(`Catalog key \"${key}\" is reserved.`);\n\n visit(value, key);\n }\n\n return messages;\n}\n","import { compileCatalog } from './_catalog';\nimport type { Catalog, MessageKey } from './types';\n\n/** Enumerate every message key in a catalog as a dotted path.\n *\n * Traverses nested grouping objects and explicit `{ plural: ... }` messages,\n * producing the same dotted paths that `MessageKey<C>` represents at the type\n * level. Use this to derive key arrays from the catalog itself instead of\n * maintaining a parallel list that can go stale.\n *\n * Pass any catalog subtree to enumerate group-scoped keys. For an `I18n`\n * instance, pass its current locale catalog explicitly.\n *\n * @example\n * ```ts\n * const messages = {\n * nav: { home: '...', settings: '...' },\n * };\n * const keys = catalogKeys(messages); // ['nav.home', 'nav.settings']\n * const navKeys = catalogKeys(messages.nav); // ['home', 'settings']\n *\n * // From an i18n instance: read the current locale's catalog first\n * const i18n = createI18n({ catalogs: { en: messages }, locale: 'en' });\n * const state = i18n.serialize();\n * const allKeys = catalogKeys(state.catalogs[state.locale]);\n * ```\n */\nexport function catalogKeys<C extends Catalog>(catalog: C): ReadonlyArray<MessageKey<C>> {\n return [...compileCatalog(catalog).keys()] as unknown as ReadonlyArray<MessageKey<C>>;\n}\n","export const isDev = !(globalThis as { __LINGUA_PROD__?: boolean }).__LINGUA_PROD__;\n\n/** @internal @security Messages may include user-supplied data. */\nexport function warn(msg: string): void {\n if (isDev) console.warn(`[@vielzeug/lingua] ${msg}`);\n}\n\n/** @internal */\nexport function error(msg: string, ...args: unknown[]): void {\n if (isDev) console.error(`[@vielzeug/lingua] ${msg}`, ...args);\n}\n","import { LinguaInvalidLocaleError } from './errors';\nimport type { Locale } from './types';\n\nconst pluralRules = new Map<string, Intl.PluralRules>();\n\nexport function canonicalLocale(locale: string): Locale {\n try {\n const [canonical] = Intl.getCanonicalLocales(locale);\n\n if (canonical) return canonical;\n } catch {\n // Error below names the failed public input.\n }\n\n throw new LinguaInvalidLocaleError(`Invalid BCP 47 locale tag: \"${locale}\".`);\n}\n\nexport function localeChain(locale: Locale, fallback: readonly Locale[]): readonly Locale[] {\n const chain = new Set<Locale>();\n\n for (const candidate of [locale, ...fallback]) {\n const parts = candidate.split('-');\n\n for (let length = parts.length; length > 0; length--) {\n chain.add(parts.slice(0, length).join('-'));\n }\n }\n\n return [...chain];\n}\n\nexport function pluralCategory(locale: Locale, count: number, ordinal: boolean): Intl.LDMLPluralRule {\n const key = `${locale}:${ordinal ? 'ordinal' : 'cardinal'}`;\n let rules = pluralRules.get(key);\n\n if (!rules) {\n rules = new Intl.PluralRules(locale, { type: ordinal ? 'ordinal' : 'cardinal' });\n pluralRules.set(key, rules);\n }\n\n return rules.select(count);\n}\n","import { type CompiledCatalog, compileCatalog } from './_catalog';\nimport { canonicalLocale } from './_locale';\nimport { LinguaMissingCatalogError } from './errors';\nimport type { Catalog, Catalogs, Locale, SerializedI18n } from './types';\n\ntype CatalogStoreOptions<C extends Catalog> = {\n readonly catalogs?: Catalogs<C>;\n readonly loadCatalog?: (locale: Locale) => Promise<C> | C;\n};\n\n/** One catalog state machine owns loaded catalogs and in-flight loading work. */\nexport function createCatalogStore<C extends Catalog>(options: CatalogStoreOptions<C>) {\n const loaded = new Map<Locale, { readonly catalog: C; readonly compiled: CompiledCatalog }>();\n const tasks = new Map<Locale, Promise<void>>();\n\n const hydrate = (locale: Locale, catalog: C): void => {\n loaded.set(canonicalLocale(locale), { catalog, compiled: compileCatalog(catalog) });\n };\n\n for (const [locale, catalog] of Object.entries(options.catalogs ?? {})) hydrate(locale, catalog);\n\n const load = async (requestedLocale: Locale): Promise<boolean> => {\n const locale = canonicalLocale(requestedLocale);\n\n if (loaded.has(locale)) return false;\n\n const existing = tasks.get(locale);\n\n if (existing) {\n await existing;\n\n return false;\n }\n\n if (!options.loadCatalog) {\n throw new LinguaMissingCatalogError(`No catalog source provided for locale \"${locale}\".`);\n }\n\n const task = Promise.resolve(options.loadCatalog(locale)).then(\n (catalog) => {\n hydrate(locale, catalog);\n tasks.delete(locale);\n },\n (error: unknown) => {\n tasks.delete(locale);\n throw error;\n },\n );\n\n tasks.set(locale, task);\n await task;\n\n return true;\n };\n\n return {\n catalogMap(): ReadonlyMap<Locale, CompiledCatalog> {\n return new Map([...loaded].map(([locale, { compiled }]) => [locale, compiled]));\n },\n isLoaded(locale: Locale): boolean {\n return loaded.has(canonicalLocale(locale));\n },\n load,\n serialized(locale: Locale): SerializedI18n<C> {\n const catalogs: Catalogs<C> = {};\n\n for (const [loadedLocale, { catalog }] of loaded) catalogs[loadedLocale] = catalog;\n\n return { catalogs, locale };\n },\n };\n}\n","import { type CompiledCatalog, type CompiledMessage, compileCatalog } from './_catalog';\nimport { isDev, warn } from './_dev';\nimport { canonicalLocale, localeChain, pluralCategory } from './_locale';\nimport { renderText, type Template } from './_template';\nimport { LinguaInvalidPluralCountError, LinguaMissingKeyError, LinguaMissingValueError } from './errors';\nimport type {\n Catalog,\n Locale,\n MissingInfo,\n MissingStrategy,\n PluralKey,\n PluralOptions,\n TextKey,\n TranslateOptions,\n TranslatorOptions,\n} from './types';\n\nexport type Translator<C extends Catalog = Catalog> = {\n readonly locale: Locale;\n translate(key: TextKey<C>, options?: TranslateOptions): string;\n translate(key: PluralKey<C>, options: PluralOptions): string;\n /**\n * Translates a runtime key that is not in the catalog's typed key set. Identical to\n * `translate` at runtime: the key is looked up the same way and a missing key follows\n * the configured `missing` strategy. The one typed escape hatch for keys that arrive\n * as strings, so consumers never cast around the typed surface.\n */\n translateUnkeyed(key: string, options?: TranslateOptions | PluralOptions): string;\n};\n\n/** Loose translate signature implemented by the engine; public overloads restrict keys per catalog type. */\nexport type LooseTranslate = (key: string, options?: TranslateOptions | PluralOptions) => string;\n\nfunction resolveMissing(strategy: MissingStrategy | undefined, info: MissingInfo): string {\n if (strategy === 'throw') {\n throw info.name\n ? new LinguaMissingValueError(\n `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`,\n )\n : new LinguaMissingKeyError(`Missing translation key \"${info.key}\" in locale \"${info.locale}\".`);\n }\n\n if (typeof strategy === 'function') return strategy(info);\n\n // 'key' or undefined (default)\n if (strategy === undefined && isDev) {\n warn(\n info.name\n ? `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`\n : `Missing translation key \"${info.key}\" in locale \"${info.locale}\".`,\n );\n }\n\n return info.name ? `{${info.name}}` : info.key;\n}\n\n/** Internal engine: resolves and renders any string key against compiled catalogs for a fixed locale. */\nexport function createTranslateEngine(\n catalogs: ReadonlyMap<Locale, CompiledCatalog>,\n options: InternalOptions = {},\n): { readonly locale: Locale; readonly translate: LooseTranslate } {\n const locale = canonicalLocale(options.locale ?? 'en');\n const fallback = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const chain = localeChain(locale, fallback);\n const missing = options.missing;\n\n const missingKey = (key: string): string => resolveMissing(missing, { key, locale });\n const missingValue = (name: string, key: string): string => resolveMissing(missing, { key, locale, name });\n\n const resolve = (key: string): { readonly locale: Locale; readonly message: CompiledMessage } | undefined => {\n for (const candidate of chain) {\n const message = catalogs.get(candidate)?.get(key);\n\n if (message) return { locale: candidate, message };\n }\n\n return undefined;\n };\n\n const templateFor = (key: string, options: TranslateOptions | PluralOptions): Template | undefined => {\n const found = resolve(key);\n\n if (!found) return undefined;\n\n if (found.message.kind === 'text') {\n if ('count' in options) return undefined;\n\n return found.message.template;\n }\n\n if (!('count' in options) || !Number.isFinite(options.count)) {\n if ('count' in options) throw new LinguaInvalidPluralCountError('`count` must be a finite number.');\n\n return undefined;\n }\n\n const category = pluralCategory(found.locale, options.count, options.ordinal ?? false);\n\n return found.message.forms.get(category) ?? found.message.forms.get('other');\n };\n\n const translate = (key: string, options: TranslateOptions | PluralOptions = {}): string => {\n const template = templateFor(key, options);\n\n if (!template) return missingKey(key);\n\n const values: Record<string, unknown> =\n 'count' in options ? { count: options.count, ...options.values } : (options.values ?? {});\n\n return renderText(template, values, (name) => missingValue(name, key));\n };\n\n return { locale, translate };\n}\n\ntype InternalOptions = {\n readonly fallback?: Locale | readonly Locale[];\n readonly locale?: Locale;\n readonly missing?: MissingStrategy;\n};\n\n/** Creates a fixed-locale translator from one catalog. Lingua snapshots catalog messages during construction. */\nexport function createTranslator<C extends Catalog>(catalog: C, options?: TranslatorOptions): Translator<C> {\n const engine = createTranslateEngine(\n new Map([[canonicalLocale(options?.locale ?? 'en'), compileCatalog(catalog)]]),\n options,\n );\n\n return { locale: engine.locale, translate: engine.translate, translateUnkeyed: engine.translate };\n}\n","import { error as logError } from './_dev';\nimport { canonicalLocale, localeChain } from './_locale';\nimport { createCatalogStore } from './_resources';\nimport { LinguaDisposedError, LinguaMissingCatalogError } from './errors';\nimport { createTranslateEngine, type Translator } from './translator';\nimport type {\n Catalog,\n I18nOptions,\n Locale,\n PluralOptions,\n SerializedI18n,\n SubscribeOptions,\n TranslateOptions,\n} from './types';\n\nexport type I18n<C extends Catalog = Catalog> = Translator<C> & {\n [Symbol.dispose](): void;\n readonly disposalSignal: AbortSignal;\n dispose(): void;\n readonly disposed: boolean;\n /** Current locale. Referentially stable until the locale changes, so frameworks can use it directly as a store snapshot. */\n getSnapshot(): Locale;\n isLoaded(options?: { locale?: Locale }): boolean;\n load(options?: { locale?: Locale }): Promise<void>;\n serialize(): SerializedI18n<C>;\n setLocale(locale: Locale): Promise<void>;\n subscribe(listener: (locale: Locale) => void, options?: SubscribeOptions): () => void;\n};\n\nexport function createI18n<C extends Catalog>(options: I18nOptions<C>): I18n<C> {\n const fallbackLocales = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const controller = new AbortController();\n const subscribers = new Set<(locale: Locale) => void>();\n let disposed = false;\n let locale = canonicalLocale(options.locale ?? 'en');\n let transition = 0;\n\n const catalogs = createCatalogStore<C>({ catalogs: options.catalogs, loadCatalog: options.loadCatalog });\n\n const loadTargets = async (target: Locale): Promise<readonly Locale[]> => {\n const changed: Locale[] = [];\n\n for (const candidate of new Set([target, ...fallbackLocales])) {\n if (await catalogs.load(candidate)) changed.push(candidate);\n }\n\n return changed;\n };\n\n const buildEngine = () => {\n const ready = localeChain(locale, fallbackLocales).some(catalogs.isLoaded);\n const effectiveMissing = ready\n ? options.missing\n : (): never => {\n throw new LinguaMissingCatalogError(\n `No catalog loaded for locale \"${locale}\". Call load() before translating.`,\n );\n };\n\n return createTranslateEngine(catalogs.catalogMap(), { ...options, locale, missing: effectiveMissing });\n };\n\n const assertLive = (): void => {\n if (disposed) throw new LinguaDisposedError();\n };\n\n const dispose = (): void => {\n if (disposed) return;\n\n disposed = true;\n subscribers.clear();\n controller.abort();\n };\n\n const notify = (): void => {\n engine = buildEngine();\n\n for (const listener of [...subscribers]) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n };\n\n const relevant = (candidate: Locale): boolean => localeChain(locale, fallbackLocales).includes(candidate);\n\n let engine = buildEngine();\n\n const instance: I18n<C> = {\n get disposalSignal() {\n return controller.signal;\n },\n dispose,\n get disposed() {\n return disposed;\n },\n getSnapshot() {\n return locale;\n },\n isLoaded(loadOptions) {\n return !disposed && catalogs.isLoaded(loadOptions?.locale ?? locale);\n },\n async load(loadOptions) {\n assertLive();\n\n const targetLocale = canonicalLocale(loadOptions?.locale ?? locale);\n const changed = await loadTargets(targetLocale);\n\n assertLive();\n if (changed.some(relevant)) notify();\n },\n get locale() {\n return locale;\n },\n serialize() {\n assertLive();\n\n return catalogs.serialized(locale);\n },\n async setLocale(nextLocale) {\n assertLive();\n\n const next = canonicalLocale(nextLocale);\n const request = ++transition;\n const changed = await loadTargets(next);\n\n assertLive();\n if (request !== transition) return;\n\n if (next === locale) {\n if (changed.some(relevant)) notify();\n\n return;\n }\n\n locale = next;\n notify();\n },\n subscribe(listener, subscribeOptions) {\n assertLive();\n\n if (subscribeOptions?.signal?.aborted) return () => {};\n\n const unsubscribe = (): void => {\n subscribers.delete(listener);\n subscribeOptions?.signal?.removeEventListener('abort', unsubscribe);\n };\n\n subscribeOptions?.signal?.addEventListener('abort', unsubscribe, { once: true });\n subscribers.add(listener);\n\n if (subscribeOptions?.immediate) {\n try {\n listener(locale);\n } catch (error) {\n logError('subscriber error', error);\n }\n }\n\n return unsubscribe;\n },\n translate: (key: string, translateOptions?: TranslateOptions | PluralOptions): string =>\n engine.translate(key, translateOptions),\n translateUnkeyed: (key: string, translateOptions?: TranslateOptions | PluralOptions): string =>\n engine.translate(key, translateOptions),\n [Symbol.dispose]: dispose,\n };\n\n return instance;\n}\n"],"mappings":";AAGA,IAAMC,IAAgB;AAEtB,SAAgB,gBAAgB,GAAyB;CACvD,IAAMC,IAAwB,CAAC,GAC3BC,IAAS;CAEb,KAAK,IAAMC,KAASC,EAAM,SAASJ,CAAa,GAAG;EACjD,IAAMK,IAAQF,EAAM,SAAS;EAK7B,AAHIE,IAAQH,KAAQD,EAAM,KAAKG,EAAM,MAAMF,GAAQG,CAAK,CAAC,GAEzDJ,EAAM,KAAK,EAAE,OAAOE,EAAM,GAAG,CAAC,GAC9B,IAASE,IAAQF,EAAM,EAAE,CAAC;CAC5B;CAIA,OAFID,IAASE,EAAM,UAAQH,EAAM,KAAKG,EAAM,MAAMF,CAAM,CAAC,GAElDD;AACT;AAEA,SAAgB,WACd,GACA,GACA,GACQ;CACR,OAAOA,EACJ,KAAK,MAAS;EACb,IAAI,OAAOK,KAAS,UAAU,OAAOA;EAErC,IAAMF,IAAQ,OAAO,OAAOG,GAAQD,EAAK,KAAK,IAAIC,EAAOD,EAAK,SAAS,KAAA;EAEvE,OAAOF,KAAS,OAAOI,EAAQF,EAAK,KAAK,IAAI,OAAOF,CAAK;CAC3D,CAAC,CAAC,CACD,KAAK,EAAE;AACZ;;ACrCA,IAAa,cAAb,cAAiC,MAAM;CACrC,YAAY,GAAiB,GAAqB;EAGhD,AAFA,MAAMK,GAASC,CAAI,GACnB,KAAK,OAAO,WAAW,MACvB,OAAO,eAAe,MAAM,WAAW,SAAS;CAClD;AACF,GAEa,sBAAb,cAAyC,YAAY;CACnD,cAAc;EACZ,MAAM,+CAA+C;CACvD;AACF,GAEa,4BAAb,cAA+C,YAAY,CAAC,GAC/C,2BAAb,cAA8C,YAAY,CAAC,GAC9C,gCAAb,cAAmD,YAAY,CAAC,GACnD,4BAAb,cAA+C,YAAY,CAAC,GAC/C,wBAAb,cAA2C,YAAY,CAAC,GAC3C,0BAAb,cAA6C,YAAY,CAAC,GCfpDC,oBAAa,IAAI,IAAI;CAAC;CAAa;CAAe;AAAW,CAAC;AAOpE,SAAgB,gBAAgB,GAA0C;CACxE,OAAO,OAAOC,KAAS,cAAYA,KAAiB,OAAO,OAAOA,GAAM,QAAQ;AAClF;AAGA,SAAgB,eAAe,GAAmC;CAChE,IAAMC,oBAAW,IAAI,IAA6B,GAE5C,SAAS,GAAmB,MAAyB;EACzD,IAAI,OAAOD,KAAS,UAClB,EAAS,IAAIE,GAAQ;GAAE,MAAM;GAAQ,UAAU,gBAAgBF,CAAI;EAAE,CAAC;OAKxE;OAAI,OAAOA,KAAS,aAAYA,KAAiB,MAAM,QAAQA,CAAI,GACjE,MAAM,IAAI,0BAA0B,iBAAiBE,EAAO,+CAA+C;GAG7G,IAAI,gBAAgBF,CAAI,GAAG;IACzB,IAAI,QAAQ,QAAQA,CAAI,CAAC,CAAC,MAAM,MAAQG,MAAQ,QAAQ,GACtD,MAAM,IAAI,0BAA0B,mBAAmBD,EAAO,yCAAyC;IAGzG,IAAI,OAAOF,EAAK,UAAW,YAAYA,EAAK,WAAW,QAAQ,MAAM,QAAQA,EAAK,MAAM,GACtF,MAAM,IAAI,0BAA0B,mBAAmBE,EAAO,0CAA0C;IAG1G,IAAME,oBAAQ,IAAI,IAAsB;IAExC,KAAK,IAAM,CAACC,GAAMC,MAAa,OAAO,QAAQN,EAAK,MAAM,GAAG;KAC1D,IAAI,OAAOM,KAAa,UACtB,MAAM,IAAI,0BAA0B,gBAAgBJ,EAAO,GAAGG,EAAK,oBAAoB;KAGzF,EAAM,IAAIA,GAAM,gBAAgBC,CAAQ,CAAC;IAC3C;IAEA,EAAS,IAAIJ,GAAQ;KAAE;KAAO,MAAM;IAAS,CAAC;GAGhD,OAEA,KAAK,IAAM,CAACC,GAAKX,MAAU,OAAO,QAAQQ,CAAI,GAAG;IAC/C,IAAID,EAAW,IAAII,CAAG,GACpB,MAAM,IAAI,0BAA0B,gBAAgBA,EAAI,eAAe;IAGzE,MAAMX,GAAOU,IAAS,GAAGA,EAAO,GAAGC,MAAQA,CAAG;GAChD;EAjC6G;CAkC/G;CAEA,KAAK,IAAM,CAACA,GAAKX,MAAU,OAAO,QAAQe,CAAO,GAAG;EAClD,IAAIR,EAAW,IAAII,CAAG,GAAG,MAAM,IAAI,0BAA0B,gBAAgBA,EAAI,eAAe;EAEhG,MAAMX,GAAOW,CAAG;CAClB;CAEA,OAAOF;AACT;;AC3CA,SAAgB,YAA+B,GAA0C;CACvF,OAAO,CAAC,GAAG,eAAeM,CAAO,CAAC,CAAC,KAAK,CAAC;AAC3C;;AE1BA,IAAMC,oBAAc,IAAI,IAA8B;AAEtD,SAAgB,gBAAgB,GAAwB;CACtD,IAAI;EACF,IAAM,CAACC,KAAa,KAAK,oBAAoBC,CAAM;EAEnD,IAAID,GAAW,OAAOA;CACxB,QAAQ,CAER;CAEA,MAAM,IAAI,yBAAyB,+BAA+BC,EAAO,GAAG;AAC9E;AAEA,SAAgB,YAAY,GAAgB,GAAgD;CAC1F,IAAMC,oBAAQ,IAAI,IAAY;CAE9B,KAAK,IAAMC,KAAa,CAACF,GAAQ,GAAGG,CAAQ,GAAG;EAC7C,IAAMxB,IAAQuB,EAAU,MAAM,GAAG;EAEjC,KAAK,IAAIE,IAASzB,EAAM,QAAQyB,IAAS,GAAG,KAC1C,EAAM,IAAIzB,EAAM,MAAM,GAAGyB,CAAM,CAAC,CAAC,KAAK,GAAG,CAAC;CAE9C;CAEA,OAAO,CAAC,GAAGH,CAAK;AAClB;AAEA,SAAgB,eAAe,GAAgB,GAAe,GAAuC;CACnG,IAAMR,IAAM,GAAGO,EAAO,GAAGK,IAAU,YAAY,cAC3CC,IAAQR,EAAY,IAAIL,CAAG;CAO/B,OALKa,MACH,IAAQ,IAAI,KAAK,YAAYN,GAAQ,EAAE,MAAMK,IAAU,YAAY,WAAW,CAAC,GAC/EP,EAAY,IAAIL,GAAKa,CAAK,IAGrBA,EAAM,OAAOC,CAAK;AAC3B;;AC9BA,SAAgB,mBAAsC,GAAiC;CACrF,IAAMC,oBAAS,IAAI,IAAyE,GACtFC,oBAAQ,IAAI,IAA2B,GAEvC,WAAW,GAAgB,MAAqB;EACpD,EAAO,IAAI,gBAAgBT,CAAM,GAAG;GAAE;GAAS,UAAU,eAAeH,CAAO;EAAE,CAAC;CACpF;CAEA,KAAK,IAAM,CAACG,GAAQH,MAAY,OAAO,QAAQa,EAAQ,YAAY,CAAC,CAAC,GAAG,QAAQV,GAAQH,CAAO;CAE/F,IAAM,OAAO,OAAO,MAA8C;EAChE,IAAMG,IAAS,gBAAgBW,CAAe;EAE9C,IAAIH,EAAO,IAAIR,CAAM,GAAG,OAAO;EAE/B,IAAMY,IAAWH,EAAM,IAAIT,CAAM;EAEjC,IAAIY,GAGF,OAFA,MAAMA,GAEC;EAGT,IAAI,CAACF,EAAQ,aACX,MAAM,IAAI,0BAA0B,0CAA0CV,EAAO,GAAG;EAG1F,IAAMa,IAAO,QAAQ,QAAQH,EAAQ,YAAYV,CAAM,CAAC,CAAC,CAAC,MACvD,MAAY;GAEX,AADA,QAAQA,GAAQH,CAAO,GACvBY,EAAM,OAAOT,CAAM;EACrB,IACC,MAAmB;GAElB,MADAS,EAAM,OAAOT,CAAM,GACbc;EACR,CACF;EAKA,OAHAL,EAAM,IAAIT,GAAQa,CAAI,GACtB,MAAMA,GAEC;CACT;CAEA,OAAO;EACL,aAAmD;GACjD,OAAO,IAAI,IAAI,CAAC,GAAGL,CAAM,CAAC,CAAC,KAAK,CAACR,GAAQ,EAAE,mBAAgB,CAACA,GAAQe,CAAQ,CAAC,CAAC;EAChF;EACA,SAAS,GAAyB;GAChC,OAAOP,EAAO,IAAI,gBAAgBR,CAAM,CAAC;EAC3C;EACA;EACA,WAAW,GAAmC;GAC5C,IAAMgB,IAAwB,CAAC;GAE/B,KAAK,IAAM,CAACC,GAAc,EAAE,iBAAcT,GAAQ,EAASS,KAAgBpB;GAE3E,OAAO;IAAE;IAAU;GAAO;EAC5B;CACF;AACF;;ACtCA,SAAS,eAAe,GAAuC,GAA2B;CACxF,IAAIqB,MAAa,SACf,MAAMC,EAAK,OACP,IAAI,wBACF,gCAAgCA,EAAK,KAAK,aAAaA,EAAK,IAAI,eAAeA,EAAK,OAAO,GAC7F,IACA,IAAI,sBAAsB,4BAA4BA,EAAK,IAAI,eAAeA,EAAK,OAAO,GAAG;CAcnG,OAXI,OAAOD,KAAa,aAAmBA,EAASC,CAAI,IAWjDA,EAAK,OAAO,IAAIA,EAAK,KAAK,KAAKA,EAAK;AAC7C;AAGA,SAAgB,sBACd,GACA,IAA2B,CAAC,GACqC;CACjE,IAAMnB,IAAS,gBAAgBU,EAAQ,UAAU,IAAI,GAI/CT,IAAQ,YAAYD,IAFxB,MAAM,QAAQU,EAAQ,QAAQ,IAAIA,EAAQ,WAAWA,EAAQ,WAAW,CAACA,EAAQ,QAAQ,IAAI,CAAC,EAAA,CAC9F,IAAI,eAC4B,CAAQ,GACpCxB,IAAUwB,EAAQ,SAElB,cAAc,MAAwB,eAAexB,GAAS;EAAE;EAAK;CAAO,CAAC,GAC7E,gBAAgB,GAAc,MAAwB,eAAeA,GAAS;EAAE;EAAK;EAAQ;CAAK,CAAC,GAEnG,WAAW,MAA4F;EAC3G,KAAK,IAAMgB,KAAaD,GAAO;GAC7B,IAAMd,IAAU6B,EAAS,IAAId,CAAS,CAAC,EAAE,IAAIT,CAAG;GAEhD,IAAIN,GAAS,OAAO;IAAE,QAAQe;IAAW;GAAQ;EACnD;CAGF,GAEM,eAAe,GAAa,MAAoE;EACpG,IAAMkB,IAAQ,QAAQ3B,CAAG;EAEzB,IAAI,CAAC2B,GAAO;EAEZ,IAAIA,EAAM,QAAQ,SAAS,QAGzB,OAFI,WAAWV,IAAS,SAEjBU,EAAM,QAAQ;EAGvB,IAAI,EAAE,WAAWV,MAAY,CAAC,OAAO,SAASA,EAAQ,KAAK,GAAG;GAC5D,IAAI,WAAWA,GAAS,MAAM,IAAI,8BAA8B,kCAAkC;GAElG;EACF;EAEA,IAAMW,IAAW,eAAeD,EAAM,QAAQV,EAAQ,OAAOA,EAAQ,WAAW,EAAK;EAErF,OAAOU,EAAM,QAAQ,MAAM,IAAIC,CAAQ,KAAKD,EAAM,QAAQ,MAAM,IAAI,OAAO;CAC7E,GAEM,aAAa,GAAa,IAA4C,CAAC,MAAc;EACzF,IAAMxB,IAAW,YAAYH,GAAKiB,CAAO;EAOzC,OALKd,IAKE,WAAWA,GAFhB,WAAWc,IAAU;GAAE,OAAOA,EAAQ;GAAO,GAAGA,EAAQ;EAAO,IAAKA,EAAQ,UAAU,CAAC,IAEpD,MAAS,aAAaY,GAAM7B,CAAG,CAAC,IAL/C,WAAWA,CAAG;CAMtC;CAEA,OAAO;EAAE;EAAQ;CAAU;AAC7B;AASA,SAAgB,iBAAoC,GAAY,GAA4C;CAC1G,IAAM8B,IAAS,sCACb,IAAI,IAAI,CAAC,CAAC,gBAAgBb,GAAS,UAAU,IAAI,GAAG,eAAeb,CAAO,CAAC,CAAC,CAAC,GAC7Ea,CACF;CAEA,OAAO;EAAE,QAAQa,EAAO;EAAQ,WAAWA,EAAO;EAAW,kBAAkBA,EAAO;CAAU;AAClG;;ACtGA,SAAgB,WAA8B,GAAkC;CAC9E,IAAMC,KACJ,MAAM,QAAQd,EAAQ,QAAQ,IAAIA,EAAQ,WAAWA,EAAQ,WAAW,CAACA,EAAQ,QAAQ,IAAI,CAAC,EAAA,CAC9F,IAAI,eAAe,GACfe,IAAa,IAAI,gBAAgB,GACjCC,oBAAc,IAAI,IAA8B,GAClDC,IAAW,IACX3B,IAAS,gBAAgBU,EAAQ,UAAU,IAAI,GAC/CkB,IAAa,GAEXZ,IAAW,mBAAsB;EAAE,UAAUN,EAAQ;EAAU,aAAaA,EAAQ;CAAY,CAAC,GAEjG,cAAc,OAAO,MAA+C;EACxE,IAAMmB,IAAoB,CAAC;EAE3B,KAAK,IAAM3B,qBAAa,IAAI,IAAI,CAAC4B,GAAQ,GAAGN,CAAe,CAAC,GAC1D,AAAI,MAAMR,EAAS,KAAKd,CAAS,KAAG2B,EAAQ,KAAK3B,CAAS;EAG5D,OAAO2B;CACT,GAEM,oBAAoB;EAExB,IAAME,IADQ,YAAY/B,GAAQwB,CAAe,CAAC,CAAC,KAAKR,EAAS,QACxC,IACrBN,EAAQ,gBACK;GACX,MAAM,IAAI,0BACR,iCAAiCV,EAAO,mCAC1C;EACF;EAEJ,OAAO,sBAAsBgB,EAAS,WAAW,GAAG;GAAE,GAAGN;GAAS;GAAQ,SAASqB;EAAiB,CAAC;CACvG,GAEM,mBAAyB;EAC7B,IAAIJ,GAAU,MAAM,IAAI,oBAAoB;CAC9C,GAEM,gBAAsB;EAC1B,AAAIA,MAEJ,IAAW,IACXD,EAAY,MAAM,GAClBD,EAAW,MAAM;CACnB,GAEM,eAAqB;EACzB,IAAS,YAAY;EAErB,KAAK,IAAMO,KAAY,CAAC,GAAGN,CAAW,GACpC,IAAI;GACF,EAAS1B,CAAM;EACjB,QAAgB,CAEhB;CAEJ,GAEM,YAAY,MAA+B,YAAYA,GAAQwB,CAAe,CAAC,CAAC,SAAStB,CAAS,GAEpGqB,IAAS,YAAY;CAkFzB,OAAO;EA/EL,IAAI,iBAAiB;GACnB,OAAOE,EAAW;EACpB;EACA;EACA,IAAI,WAAW;GACb,OAAOE;EACT;EACA,cAAc;GACZ,OAAO3B;EACT;EACA,SAAS,GAAa;GACpB,OAAO,CAAC2B,KAAYX,EAAS,SAASiB,GAAa,UAAUjC,CAAM;EACrE;EACA,MAAM,KAAK,GAAa;GACtB,WAAW;GAEX,IAAMkC,IAAe,gBAAgBD,GAAa,UAAUjC,CAAM,GAC5D6B,IAAU,MAAM,YAAYK,CAAY;GAG9C,AADA,WAAW,GACPL,EAAQ,KAAK,QAAQ,KAAG,OAAO;EACrC;EACA,IAAI,SAAS;GACX,OAAO7B;EACT;EACA,YAAY;GAGV,OAFA,WAAW,GAEJgB,EAAS,WAAWhB,CAAM;EACnC;EACA,MAAM,UAAU,GAAY;GAC1B,WAAW;GAEX,IAAMmC,IAAO,gBAAgBC,CAAU,GACjCC,IAAU,EAAET,GACZC,IAAU,MAAM,YAAYM,CAAI;GAGtC,AADA,WAAW,GACPE,MAAYT,MAEZO,MAASnC,IACP6B,EAAQ,KAAK,QAAQ,KAAG,OAAO,KAKrC,IAASM,GACT,OAAO;EACT;EACA,UAAU,GAAU,GAAkB;GAGpC,IAFA,WAAW,GAEPG,GAAkB,QAAQ,SAAS,aAAa,CAAC;GAErD,IAAM,oBAA0B;IAE9B,AADAZ,EAAY,OAAOM,CAAQ,GAC3BM,GAAkB,QAAQ,oBAAoB,SAAS,WAAW;GACpE;GAKA,IAHAA,GAAkB,QAAQ,iBAAiB,SAAS,aAAa,EAAE,MAAM,GAAK,CAAC,GAC/EZ,EAAY,IAAIM,CAAQ,GAEpBM,GAAkB,WACpB,IAAI;IACF,EAAStC,CAAM;GACjB,QAAgB,CAEhB;GAGF,OAAO;EACT;EACA,YAAY,GAAa,MACvBuB,EAAO,UAAU9B,GAAK8C,CAAgB;EACxC,mBAAmB,GAAa,MAC9BhB,EAAO,UAAU9B,GAAK8C,CAAgB;GACvC,OAAO,UAAU;CAGb;AACT"}
package/dist/ripple.cjs CHANGED
@@ -1,2 +1,2 @@
1
- Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@vielzeug/ripple");function createReactiveI18n(t){let n=(0,e.fromSubscribable)({getSnapshot:()=>t.getSnapshot(),subscribe:e=>t.subscribe(()=>e())}),translate=(e,r)=>(n.value,t.translate(e,r));return{i18n:t,locale:n,translate}}exports.createReactiveI18n=createReactiveI18n;
1
+ Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});let e=require("@vielzeug/ripple");function createReactiveI18n(t){let n=(0,e.fromSubscribable)({getSnapshot:()=>t.getSnapshot(),subscribe:e=>t.subscribe(()=>e())}),translate=(e,r)=>(n.value,t.translate(e,r)),translateUnkeyed=(e,r)=>(n.value,t.translateUnkeyed(e,r));return{i18n:t,locale:n,translate,translateUnkeyed}}exports.createReactiveI18n=createReactiveI18n;
2
2
  //# sourceMappingURL=ripple.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"ripple.cjs","names":["fromSubscribable","_vielzeug_ripple","locale","i18n","listener","key","options"],"sources":["../src/ripple.ts"],"sourcesContent":["import { fromSubscribable, type Readable } from '@vielzeug/ripple';\n\nimport type { I18n } from './i18n';\nimport type { Translator } from './translator';\nimport type { Catalog, Locale, PluralOptions, TranslateOptions } from './types';\n\n/** A lingua instance mirrored into ripple readables so templates react to locale changes. */\nexport interface ReactiveI18n<C extends Catalog> {\n /** The underlying instance for imperative work (`setLocale`, `load`, disposal). */\n readonly i18n: I18n<C>;\n /** Reactive locale: reading it inside a computed/template registers the dependency. */\n readonly locale: Readable<Locale>;\n /** Translate a text or plural key; re-evaluates whenever the locale changes. */\n readonly translate: Translator<C>['translate'];\n}\n\n/**\n * Bridges a lingua instance into ripple so translated strings are reactive.\n *\n * `i18n.translate()` is not ripple-reactive on its own: reading it inside a `computed()`\n * computes once and never re-runs, since it registers no tracked dependency. The bridge's\n * `translate` reads the reactive {@link ReactiveI18n.locale} first, so any binding built on\n * it re-evaluates the instant `setLocale()` resolves.\n */\nexport function createReactiveI18n<C extends Catalog>(i18n: I18n<C>): ReactiveI18n<C> {\n const locale = fromSubscribable<Locale>({\n getSnapshot: () => i18n.getSnapshot(),\n subscribe: (listener) => i18n.subscribe(() => listener()),\n });\n\n const translate = (key: string, options?: TranslateOptions | PluralOptions): string => {\n void locale.value;\n return i18n.translate(key as never, options as never);\n };\n\n return { i18n, locale, translate: translate as ReactiveI18n<C>['translate'] };\n}\n"],"mappings":"A,qGAwBA,SAAgB,mBAAsC,EAAgC,CACpF,IAAME,GAAA,EAASF,EAAAA,iBAAAA,CAAyB,CACtC,gBAAmBG,EAAK,YAAY,EACpC,UAAY,GAAaA,EAAK,cAAgBC,EAAS,CAAC,CAC1D,CAAC,EAEK,WAAa,EAAa,KAC9BF,EAAY,MACLC,EAAK,UAAUE,EAAcC,CAAgB,GAGtD,MAAO,CAAE,OAAM,SAAmB,SAA0C,CAC9E,C"}
1
+ {"version":3,"file":"ripple.cjs","names":["fromSubscribable","_vielzeug_ripple","locale","i18n","listener","key","options"],"sources":["../src/ripple.ts"],"sourcesContent":["import { fromSubscribable, type Readable } from '@vielzeug/ripple';\n\nimport type { I18n } from './i18n';\nimport type { Translator } from './translator';\nimport type { Catalog, Locale, PluralOptions, TranslateOptions } from './types';\n\n/** A lingua instance mirrored into ripple readables so templates react to locale changes. */\nexport interface ReactiveI18n<C extends Catalog> {\n /** The underlying instance for imperative work (`setLocale`, `load`, disposal). */\n readonly i18n: I18n<C>;\n /** Reactive locale: reading it inside a computed/template registers the dependency. */\n readonly locale: Readable<Locale>;\n /** Translate a text or plural key; re-evaluates whenever the locale changes. */\n readonly translate: Translator<C>['translate'];\n /** Translate a runtime key outside the typed key set; re-evaluates whenever the locale changes. */\n readonly translateUnkeyed: (key: string, options?: TranslateOptions | PluralOptions) => string;\n}\n\n/**\n * Bridges a lingua instance into ripple so translated strings are reactive.\n *\n * `i18n.translate()` is not ripple-reactive on its own: reading it inside a `computed()`\n * computes once and never re-runs, since it registers no tracked dependency. The bridge's\n * `translate` reads the reactive {@link ReactiveI18n.locale} first, so any binding built on\n * it re-evaluates the instant `setLocale()` resolves.\n */\nexport function createReactiveI18n<C extends Catalog>(i18n: I18n<C>): ReactiveI18n<C> {\n const locale = fromSubscribable<Locale>({\n getSnapshot: () => i18n.getSnapshot(),\n subscribe: (listener) => i18n.subscribe(() => listener()),\n });\n\n const translate = (key: string, options?: TranslateOptions | PluralOptions): string => {\n void locale.value;\n return i18n.translate(key as never, options as never);\n };\n\n const translateUnkeyed = (key: string, options?: TranslateOptions | PluralOptions): string => {\n void locale.value;\n return i18n.translateUnkeyed(key, options);\n };\n\n return { i18n, locale, translate: translate as ReactiveI18n<C>['translate'], translateUnkeyed };\n}\n"],"mappings":"A,qGA0BA,SAAgB,mBAAsC,EAAgC,CACpF,IAAME,GAAA,EAASF,EAAAA,iBAAAA,CAAyB,CACtC,gBAAmBG,EAAK,YAAY,EACpC,UAAY,GAAaA,EAAK,cAAgBC,EAAS,CAAC,CAC1D,CAAC,EAEK,WAAa,EAAa,KAC9BF,EAAY,MACLC,EAAK,UAAUE,EAAcC,CAAgB,GAGhD,kBAAoB,EAAa,KACrCJ,EAAY,MACLC,EAAK,iBAAiBE,EAAKC,CAAO,GAG3C,MAAO,CAAE,OAAM,SAAmB,UAA2C,gBAAiB,CAChG,C"}
package/dist/ripple.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { type Readable } from '@vielzeug/ripple';
2
2
  import type { I18n } from './i18n';
3
3
  import type { Translator } from './translator';
4
- import type { Catalog, Locale } from './types';
4
+ import type { Catalog, Locale, PluralOptions, TranslateOptions } from './types';
5
5
  /** A lingua instance mirrored into ripple readables so templates react to locale changes. */
6
6
  export interface ReactiveI18n<C extends Catalog> {
7
7
  /** The underlying instance for imperative work (`setLocale`, `load`, disposal). */
@@ -10,6 +10,8 @@ export interface ReactiveI18n<C extends Catalog> {
10
10
  readonly locale: Readable<Locale>;
11
11
  /** Translate a text or plural key; re-evaluates whenever the locale changes. */
12
12
  readonly translate: Translator<C>['translate'];
13
+ /** Translate a runtime key outside the typed key set; re-evaluates whenever the locale changes. */
14
+ readonly translateUnkeyed: (key: string, options?: TranslateOptions | PluralOptions) => string;
13
15
  }
14
16
  /**
15
17
  * Bridges a lingua instance into ripple so translated strings are reactive.
@@ -1 +1 @@
1
- {"version":3,"file":"ripple.d.ts","sourceRoot":"","sources":["../src/ripple.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEnE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAmC,MAAM,SAAS,CAAC;AAEhF,6FAA6F;AAC7F,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,OAAO;IAC7C,mFAAmF;IACnF,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACvB,uFAAuF;IACvF,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClC,gFAAgF;IAChF,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;CAChD;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAYpF"}
1
+ {"version":3,"file":"ripple.d.ts","sourceRoot":"","sources":["../src/ripple.ts"],"names":[],"mappings":"AAAA,OAAO,EAAoB,KAAK,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAEnE,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AACnC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhF,6FAA6F;AAC7F,MAAM,WAAW,YAAY,CAAC,CAAC,SAAS,OAAO;IAC7C,mFAAmF;IACnF,QAAQ,CAAC,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC;IACvB,uFAAuF;IACvF,QAAQ,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAClC,gFAAgF;IAChF,QAAQ,CAAC,SAAS,EAAE,UAAU,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC;IAC/C,mGAAmG;IACnG,QAAQ,CAAC,gBAAgB,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,aAAa,KAAK,MAAM,CAAC;CAChG;AAED;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,CAAC,SAAS,OAAO,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC,CAAC,GAAG,YAAY,CAAC,CAAC,CAAC,CAiBpF"}
package/dist/ripple.js CHANGED
@@ -4,11 +4,12 @@ function createReactiveI18n(t) {
4
4
  let n = e({
5
5
  getSnapshot: () => t.getSnapshot(),
6
6
  subscribe: (e) => t.subscribe(() => e())
7
- }), translate = (e, r) => (n.value, t.translate(e, r));
7
+ }), translate = (e, r) => (n.value, t.translate(e, r)), translateUnkeyed = (e, r) => (n.value, t.translateUnkeyed(e, r));
8
8
  return {
9
9
  i18n: t,
10
10
  locale: n,
11
- translate
11
+ translate,
12
+ translateUnkeyed
12
13
  };
13
14
  }
14
15
  export { createReactiveI18n };
@@ -1 +1 @@
1
- {"version":3,"file":"ripple.js","names":["locale","fromSubscribable","i18n","listener","key","options"],"sources":["../src/ripple.ts"],"sourcesContent":["import { fromSubscribable, type Readable } from '@vielzeug/ripple';\n\nimport type { I18n } from './i18n';\nimport type { Translator } from './translator';\nimport type { Catalog, Locale, PluralOptions, TranslateOptions } from './types';\n\n/** A lingua instance mirrored into ripple readables so templates react to locale changes. */\nexport interface ReactiveI18n<C extends Catalog> {\n /** The underlying instance for imperative work (`setLocale`, `load`, disposal). */\n readonly i18n: I18n<C>;\n /** Reactive locale: reading it inside a computed/template registers the dependency. */\n readonly locale: Readable<Locale>;\n /** Translate a text or plural key; re-evaluates whenever the locale changes. */\n readonly translate: Translator<C>['translate'];\n}\n\n/**\n * Bridges a lingua instance into ripple so translated strings are reactive.\n *\n * `i18n.translate()` is not ripple-reactive on its own: reading it inside a `computed()`\n * computes once and never re-runs, since it registers no tracked dependency. The bridge's\n * `translate` reads the reactive {@link ReactiveI18n.locale} first, so any binding built on\n * it re-evaluates the instant `setLocale()` resolves.\n */\nexport function createReactiveI18n<C extends Catalog>(i18n: I18n<C>): ReactiveI18n<C> {\n const locale = fromSubscribable<Locale>({\n getSnapshot: () => i18n.getSnapshot(),\n subscribe: (listener) => i18n.subscribe(() => listener()),\n });\n\n const translate = (key: string, options?: TranslateOptions | PluralOptions): string => {\n void locale.value;\n return i18n.translate(key as never, options as never);\n };\n\n return { i18n, locale, translate: translate as ReactiveI18n<C>['translate'] };\n}\n"],"mappings":";;AAwBA,SAAgB,mBAAsC,GAAgC;CACpF,IAAMA,IAASC,EAAyB;EACtC,mBAAmBC,EAAK,YAAY;EACpC,YAAY,MAAaA,EAAK,gBAAgBC,EAAS,CAAC;CAC1D,CAAC,GAEK,aAAa,GAAa,OAC9BH,EAAY,OACLE,EAAK,UAAUE,GAAcC,CAAgB;CAGtD,OAAO;EAAE;EAAM;EAAmB;CAA0C;AAC9E"}
1
+ {"version":3,"file":"ripple.js","names":["locale","fromSubscribable","i18n","listener","key","options"],"sources":["../src/ripple.ts"],"sourcesContent":["import { fromSubscribable, type Readable } from '@vielzeug/ripple';\n\nimport type { I18n } from './i18n';\nimport type { Translator } from './translator';\nimport type { Catalog, Locale, PluralOptions, TranslateOptions } from './types';\n\n/** A lingua instance mirrored into ripple readables so templates react to locale changes. */\nexport interface ReactiveI18n<C extends Catalog> {\n /** The underlying instance for imperative work (`setLocale`, `load`, disposal). */\n readonly i18n: I18n<C>;\n /** Reactive locale: reading it inside a computed/template registers the dependency. */\n readonly locale: Readable<Locale>;\n /** Translate a text or plural key; re-evaluates whenever the locale changes. */\n readonly translate: Translator<C>['translate'];\n /** Translate a runtime key outside the typed key set; re-evaluates whenever the locale changes. */\n readonly translateUnkeyed: (key: string, options?: TranslateOptions | PluralOptions) => string;\n}\n\n/**\n * Bridges a lingua instance into ripple so translated strings are reactive.\n *\n * `i18n.translate()` is not ripple-reactive on its own: reading it inside a `computed()`\n * computes once and never re-runs, since it registers no tracked dependency. The bridge's\n * `translate` reads the reactive {@link ReactiveI18n.locale} first, so any binding built on\n * it re-evaluates the instant `setLocale()` resolves.\n */\nexport function createReactiveI18n<C extends Catalog>(i18n: I18n<C>): ReactiveI18n<C> {\n const locale = fromSubscribable<Locale>({\n getSnapshot: () => i18n.getSnapshot(),\n subscribe: (listener) => i18n.subscribe(() => listener()),\n });\n\n const translate = (key: string, options?: TranslateOptions | PluralOptions): string => {\n void locale.value;\n return i18n.translate(key as never, options as never);\n };\n\n const translateUnkeyed = (key: string, options?: TranslateOptions | PluralOptions): string => {\n void locale.value;\n return i18n.translateUnkeyed(key, options);\n };\n\n return { i18n, locale, translate: translate as ReactiveI18n<C>['translate'], translateUnkeyed };\n}\n"],"mappings":";;AA0BA,SAAgB,mBAAsC,GAAgC;CACpF,IAAMA,IAASC,EAAyB;EACtC,mBAAmBC,EAAK,YAAY;EACpC,YAAY,MAAaA,EAAK,gBAAgBC,EAAS,CAAC;CAC1D,CAAC,GAEK,aAAa,GAAa,OAC9BH,EAAY,OACLE,EAAK,UAAUE,GAAcC,CAAgB,IAGhD,oBAAoB,GAAa,OACrCL,EAAY,OACLE,EAAK,iBAAiBE,GAAKC,CAAO;CAG3C,OAAO;EAAE;EAAM;EAAmB;EAA2C;CAAiB;AAChG"}
@@ -1,2 +1,2 @@
1
- const e=require("./_catalog.cjs");require("./_dev.cjs");const t=require("./_locale.cjs"),n=require("./_template.cjs"),r=require("./errors.cjs");function resolveMissing(e,t){if(e===`throw`)throw t.name?new r.LinguaMissingValueError(`Missing interpolation value "${t.name}" for key "${t.key}" in locale "${t.locale}".`):new r.LinguaMissingKeyError(`Missing translation key "${t.key}" in locale "${t.locale}".`);return typeof e==`function`?e(t):t.name?`{${t.name}}`:t.key}function createTranslateEngine(e,i={}){let a=t.canonicalLocale(i.locale??`en`),o=(Array.isArray(i.fallback)?i.fallback:i.fallback?[i.fallback]:[]).map(t.canonicalLocale),s=t.localeChain(a,o),c=i.missing,missingKey=e=>resolveMissing(c,{key:e,locale:a}),missingValue=(e,t)=>resolveMissing(c,{key:t,locale:a,name:e}),resolve=t=>{for(let n of s){let r=e.get(n)?.get(t);if(r)return{locale:n,message:r}}},templateFor=(e,n)=>{let i=resolve(e);if(!i)return;if(i.message.kind===`text`)return`count`in n?void 0:i.message.template;if(!(`count`in n)||!Number.isFinite(n.count)){if(`count`in n)throw new r.LinguaInvalidPluralCountError("`count` must be a finite number.");return}let a=t.pluralCategory(i.locale,n.count,n.ordinal??!1);return i.message.forms.get(a)??i.message.forms.get(`other`)},translate=(e,t={})=>{let r=templateFor(e,t);if(!r)return missingKey(e);let i=`count`in t?{count:t.count,...t.values}:t.values??{};return n.renderText(r,i,t=>missingValue(t,e))};return{locale:a,translate}}function createTranslator(n,r){let i=createTranslateEngine(new Map([[t.canonicalLocale(r?.locale??`en`),e.compileCatalog(n)]]),r);return{locale:i.locale,translate:i.translate}}exports.createTranslateEngine=createTranslateEngine,exports.createTranslator=createTranslator;
1
+ const e=require("./_catalog.cjs");require("./_dev.cjs");const t=require("./_locale.cjs"),n=require("./_template.cjs"),r=require("./errors.cjs");function resolveMissing(e,t){if(e===`throw`)throw t.name?new r.LinguaMissingValueError(`Missing interpolation value "${t.name}" for key "${t.key}" in locale "${t.locale}".`):new r.LinguaMissingKeyError(`Missing translation key "${t.key}" in locale "${t.locale}".`);return typeof e==`function`?e(t):t.name?`{${t.name}}`:t.key}function createTranslateEngine(e,i={}){let a=t.canonicalLocale(i.locale??`en`),o=(Array.isArray(i.fallback)?i.fallback:i.fallback?[i.fallback]:[]).map(t.canonicalLocale),s=t.localeChain(a,o),c=i.missing,missingKey=e=>resolveMissing(c,{key:e,locale:a}),missingValue=(e,t)=>resolveMissing(c,{key:t,locale:a,name:e}),resolve=t=>{for(let n of s){let r=e.get(n)?.get(t);if(r)return{locale:n,message:r}}},templateFor=(e,n)=>{let i=resolve(e);if(!i)return;if(i.message.kind===`text`)return`count`in n?void 0:i.message.template;if(!(`count`in n)||!Number.isFinite(n.count)){if(`count`in n)throw new r.LinguaInvalidPluralCountError("`count` must be a finite number.");return}let a=t.pluralCategory(i.locale,n.count,n.ordinal??!1);return i.message.forms.get(a)??i.message.forms.get(`other`)},translate=(e,t={})=>{let r=templateFor(e,t);if(!r)return missingKey(e);let i=`count`in t?{count:t.count,...t.values}:t.values??{};return n.renderText(r,i,t=>missingValue(t,e))};return{locale:a,translate}}function createTranslator(n,r){let i=createTranslateEngine(new Map([[t.canonicalLocale(r?.locale??`en`),e.compileCatalog(n)]]),r);return{locale:i.locale,translate:i.translate,translateUnkeyed:i.translate}}exports.createTranslateEngine=createTranslateEngine,exports.createTranslator=createTranslator;
2
2
  //# sourceMappingURL=translator.cjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"translator.cjs","names":["LinguaMissingValueError","LinguaMissingKeyError","canonicalLocale","localeChain","LinguaInvalidPluralCountError","pluralCategory","renderText","compileCatalog","require__catalog","require__locale","require__template","require_errors","strategy","info","locale","options","fallback","chain","missing","candidate","message","catalogs","key","found","category","template","values","name","engine","catalog"],"sources":["../src/translator.ts"],"sourcesContent":["import { type CompiledCatalog, type CompiledMessage, compileCatalog } from './_catalog';\nimport { isDev, warn } from './_dev';\nimport { canonicalLocale, localeChain, pluralCategory } from './_locale';\nimport { renderText, type Template } from './_template';\nimport { LinguaInvalidPluralCountError, LinguaMissingKeyError, LinguaMissingValueError } from './errors';\nimport type {\n Catalog,\n Locale,\n MissingInfo,\n MissingStrategy,\n PluralKey,\n PluralOptions,\n TextKey,\n TranslateOptions,\n TranslatorOptions,\n} from './types';\n\nexport type Translator<C extends Catalog = Catalog> = {\n readonly locale: Locale;\n translate(key: TextKey<C>, options?: TranslateOptions): string;\n translate(key: PluralKey<C>, options: PluralOptions): string;\n};\n\n/** Loose translate signature implemented by the engine; public overloads restrict keys per catalog type. */\nexport type LooseTranslate = (key: string, options?: TranslateOptions | PluralOptions) => string;\n\nfunction resolveMissing(strategy: MissingStrategy | undefined, info: MissingInfo): string {\n if (strategy === 'throw') {\n throw info.name\n ? new LinguaMissingValueError(\n `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`,\n )\n : new LinguaMissingKeyError(`Missing translation key \"${info.key}\" in locale \"${info.locale}\".`);\n }\n\n if (typeof strategy === 'function') return strategy(info);\n\n // 'key' or undefined (default)\n if (strategy === undefined && isDev) {\n warn(\n info.name\n ? `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`\n : `Missing translation key \"${info.key}\" in locale \"${info.locale}\".`,\n );\n }\n\n return info.name ? `{${info.name}}` : info.key;\n}\n\n/** Internal engine: resolves and renders any string key against compiled catalogs for a fixed locale. */\nexport function createTranslateEngine(\n catalogs: ReadonlyMap<Locale, CompiledCatalog>,\n options: InternalOptions = {},\n): { readonly locale: Locale; readonly translate: LooseTranslate } {\n const locale = canonicalLocale(options.locale ?? 'en');\n const fallback = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const chain = localeChain(locale, fallback);\n const missing = options.missing;\n\n const missingKey = (key: string): string => resolveMissing(missing, { key, locale });\n const missingValue = (name: string, key: string): string => resolveMissing(missing, { key, locale, name });\n\n const resolve = (key: string): { readonly locale: Locale; readonly message: CompiledMessage } | undefined => {\n for (const candidate of chain) {\n const message = catalogs.get(candidate)?.get(key);\n\n if (message) return { locale: candidate, message };\n }\n\n return undefined;\n };\n\n const templateFor = (key: string, options: TranslateOptions | PluralOptions): Template | undefined => {\n const found = resolve(key);\n\n if (!found) return undefined;\n\n if (found.message.kind === 'text') {\n if ('count' in options) return undefined;\n\n return found.message.template;\n }\n\n if (!('count' in options) || !Number.isFinite(options.count)) {\n if ('count' in options) throw new LinguaInvalidPluralCountError('`count` must be a finite number.');\n\n return undefined;\n }\n\n const category = pluralCategory(found.locale, options.count, options.ordinal ?? false);\n\n return found.message.forms.get(category) ?? found.message.forms.get('other');\n };\n\n const translate = (key: string, options: TranslateOptions | PluralOptions = {}): string => {\n const template = templateFor(key, options);\n\n if (!template) return missingKey(key);\n\n const values: Record<string, unknown> =\n 'count' in options ? { count: options.count, ...options.values } : (options.values ?? {});\n\n return renderText(template, values, (name) => missingValue(name, key));\n };\n\n return { locale, translate };\n}\n\ntype InternalOptions = {\n readonly fallback?: Locale | readonly Locale[];\n readonly locale?: Locale;\n readonly missing?: MissingStrategy;\n};\n\n/** Creates a fixed-locale translator from one catalog. Lingua snapshots catalog messages during construction. */\nexport function createTranslator<C extends Catalog>(catalog: C, options?: TranslatorOptions): Translator<C> {\n const engine = createTranslateEngine(\n new Map([[canonicalLocale(options?.locale ?? 'en'), compileCatalog(catalog)]]),\n options,\n );\n\n return { locale: engine.locale, translate: engine.translate };\n}\n"],"mappings":"A,gJA0BA,SAAS,eAAe,EAAuC,EAA2B,CACxF,GAAIY,IAAa,QACf,MAAMC,EAAK,KACP,IAAIb,EAAAA,wBACF,gCAAgCa,EAAK,KAAK,aAAaA,EAAK,IAAI,eAAeA,EAAK,OAAO,GAC7F,EACA,IAAIZ,EAAAA,sBAAsB,4BAA4BY,EAAK,IAAI,eAAeA,EAAK,OAAO,GAAG,EAcnG,OAXI,OAAOD,GAAa,WAAmBA,EAASC,CAAI,EAWjDA,EAAK,KAAO,IAAIA,EAAK,KAAK,GAAKA,EAAK,GAC7C,CAGA,SAAgB,sBACd,EACA,EAA2B,CAAC,EACqC,CACjE,IAAMC,EAASZ,EAAAA,gBAAgBa,EAAQ,QAAU,IAAI,EAC/CC,GACJ,MAAM,QAAQD,EAAQ,QAAQ,EAAIA,EAAQ,SAAWA,EAAQ,SAAW,CAACA,EAAQ,QAAQ,EAAI,CAAC,EAAA,CAC9F,IAAIb,EAAAA,eAAe,EACfe,EAAQd,EAAAA,YAAYW,EAAQE,CAAQ,EACpCE,EAAUH,EAAQ,QAElB,WAAc,GAAwB,eAAeG,EAAS,CAAE,MAAK,QAAO,CAAC,EAC7E,cAAgB,EAAc,IAAwB,eAAeA,EAAS,CAAE,MAAK,SAAQ,MAAK,CAAC,EAEnG,QAAW,GAA4F,CAC3G,IAAK,IAAMC,KAAaF,EAAO,CAC7B,IAAMG,EAAUC,EAAS,IAAIF,CAAS,CAAC,EAAE,IAAIG,CAAG,EAEhD,GAAIF,EAAS,MAAO,CAAE,OAAQD,EAAW,SAAQ,CACnD,CAGF,EAEM,aAAe,EAAa,IAAoE,CACpG,IAAMI,EAAQ,QAAQD,CAAG,EAEzB,GAAI,CAACC,EAAO,OAEZ,GAAIA,EAAM,QAAQ,OAAS,OAGzB,MAFI,UAAWR,EAAS,OAEjBQ,EAAM,QAAQ,SAGvB,GAAI,EAAE,UAAWR,IAAY,CAAC,OAAO,SAASA,EAAQ,KAAK,EAAG,CAC5D,GAAI,UAAWA,EAAS,MAAM,IAAIX,EAAAA,8BAA8B,kCAAkC,EAElG,MACF,CAEA,IAAMoB,EAAWnB,EAAAA,eAAekB,EAAM,OAAQR,EAAQ,MAAOA,EAAQ,SAAW,EAAK,EAErF,OAAOQ,EAAM,QAAQ,MAAM,IAAIC,CAAQ,GAAKD,EAAM,QAAQ,MAAM,IAAI,OAAO,CAC7E,EAEM,WAAa,EAAa,EAA4C,CAAC,IAAc,CACzF,IAAME,EAAW,YAAYH,EAAKP,CAAO,EAEzC,GAAI,CAACU,EAAU,OAAO,WAAWH,CAAG,EAEpC,IAAMI,EACJ,UAAWX,EAAU,CAAE,MAAOA,EAAQ,MAAO,GAAGA,EAAQ,MAAO,EAAKA,EAAQ,QAAU,CAAC,EAEzF,OAAOT,EAAAA,WAAWmB,EAAUC,EAAS,GAAS,aAAaC,EAAML,CAAG,CAAC,CACvE,EAEA,MAAO,CAAE,SAAQ,SAAU,CAC7B,CASA,SAAgB,iBAAoC,EAAY,EAA4C,CAC1G,IAAMM,EAAS,sBACb,IAAI,IAAI,CAAC,CAAC1B,EAAAA,gBAAgBa,GAAS,QAAU,IAAI,EAAGR,EAAAA,eAAesB,CAAO,CAAC,CAAC,CAAC,EAC7Ed,CACF,EAEA,MAAO,CAAE,OAAQa,EAAO,OAAQ,UAAWA,EAAO,SAAU,CAC9D,C"}
1
+ {"version":3,"file":"translator.cjs","names":["LinguaMissingValueError","LinguaMissingKeyError","canonicalLocale","localeChain","LinguaInvalidPluralCountError","pluralCategory","renderText","compileCatalog","require__catalog","require__locale","require__template","require_errors","strategy","info","locale","options","fallback","chain","missing","candidate","message","catalogs","key","found","category","template","values","name","engine","catalog"],"sources":["../src/translator.ts"],"sourcesContent":["import { type CompiledCatalog, type CompiledMessage, compileCatalog } from './_catalog';\nimport { isDev, warn } from './_dev';\nimport { canonicalLocale, localeChain, pluralCategory } from './_locale';\nimport { renderText, type Template } from './_template';\nimport { LinguaInvalidPluralCountError, LinguaMissingKeyError, LinguaMissingValueError } from './errors';\nimport type {\n Catalog,\n Locale,\n MissingInfo,\n MissingStrategy,\n PluralKey,\n PluralOptions,\n TextKey,\n TranslateOptions,\n TranslatorOptions,\n} from './types';\n\nexport type Translator<C extends Catalog = Catalog> = {\n readonly locale: Locale;\n translate(key: TextKey<C>, options?: TranslateOptions): string;\n translate(key: PluralKey<C>, options: PluralOptions): string;\n /**\n * Translates a runtime key that is not in the catalog's typed key set. Identical to\n * `translate` at runtime: the key is looked up the same way and a missing key follows\n * the configured `missing` strategy. The one typed escape hatch for keys that arrive\n * as strings, so consumers never cast around the typed surface.\n */\n translateUnkeyed(key: string, options?: TranslateOptions | PluralOptions): string;\n};\n\n/** Loose translate signature implemented by the engine; public overloads restrict keys per catalog type. */\nexport type LooseTranslate = (key: string, options?: TranslateOptions | PluralOptions) => string;\n\nfunction resolveMissing(strategy: MissingStrategy | undefined, info: MissingInfo): string {\n if (strategy === 'throw') {\n throw info.name\n ? new LinguaMissingValueError(\n `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`,\n )\n : new LinguaMissingKeyError(`Missing translation key \"${info.key}\" in locale \"${info.locale}\".`);\n }\n\n if (typeof strategy === 'function') return strategy(info);\n\n // 'key' or undefined (default)\n if (strategy === undefined && isDev) {\n warn(\n info.name\n ? `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`\n : `Missing translation key \"${info.key}\" in locale \"${info.locale}\".`,\n );\n }\n\n return info.name ? `{${info.name}}` : info.key;\n}\n\n/** Internal engine: resolves and renders any string key against compiled catalogs for a fixed locale. */\nexport function createTranslateEngine(\n catalogs: ReadonlyMap<Locale, CompiledCatalog>,\n options: InternalOptions = {},\n): { readonly locale: Locale; readonly translate: LooseTranslate } {\n const locale = canonicalLocale(options.locale ?? 'en');\n const fallback = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const chain = localeChain(locale, fallback);\n const missing = options.missing;\n\n const missingKey = (key: string): string => resolveMissing(missing, { key, locale });\n const missingValue = (name: string, key: string): string => resolveMissing(missing, { key, locale, name });\n\n const resolve = (key: string): { readonly locale: Locale; readonly message: CompiledMessage } | undefined => {\n for (const candidate of chain) {\n const message = catalogs.get(candidate)?.get(key);\n\n if (message) return { locale: candidate, message };\n }\n\n return undefined;\n };\n\n const templateFor = (key: string, options: TranslateOptions | PluralOptions): Template | undefined => {\n const found = resolve(key);\n\n if (!found) return undefined;\n\n if (found.message.kind === 'text') {\n if ('count' in options) return undefined;\n\n return found.message.template;\n }\n\n if (!('count' in options) || !Number.isFinite(options.count)) {\n if ('count' in options) throw new LinguaInvalidPluralCountError('`count` must be a finite number.');\n\n return undefined;\n }\n\n const category = pluralCategory(found.locale, options.count, options.ordinal ?? false);\n\n return found.message.forms.get(category) ?? found.message.forms.get('other');\n };\n\n const translate = (key: string, options: TranslateOptions | PluralOptions = {}): string => {\n const template = templateFor(key, options);\n\n if (!template) return missingKey(key);\n\n const values: Record<string, unknown> =\n 'count' in options ? { count: options.count, ...options.values } : (options.values ?? {});\n\n return renderText(template, values, (name) => missingValue(name, key));\n };\n\n return { locale, translate };\n}\n\ntype InternalOptions = {\n readonly fallback?: Locale | readonly Locale[];\n readonly locale?: Locale;\n readonly missing?: MissingStrategy;\n};\n\n/** Creates a fixed-locale translator from one catalog. Lingua snapshots catalog messages during construction. */\nexport function createTranslator<C extends Catalog>(catalog: C, options?: TranslatorOptions): Translator<C> {\n const engine = createTranslateEngine(\n new Map([[canonicalLocale(options?.locale ?? 'en'), compileCatalog(catalog)]]),\n options,\n );\n\n return { locale: engine.locale, translate: engine.translate, translateUnkeyed: engine.translate };\n}\n"],"mappings":"A,gJAiCA,SAAS,eAAe,EAAuC,EAA2B,CACxF,GAAIY,IAAa,QACf,MAAMC,EAAK,KACP,IAAIb,EAAAA,wBACF,gCAAgCa,EAAK,KAAK,aAAaA,EAAK,IAAI,eAAeA,EAAK,OAAO,GAC7F,EACA,IAAIZ,EAAAA,sBAAsB,4BAA4BY,EAAK,IAAI,eAAeA,EAAK,OAAO,GAAG,EAcnG,OAXI,OAAOD,GAAa,WAAmBA,EAASC,CAAI,EAWjDA,EAAK,KAAO,IAAIA,EAAK,KAAK,GAAKA,EAAK,GAC7C,CAGA,SAAgB,sBACd,EACA,EAA2B,CAAC,EACqC,CACjE,IAAMC,EAASZ,EAAAA,gBAAgBa,EAAQ,QAAU,IAAI,EAC/CC,GACJ,MAAM,QAAQD,EAAQ,QAAQ,EAAIA,EAAQ,SAAWA,EAAQ,SAAW,CAACA,EAAQ,QAAQ,EAAI,CAAC,EAAA,CAC9F,IAAIb,EAAAA,eAAe,EACfe,EAAQd,EAAAA,YAAYW,EAAQE,CAAQ,EACpCE,EAAUH,EAAQ,QAElB,WAAc,GAAwB,eAAeG,EAAS,CAAE,MAAK,QAAO,CAAC,EAC7E,cAAgB,EAAc,IAAwB,eAAeA,EAAS,CAAE,MAAK,SAAQ,MAAK,CAAC,EAEnG,QAAW,GAA4F,CAC3G,IAAK,IAAMC,KAAaF,EAAO,CAC7B,IAAMG,EAAUC,EAAS,IAAIF,CAAS,CAAC,EAAE,IAAIG,CAAG,EAEhD,GAAIF,EAAS,MAAO,CAAE,OAAQD,EAAW,SAAQ,CACnD,CAGF,EAEM,aAAe,EAAa,IAAoE,CACpG,IAAMI,EAAQ,QAAQD,CAAG,EAEzB,GAAI,CAACC,EAAO,OAEZ,GAAIA,EAAM,QAAQ,OAAS,OAGzB,MAFI,UAAWR,EAAS,OAEjBQ,EAAM,QAAQ,SAGvB,GAAI,EAAE,UAAWR,IAAY,CAAC,OAAO,SAASA,EAAQ,KAAK,EAAG,CAC5D,GAAI,UAAWA,EAAS,MAAM,IAAIX,EAAAA,8BAA8B,kCAAkC,EAElG,MACF,CAEA,IAAMoB,EAAWnB,EAAAA,eAAekB,EAAM,OAAQR,EAAQ,MAAOA,EAAQ,SAAW,EAAK,EAErF,OAAOQ,EAAM,QAAQ,MAAM,IAAIC,CAAQ,GAAKD,EAAM,QAAQ,MAAM,IAAI,OAAO,CAC7E,EAEM,WAAa,EAAa,EAA4C,CAAC,IAAc,CACzF,IAAME,EAAW,YAAYH,EAAKP,CAAO,EAEzC,GAAI,CAACU,EAAU,OAAO,WAAWH,CAAG,EAEpC,IAAMI,EACJ,UAAWX,EAAU,CAAE,MAAOA,EAAQ,MAAO,GAAGA,EAAQ,MAAO,EAAKA,EAAQ,QAAU,CAAC,EAEzF,OAAOT,EAAAA,WAAWmB,EAAUC,EAAS,GAAS,aAAaC,EAAML,CAAG,CAAC,CACvE,EAEA,MAAO,CAAE,SAAQ,SAAU,CAC7B,CASA,SAAgB,iBAAoC,EAAY,EAA4C,CAC1G,IAAMM,EAAS,sBACb,IAAI,IAAI,CAAC,CAAC1B,EAAAA,gBAAgBa,GAAS,QAAU,IAAI,EAAGR,EAAAA,eAAesB,CAAO,CAAC,CAAC,CAAC,EAC7Ed,CACF,EAEA,MAAO,CAAE,OAAQa,EAAO,OAAQ,UAAWA,EAAO,UAAW,iBAAkBA,EAAO,SAAU,CAClG,C"}
@@ -4,6 +4,13 @@ export type Translator<C extends Catalog = Catalog> = {
4
4
  readonly locale: Locale;
5
5
  translate(key: TextKey<C>, options?: TranslateOptions): string;
6
6
  translate(key: PluralKey<C>, options: PluralOptions): string;
7
+ /**
8
+ * Translates a runtime key that is not in the catalog's typed key set. Identical to
9
+ * `translate` at runtime: the key is looked up the same way and a missing key follows
10
+ * the configured `missing` strategy. The one typed escape hatch for keys that arrive
11
+ * as strings, so consumers never cast around the typed surface.
12
+ */
13
+ translateUnkeyed(key: string, options?: TranslateOptions | PluralOptions): string;
7
14
  };
8
15
  /** Loose translate signature implemented by the engine; public overloads restrict keys per catalog type. */
9
16
  export type LooseTranslate = (key: string, options?: TranslateOptions | PluralOptions) => string;
@@ -1 +1 @@
1
- {"version":3,"file":"translator.d.ts","sourceRoot":"","sources":["../src/translator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAwC,MAAM,YAAY,CAAC;AAKxF,OAAO,KAAK,EACV,OAAO,EACP,MAAM,EAEN,eAAe,EACf,SAAS,EACT,aAAa,EACb,OAAO,EACP,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAAC;IAC/D,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAAC;CAC9D,CAAC;AAEF,4GAA4G;AAC5G,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,aAAa,KAAK,MAAM,CAAC;AAyBjG,yGAAyG;AACzG,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,EAC9C,OAAO,GAAE,eAAoB,GAC5B;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAA;CAAE,CAuDjE;AAED,KAAK,eAAe,GAAG;IACrB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IAC/C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC;CACpC,CAAC;AAEF,iHAAiH;AACjH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,UAAU,CAAC,CAAC,CAAC,CAO1G"}
1
+ {"version":3,"file":"translator.d.ts","sourceRoot":"","sources":["../src/translator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,eAAe,EAAwC,MAAM,YAAY,CAAC;AAKxF,OAAO,KAAK,EACV,OAAO,EACP,MAAM,EAEN,eAAe,EACf,SAAS,EACT,aAAa,EACb,OAAO,EACP,gBAAgB,EAChB,iBAAiB,EAClB,MAAM,SAAS,CAAC;AAEjB,MAAM,MAAM,UAAU,CAAC,CAAC,SAAS,OAAO,GAAG,OAAO,IAAI;IACpD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,SAAS,CAAC,GAAG,EAAE,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,MAAM,CAAC;IAC/D,SAAS,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAAC;IAC7D;;;;;OAKG;IACH,gBAAgB,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,aAAa,GAAG,MAAM,CAAC;CACnF,CAAC;AAEF,4GAA4G;AAC5G,MAAM,MAAM,cAAc,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,aAAa,KAAK,MAAM,CAAC;AAyBjG,yGAAyG;AACzG,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,eAAe,CAAC,EAC9C,OAAO,GAAE,eAAoB,GAC5B;IAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,SAAS,EAAE,cAAc,CAAA;CAAE,CAuDjE;AAED,KAAK,eAAe,GAAG;IACrB,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,SAAS,MAAM,EAAE,CAAC;IAC/C,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC;CACpC,CAAC;AAEF,iHAAiH;AACjH,wBAAgB,gBAAgB,CAAC,CAAC,SAAS,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,iBAAiB,GAAG,UAAU,CAAC,CAAC,CAAC,CAO1G"}
@@ -52,7 +52,8 @@ function createTranslator(n, r) {
52
52
  let i = createTranslateEngine(/* @__PURE__ */ new Map([[t(r?.locale ?? "en"), e(n)]]), r);
53
53
  return {
54
54
  locale: i.locale,
55
- translate: i.translate
55
+ translate: i.translate,
56
+ translateUnkeyed: i.translate
56
57
  };
57
58
  }
58
59
  export { createTranslateEngine, createTranslator };
@@ -1 +1 @@
1
- {"version":3,"file":"translator.js","names":["strategy","info","LinguaMissingValueError","LinguaMissingKeyError","locale","canonicalLocale","options","fallback","chain","localeChain","missing","candidate","message","catalogs","key","found","LinguaInvalidPluralCountError","category","pluralCategory","template","values","renderText","name","engine","compileCatalog","catalog"],"sources":["../src/translator.ts"],"sourcesContent":["import { type CompiledCatalog, type CompiledMessage, compileCatalog } from './_catalog';\nimport { isDev, warn } from './_dev';\nimport { canonicalLocale, localeChain, pluralCategory } from './_locale';\nimport { renderText, type Template } from './_template';\nimport { LinguaInvalidPluralCountError, LinguaMissingKeyError, LinguaMissingValueError } from './errors';\nimport type {\n Catalog,\n Locale,\n MissingInfo,\n MissingStrategy,\n PluralKey,\n PluralOptions,\n TextKey,\n TranslateOptions,\n TranslatorOptions,\n} from './types';\n\nexport type Translator<C extends Catalog = Catalog> = {\n readonly locale: Locale;\n translate(key: TextKey<C>, options?: TranslateOptions): string;\n translate(key: PluralKey<C>, options: PluralOptions): string;\n};\n\n/** Loose translate signature implemented by the engine; public overloads restrict keys per catalog type. */\nexport type LooseTranslate = (key: string, options?: TranslateOptions | PluralOptions) => string;\n\nfunction resolveMissing(strategy: MissingStrategy | undefined, info: MissingInfo): string {\n if (strategy === 'throw') {\n throw info.name\n ? new LinguaMissingValueError(\n `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`,\n )\n : new LinguaMissingKeyError(`Missing translation key \"${info.key}\" in locale \"${info.locale}\".`);\n }\n\n if (typeof strategy === 'function') return strategy(info);\n\n // 'key' or undefined (default)\n if (strategy === undefined && isDev) {\n warn(\n info.name\n ? `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`\n : `Missing translation key \"${info.key}\" in locale \"${info.locale}\".`,\n );\n }\n\n return info.name ? `{${info.name}}` : info.key;\n}\n\n/** Internal engine: resolves and renders any string key against compiled catalogs for a fixed locale. */\nexport function createTranslateEngine(\n catalogs: ReadonlyMap<Locale, CompiledCatalog>,\n options: InternalOptions = {},\n): { readonly locale: Locale; readonly translate: LooseTranslate } {\n const locale = canonicalLocale(options.locale ?? 'en');\n const fallback = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const chain = localeChain(locale, fallback);\n const missing = options.missing;\n\n const missingKey = (key: string): string => resolveMissing(missing, { key, locale });\n const missingValue = (name: string, key: string): string => resolveMissing(missing, { key, locale, name });\n\n const resolve = (key: string): { readonly locale: Locale; readonly message: CompiledMessage } | undefined => {\n for (const candidate of chain) {\n const message = catalogs.get(candidate)?.get(key);\n\n if (message) return { locale: candidate, message };\n }\n\n return undefined;\n };\n\n const templateFor = (key: string, options: TranslateOptions | PluralOptions): Template | undefined => {\n const found = resolve(key);\n\n if (!found) return undefined;\n\n if (found.message.kind === 'text') {\n if ('count' in options) return undefined;\n\n return found.message.template;\n }\n\n if (!('count' in options) || !Number.isFinite(options.count)) {\n if ('count' in options) throw new LinguaInvalidPluralCountError('`count` must be a finite number.');\n\n return undefined;\n }\n\n const category = pluralCategory(found.locale, options.count, options.ordinal ?? false);\n\n return found.message.forms.get(category) ?? found.message.forms.get('other');\n };\n\n const translate = (key: string, options: TranslateOptions | PluralOptions = {}): string => {\n const template = templateFor(key, options);\n\n if (!template) return missingKey(key);\n\n const values: Record<string, unknown> =\n 'count' in options ? { count: options.count, ...options.values } : (options.values ?? {});\n\n return renderText(template, values, (name) => missingValue(name, key));\n };\n\n return { locale, translate };\n}\n\ntype InternalOptions = {\n readonly fallback?: Locale | readonly Locale[];\n readonly locale?: Locale;\n readonly missing?: MissingStrategy;\n};\n\n/** Creates a fixed-locale translator from one catalog. Lingua snapshots catalog messages during construction. */\nexport function createTranslator<C extends Catalog>(catalog: C, options?: TranslatorOptions): Translator<C> {\n const engine = createTranslateEngine(\n new Map([[canonicalLocale(options?.locale ?? 'en'), compileCatalog(catalog)]]),\n options,\n );\n\n return { locale: engine.locale, translate: engine.translate };\n}\n"],"mappings":";;;;;;AA0BA,SAAS,eAAe,GAAuC,GAA2B;CACxF,IAAIA,MAAa,SACf,MAAMC,EAAK,OACP,IAAIC,EACF,gCAAgCD,EAAK,KAAK,aAAaA,EAAK,IAAI,eAAeA,EAAK,OAAO,GAC7F,IACA,IAAIE,EAAsB,4BAA4BF,EAAK,IAAI,eAAeA,EAAK,OAAO,GAAG;CAcnG,OAXI,OAAOD,KAAa,aAAmBA,EAASC,CAAI,IAWjDA,EAAK,OAAO,IAAIA,EAAK,KAAK,KAAKA,EAAK;AAC7C;AAGA,SAAgB,sBACd,GACA,IAA2B,CAAC,GACqC;CACjE,IAAMG,IAASC,EAAgBC,EAAQ,UAAU,IAAI,GAC/CC,KACJ,MAAM,QAAQD,EAAQ,QAAQ,IAAIA,EAAQ,WAAWA,EAAQ,WAAW,CAACA,EAAQ,QAAQ,IAAI,CAAC,EAAA,CAC9F,IAAID,CAAe,GACfG,IAAQC,EAAYL,GAAQG,CAAQ,GACpCG,IAAUJ,EAAQ,SAElB,cAAc,MAAwB,eAAeI,GAAS;EAAE;EAAK;CAAO,CAAC,GAC7E,gBAAgB,GAAc,MAAwB,eAAeA,GAAS;EAAE;EAAK;EAAQ;CAAK,CAAC,GAEnG,WAAW,MAA4F;EAC3G,KAAK,IAAMC,KAAaH,GAAO;GAC7B,IAAMI,IAAUC,EAAS,IAAIF,CAAS,CAAC,EAAE,IAAIG,CAAG;GAEhD,IAAIF,GAAS,OAAO;IAAE,QAAQD;IAAW;GAAQ;EACnD;CAGF,GAEM,eAAe,GAAa,MAAoE;EACpG,IAAMI,IAAQ,QAAQD,CAAG;EAEzB,IAAI,CAACC,GAAO;EAEZ,IAAIA,EAAM,QAAQ,SAAS,QAGzB,OAFI,WAAWT,IAAS,SAEjBS,EAAM,QAAQ;EAGvB,IAAI,EAAE,WAAWT,MAAY,CAAC,OAAO,SAASA,EAAQ,KAAK,GAAG;GAC5D,IAAI,WAAWA,GAAS,MAAM,IAAIU,EAA8B,kCAAkC;GAElG;EACF;EAEA,IAAMC,IAAWC,EAAeH,EAAM,QAAQT,EAAQ,OAAOA,EAAQ,WAAW,EAAK;EAErF,OAAOS,EAAM,QAAQ,MAAM,IAAIE,CAAQ,KAAKF,EAAM,QAAQ,MAAM,IAAI,OAAO;CAC7E,GAEM,aAAa,GAAa,IAA4C,CAAC,MAAc;EACzF,IAAMI,IAAW,YAAYL,GAAKR,CAAO;EAEzC,IAAI,CAACa,GAAU,OAAO,WAAWL,CAAG;EAEpC,IAAMM,IACJ,WAAWd,IAAU;GAAE,OAAOA,EAAQ;GAAO,GAAGA,EAAQ;EAAO,IAAKA,EAAQ,UAAU,CAAC;EAEzF,OAAOe,EAAWF,GAAUC,IAAS,MAAS,aAAaE,GAAMR,CAAG,CAAC;CACvE;CAEA,OAAO;EAAE;EAAQ;CAAU;AAC7B;AASA,SAAgB,iBAAoC,GAAY,GAA4C;CAC1G,IAAMS,IAAS,sCACb,IAAI,IAAI,CAAC,CAAClB,EAAgBC,GAAS,UAAU,IAAI,GAAGkB,EAAeC,CAAO,CAAC,CAAC,CAAC,GAC7EnB,CACF;CAEA,OAAO;EAAE,QAAQiB,EAAO;EAAQ,WAAWA,EAAO;CAAU;AAC9D"}
1
+ {"version":3,"file":"translator.js","names":["strategy","info","LinguaMissingValueError","LinguaMissingKeyError","locale","canonicalLocale","options","fallback","chain","localeChain","missing","candidate","message","catalogs","key","found","LinguaInvalidPluralCountError","category","pluralCategory","template","values","renderText","name","engine","compileCatalog","catalog"],"sources":["../src/translator.ts"],"sourcesContent":["import { type CompiledCatalog, type CompiledMessage, compileCatalog } from './_catalog';\nimport { isDev, warn } from './_dev';\nimport { canonicalLocale, localeChain, pluralCategory } from './_locale';\nimport { renderText, type Template } from './_template';\nimport { LinguaInvalidPluralCountError, LinguaMissingKeyError, LinguaMissingValueError } from './errors';\nimport type {\n Catalog,\n Locale,\n MissingInfo,\n MissingStrategy,\n PluralKey,\n PluralOptions,\n TextKey,\n TranslateOptions,\n TranslatorOptions,\n} from './types';\n\nexport type Translator<C extends Catalog = Catalog> = {\n readonly locale: Locale;\n translate(key: TextKey<C>, options?: TranslateOptions): string;\n translate(key: PluralKey<C>, options: PluralOptions): string;\n /**\n * Translates a runtime key that is not in the catalog's typed key set. Identical to\n * `translate` at runtime: the key is looked up the same way and a missing key follows\n * the configured `missing` strategy. The one typed escape hatch for keys that arrive\n * as strings, so consumers never cast around the typed surface.\n */\n translateUnkeyed(key: string, options?: TranslateOptions | PluralOptions): string;\n};\n\n/** Loose translate signature implemented by the engine; public overloads restrict keys per catalog type. */\nexport type LooseTranslate = (key: string, options?: TranslateOptions | PluralOptions) => string;\n\nfunction resolveMissing(strategy: MissingStrategy | undefined, info: MissingInfo): string {\n if (strategy === 'throw') {\n throw info.name\n ? new LinguaMissingValueError(\n `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`,\n )\n : new LinguaMissingKeyError(`Missing translation key \"${info.key}\" in locale \"${info.locale}\".`);\n }\n\n if (typeof strategy === 'function') return strategy(info);\n\n // 'key' or undefined (default)\n if (strategy === undefined && isDev) {\n warn(\n info.name\n ? `Missing interpolation value \"${info.name}\" for key \"${info.key}\" in locale \"${info.locale}\".`\n : `Missing translation key \"${info.key}\" in locale \"${info.locale}\".`,\n );\n }\n\n return info.name ? `{${info.name}}` : info.key;\n}\n\n/** Internal engine: resolves and renders any string key against compiled catalogs for a fixed locale. */\nexport function createTranslateEngine(\n catalogs: ReadonlyMap<Locale, CompiledCatalog>,\n options: InternalOptions = {},\n): { readonly locale: Locale; readonly translate: LooseTranslate } {\n const locale = canonicalLocale(options.locale ?? 'en');\n const fallback = (\n Array.isArray(options.fallback) ? options.fallback : options.fallback ? [options.fallback] : []\n ).map(canonicalLocale);\n const chain = localeChain(locale, fallback);\n const missing = options.missing;\n\n const missingKey = (key: string): string => resolveMissing(missing, { key, locale });\n const missingValue = (name: string, key: string): string => resolveMissing(missing, { key, locale, name });\n\n const resolve = (key: string): { readonly locale: Locale; readonly message: CompiledMessage } | undefined => {\n for (const candidate of chain) {\n const message = catalogs.get(candidate)?.get(key);\n\n if (message) return { locale: candidate, message };\n }\n\n return undefined;\n };\n\n const templateFor = (key: string, options: TranslateOptions | PluralOptions): Template | undefined => {\n const found = resolve(key);\n\n if (!found) return undefined;\n\n if (found.message.kind === 'text') {\n if ('count' in options) return undefined;\n\n return found.message.template;\n }\n\n if (!('count' in options) || !Number.isFinite(options.count)) {\n if ('count' in options) throw new LinguaInvalidPluralCountError('`count` must be a finite number.');\n\n return undefined;\n }\n\n const category = pluralCategory(found.locale, options.count, options.ordinal ?? false);\n\n return found.message.forms.get(category) ?? found.message.forms.get('other');\n };\n\n const translate = (key: string, options: TranslateOptions | PluralOptions = {}): string => {\n const template = templateFor(key, options);\n\n if (!template) return missingKey(key);\n\n const values: Record<string, unknown> =\n 'count' in options ? { count: options.count, ...options.values } : (options.values ?? {});\n\n return renderText(template, values, (name) => missingValue(name, key));\n };\n\n return { locale, translate };\n}\n\ntype InternalOptions = {\n readonly fallback?: Locale | readonly Locale[];\n readonly locale?: Locale;\n readonly missing?: MissingStrategy;\n};\n\n/** Creates a fixed-locale translator from one catalog. Lingua snapshots catalog messages during construction. */\nexport function createTranslator<C extends Catalog>(catalog: C, options?: TranslatorOptions): Translator<C> {\n const engine = createTranslateEngine(\n new Map([[canonicalLocale(options?.locale ?? 'en'), compileCatalog(catalog)]]),\n options,\n );\n\n return { locale: engine.locale, translate: engine.translate, translateUnkeyed: engine.translate };\n}\n"],"mappings":";;;;;;AAiCA,SAAS,eAAe,GAAuC,GAA2B;CACxF,IAAIA,MAAa,SACf,MAAMC,EAAK,OACP,IAAIC,EACF,gCAAgCD,EAAK,KAAK,aAAaA,EAAK,IAAI,eAAeA,EAAK,OAAO,GAC7F,IACA,IAAIE,EAAsB,4BAA4BF,EAAK,IAAI,eAAeA,EAAK,OAAO,GAAG;CAcnG,OAXI,OAAOD,KAAa,aAAmBA,EAASC,CAAI,IAWjDA,EAAK,OAAO,IAAIA,EAAK,KAAK,KAAKA,EAAK;AAC7C;AAGA,SAAgB,sBACd,GACA,IAA2B,CAAC,GACqC;CACjE,IAAMG,IAASC,EAAgBC,EAAQ,UAAU,IAAI,GAC/CC,KACJ,MAAM,QAAQD,EAAQ,QAAQ,IAAIA,EAAQ,WAAWA,EAAQ,WAAW,CAACA,EAAQ,QAAQ,IAAI,CAAC,EAAA,CAC9F,IAAID,CAAe,GACfG,IAAQC,EAAYL,GAAQG,CAAQ,GACpCG,IAAUJ,EAAQ,SAElB,cAAc,MAAwB,eAAeI,GAAS;EAAE;EAAK;CAAO,CAAC,GAC7E,gBAAgB,GAAc,MAAwB,eAAeA,GAAS;EAAE;EAAK;EAAQ;CAAK,CAAC,GAEnG,WAAW,MAA4F;EAC3G,KAAK,IAAMC,KAAaH,GAAO;GAC7B,IAAMI,IAAUC,EAAS,IAAIF,CAAS,CAAC,EAAE,IAAIG,CAAG;GAEhD,IAAIF,GAAS,OAAO;IAAE,QAAQD;IAAW;GAAQ;EACnD;CAGF,GAEM,eAAe,GAAa,MAAoE;EACpG,IAAMI,IAAQ,QAAQD,CAAG;EAEzB,IAAI,CAACC,GAAO;EAEZ,IAAIA,EAAM,QAAQ,SAAS,QAGzB,OAFI,WAAWT,IAAS,SAEjBS,EAAM,QAAQ;EAGvB,IAAI,EAAE,WAAWT,MAAY,CAAC,OAAO,SAASA,EAAQ,KAAK,GAAG;GAC5D,IAAI,WAAWA,GAAS,MAAM,IAAIU,EAA8B,kCAAkC;GAElG;EACF;EAEA,IAAMC,IAAWC,EAAeH,EAAM,QAAQT,EAAQ,OAAOA,EAAQ,WAAW,EAAK;EAErF,OAAOS,EAAM,QAAQ,MAAM,IAAIE,CAAQ,KAAKF,EAAM,QAAQ,MAAM,IAAI,OAAO;CAC7E,GAEM,aAAa,GAAa,IAA4C,CAAC,MAAc;EACzF,IAAMI,IAAW,YAAYL,GAAKR,CAAO;EAEzC,IAAI,CAACa,GAAU,OAAO,WAAWL,CAAG;EAEpC,IAAMM,IACJ,WAAWd,IAAU;GAAE,OAAOA,EAAQ;GAAO,GAAGA,EAAQ;EAAO,IAAKA,EAAQ,UAAU,CAAC;EAEzF,OAAOe,EAAWF,GAAUC,IAAS,MAAS,aAAaE,GAAMR,CAAG,CAAC;CACvE;CAEA,OAAO;EAAE;EAAQ;CAAU;AAC7B;AASA,SAAgB,iBAAoC,GAAY,GAA4C;CAC1G,IAAMS,IAAS,sCACb,IAAI,IAAI,CAAC,CAAClB,EAAgBC,GAAS,UAAU,IAAI,GAAGkB,EAAeC,CAAO,CAAC,CAAC,CAAC,GAC7EnB,CACF;CAEA,OAAO;EAAE,QAAQiB,EAAO;EAAQ,WAAWA,EAAO;EAAW,kBAAkBA,EAAO;CAAU;AAClG"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vielzeug/lingua",
3
- "version": "26.11.13",
3
+ "version": "26.11.15",
4
4
  "description": "Typed i18n with pluralization and lazy locale loading",
5
5
  "repository": {
6
6
  "type": "git",
@@ -71,7 +71,7 @@
71
71
  "verify:exports": "bun ./scripts/verify-exports.mjs"
72
72
  },
73
73
  "peerDependencies": {
74
- "@vielzeug/ripple": "^26.11.13"
74
+ "@vielzeug/ripple": "^26.11.15"
75
75
  },
76
76
  "peerDependenciesMeta": {
77
77
  "@vielzeug/ripple": {
@@ -80,7 +80,7 @@
80
80
  },
81
81
  "devDependencies": {
82
82
  "@types/node": "^26.6.6",
83
- "@vielzeug/ripple": "26.11.13",
83
+ "@vielzeug/ripple": "26.11.15",
84
84
  "typescript": "^6.0.3",
85
85
  "vite": "^8.3.6",
86
86
  "vitest": "^5.1.0"