vanilla-intlayer 8.12.4 → 9.0.0-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/dist/cjs/client/useDictionary.cjs +18 -39
  2. package/dist/cjs/client/useDictionary.cjs.map +1 -1
  3. package/dist/cjs/client/useDictionaryDynamic.cjs +70 -38
  4. package/dist/cjs/client/useDictionaryDynamic.cjs.map +1 -1
  5. package/dist/cjs/client/useIntlayer.cjs +19 -30
  6. package/dist/cjs/client/useIntlayer.cjs.map +1 -1
  7. package/dist/cjs/getDictionary.cjs +8 -1
  8. package/dist/cjs/getDictionary.cjs.map +1 -1
  9. package/dist/cjs/getIntlayer.cjs +8 -1
  10. package/dist/cjs/getIntlayer.cjs.map +1 -1
  11. package/dist/esm/client/useDictionary.mjs +18 -39
  12. package/dist/esm/client/useDictionary.mjs.map +1 -1
  13. package/dist/esm/client/useDictionaryDynamic.mjs +70 -38
  14. package/dist/esm/client/useDictionaryDynamic.mjs.map +1 -1
  15. package/dist/esm/client/useIntlayer.mjs +19 -30
  16. package/dist/esm/client/useIntlayer.mjs.map +1 -1
  17. package/dist/esm/getDictionary.mjs +8 -1
  18. package/dist/esm/getDictionary.mjs.map +1 -1
  19. package/dist/esm/getIntlayer.mjs +8 -1
  20. package/dist/esm/getIntlayer.mjs.map +1 -1
  21. package/dist/types/client/useDictionary.d.ts +12 -39
  22. package/dist/types/client/useDictionary.d.ts.map +1 -1
  23. package/dist/types/client/useDictionaryDynamic.d.ts +11 -27
  24. package/dist/types/client/useDictionaryDynamic.d.ts.map +1 -1
  25. package/dist/types/client/useIntlayer.d.ts +11 -28
  26. package/dist/types/client/useIntlayer.d.ts.map +1 -1
  27. package/dist/types/getDictionary.d.ts +8 -3
  28. package/dist/types/getDictionary.d.ts.map +1 -1
  29. package/dist/types/getIntlayer.d.ts +7 -2
  30. package/dist/types/getIntlayer.d.ts.map +1 -1
  31. package/package.json +8 -8
@@ -4,50 +4,29 @@ const require_client_installIntlayer = require('./installIntlayer.cjs');
4
4
 
5
5
  //#region src/client/useDictionary.ts
6
6
  /**
7
- * Get the translated content for a raw dictionary object and optionally
8
- * subscribe to locale changes via the chainable `.onChange()` method —
9
- * mirroring the API of `react-intlayer`.
7
+ * Get the content for a raw dictionary (or qualified dictionary group) and
8
+ * optionally subscribe to locale changes via the chainable `.onChange()`
9
+ * method — mirroring the API of `react-intlayer`.
10
10
  *
11
- * Unlike `useIntlayer` (which takes a registered key), this function accepts a
12
- * dictionary object directly useful for dictionaries loaded asynchronously
13
- * or defined inline.
11
+ * The second argument is either a locale or a selector object (`{ item }`,
12
+ * `{ variant }`, `{ id, ...meta }`, optionally combined with `locale`).
14
13
  *
15
- * The function returns the current content **directly** (same shape as
16
- * `getDictionary(dict)`), plus the `.onChange()` helper:
17
- *
18
- * ```ts
19
- * // React
20
- * const content = useDictionary(myDict);
21
- *
22
- * // Vanilla — identical surface API, opt-in reactivity via .onChange()
23
- * const content = useDictionary(myDict);
24
- * const content = useDictionary(myDict).onChange((c) => render(c));
25
- * ```
26
- *
27
- * @param dictionary - The raw dictionary object.
28
- * @param locale - Optional locale override.
29
- * @returns The current translated content with an `.onChange()` method.
30
- *
31
- * @example
32
- * ```ts
33
- * import myDict from './myDictionary.content';
34
- * import { installIntlayer, useDictionary } from 'vanilla-intlayer';
35
- *
36
- * installIntlayer();
37
- *
38
- * const content = useDictionary(myDict).onChange((c) => {
39
- * document.querySelector('p').textContent = String(c.greeting);
40
- * });
41
- *
42
- * document.querySelector('p').textContent = String(content.greeting);
43
- * ```
14
+ * @param dictionary - The raw dictionary (or qualified group) object.
15
+ * @param localeOrSelector - Optional locale or selector.
16
+ * @returns The current content with an `.onChange()` method.
44
17
  */
45
- const useDictionary = (dictionary, locale) => {
18
+ const useDictionary = (dictionary, localeOrSelector) => {
46
19
  const client = require_client_installIntlayer.getIntlayerClient();
47
- const content = require_getDictionary.getDictionary(dictionary, locale ?? client.locale);
48
- content.onChange = (callback) => {
20
+ const isSelector = typeof localeOrSelector === "object" && localeOrSelector !== null;
21
+ const explicitLocale = isSelector ? localeOrSelector.locale : localeOrSelector;
22
+ const read = (locale) => isSelector ? require_getDictionary.getDictionary(dictionary, {
23
+ ...localeOrSelector,
24
+ locale
25
+ }) : require_getDictionary.getDictionary(dictionary, locale);
26
+ const content = read(explicitLocale ?? client.locale);
27
+ if (content != null && typeof content === "object") content.onChange = (callback) => {
49
28
  client.subscribe((newLocale) => {
50
- callback(require_getDictionary.getDictionary(dictionary, locale ?? newLocale));
29
+ callback(read(explicitLocale ?? newLocale));
51
30
  });
52
31
  return content;
53
32
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useDictionary.cjs","names":["getIntlayerClient","getDictionary"],"sources":["../../../src/client/useDictionary.ts"],"sourcesContent":["import type { Dictionary } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { getDictionary } from '../getDictionary';\nimport type { DeepTransformContent } from '../plugins';\nimport { getIntlayerClient } from './installIntlayer';\n\nexport type WithOnChange<T> = T & {\n /**\n * Subscribe to locale changes. The callback receives the fresh content\n * whenever the active locale changes. Returns the content object itself\n * for convenient one-liner patterns:\n *\n * ```ts\n * const content = useDictionary(myDict).onChange((c) => {\n * document.querySelector('p')!.textContent = String(c.greeting);\n * });\n * ```\n */\n onChange: (\n callback: <T extends Dictionary, L extends LocalesValues>(\n content: DeepTransformContent<T['content'], L>\n ) => void\n ) => WithOnChange<T>;\n};\n\n/**\n * Get the translated content for a raw dictionary object and optionally\n * subscribe to locale changes via the chainable `.onChange()` method —\n * mirroring the API of `react-intlayer`.\n *\n * Unlike `useIntlayer` (which takes a registered key), this function accepts a\n * dictionary object directly — useful for dictionaries loaded asynchronously\n * or defined inline.\n *\n * The function returns the current content **directly** (same shape as\n * `getDictionary(dict)`), plus the `.onChange()` helper:\n *\n * ```ts\n * // React\n * const content = useDictionary(myDict);\n *\n * // Vanilla — identical surface API, opt-in reactivity via .onChange()\n * const content = useDictionary(myDict);\n * const content = useDictionary(myDict).onChange((c) => render(c));\n * ```\n *\n * @param dictionary - The raw dictionary object.\n * @param locale - Optional locale override.\n * @returns The current translated content with an `.onChange()` method.\n *\n * @example\n * ```ts\n * import myDict from './myDictionary.content';\n * import { installIntlayer, useDictionary } from 'vanilla-intlayer';\n *\n * installIntlayer();\n *\n * const content = useDictionary(myDict).onChange((c) => {\n * document.querySelector('p').textContent = String(c.greeting);\n * });\n *\n * document.querySelector('p').textContent = String(content.greeting);\n * ```\n */\nexport const useDictionary = <\n const T extends Dictionary,\n const L extends LocalesValues = DeclaredLocales,\n>(\n dictionary: T,\n locale?: L\n): WithOnChange<DeepTransformContent<T['content'], L>> => {\n const client = getIntlayerClient();\n const content = getDictionary(\n dictionary,\n (locale ?? client.locale) as L\n ) as WithOnChange<DeepTransformContent<T['content'], L>>;\n\n content.onChange = (\n callback: (content: DeepTransformContent<T['content'], L>) => void\n ) => {\n client.subscribe((newLocale) => {\n callback(getDictionary(dictionary, (locale ?? newLocale) as L));\n });\n return content;\n };\n\n return content;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEA,MAAa,iBAIX,YACA,WACwD;CACxD,MAAM,SAASA,kDAAmB;CAClC,MAAM,UAAUC,oCACd,YACC,UAAU,OAAO,OACnB;AAED,SAAQ,YACN,aACG;AACH,SAAO,WAAW,cAAc;AAC9B,YAASA,oCAAc,YAAa,UAAU,UAAgB,CAAC;IAC/D;AACF,SAAO;;AAGT,QAAO"}
1
+ {"version":3,"file":"useDictionary.cjs","names":["getIntlayerClient","getDictionary"],"sources":["../../../src/client/useDictionary.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionarySelector,\n DictionarySelectorForGroup,\n QualifiedDictionaryGroup,\n ResolveQualifiedDictionaryContent,\n} from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { getDictionary } from '../getDictionary';\nimport type { DeepTransformContent } from '../plugins';\nimport { getIntlayerClient } from './installIntlayer';\n\nexport type WithOnChange<T> = T & {\n /**\n * Subscribe to locale changes. The callback receives the fresh content\n * whenever the active locale changes. Returns the content object itself\n * for convenient one-liner patterns:\n *\n * ```ts\n * const content = useDictionary(myDict).onChange((c) => {\n * document.querySelector('p')!.textContent = String(c.greeting);\n * });\n * ```\n */\n onChange: (callback: (content: any) => void) => WithOnChange<T>;\n};\n\n/**\n * Get the content for a raw dictionary (or qualified dictionary group) and\n * optionally subscribe to locale changes via the chainable `.onChange()`\n * method mirroring the API of `react-intlayer`.\n *\n * The second argument is either a locale or a selector object (`{ item }`,\n * `{ variant }`, `{ id, ...meta }`, optionally combined with `locale`).\n *\n * @param dictionary - The raw dictionary (or qualified group) object.\n * @param localeOrSelector - Optional locale or selector.\n * @returns The current content with an `.onChange()` method.\n */\nexport const useDictionary = <\n const T extends Dictionary | QualifiedDictionaryGroup,\n const A extends\n | LocalesValues\n | DictionarySelectorForGroup<T> = DeclaredLocales,\n>(\n dictionary: T,\n localeOrSelector?: A\n): WithOnChange<\n DeepTransformContent<\n ResolveQualifiedDictionaryContent<T, A>,\n ExtractSelectorLocale<A>\n >\n> => {\n const client = getIntlayerClient();\n\n const isSelector =\n typeof localeOrSelector === 'object' && localeOrSelector !== null;\n const explicitLocale = isSelector\n ? (localeOrSelector as DictionarySelector).locale\n : (localeOrSelector as LocalesValues | undefined);\n\n const read = (locale: LocalesValues | undefined) =>\n isSelector\n ? getDictionary(dictionary, {\n ...(localeOrSelector as DictionarySelector),\n locale,\n } as A)\n : getDictionary(dictionary, locale as A);\n\n const content = read(explicitLocale ?? client.locale) as WithOnChange<any>;\n\n // A selector can resolve to null/array; only objects carry `.onChange`.\n if (content != null && typeof content === 'object') {\n content.onChange = (callback) => {\n client.subscribe((newLocale) => {\n callback(read(explicitLocale ?? newLocale));\n });\n return content;\n };\n }\n\n return content;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AA2CA,MAAa,iBAMX,YACA,qBAMG;CACH,MAAM,SAASA,kDAAmB;CAElC,MAAM,aACJ,OAAO,qBAAqB,YAAY,qBAAqB;CAC/D,MAAM,iBAAiB,aAClB,iBAAwC,SACxC;CAEL,MAAM,QAAQ,WACZ,aACIC,oCAAc,YAAY;EACxB,GAAI;EACJ;EACD,CAAM,GACPA,oCAAc,YAAY,OAAY;CAE5C,MAAM,UAAU,KAAK,kBAAkB,OAAO,OAAO;AAGrD,KAAI,WAAW,QAAQ,OAAO,YAAY,SACxC,SAAQ,YAAY,aAAa;AAC/B,SAAO,WAAW,cAAc;AAC9B,YAAS,KAAK,kBAAkB,UAAU,CAAC;IAC3C;AACF,SAAO;;AAIX,QAAO"}
@@ -3,12 +3,16 @@ const require_runtime = require('../_virtual/_rolldown/runtime.cjs');
3
3
  const require_getDictionary = require('../getDictionary.cjs');
4
4
  const require_client_installIntlayer = require('./installIntlayer.cjs');
5
5
  let _intlayer_config_built = require("@intlayer/config/built");
6
+ let _intlayer_core_dictionaryManipulator = require("@intlayer/core/dictionaryManipulator");
6
7
 
7
8
  //#region src/client/useDictionaryDynamic.ts
8
9
  /** Simple in-memory cache shared across all calls in the same page. */
9
10
  const cache = /* @__PURE__ */ new Map();
10
11
  /** Tracks in-flight loads to avoid duplicate fetches. */
11
12
  const inflight = /* @__PURE__ */ new Map();
13
+ /** Resolved-content cache for qualified (collection/variant/meta) keys. */
14
+ const qualifiedCache = /* @__PURE__ */ new Map();
15
+ const qualifiedInflight = /* @__PURE__ */ new Map();
12
16
  const loadDictionary = (cacheKey, loader) => {
13
17
  if (cache.has(cacheKey)) return Promise.resolve();
14
18
  if (inflight.has(cacheKey)) return inflight.get(cacheKey);
@@ -19,39 +23,78 @@ const loadDictionary = (cacheKey, loader) => {
19
23
  inflight.set(cacheKey, promise);
20
24
  return promise;
21
25
  };
26
+ /** Placeholder proxy — safe for any property access while loading. */
27
+ const recursiveProxy = new Proxy(() => {}, {
28
+ get: (_t, prop) => {
29
+ if (prop === Symbol.toPrimitive) return () => "";
30
+ if (prop === "toString") return () => "";
31
+ if (prop === "valueOf") return () => "";
32
+ if (prop === "then") return void 0;
33
+ if (prop === "onChange") return (_cb) => recursiveProxy;
34
+ return recursiveProxy;
35
+ },
36
+ apply: () => recursiveProxy
37
+ });
22
38
  /**
23
- * Dynamically load and transform a locale-keyed dictionary.
39
+ * Dynamically load and transform a dictionary (plain or qualified).
24
40
  *
25
- * Works like `useIntlayer` / `useDictionary` but accepts a locale-keyed map
26
- * of lazy loaders instead of a pre-imported dictionary. Call it inside your
27
- * render function the first call triggers a background fetch and returns
28
- * placeholder values; when the load completes the client notifies all
29
- * subscribers (including your render loop) so the render runs again with real
30
- * content. Subsequent calls for the same locale return immediately from cache.
41
+ * For a qualified loader map (collection / variant / meta record, possibly
42
+ * combined), only the chunk(s) the selector targets are loaded. For a plain
43
+ * loader map, the locale chunk is loaded. The first call returns a placeholder
44
+ * and triggers a background load; the client then notifies subscribers so the
45
+ * render runs again with real content.
31
46
  *
32
- * Locale switches follow the same two-phase pattern: placeholder on first
33
- * render, real content after the locale bundle loads.
34
- *
35
- * @param dictionaryLoaders - Locale-keyed map of `() => Promise<Dictionary>`.
47
+ * @param dictionaryLoaders - Locale-keyed loader map, or a qualified loader map.
36
48
  * @param key - Dictionary key (used for cache namespacing).
37
- * @param locale - Optional locale override.
38
- *
39
- * @example
40
- * ```ts
41
- * import dynDic from '../.intlayer/dynamic_dictionary/app.mjs';
42
- *
43
- * const render = () => {
44
- * const content = useDictionaryDynamic(dynDic, 'app');
45
- * document.querySelector('h1')!.textContent = String(content.title);
46
- * };
47
- *
48
- * render();
49
- * getIntlayerClient().subscribe(() => render());
50
- * ```
49
+ * @param localeOrSelector - Optional locale or selector.
51
50
  */
52
- const useDictionaryDynamic = (dictionaryLoaders, key, locale) => {
51
+ const useDictionaryDynamic = (dictionaryLoaders, key, localeOrSelector) => {
53
52
  const client = require_client_installIntlayer.getIntlayerClient();
54
- const currentLocale = locale ?? client.locale ?? _intlayer_config_built.internationalization.defaultLocale;
53
+ const defaultLocale = _intlayer_config_built.internationalization.defaultLocale;
54
+ if ((0, _intlayer_core_dictionaryManipulator.isQualifiedDynamicLoaderMap)(dictionaryLoaders)) {
55
+ const loaderMap = dictionaryLoaders;
56
+ const { locale: selectorLocale, selector } = (0, _intlayer_core_dictionaryManipulator.parseDictionarySelector)(localeOrSelector);
57
+ const selectorId = (0, _intlayer_core_dictionaryManipulator.getDictionarySelectorCacheKey)(selector);
58
+ const buildKey = (locale) => `${String(key)}.${locale}.${selectorId}`;
59
+ const resolveContent = (locale) => (0, _intlayer_core_dictionaryManipulator.resolveQualifiedDynamicContentAsync)({
60
+ loaderMap,
61
+ key: String(key),
62
+ locale,
63
+ selector,
64
+ transform: (dictionary) => require_getDictionary.getDictionary(dictionary, locale)
65
+ });
66
+ const currentLocale = selectorLocale ?? client.locale ?? defaultLocale;
67
+ const cacheKey = buildKey(currentLocale);
68
+ const attachOnChange = (content) => {
69
+ if (content != null && typeof content === "object") content.onChange = (callback) => {
70
+ client.subscribe((newLocale) => {
71
+ const locale = selectorLocale ?? newLocale;
72
+ const newKey = buildKey(locale);
73
+ if (qualifiedCache.has(newKey)) {
74
+ callback(qualifiedCache.get(newKey));
75
+ return;
76
+ }
77
+ resolveContent(locale).then((resolved) => {
78
+ qualifiedCache.set(newKey, resolved);
79
+ callback(resolved);
80
+ });
81
+ });
82
+ return content;
83
+ };
84
+ return content;
85
+ };
86
+ if (qualifiedCache.has(cacheKey)) return attachOnChange(qualifiedCache.get(cacheKey));
87
+ if (!qualifiedInflight.has(cacheKey)) {
88
+ const promise = resolveContent(currentLocale).then((resolved) => {
89
+ qualifiedCache.set(cacheKey, resolved);
90
+ qualifiedInflight.delete(cacheKey);
91
+ client.notify();
92
+ });
93
+ qualifiedInflight.set(cacheKey, promise);
94
+ }
95
+ return recursiveProxy;
96
+ }
97
+ const currentLocale = (typeof localeOrSelector === "string" ? localeOrSelector : void 0) ?? client.locale ?? defaultLocale;
55
98
  const cacheKey = `${String(key)}.${currentLocale}`;
56
99
  const loader = dictionaryLoaders[currentLocale];
57
100
  const cached = cache.get(cacheKey);
@@ -74,17 +117,6 @@ const useDictionaryDynamic = (dictionaryLoaders, key, locale) => {
74
117
  if (loader) loadDictionary(cacheKey, loader).then(() => {
75
118
  client.notify();
76
119
  });
77
- const recursiveProxy = new Proxy(() => {}, {
78
- get: (_t, prop) => {
79
- if (prop === Symbol.toPrimitive) return () => "";
80
- if (prop === "toString") return () => "";
81
- if (prop === "valueOf") return () => "";
82
- if (prop === "then") return void 0;
83
- if (prop === "onChange") return (_cb) => recursiveProxy;
84
- return recursiveProxy;
85
- },
86
- apply: () => recursiveProxy
87
- });
88
120
  return recursiveProxy;
89
121
  };
90
122
 
@@ -1 +1 @@
1
- {"version":3,"file":"useDictionaryDynamic.cjs","names":["getIntlayerClient","internationalization","getDictionary"],"sources":["../../../src/client/useDictionaryDynamic.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type {\n DictionaryKeys,\n LocalesValues,\n StrictModeLocaleMap,\n} from '@intlayer/types/module_augmentation';\nimport { getDictionary } from '../getDictionary';\nimport type { DeepTransformContent } from '../plugins';\nimport { getIntlayerClient } from './installIntlayer';\nimport type { WithOnChange } from './useDictionary';\n\n/** Simple in-memory cache shared across all calls in the same page. */\nconst cache = new Map<string, Dictionary>();\n\n/** Tracks in-flight loads to avoid duplicate fetches. */\nconst inflight = new Map<string, Promise<void>>();\n\nconst loadDictionary = <T extends Dictionary>(\n cacheKey: string,\n loader: () => Promise<T>\n): Promise<void> => {\n if (cache.has(cacheKey)) return Promise.resolve();\n if (inflight.has(cacheKey)) return inflight.get(cacheKey)!;\n\n const promise = loader().then((dictionary) => {\n cache.set(cacheKey, dictionary);\n inflight.delete(cacheKey);\n });\n\n inflight.set(cacheKey, promise);\n return promise;\n};\n\n/**\n * Dynamically load and transform a locale-keyed dictionary.\n *\n * Works like `useIntlayer` / `useDictionary` but accepts a locale-keyed map\n * of lazy loaders instead of a pre-imported dictionary. Call it inside your\n * render function the first call triggers a background fetch and returns\n * placeholder values; when the load completes the client notifies all\n * subscribers (including your render loop) so the render runs again with real\n * content. Subsequent calls for the same locale return immediately from cache.\n *\n * Locale switches follow the same two-phase pattern: placeholder on first\n * render, real content after the locale bundle loads.\n *\n * @param dictionaryLoaders - Locale-keyed map of `() => Promise<Dictionary>`.\n * @param key - Dictionary key (used for cache namespacing).\n * @param locale - Optional locale override.\n *\n * @example\n * ```ts\n * import dynDic from '../.intlayer/dynamic_dictionary/app.mjs';\n *\n * const render = () => {\n * const content = useDictionaryDynamic(dynDic, 'app');\n * document.querySelector('h1')!.textContent = String(content.title);\n * };\n *\n * render();\n * getIntlayerClient().subscribe(() => render());\n * ```\n */\nexport const useDictionaryDynamic = <\n const T extends Dictionary,\n const K extends DictionaryKeys,\n const L extends LocalesValues = LocalesValues,\n>(\n dictionaryLoaders: StrictModeLocaleMap<() => Promise<T>>,\n key: K,\n locale?: L\n): WithOnChange<DeepTransformContent<T['content'], L>> => {\n const client = getIntlayerClient();\n const currentLocale = (locale ??\n client.locale ??\n internationalization.defaultLocale) as L;\n\n const cacheKey = `${String(key)}.${currentLocale}`;\n const loader = (dictionaryLoaders as Record<string, () => Promise<T>>)[\n currentLocale\n ];\n\n // --- Cache hit: return real content synchronously ---\n const cached = cache.get(cacheKey);\n if (cached) {\n const content = getDictionary(cached as T, currentLocale) as WithOnChange<\n DeepTransformContent<T['content'], L>\n >;\n\n content.onChange = (\n callback: (content: DeepTransformContent<T['content'], L>) => void\n ) => {\n // Re-fire whenever content reloads (locale change → new cache entry).\n client.subscribe((newLocale) => {\n const newKey = `${String(key)}.${newLocale}`;\n const newLoader = (\n dictionaryLoaders as Record<string, () => Promise<T>>\n )[newLocale];\n\n if (!newLoader) return;\n\n loadDictionary(newKey, newLoader).then(() => {\n const dict = cache.get(newKey);\n if (dict) {\n callback(\n getDictionary(dict as T, newLocale as L) as DeepTransformContent<\n T['content'],\n L\n >\n );\n }\n });\n });\n\n return content;\n };\n\n return content;\n }\n\n // --- Cache miss: kick off background load, then notify to re-render ---\n if (loader) {\n loadDictionary(cacheKey, loader).then(() => {\n client.notify();\n });\n }\n\n // Placeholder proxy — safe for any property access while loading.\n const recursiveProxy: any = new Proxy(() => {}, {\n get: (_t, prop) => {\n if (prop === Symbol.toPrimitive) return () => '';\n if (prop === 'toString') return () => '';\n if (prop === 'valueOf') return () => '';\n if (prop === 'then') return undefined; // not a Promise\n if (prop === 'onChange') return (_cb: any) => recursiveProxy;\n return recursiveProxy;\n },\n apply: () => recursiveProxy,\n });\n\n return recursiveProxy as WithOnChange<DeepTransformContent<T['content'], L>>;\n};\n"],"mappings":";;;;;;;;AAaA,MAAM,wBAAQ,IAAI,KAAyB;;AAG3C,MAAM,2BAAW,IAAI,KAA4B;AAEjD,MAAM,kBACJ,UACA,WACkB;AAClB,KAAI,MAAM,IAAI,SAAS,CAAE,QAAO,QAAQ,SAAS;AACjD,KAAI,SAAS,IAAI,SAAS,CAAE,QAAO,SAAS,IAAI,SAAS;CAEzD,MAAM,UAAU,QAAQ,CAAC,MAAM,eAAe;AAC5C,QAAM,IAAI,UAAU,WAAW;AAC/B,WAAS,OAAO,SAAS;GACzB;AAEF,UAAS,IAAI,UAAU,QAAQ;AAC/B,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCT,MAAa,wBAKX,mBACA,KACA,WACwD;CACxD,MAAM,SAASA,kDAAmB;CAClC,MAAM,gBAAiB,UACrB,OAAO,UACPC,4CAAqB;CAEvB,MAAM,WAAW,GAAG,OAAO,IAAI,CAAC,GAAG;CACnC,MAAM,SAAU,kBACd;CAIF,MAAM,SAAS,MAAM,IAAI,SAAS;AAClC,KAAI,QAAQ;EACV,MAAM,UAAUC,oCAAc,QAAa,cAAc;AAIzD,UAAQ,YACN,aACG;AAEH,UAAO,WAAW,cAAc;IAC9B,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,GAAG;IACjC,MAAM,YACJ,kBACA;AAEF,QAAI,CAAC,UAAW;AAEhB,mBAAe,QAAQ,UAAU,CAAC,WAAW;KAC3C,MAAM,OAAO,MAAM,IAAI,OAAO;AAC9B,SAAI,KACF,UACEA,oCAAc,MAAW,UAAe,CAIzC;MAEH;KACF;AAEF,UAAO;;AAGT,SAAO;;AAIT,KAAI,OACF,gBAAe,UAAU,OAAO,CAAC,WAAW;AAC1C,SAAO,QAAQ;GACf;CAIJ,MAAM,iBAAsB,IAAI,YAAY,IAAI;EAC9C,MAAM,IAAI,SAAS;AACjB,OAAI,SAAS,OAAO,YAAa,cAAa;AAC9C,OAAI,SAAS,WAAY,cAAa;AACtC,OAAI,SAAS,UAAW,cAAa;AACrC,OAAI,SAAS,OAAQ,QAAO;AAC5B,OAAI,SAAS,WAAY,SAAQ,QAAa;AAC9C,UAAO;;EAET,aAAa;EACd,CAAC;AAEF,QAAO"}
1
+ {"version":3,"file":"useDictionaryDynamic.cjs","names":["getIntlayerClient","internationalization","getDictionary"],"sources":["../../../src/client/useDictionaryDynamic.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\nimport {\n getDictionarySelectorCacheKey,\n isQualifiedDynamicLoaderMap,\n parseDictionarySelector,\n type QualifiedDynamicLoaderMap,\n resolveQualifiedDynamicContentAsync,\n} from '@intlayer/core/dictionaryManipulator';\nimport type {\n Dictionary,\n DictionarySelector,\n} from '@intlayer/types/dictionary';\nimport type {\n DictionaryKeys,\n DictionarySelectorForKey,\n LocalesValues,\n StrictModeLocaleMap,\n} from '@intlayer/types/module_augmentation';\nimport { getDictionary } from '../getDictionary';\nimport type { DeepTransformContent } from '../plugins';\nimport { getIntlayerClient } from './installIntlayer';\nimport type { WithOnChange } from './useDictionary';\n\n/** Simple in-memory cache shared across all calls in the same page. */\nconst cache = new Map<string, Dictionary>();\n\n/** Tracks in-flight loads to avoid duplicate fetches. */\nconst inflight = new Map<string, Promise<void>>();\n\n/** Resolved-content cache for qualified (collection/variant/meta) keys. */\nconst qualifiedCache = new Map<string, unknown>();\nconst qualifiedInflight = new Map<string, Promise<void>>();\n\nconst loadDictionary = <T extends Dictionary>(\n cacheKey: string,\n loader: () => Promise<T>\n): Promise<void> => {\n if (cache.has(cacheKey)) return Promise.resolve();\n if (inflight.has(cacheKey)) return inflight.get(cacheKey)!;\n\n const promise = loader().then((dictionary) => {\n cache.set(cacheKey, dictionary);\n inflight.delete(cacheKey);\n });\n\n inflight.set(cacheKey, promise);\n return promise;\n};\n\n/** Placeholder proxy safe for any property access while loading. */\nconst recursiveProxy: any = new Proxy(() => {}, {\n get: (_t, prop) => {\n if (prop === Symbol.toPrimitive) return () => '';\n if (prop === 'toString') return () => '';\n if (prop === 'valueOf') return () => '';\n if (prop === 'then') return undefined; // not a Promise\n if (prop === 'onChange') return (_cb: any) => recursiveProxy;\n return recursiveProxy;\n },\n apply: () => recursiveProxy,\n});\n\n/**\n * Dynamically load and transform a dictionary (plain or qualified).\n *\n * For a qualified loader map (collection / variant / meta record, possibly\n * combined), only the chunk(s) the selector targets are loaded. For a plain\n * loader map, the locale chunk is loaded. The first call returns a placeholder\n * and triggers a background load; the client then notifies subscribers so the\n * render runs again with real content.\n *\n * @param dictionaryLoaders - Locale-keyed loader map, or a qualified loader map.\n * @param key - Dictionary key (used for cache namespacing).\n * @param localeOrSelector - Optional locale or selector.\n */\nexport const useDictionaryDynamic = <\n const T extends Dictionary,\n const K extends DictionaryKeys,\n const A extends LocalesValues | DictionarySelectorForKey<K> = LocalesValues,\n>(\n dictionaryLoaders:\n | StrictModeLocaleMap<() => Promise<T>>\n | QualifiedDynamicLoaderMap,\n key: K,\n localeOrSelector?: A\n): WithOnChange<DeepTransformContent<T['content']>> => {\n const client = getIntlayerClient();\n const defaultLocale = internationalization.defaultLocale;\n\n // --- Qualified loader map (collection / variant / meta record) ---\n if (isQualifiedDynamicLoaderMap(dictionaryLoaders)) {\n const loaderMap = dictionaryLoaders;\n const { locale: selectorLocale, selector } =\n parseDictionarySelector<LocalesValues>(localeOrSelector);\n const selectorId = getDictionarySelectorCacheKey(selector);\n\n const buildKey = (locale: string) =>\n `${String(key)}.${locale}.${selectorId}`;\n const resolveContent = (locale: LocalesValues) =>\n resolveQualifiedDynamicContentAsync({\n loaderMap,\n key: String(key),\n locale,\n selector,\n transform: (dictionary) => getDictionary(dictionary, locale),\n });\n\n const currentLocale = (selectorLocale ??\n client.locale ??\n defaultLocale) as LocalesValues;\n const cacheKey = buildKey(currentLocale);\n\n const attachOnChange = (content: any) => {\n if (content != null && typeof content === 'object') {\n content.onChange = (callback: (c: any) => void) => {\n client.subscribe((newLocale) => {\n const locale = (selectorLocale ?? newLocale) as LocalesValues;\n const newKey = buildKey(locale);\n\n if (qualifiedCache.has(newKey)) {\n callback(qualifiedCache.get(newKey));\n return;\n }\n\n resolveContent(locale).then((resolved) => {\n qualifiedCache.set(newKey, resolved);\n callback(resolved);\n });\n });\n return content;\n };\n }\n return content;\n };\n\n if (qualifiedCache.has(cacheKey)) {\n return attachOnChange(qualifiedCache.get(cacheKey)) as any;\n }\n\n if (!qualifiedInflight.has(cacheKey)) {\n const promise = resolveContent(currentLocale).then((resolved) => {\n qualifiedCache.set(cacheKey, resolved);\n qualifiedInflight.delete(cacheKey);\n client.notify();\n });\n qualifiedInflight.set(cacheKey, promise);\n }\n\n return recursiveProxy;\n }\n\n // --- Plain locale-keyed loader map ---\n const locale =\n typeof localeOrSelector === 'string'\n ? (localeOrSelector as LocalesValues)\n : undefined;\n const currentLocale = (locale ?? client.locale ?? defaultLocale) as A;\n\n const cacheKey = `${String(key)}.${currentLocale}`;\n const loader = (dictionaryLoaders as Record<string, () => Promise<T>>)[\n currentLocale as string\n ];\n\n // --- Cache hit: return real content synchronously ---\n const cached = cache.get(cacheKey);\n if (cached) {\n const content = getDictionary(cached, currentLocale) as WithOnChange<\n DeepTransformContent<T['content']>\n >;\n\n content.onChange = (callback) => {\n // Re-fire whenever content reloads (locale change → new cache entry).\n client.subscribe((newLocale) => {\n const newKey = `${String(key)}.${newLocale}`;\n const newLoader = (\n dictionaryLoaders as Record<string, () => Promise<T>>\n )[newLocale];\n\n if (!newLoader) return;\n\n loadDictionary(newKey, newLoader).then(() => {\n const dict = cache.get(newKey);\n if (dict) {\n callback(getDictionary(dict, newLocale as A) as any);\n }\n });\n });\n\n return content;\n };\n\n return content;\n }\n\n // --- Cache miss: kick off background load, then notify to re-render ---\n if (loader) {\n loadDictionary(cacheKey, loader).then(() => {\n client.notify();\n });\n }\n\n return recursiveProxy;\n};\n"],"mappings":";;;;;;;;;AAwBA,MAAM,wBAAQ,IAAI,KAAyB;;AAG3C,MAAM,2BAAW,IAAI,KAA4B;;AAGjD,MAAM,iCAAiB,IAAI,KAAsB;AACjD,MAAM,oCAAoB,IAAI,KAA4B;AAE1D,MAAM,kBACJ,UACA,WACkB;AAClB,KAAI,MAAM,IAAI,SAAS,CAAE,QAAO,QAAQ,SAAS;AACjD,KAAI,SAAS,IAAI,SAAS,CAAE,QAAO,SAAS,IAAI,SAAS;CAEzD,MAAM,UAAU,QAAQ,CAAC,MAAM,eAAe;AAC5C,QAAM,IAAI,UAAU,WAAW;AAC/B,WAAS,OAAO,SAAS;GACzB;AAEF,UAAS,IAAI,UAAU,QAAQ;AAC/B,QAAO;;;AAIT,MAAM,iBAAsB,IAAI,YAAY,IAAI;CAC9C,MAAM,IAAI,SAAS;AACjB,MAAI,SAAS,OAAO,YAAa,cAAa;AAC9C,MAAI,SAAS,WAAY,cAAa;AACtC,MAAI,SAAS,UAAW,cAAa;AACrC,MAAI,SAAS,OAAQ,QAAO;AAC5B,MAAI,SAAS,WAAY,SAAQ,QAAa;AAC9C,SAAO;;CAET,aAAa;CACd,CAAC;;;;;;;;;;;;;;AAeF,MAAa,wBAKX,mBAGA,KACA,qBACqD;CACrD,MAAM,SAASA,kDAAmB;CAClC,MAAM,gBAAgBC,4CAAqB;AAG3C,2EAAgC,kBAAkB,EAAE;EAClD,MAAM,YAAY;EAClB,MAAM,EAAE,QAAQ,gBAAgB,+EACS,iBAAiB;EAC1D,MAAM,qFAA2C,SAAS;EAE1D,MAAM,YAAY,WAChB,GAAG,OAAO,IAAI,CAAC,GAAG,OAAO,GAAG;EAC9B,MAAM,kBAAkB,yFACc;GAClC;GACA,KAAK,OAAO,IAAI;GAChB;GACA;GACA,YAAY,eAAeC,oCAAc,YAAY,OAAO;GAC7D,CAAC;EAEJ,MAAM,gBAAiB,kBACrB,OAAO,UACP;EACF,MAAM,WAAW,SAAS,cAAc;EAExC,MAAM,kBAAkB,YAAiB;AACvC,OAAI,WAAW,QAAQ,OAAO,YAAY,SACxC,SAAQ,YAAY,aAA+B;AACjD,WAAO,WAAW,cAAc;KAC9B,MAAM,SAAU,kBAAkB;KAClC,MAAM,SAAS,SAAS,OAAO;AAE/B,SAAI,eAAe,IAAI,OAAO,EAAE;AAC9B,eAAS,eAAe,IAAI,OAAO,CAAC;AACpC;;AAGF,oBAAe,OAAO,CAAC,MAAM,aAAa;AACxC,qBAAe,IAAI,QAAQ,SAAS;AACpC,eAAS,SAAS;OAClB;MACF;AACF,WAAO;;AAGX,UAAO;;AAGT,MAAI,eAAe,IAAI,SAAS,CAC9B,QAAO,eAAe,eAAe,IAAI,SAAS,CAAC;AAGrD,MAAI,CAAC,kBAAkB,IAAI,SAAS,EAAE;GACpC,MAAM,UAAU,eAAe,cAAc,CAAC,MAAM,aAAa;AAC/D,mBAAe,IAAI,UAAU,SAAS;AACtC,sBAAkB,OAAO,SAAS;AAClC,WAAO,QAAQ;KACf;AACF,qBAAkB,IAAI,UAAU,QAAQ;;AAG1C,SAAO;;CAQT,MAAM,iBAHJ,OAAO,qBAAqB,WACvB,mBACD,WAC2B,OAAO,UAAU;CAElD,MAAM,WAAW,GAAG,OAAO,IAAI,CAAC,GAAG;CACnC,MAAM,SAAU,kBACd;CAIF,MAAM,SAAS,MAAM,IAAI,SAAS;AAClC,KAAI,QAAQ;EACV,MAAM,UAAUA,oCAAc,QAAQ,cAAc;AAIpD,UAAQ,YAAY,aAAa;AAE/B,UAAO,WAAW,cAAc;IAC9B,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,GAAG;IACjC,MAAM,YACJ,kBACA;AAEF,QAAI,CAAC,UAAW;AAEhB,mBAAe,QAAQ,UAAU,CAAC,WAAW;KAC3C,MAAM,OAAO,MAAM,IAAI,OAAO;AAC9B,SAAI,KACF,UAASA,oCAAc,MAAM,UAAe,CAAQ;MAEtD;KACF;AAEF,UAAO;;AAGT,SAAO;;AAIT,KAAI,OACF,gBAAe,UAAU,OAAO,CAAC,WAAW;AAC1C,SAAO,QAAQ;GACf;AAGJ,QAAO"}
@@ -8,28 +8,17 @@ const require_client_installIntlayer = require('./installIntlayer.cjs');
8
8
  * locale changes via the chainable `.onChange()` method — mirroring the API
9
9
  * of `react-intlayer`'s `useIntlayer`.
10
10
  *
11
- * Unlike React (where the hook system automatically re-runs on re-render),
12
- * vanilla JS has no component lifecycle. The `.onChange()` callback is the
13
- * explicit equivalent: it is called with fresh content whenever the active
14
- * locale changes. Use it to patch your DOM or trigger a full re-render.
11
+ * The second argument is either a locale or a selector object:
12
+ * - `{ item: 2 }` collection item (omit `item` to get every item as array)
13
+ * - `{ variant: 'black-friday' }` named variant (omit for the `default` one)
14
+ * - `{ id: 'prod_abc', ...metaFields }` meta record
15
+ * - `locale` composes with any selector and overrides the current locale
15
16
  *
16
17
  * The function returns the current content **directly** (same shape as
17
- * `getIntlayer(key)`), plus the `.onChange()` helper:
18
+ * `getIntlayer(key)`), plus the `.onChange()` helper.
18
19
  *
19
- * ```ts
20
- * // React
21
- * const content = useIntlayer('app');
22
- *
23
- * // Vanilla — identical surface API, opt-in reactivity via .onChange()
24
- * const content = useIntlayer('app');
25
- * const content = useIntlayer('app').onChange((c) => render(c));
26
- * ```
27
- *
28
- * For cleanup (e.g. Vite HMR), subscribe via `getIntlayerClient().subscribe()`
29
- * and hold the returned unsubscribe function.
30
- *
31
- * @param key - Dictionary key registered in your intlayer content files.
32
- * @param locale - Optional locale override (defaults to the current app locale).
20
+ * @param key - Dictionary key registered in your intlayer content files.
21
+ * @param localeOrSelector - Optional locale or selector.
33
22
  * @returns The current translated content with an `.onChange()` method.
34
23
  *
35
24
  * @example
@@ -38,22 +27,22 @@ const require_client_installIntlayer = require('./installIntlayer.cjs');
38
27
  *
39
28
  * installIntlayer();
40
29
  *
41
- * // Static read (no subscription)
42
30
  * const content = useIntlayer('homepage');
43
- * document.querySelector('h1').textContent = String(content.title);
44
- *
45
- * // Reactive read — onChange is called on every locale change
46
- * useIntlayer('homepage').onChange((c) => {
47
- * document.querySelector('h1').textContent = String(c.title);
48
- * });
31
+ * const faq2 = useIntlayer('faq', { item: 2 });
49
32
  * ```
50
33
  */
51
- const useIntlayer = (key, locale) => {
34
+ const useIntlayer = (key, localeOrSelector) => {
52
35
  const client = require_client_installIntlayer.getIntlayerClient();
53
- const content = require_getIntlayer.getIntlayer(key, locale ?? client.locale);
54
- content.onChange = (callback) => {
36
+ const isSelector = typeof localeOrSelector === "object" && localeOrSelector !== null;
37
+ const explicitLocale = isSelector ? localeOrSelector.locale : localeOrSelector;
38
+ const read = (locale) => isSelector ? require_getIntlayer.getIntlayer(key, {
39
+ ...localeOrSelector,
40
+ locale
41
+ }) : require_getIntlayer.getIntlayer(key, locale);
42
+ const content = read(explicitLocale ?? client.locale);
43
+ if (content != null && typeof content === "object") content.onChange = (callback) => {
55
44
  client.subscribe((newLocale) => {
56
- callback(require_getIntlayer.getIntlayer(key, locale ?? newLocale));
45
+ callback(read(explicitLocale ?? newLocale));
57
46
  });
58
47
  return content;
59
48
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useIntlayer.cjs","names":["getIntlayerClient","getIntlayer"],"sources":["../../../src/client/useIntlayer.ts"],"sourcesContent":["import type {\n DictionaryKeys,\n DictionaryRegistryContent,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { getIntlayer } from '../getIntlayer';\nimport type { DeepTransformContent } from '../plugins';\nimport { getIntlayerClient } from './installIntlayer';\nimport type { WithOnChange } from './useDictionary';\n\n/**\n * Get the translated content for the given key and optionally subscribe to\n * locale changes via the chainable `.onChange()` method — mirroring the API\n * of `react-intlayer`'s `useIntlayer`.\n *\n * Unlike React (where the hook system automatically re-runs on re-render),\n * vanilla JS has no component lifecycle. The `.onChange()` callback is the\n * explicit equivalent: it is called with fresh content whenever the active\n * locale changes. Use it to patch your DOM or trigger a full re-render.\n *\n * The function returns the current content **directly** (same shape as\n * `getIntlayer(key)`), plus the `.onChange()` helper:\n *\n * ```ts\n * // React\n * const content = useIntlayer('app');\n *\n * // Vanilla — identical surface API, opt-in reactivity via .onChange()\n * const content = useIntlayer('app');\n * const content = useIntlayer('app').onChange((c) => render(c));\n * ```\n *\n * For cleanup (e.g. Vite HMR), subscribe via `getIntlayerClient().subscribe()`\n * and hold the returned unsubscribe function.\n *\n * @param key - Dictionary key registered in your intlayer content files.\n * @param locale - Optional locale override (defaults to the current app locale).\n * @returns The current translated content with an `.onChange()` method.\n *\n * @example\n * ```ts\n * import { installIntlayer, useIntlayer } from 'vanilla-intlayer';\n *\n * installIntlayer();\n *\n * // Static read (no subscription)\n * const content = useIntlayer('homepage');\n * document.querySelector('h1').textContent = String(content.title);\n *\n * // Reactive read — onChange is called on every locale change\n * useIntlayer('homepage').onChange((c) => {\n * document.querySelector('h1').textContent = String(c.title);\n * });\n * ```\n */\nexport const useIntlayer = <const T extends DictionaryKeys>(\n key: T,\n locale?: LocalesValues\n): WithOnChange<DeepTransformContent<DictionaryRegistryContent<T>>> => {\n const client = getIntlayerClient();\n const content = getIntlayer(key, locale ?? client.locale) as WithOnChange<\n DeepTransformContent<DictionaryRegistryContent<T>>\n >;\n\n content.onChange = (callback) => {\n client.subscribe((newLocale) => {\n callback(getIntlayer(key, (locale ?? newLocale) as typeof locale));\n });\n return content;\n };\n\n return content;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDA,MAAa,eACX,KACA,WACqE;CACrE,MAAM,SAASA,kDAAmB;CAClC,MAAM,UAAUC,gCAAY,KAAK,UAAU,OAAO,OAAO;AAIzD,SAAQ,YAAY,aAAa;AAC/B,SAAO,WAAW,cAAc;AAC9B,YAASA,gCAAY,KAAM,UAAU,UAA4B,CAAC;IAClE;AACF,SAAO;;AAGT,QAAO"}
1
+ {"version":3,"file":"useIntlayer.cjs","names":["getIntlayerClient","getIntlayer"],"sources":["../../../src/client/useIntlayer.ts"],"sourcesContent":["import type { DictionarySelector } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n DictionaryKeys,\n DictionaryRegistryResult,\n DictionarySelectorForKey,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { getIntlayer } from '../getIntlayer';\nimport type { DeepTransformContent } from '../plugins';\nimport { getIntlayerClient } from './installIntlayer';\nimport type { WithOnChange } from './useDictionary';\n\n/**\n * Get the translated content for the given key and optionally subscribe to\n * locale changes via the chainable `.onChange()` method — mirroring the API\n * of `react-intlayer`'s `useIntlayer`.\n *\n * The second argument is either a locale or a selector object:\n * - `{ item: 2 }` collection item (omit `item` to get every item as array)\n * - `{ variant: 'black-friday' }` named variant (omit for the `default` one)\n * - `{ id: 'prod_abc', ...metaFields }` meta record\n * - `locale` composes with any selector and overrides the current locale\n *\n * The function returns the current content **directly** (same shape as\n * `getIntlayer(key)`), plus the `.onChange()` helper.\n *\n * @param key - Dictionary key registered in your intlayer content files.\n * @param localeOrSelector - Optional locale or selector.\n * @returns The current translated content with an `.onChange()` method.\n *\n * @example\n * ```ts\n * import { installIntlayer, useIntlayer } from 'vanilla-intlayer';\n *\n * installIntlayer();\n *\n * const content = useIntlayer('homepage');\n * const faq2 = useIntlayer('faq', { item: 2 });\n * ```\n */\nexport const useIntlayer = <\n const T extends DictionaryKeys,\n const A extends LocalesValues | DictionarySelectorForKey<T> = DeclaredLocales,\n>(\n key: T,\n localeOrSelector?: A\n): WithOnChange<\n DeepTransformContent<DictionaryRegistryResult<T, A>, ExtractSelectorLocale<A>>\n> => {\n const client = getIntlayerClient();\n\n const isSelector =\n typeof localeOrSelector === 'object' && localeOrSelector !== null;\n const explicitLocale = isSelector\n ? (localeOrSelector as DictionarySelector).locale\n : (localeOrSelector as LocalesValues | undefined);\n\n const read = (locale: LocalesValues | undefined) =>\n isSelector\n ? getIntlayer(key, {\n ...(localeOrSelector as DictionarySelector),\n locale,\n } as A)\n : getIntlayer(key, locale as A);\n\n const content = read(explicitLocale ?? client.locale) as WithOnChange<any>;\n\n // A selector can resolve to null/array; only objects carry `.onChange`.\n if (content != null && typeof content === 'object') {\n content.onChange = (callback) => {\n client.subscribe((newLocale) => {\n callback(read(explicitLocale ?? newLocale));\n });\n return content;\n };\n }\n\n return content;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,MAAa,eAIX,KACA,qBAGG;CACH,MAAM,SAASA,kDAAmB;CAElC,MAAM,aACJ,OAAO,qBAAqB,YAAY,qBAAqB;CAC/D,MAAM,iBAAiB,aAClB,iBAAwC,SACxC;CAEL,MAAM,QAAQ,WACZ,aACIC,gCAAY,KAAK;EACf,GAAI;EACJ;EACD,CAAM,GACPA,gCAAY,KAAK,OAAY;CAEnC,MAAM,UAAU,KAAK,kBAAkB,OAAO,OAAO;AAGrD,KAAI,WAAW,QAAQ,OAAO,YAAY,SACxC,SAAQ,YAAY,aAAa;AAC/B,SAAO,WAAW,cAAc;AAC9B,YAAS,KAAK,kBAAkB,UAAU,CAAC;IAC3C;AACF,SAAO;;AAIX,QAAO"}
@@ -4,7 +4,14 @@ const require_plugins = require('./plugins.cjs');
4
4
  let _intlayer_core_interpreter = require("@intlayer/core/interpreter");
5
5
 
6
6
  //#region src/getDictionary.ts
7
- const getDictionary = (dictionary, locale) => (0, _intlayer_core_interpreter.getDictionary)(dictionary, locale, require_plugins.getPlugins(locale));
7
+ /**
8
+ * Transforms a dictionary (or qualified dictionary group) and returns its
9
+ * content for the given locale or selector (`{ item }`, `{ variant }`,
10
+ * `{ id, ...meta }`, optionally combined with `locale`).
11
+ */
12
+ const getDictionary = (dictionary, localeOrSelector) => {
13
+ return (0, _intlayer_core_interpreter.getDictionary)(dictionary, localeOrSelector, require_plugins.getPlugins(typeof localeOrSelector === "object" && localeOrSelector !== null ? localeOrSelector.locale : localeOrSelector));
14
+ };
8
15
 
9
16
  //#endregion
10
17
  exports.getDictionary = getDictionary;
@@ -1 +1 @@
1
- {"version":3,"file":"getDictionary.cjs","names":["getPlugins"],"sources":["../../src/getDictionary.ts"],"sourcesContent":["import { getDictionary as getDictionaryCore } from '@intlayer/core/interpreter';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { type DeepTransformContent, getPlugins } from './plugins';\n\nexport const getDictionary = <\n const T extends Dictionary,\n const L extends LocalesValues = DeclaredLocales,\n>(\n dictionary: T,\n locale?: L\n): DeepTransformContent<T['content'], L> =>\n getDictionaryCore(dictionary, locale, getPlugins(locale)) as any;\n"],"mappings":";;;;;;AAQA,MAAa,iBAIX,YACA,yDAEkB,YAAY,QAAQA,2BAAW,OAAO,CAAC"}
1
+ {"version":3,"file":"getDictionary.cjs","names":["getPlugins"],"sources":["../../src/getDictionary.ts"],"sourcesContent":["import { getDictionary as getDictionaryCore } from '@intlayer/core/interpreter';\nimport type {\n Dictionary,\n DictionarySelector,\n DictionarySelectorForGroup,\n QualifiedDictionaryGroup,\n ResolveQualifiedDictionaryContent,\n} from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { type DeepTransformContent, getPlugins } from './plugins';\n\n/**\n * Transforms a dictionary (or qualified dictionary group) and returns its\n * content for the given locale or selector (`{ item }`, `{ variant }`,\n * `{ id, ...meta }`, optionally combined with `locale`).\n */\nexport const getDictionary = <\n const T extends Dictionary | QualifiedDictionaryGroup,\n const A extends\n | LocalesValues\n | DictionarySelectorForGroup<T> = DeclaredLocales,\n>(\n dictionary: T,\n localeOrSelector?: A\n): DeepTransformContent<\n ResolveQualifiedDictionaryContent<T, A>,\n ExtractSelectorLocale<A>\n> => {\n const locale = (\n typeof localeOrSelector === 'object' && localeOrSelector !== null\n ? localeOrSelector.locale\n : localeOrSelector\n ) as LocalesValues | undefined;\n\n return getDictionaryCore(\n dictionary,\n localeOrSelector,\n getPlugins(locale)\n ) as any;\n};\n"],"mappings":";;;;;;;;;;;AAoBA,MAAa,iBAMX,YACA,qBAIG;AAOH,sDACE,YACA,kBACAA,2BARA,OAAO,qBAAqB,YAAY,qBAAqB,OACzD,iBAAiB,SACjB,iBAMc,CACnB"}
@@ -4,7 +4,14 @@ const require_plugins = require('./plugins.cjs');
4
4
  let _intlayer_core_interpreter = require("@intlayer/core/interpreter");
5
5
 
6
6
  //#region src/getIntlayer.ts
7
- const getIntlayer = (key, locale) => (0, _intlayer_core_interpreter.getIntlayer)(key, locale, require_plugins.getPlugins(locale));
7
+ /**
8
+ * Picks one dictionary by its key and returns its content for the given
9
+ * locale or selector (`{ item }`, `{ variant }`, `{ id, ...meta }`,
10
+ * optionally combined with `locale`).
11
+ */
12
+ const getIntlayer = (key, localeOrSelector) => {
13
+ return (0, _intlayer_core_interpreter.getIntlayer)(key, localeOrSelector, require_plugins.getPlugins(typeof localeOrSelector === "object" && localeOrSelector !== null ? localeOrSelector.locale : localeOrSelector));
14
+ };
8
15
 
9
16
  //#endregion
10
17
  exports.getIntlayer = getIntlayer;
@@ -1 +1 @@
1
- {"version":3,"file":"getIntlayer.cjs","names":["getPlugins"],"sources":["../../src/getIntlayer.ts"],"sourcesContent":["import { getIntlayer as getIntlayerCore } from '@intlayer/core/interpreter';\nimport type {\n DeclaredLocales,\n DictionaryKeys,\n DictionaryRegistryContent,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { type DeepTransformContent, getPlugins } from './plugins';\n\nexport const getIntlayer = <\n const T extends DictionaryKeys,\n const L extends LocalesValues = DeclaredLocales,\n>(\n key: T,\n locale?: L\n): DeepTransformContent<DictionaryRegistryContent<T>, L> =>\n getIntlayerCore(key, locale, getPlugins(locale)) as any;\n"],"mappings":";;;;;;AASA,MAAa,eAIX,KACA,uDAEgB,KAAK,QAAQA,2BAAW,OAAO,CAAC"}
1
+ {"version":3,"file":"getIntlayer.cjs","names":["getPlugins"],"sources":["../../src/getIntlayer.ts"],"sourcesContent":["import { getIntlayer as getIntlayerCore } from '@intlayer/core/interpreter';\nimport type { DictionarySelector } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n DictionaryKeys,\n DictionaryRegistryResult,\n DictionarySelectorForKey,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { type DeepTransformContent, getPlugins } from './plugins';\n\n/**\n * Picks one dictionary by its key and returns its content for the given\n * locale or selector (`{ item }`, `{ variant }`, `{ id, ...meta }`,\n * optionally combined with `locale`).\n */\nexport const getIntlayer = <\n const T extends DictionaryKeys,\n const A extends LocalesValues | DictionarySelectorForKey<T> = DeclaredLocales,\n>(\n key: T,\n localeOrSelector?: A\n): DeepTransformContent<\n DictionaryRegistryResult<T, A>,\n ExtractSelectorLocale<A>\n> => {\n const locale = (\n typeof localeOrSelector === 'object' && localeOrSelector !== null\n ? localeOrSelector.locale\n : localeOrSelector\n ) as LocalesValues | undefined;\n\n return getIntlayerCore(key, localeOrSelector, getPlugins(locale)) as any;\n};\n"],"mappings":";;;;;;;;;;;AAiBA,MAAa,eAIX,KACA,qBAIG;AAOH,oDAAuB,KAAK,kBAAkBA,2BAL5C,OAAO,qBAAqB,YAAY,qBAAqB,OACzD,iBAAiB,SACjB,iBAG0D,CAAC"}
@@ -3,50 +3,29 @@ import { getIntlayerClient } from "./installIntlayer.mjs";
3
3
 
4
4
  //#region src/client/useDictionary.ts
5
5
  /**
6
- * Get the translated content for a raw dictionary object and optionally
7
- * subscribe to locale changes via the chainable `.onChange()` method —
8
- * mirroring the API of `react-intlayer`.
6
+ * Get the content for a raw dictionary (or qualified dictionary group) and
7
+ * optionally subscribe to locale changes via the chainable `.onChange()`
8
+ * method — mirroring the API of `react-intlayer`.
9
9
  *
10
- * Unlike `useIntlayer` (which takes a registered key), this function accepts a
11
- * dictionary object directly useful for dictionaries loaded asynchronously
12
- * or defined inline.
10
+ * The second argument is either a locale or a selector object (`{ item }`,
11
+ * `{ variant }`, `{ id, ...meta }`, optionally combined with `locale`).
13
12
  *
14
- * The function returns the current content **directly** (same shape as
15
- * `getDictionary(dict)`), plus the `.onChange()` helper:
16
- *
17
- * ```ts
18
- * // React
19
- * const content = useDictionary(myDict);
20
- *
21
- * // Vanilla — identical surface API, opt-in reactivity via .onChange()
22
- * const content = useDictionary(myDict);
23
- * const content = useDictionary(myDict).onChange((c) => render(c));
24
- * ```
25
- *
26
- * @param dictionary - The raw dictionary object.
27
- * @param locale - Optional locale override.
28
- * @returns The current translated content with an `.onChange()` method.
29
- *
30
- * @example
31
- * ```ts
32
- * import myDict from './myDictionary.content';
33
- * import { installIntlayer, useDictionary } from 'vanilla-intlayer';
34
- *
35
- * installIntlayer();
36
- *
37
- * const content = useDictionary(myDict).onChange((c) => {
38
- * document.querySelector('p').textContent = String(c.greeting);
39
- * });
40
- *
41
- * document.querySelector('p').textContent = String(content.greeting);
42
- * ```
13
+ * @param dictionary - The raw dictionary (or qualified group) object.
14
+ * @param localeOrSelector - Optional locale or selector.
15
+ * @returns The current content with an `.onChange()` method.
43
16
  */
44
- const useDictionary = (dictionary, locale) => {
17
+ const useDictionary = (dictionary, localeOrSelector) => {
45
18
  const client = getIntlayerClient();
46
- const content = getDictionary(dictionary, locale ?? client.locale);
47
- content.onChange = (callback) => {
19
+ const isSelector = typeof localeOrSelector === "object" && localeOrSelector !== null;
20
+ const explicitLocale = isSelector ? localeOrSelector.locale : localeOrSelector;
21
+ const read = (locale) => isSelector ? getDictionary(dictionary, {
22
+ ...localeOrSelector,
23
+ locale
24
+ }) : getDictionary(dictionary, locale);
25
+ const content = read(explicitLocale ?? client.locale);
26
+ if (content != null && typeof content === "object") content.onChange = (callback) => {
48
27
  client.subscribe((newLocale) => {
49
- callback(getDictionary(dictionary, locale ?? newLocale));
28
+ callback(read(explicitLocale ?? newLocale));
50
29
  });
51
30
  return content;
52
31
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useDictionary.mjs","names":[],"sources":["../../../src/client/useDictionary.ts"],"sourcesContent":["import type { Dictionary } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { getDictionary } from '../getDictionary';\nimport type { DeepTransformContent } from '../plugins';\nimport { getIntlayerClient } from './installIntlayer';\n\nexport type WithOnChange<T> = T & {\n /**\n * Subscribe to locale changes. The callback receives the fresh content\n * whenever the active locale changes. Returns the content object itself\n * for convenient one-liner patterns:\n *\n * ```ts\n * const content = useDictionary(myDict).onChange((c) => {\n * document.querySelector('p')!.textContent = String(c.greeting);\n * });\n * ```\n */\n onChange: (\n callback: <T extends Dictionary, L extends LocalesValues>(\n content: DeepTransformContent<T['content'], L>\n ) => void\n ) => WithOnChange<T>;\n};\n\n/**\n * Get the translated content for a raw dictionary object and optionally\n * subscribe to locale changes via the chainable `.onChange()` method —\n * mirroring the API of `react-intlayer`.\n *\n * Unlike `useIntlayer` (which takes a registered key), this function accepts a\n * dictionary object directly — useful for dictionaries loaded asynchronously\n * or defined inline.\n *\n * The function returns the current content **directly** (same shape as\n * `getDictionary(dict)`), plus the `.onChange()` helper:\n *\n * ```ts\n * // React\n * const content = useDictionary(myDict);\n *\n * // Vanilla — identical surface API, opt-in reactivity via .onChange()\n * const content = useDictionary(myDict);\n * const content = useDictionary(myDict).onChange((c) => render(c));\n * ```\n *\n * @param dictionary - The raw dictionary object.\n * @param locale - Optional locale override.\n * @returns The current translated content with an `.onChange()` method.\n *\n * @example\n * ```ts\n * import myDict from './myDictionary.content';\n * import { installIntlayer, useDictionary } from 'vanilla-intlayer';\n *\n * installIntlayer();\n *\n * const content = useDictionary(myDict).onChange((c) => {\n * document.querySelector('p').textContent = String(c.greeting);\n * });\n *\n * document.querySelector('p').textContent = String(content.greeting);\n * ```\n */\nexport const useDictionary = <\n const T extends Dictionary,\n const L extends LocalesValues = DeclaredLocales,\n>(\n dictionary: T,\n locale?: L\n): WithOnChange<DeepTransformContent<T['content'], L>> => {\n const client = getIntlayerClient();\n const content = getDictionary(\n dictionary,\n (locale ?? client.locale) as L\n ) as WithOnChange<DeepTransformContent<T['content'], L>>;\n\n content.onChange = (\n callback: (content: DeepTransformContent<T['content'], L>) => void\n ) => {\n client.subscribe((newLocale) => {\n callback(getDictionary(dictionary, (locale ?? newLocale) as L));\n });\n return content;\n };\n\n return content;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmEA,MAAa,iBAIX,YACA,WACwD;CACxD,MAAM,SAAS,mBAAmB;CAClC,MAAM,UAAU,cACd,YACC,UAAU,OAAO,OACnB;AAED,SAAQ,YACN,aACG;AACH,SAAO,WAAW,cAAc;AAC9B,YAAS,cAAc,YAAa,UAAU,UAAgB,CAAC;IAC/D;AACF,SAAO;;AAGT,QAAO"}
1
+ {"version":3,"file":"useDictionary.mjs","names":[],"sources":["../../../src/client/useDictionary.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionarySelector,\n DictionarySelectorForGroup,\n QualifiedDictionaryGroup,\n ResolveQualifiedDictionaryContent,\n} from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { getDictionary } from '../getDictionary';\nimport type { DeepTransformContent } from '../plugins';\nimport { getIntlayerClient } from './installIntlayer';\n\nexport type WithOnChange<T> = T & {\n /**\n * Subscribe to locale changes. The callback receives the fresh content\n * whenever the active locale changes. Returns the content object itself\n * for convenient one-liner patterns:\n *\n * ```ts\n * const content = useDictionary(myDict).onChange((c) => {\n * document.querySelector('p')!.textContent = String(c.greeting);\n * });\n * ```\n */\n onChange: (callback: (content: any) => void) => WithOnChange<T>;\n};\n\n/**\n * Get the content for a raw dictionary (or qualified dictionary group) and\n * optionally subscribe to locale changes via the chainable `.onChange()`\n * method mirroring the API of `react-intlayer`.\n *\n * The second argument is either a locale or a selector object (`{ item }`,\n * `{ variant }`, `{ id, ...meta }`, optionally combined with `locale`).\n *\n * @param dictionary - The raw dictionary (or qualified group) object.\n * @param localeOrSelector - Optional locale or selector.\n * @returns The current content with an `.onChange()` method.\n */\nexport const useDictionary = <\n const T extends Dictionary | QualifiedDictionaryGroup,\n const A extends\n | LocalesValues\n | DictionarySelectorForGroup<T> = DeclaredLocales,\n>(\n dictionary: T,\n localeOrSelector?: A\n): WithOnChange<\n DeepTransformContent<\n ResolveQualifiedDictionaryContent<T, A>,\n ExtractSelectorLocale<A>\n >\n> => {\n const client = getIntlayerClient();\n\n const isSelector =\n typeof localeOrSelector === 'object' && localeOrSelector !== null;\n const explicitLocale = isSelector\n ? (localeOrSelector as DictionarySelector).locale\n : (localeOrSelector as LocalesValues | undefined);\n\n const read = (locale: LocalesValues | undefined) =>\n isSelector\n ? getDictionary(dictionary, {\n ...(localeOrSelector as DictionarySelector),\n locale,\n } as A)\n : getDictionary(dictionary, locale as A);\n\n const content = read(explicitLocale ?? client.locale) as WithOnChange<any>;\n\n // A selector can resolve to null/array; only objects carry `.onChange`.\n if (content != null && typeof content === 'object') {\n content.onChange = (callback) => {\n client.subscribe((newLocale) => {\n callback(read(explicitLocale ?? newLocale));\n });\n return content;\n };\n }\n\n return content;\n};\n"],"mappings":";;;;;;;;;;;;;;;;AA2CA,MAAa,iBAMX,YACA,qBAMG;CACH,MAAM,SAAS,mBAAmB;CAElC,MAAM,aACJ,OAAO,qBAAqB,YAAY,qBAAqB;CAC/D,MAAM,iBAAiB,aAClB,iBAAwC,SACxC;CAEL,MAAM,QAAQ,WACZ,aACI,cAAc,YAAY;EACxB,GAAI;EACJ;EACD,CAAM,GACP,cAAc,YAAY,OAAY;CAE5C,MAAM,UAAU,KAAK,kBAAkB,OAAO,OAAO;AAGrD,KAAI,WAAW,QAAQ,OAAO,YAAY,SACxC,SAAQ,YAAY,aAAa;AAC/B,SAAO,WAAW,cAAc;AAC9B,YAAS,KAAK,kBAAkB,UAAU,CAAC;IAC3C;AACF,SAAO;;AAIX,QAAO"}
@@ -1,12 +1,16 @@
1
1
  import { getDictionary } from "../getDictionary.mjs";
2
2
  import { getIntlayerClient } from "./installIntlayer.mjs";
3
3
  import { internationalization } from "@intlayer/config/built";
4
+ import { getDictionarySelectorCacheKey, isQualifiedDynamicLoaderMap, parseDictionarySelector, resolveQualifiedDynamicContentAsync } from "@intlayer/core/dictionaryManipulator";
4
5
 
5
6
  //#region src/client/useDictionaryDynamic.ts
6
7
  /** Simple in-memory cache shared across all calls in the same page. */
7
8
  const cache = /* @__PURE__ */ new Map();
8
9
  /** Tracks in-flight loads to avoid duplicate fetches. */
9
10
  const inflight = /* @__PURE__ */ new Map();
11
+ /** Resolved-content cache for qualified (collection/variant/meta) keys. */
12
+ const qualifiedCache = /* @__PURE__ */ new Map();
13
+ const qualifiedInflight = /* @__PURE__ */ new Map();
10
14
  const loadDictionary = (cacheKey, loader) => {
11
15
  if (cache.has(cacheKey)) return Promise.resolve();
12
16
  if (inflight.has(cacheKey)) return inflight.get(cacheKey);
@@ -17,39 +21,78 @@ const loadDictionary = (cacheKey, loader) => {
17
21
  inflight.set(cacheKey, promise);
18
22
  return promise;
19
23
  };
24
+ /** Placeholder proxy — safe for any property access while loading. */
25
+ const recursiveProxy = new Proxy(() => {}, {
26
+ get: (_t, prop) => {
27
+ if (prop === Symbol.toPrimitive) return () => "";
28
+ if (prop === "toString") return () => "";
29
+ if (prop === "valueOf") return () => "";
30
+ if (prop === "then") return void 0;
31
+ if (prop === "onChange") return (_cb) => recursiveProxy;
32
+ return recursiveProxy;
33
+ },
34
+ apply: () => recursiveProxy
35
+ });
20
36
  /**
21
- * Dynamically load and transform a locale-keyed dictionary.
37
+ * Dynamically load and transform a dictionary (plain or qualified).
22
38
  *
23
- * Works like `useIntlayer` / `useDictionary` but accepts a locale-keyed map
24
- * of lazy loaders instead of a pre-imported dictionary. Call it inside your
25
- * render function the first call triggers a background fetch and returns
26
- * placeholder values; when the load completes the client notifies all
27
- * subscribers (including your render loop) so the render runs again with real
28
- * content. Subsequent calls for the same locale return immediately from cache.
39
+ * For a qualified loader map (collection / variant / meta record, possibly
40
+ * combined), only the chunk(s) the selector targets are loaded. For a plain
41
+ * loader map, the locale chunk is loaded. The first call returns a placeholder
42
+ * and triggers a background load; the client then notifies subscribers so the
43
+ * render runs again with real content.
29
44
  *
30
- * Locale switches follow the same two-phase pattern: placeholder on first
31
- * render, real content after the locale bundle loads.
32
- *
33
- * @param dictionaryLoaders - Locale-keyed map of `() => Promise<Dictionary>`.
45
+ * @param dictionaryLoaders - Locale-keyed loader map, or a qualified loader map.
34
46
  * @param key - Dictionary key (used for cache namespacing).
35
- * @param locale - Optional locale override.
36
- *
37
- * @example
38
- * ```ts
39
- * import dynDic from '../.intlayer/dynamic_dictionary/app.mjs';
40
- *
41
- * const render = () => {
42
- * const content = useDictionaryDynamic(dynDic, 'app');
43
- * document.querySelector('h1')!.textContent = String(content.title);
44
- * };
45
- *
46
- * render();
47
- * getIntlayerClient().subscribe(() => render());
48
- * ```
47
+ * @param localeOrSelector - Optional locale or selector.
49
48
  */
50
- const useDictionaryDynamic = (dictionaryLoaders, key, locale) => {
49
+ const useDictionaryDynamic = (dictionaryLoaders, key, localeOrSelector) => {
51
50
  const client = getIntlayerClient();
52
- const currentLocale = locale ?? client.locale ?? internationalization.defaultLocale;
51
+ const defaultLocale = internationalization.defaultLocale;
52
+ if (isQualifiedDynamicLoaderMap(dictionaryLoaders)) {
53
+ const loaderMap = dictionaryLoaders;
54
+ const { locale: selectorLocale, selector } = parseDictionarySelector(localeOrSelector);
55
+ const selectorId = getDictionarySelectorCacheKey(selector);
56
+ const buildKey = (locale) => `${String(key)}.${locale}.${selectorId}`;
57
+ const resolveContent = (locale) => resolveQualifiedDynamicContentAsync({
58
+ loaderMap,
59
+ key: String(key),
60
+ locale,
61
+ selector,
62
+ transform: (dictionary) => getDictionary(dictionary, locale)
63
+ });
64
+ const currentLocale = selectorLocale ?? client.locale ?? defaultLocale;
65
+ const cacheKey = buildKey(currentLocale);
66
+ const attachOnChange = (content) => {
67
+ if (content != null && typeof content === "object") content.onChange = (callback) => {
68
+ client.subscribe((newLocale) => {
69
+ const locale = selectorLocale ?? newLocale;
70
+ const newKey = buildKey(locale);
71
+ if (qualifiedCache.has(newKey)) {
72
+ callback(qualifiedCache.get(newKey));
73
+ return;
74
+ }
75
+ resolveContent(locale).then((resolved) => {
76
+ qualifiedCache.set(newKey, resolved);
77
+ callback(resolved);
78
+ });
79
+ });
80
+ return content;
81
+ };
82
+ return content;
83
+ };
84
+ if (qualifiedCache.has(cacheKey)) return attachOnChange(qualifiedCache.get(cacheKey));
85
+ if (!qualifiedInflight.has(cacheKey)) {
86
+ const promise = resolveContent(currentLocale).then((resolved) => {
87
+ qualifiedCache.set(cacheKey, resolved);
88
+ qualifiedInflight.delete(cacheKey);
89
+ client.notify();
90
+ });
91
+ qualifiedInflight.set(cacheKey, promise);
92
+ }
93
+ return recursiveProxy;
94
+ }
95
+ const currentLocale = (typeof localeOrSelector === "string" ? localeOrSelector : void 0) ?? client.locale ?? defaultLocale;
53
96
  const cacheKey = `${String(key)}.${currentLocale}`;
54
97
  const loader = dictionaryLoaders[currentLocale];
55
98
  const cached = cache.get(cacheKey);
@@ -72,17 +115,6 @@ const useDictionaryDynamic = (dictionaryLoaders, key, locale) => {
72
115
  if (loader) loadDictionary(cacheKey, loader).then(() => {
73
116
  client.notify();
74
117
  });
75
- const recursiveProxy = new Proxy(() => {}, {
76
- get: (_t, prop) => {
77
- if (prop === Symbol.toPrimitive) return () => "";
78
- if (prop === "toString") return () => "";
79
- if (prop === "valueOf") return () => "";
80
- if (prop === "then") return void 0;
81
- if (prop === "onChange") return (_cb) => recursiveProxy;
82
- return recursiveProxy;
83
- },
84
- apply: () => recursiveProxy
85
- });
86
118
  return recursiveProxy;
87
119
  };
88
120
 
@@ -1 +1 @@
1
- {"version":3,"file":"useDictionaryDynamic.mjs","names":[],"sources":["../../../src/client/useDictionaryDynamic.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type {\n DictionaryKeys,\n LocalesValues,\n StrictModeLocaleMap,\n} from '@intlayer/types/module_augmentation';\nimport { getDictionary } from '../getDictionary';\nimport type { DeepTransformContent } from '../plugins';\nimport { getIntlayerClient } from './installIntlayer';\nimport type { WithOnChange } from './useDictionary';\n\n/** Simple in-memory cache shared across all calls in the same page. */\nconst cache = new Map<string, Dictionary>();\n\n/** Tracks in-flight loads to avoid duplicate fetches. */\nconst inflight = new Map<string, Promise<void>>();\n\nconst loadDictionary = <T extends Dictionary>(\n cacheKey: string,\n loader: () => Promise<T>\n): Promise<void> => {\n if (cache.has(cacheKey)) return Promise.resolve();\n if (inflight.has(cacheKey)) return inflight.get(cacheKey)!;\n\n const promise = loader().then((dictionary) => {\n cache.set(cacheKey, dictionary);\n inflight.delete(cacheKey);\n });\n\n inflight.set(cacheKey, promise);\n return promise;\n};\n\n/**\n * Dynamically load and transform a locale-keyed dictionary.\n *\n * Works like `useIntlayer` / `useDictionary` but accepts a locale-keyed map\n * of lazy loaders instead of a pre-imported dictionary. Call it inside your\n * render function the first call triggers a background fetch and returns\n * placeholder values; when the load completes the client notifies all\n * subscribers (including your render loop) so the render runs again with real\n * content. Subsequent calls for the same locale return immediately from cache.\n *\n * Locale switches follow the same two-phase pattern: placeholder on first\n * render, real content after the locale bundle loads.\n *\n * @param dictionaryLoaders - Locale-keyed map of `() => Promise<Dictionary>`.\n * @param key - Dictionary key (used for cache namespacing).\n * @param locale - Optional locale override.\n *\n * @example\n * ```ts\n * import dynDic from '../.intlayer/dynamic_dictionary/app.mjs';\n *\n * const render = () => {\n * const content = useDictionaryDynamic(dynDic, 'app');\n * document.querySelector('h1')!.textContent = String(content.title);\n * };\n *\n * render();\n * getIntlayerClient().subscribe(() => render());\n * ```\n */\nexport const useDictionaryDynamic = <\n const T extends Dictionary,\n const K extends DictionaryKeys,\n const L extends LocalesValues = LocalesValues,\n>(\n dictionaryLoaders: StrictModeLocaleMap<() => Promise<T>>,\n key: K,\n locale?: L\n): WithOnChange<DeepTransformContent<T['content'], L>> => {\n const client = getIntlayerClient();\n const currentLocale = (locale ??\n client.locale ??\n internationalization.defaultLocale) as L;\n\n const cacheKey = `${String(key)}.${currentLocale}`;\n const loader = (dictionaryLoaders as Record<string, () => Promise<T>>)[\n currentLocale\n ];\n\n // --- Cache hit: return real content synchronously ---\n const cached = cache.get(cacheKey);\n if (cached) {\n const content = getDictionary(cached as T, currentLocale) as WithOnChange<\n DeepTransformContent<T['content'], L>\n >;\n\n content.onChange = (\n callback: (content: DeepTransformContent<T['content'], L>) => void\n ) => {\n // Re-fire whenever content reloads (locale change → new cache entry).\n client.subscribe((newLocale) => {\n const newKey = `${String(key)}.${newLocale}`;\n const newLoader = (\n dictionaryLoaders as Record<string, () => Promise<T>>\n )[newLocale];\n\n if (!newLoader) return;\n\n loadDictionary(newKey, newLoader).then(() => {\n const dict = cache.get(newKey);\n if (dict) {\n callback(\n getDictionary(dict as T, newLocale as L) as DeepTransformContent<\n T['content'],\n L\n >\n );\n }\n });\n });\n\n return content;\n };\n\n return content;\n }\n\n // --- Cache miss: kick off background load, then notify to re-render ---\n if (loader) {\n loadDictionary(cacheKey, loader).then(() => {\n client.notify();\n });\n }\n\n // Placeholder proxy — safe for any property access while loading.\n const recursiveProxy: any = new Proxy(() => {}, {\n get: (_t, prop) => {\n if (prop === Symbol.toPrimitive) return () => '';\n if (prop === 'toString') return () => '';\n if (prop === 'valueOf') return () => '';\n if (prop === 'then') return undefined; // not a Promise\n if (prop === 'onChange') return (_cb: any) => recursiveProxy;\n return recursiveProxy;\n },\n apply: () => recursiveProxy,\n });\n\n return recursiveProxy as WithOnChange<DeepTransformContent<T['content'], L>>;\n};\n"],"mappings":";;;;;;AAaA,MAAM,wBAAQ,IAAI,KAAyB;;AAG3C,MAAM,2BAAW,IAAI,KAA4B;AAEjD,MAAM,kBACJ,UACA,WACkB;AAClB,KAAI,MAAM,IAAI,SAAS,CAAE,QAAO,QAAQ,SAAS;AACjD,KAAI,SAAS,IAAI,SAAS,CAAE,QAAO,SAAS,IAAI,SAAS;CAEzD,MAAM,UAAU,QAAQ,CAAC,MAAM,eAAe;AAC5C,QAAM,IAAI,UAAU,WAAW;AAC/B,WAAS,OAAO,SAAS;GACzB;AAEF,UAAS,IAAI,UAAU,QAAQ;AAC/B,QAAO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCT,MAAa,wBAKX,mBACA,KACA,WACwD;CACxD,MAAM,SAAS,mBAAmB;CAClC,MAAM,gBAAiB,UACrB,OAAO,UACP,qBAAqB;CAEvB,MAAM,WAAW,GAAG,OAAO,IAAI,CAAC,GAAG;CACnC,MAAM,SAAU,kBACd;CAIF,MAAM,SAAS,MAAM,IAAI,SAAS;AAClC,KAAI,QAAQ;EACV,MAAM,UAAU,cAAc,QAAa,cAAc;AAIzD,UAAQ,YACN,aACG;AAEH,UAAO,WAAW,cAAc;IAC9B,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,GAAG;IACjC,MAAM,YACJ,kBACA;AAEF,QAAI,CAAC,UAAW;AAEhB,mBAAe,QAAQ,UAAU,CAAC,WAAW;KAC3C,MAAM,OAAO,MAAM,IAAI,OAAO;AAC9B,SAAI,KACF,UACE,cAAc,MAAW,UAAe,CAIzC;MAEH;KACF;AAEF,UAAO;;AAGT,SAAO;;AAIT,KAAI,OACF,gBAAe,UAAU,OAAO,CAAC,WAAW;AAC1C,SAAO,QAAQ;GACf;CAIJ,MAAM,iBAAsB,IAAI,YAAY,IAAI;EAC9C,MAAM,IAAI,SAAS;AACjB,OAAI,SAAS,OAAO,YAAa,cAAa;AAC9C,OAAI,SAAS,WAAY,cAAa;AACtC,OAAI,SAAS,UAAW,cAAa;AACrC,OAAI,SAAS,OAAQ,QAAO;AAC5B,OAAI,SAAS,WAAY,SAAQ,QAAa;AAC9C,UAAO;;EAET,aAAa;EACd,CAAC;AAEF,QAAO"}
1
+ {"version":3,"file":"useDictionaryDynamic.mjs","names":[],"sources":["../../../src/client/useDictionaryDynamic.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\nimport {\n getDictionarySelectorCacheKey,\n isQualifiedDynamicLoaderMap,\n parseDictionarySelector,\n type QualifiedDynamicLoaderMap,\n resolveQualifiedDynamicContentAsync,\n} from '@intlayer/core/dictionaryManipulator';\nimport type {\n Dictionary,\n DictionarySelector,\n} from '@intlayer/types/dictionary';\nimport type {\n DictionaryKeys,\n DictionarySelectorForKey,\n LocalesValues,\n StrictModeLocaleMap,\n} from '@intlayer/types/module_augmentation';\nimport { getDictionary } from '../getDictionary';\nimport type { DeepTransformContent } from '../plugins';\nimport { getIntlayerClient } from './installIntlayer';\nimport type { WithOnChange } from './useDictionary';\n\n/** Simple in-memory cache shared across all calls in the same page. */\nconst cache = new Map<string, Dictionary>();\n\n/** Tracks in-flight loads to avoid duplicate fetches. */\nconst inflight = new Map<string, Promise<void>>();\n\n/** Resolved-content cache for qualified (collection/variant/meta) keys. */\nconst qualifiedCache = new Map<string, unknown>();\nconst qualifiedInflight = new Map<string, Promise<void>>();\n\nconst loadDictionary = <T extends Dictionary>(\n cacheKey: string,\n loader: () => Promise<T>\n): Promise<void> => {\n if (cache.has(cacheKey)) return Promise.resolve();\n if (inflight.has(cacheKey)) return inflight.get(cacheKey)!;\n\n const promise = loader().then((dictionary) => {\n cache.set(cacheKey, dictionary);\n inflight.delete(cacheKey);\n });\n\n inflight.set(cacheKey, promise);\n return promise;\n};\n\n/** Placeholder proxy safe for any property access while loading. */\nconst recursiveProxy: any = new Proxy(() => {}, {\n get: (_t, prop) => {\n if (prop === Symbol.toPrimitive) return () => '';\n if (prop === 'toString') return () => '';\n if (prop === 'valueOf') return () => '';\n if (prop === 'then') return undefined; // not a Promise\n if (prop === 'onChange') return (_cb: any) => recursiveProxy;\n return recursiveProxy;\n },\n apply: () => recursiveProxy,\n});\n\n/**\n * Dynamically load and transform a dictionary (plain or qualified).\n *\n * For a qualified loader map (collection / variant / meta record, possibly\n * combined), only the chunk(s) the selector targets are loaded. For a plain\n * loader map, the locale chunk is loaded. The first call returns a placeholder\n * and triggers a background load; the client then notifies subscribers so the\n * render runs again with real content.\n *\n * @param dictionaryLoaders - Locale-keyed loader map, or a qualified loader map.\n * @param key - Dictionary key (used for cache namespacing).\n * @param localeOrSelector - Optional locale or selector.\n */\nexport const useDictionaryDynamic = <\n const T extends Dictionary,\n const K extends DictionaryKeys,\n const A extends LocalesValues | DictionarySelectorForKey<K> = LocalesValues,\n>(\n dictionaryLoaders:\n | StrictModeLocaleMap<() => Promise<T>>\n | QualifiedDynamicLoaderMap,\n key: K,\n localeOrSelector?: A\n): WithOnChange<DeepTransformContent<T['content']>> => {\n const client = getIntlayerClient();\n const defaultLocale = internationalization.defaultLocale;\n\n // --- Qualified loader map (collection / variant / meta record) ---\n if (isQualifiedDynamicLoaderMap(dictionaryLoaders)) {\n const loaderMap = dictionaryLoaders;\n const { locale: selectorLocale, selector } =\n parseDictionarySelector<LocalesValues>(localeOrSelector);\n const selectorId = getDictionarySelectorCacheKey(selector);\n\n const buildKey = (locale: string) =>\n `${String(key)}.${locale}.${selectorId}`;\n const resolveContent = (locale: LocalesValues) =>\n resolveQualifiedDynamicContentAsync({\n loaderMap,\n key: String(key),\n locale,\n selector,\n transform: (dictionary) => getDictionary(dictionary, locale),\n });\n\n const currentLocale = (selectorLocale ??\n client.locale ??\n defaultLocale) as LocalesValues;\n const cacheKey = buildKey(currentLocale);\n\n const attachOnChange = (content: any) => {\n if (content != null && typeof content === 'object') {\n content.onChange = (callback: (c: any) => void) => {\n client.subscribe((newLocale) => {\n const locale = (selectorLocale ?? newLocale) as LocalesValues;\n const newKey = buildKey(locale);\n\n if (qualifiedCache.has(newKey)) {\n callback(qualifiedCache.get(newKey));\n return;\n }\n\n resolveContent(locale).then((resolved) => {\n qualifiedCache.set(newKey, resolved);\n callback(resolved);\n });\n });\n return content;\n };\n }\n return content;\n };\n\n if (qualifiedCache.has(cacheKey)) {\n return attachOnChange(qualifiedCache.get(cacheKey)) as any;\n }\n\n if (!qualifiedInflight.has(cacheKey)) {\n const promise = resolveContent(currentLocale).then((resolved) => {\n qualifiedCache.set(cacheKey, resolved);\n qualifiedInflight.delete(cacheKey);\n client.notify();\n });\n qualifiedInflight.set(cacheKey, promise);\n }\n\n return recursiveProxy;\n }\n\n // --- Plain locale-keyed loader map ---\n const locale =\n typeof localeOrSelector === 'string'\n ? (localeOrSelector as LocalesValues)\n : undefined;\n const currentLocale = (locale ?? client.locale ?? defaultLocale) as A;\n\n const cacheKey = `${String(key)}.${currentLocale}`;\n const loader = (dictionaryLoaders as Record<string, () => Promise<T>>)[\n currentLocale as string\n ];\n\n // --- Cache hit: return real content synchronously ---\n const cached = cache.get(cacheKey);\n if (cached) {\n const content = getDictionary(cached, currentLocale) as WithOnChange<\n DeepTransformContent<T['content']>\n >;\n\n content.onChange = (callback) => {\n // Re-fire whenever content reloads (locale change → new cache entry).\n client.subscribe((newLocale) => {\n const newKey = `${String(key)}.${newLocale}`;\n const newLoader = (\n dictionaryLoaders as Record<string, () => Promise<T>>\n )[newLocale];\n\n if (!newLoader) return;\n\n loadDictionary(newKey, newLoader).then(() => {\n const dict = cache.get(newKey);\n if (dict) {\n callback(getDictionary(dict, newLocale as A) as any);\n }\n });\n });\n\n return content;\n };\n\n return content;\n }\n\n // --- Cache miss: kick off background load, then notify to re-render ---\n if (loader) {\n loadDictionary(cacheKey, loader).then(() => {\n client.notify();\n });\n }\n\n return recursiveProxy;\n};\n"],"mappings":";;;;;;;AAwBA,MAAM,wBAAQ,IAAI,KAAyB;;AAG3C,MAAM,2BAAW,IAAI,KAA4B;;AAGjD,MAAM,iCAAiB,IAAI,KAAsB;AACjD,MAAM,oCAAoB,IAAI,KAA4B;AAE1D,MAAM,kBACJ,UACA,WACkB;AAClB,KAAI,MAAM,IAAI,SAAS,CAAE,QAAO,QAAQ,SAAS;AACjD,KAAI,SAAS,IAAI,SAAS,CAAE,QAAO,SAAS,IAAI,SAAS;CAEzD,MAAM,UAAU,QAAQ,CAAC,MAAM,eAAe;AAC5C,QAAM,IAAI,UAAU,WAAW;AAC/B,WAAS,OAAO,SAAS;GACzB;AAEF,UAAS,IAAI,UAAU,QAAQ;AAC/B,QAAO;;;AAIT,MAAM,iBAAsB,IAAI,YAAY,IAAI;CAC9C,MAAM,IAAI,SAAS;AACjB,MAAI,SAAS,OAAO,YAAa,cAAa;AAC9C,MAAI,SAAS,WAAY,cAAa;AACtC,MAAI,SAAS,UAAW,cAAa;AACrC,MAAI,SAAS,OAAQ,QAAO;AAC5B,MAAI,SAAS,WAAY,SAAQ,QAAa;AAC9C,SAAO;;CAET,aAAa;CACd,CAAC;;;;;;;;;;;;;;AAeF,MAAa,wBAKX,mBAGA,KACA,qBACqD;CACrD,MAAM,SAAS,mBAAmB;CAClC,MAAM,gBAAgB,qBAAqB;AAG3C,KAAI,4BAA4B,kBAAkB,EAAE;EAClD,MAAM,YAAY;EAClB,MAAM,EAAE,QAAQ,gBAAgB,aAC9B,wBAAuC,iBAAiB;EAC1D,MAAM,aAAa,8BAA8B,SAAS;EAE1D,MAAM,YAAY,WAChB,GAAG,OAAO,IAAI,CAAC,GAAG,OAAO,GAAG;EAC9B,MAAM,kBAAkB,WACtB,oCAAoC;GAClC;GACA,KAAK,OAAO,IAAI;GAChB;GACA;GACA,YAAY,eAAe,cAAc,YAAY,OAAO;GAC7D,CAAC;EAEJ,MAAM,gBAAiB,kBACrB,OAAO,UACP;EACF,MAAM,WAAW,SAAS,cAAc;EAExC,MAAM,kBAAkB,YAAiB;AACvC,OAAI,WAAW,QAAQ,OAAO,YAAY,SACxC,SAAQ,YAAY,aAA+B;AACjD,WAAO,WAAW,cAAc;KAC9B,MAAM,SAAU,kBAAkB;KAClC,MAAM,SAAS,SAAS,OAAO;AAE/B,SAAI,eAAe,IAAI,OAAO,EAAE;AAC9B,eAAS,eAAe,IAAI,OAAO,CAAC;AACpC;;AAGF,oBAAe,OAAO,CAAC,MAAM,aAAa;AACxC,qBAAe,IAAI,QAAQ,SAAS;AACpC,eAAS,SAAS;OAClB;MACF;AACF,WAAO;;AAGX,UAAO;;AAGT,MAAI,eAAe,IAAI,SAAS,CAC9B,QAAO,eAAe,eAAe,IAAI,SAAS,CAAC;AAGrD,MAAI,CAAC,kBAAkB,IAAI,SAAS,EAAE;GACpC,MAAM,UAAU,eAAe,cAAc,CAAC,MAAM,aAAa;AAC/D,mBAAe,IAAI,UAAU,SAAS;AACtC,sBAAkB,OAAO,SAAS;AAClC,WAAO,QAAQ;KACf;AACF,qBAAkB,IAAI,UAAU,QAAQ;;AAG1C,SAAO;;CAQT,MAAM,iBAHJ,OAAO,qBAAqB,WACvB,mBACD,WAC2B,OAAO,UAAU;CAElD,MAAM,WAAW,GAAG,OAAO,IAAI,CAAC,GAAG;CACnC,MAAM,SAAU,kBACd;CAIF,MAAM,SAAS,MAAM,IAAI,SAAS;AAClC,KAAI,QAAQ;EACV,MAAM,UAAU,cAAc,QAAQ,cAAc;AAIpD,UAAQ,YAAY,aAAa;AAE/B,UAAO,WAAW,cAAc;IAC9B,MAAM,SAAS,GAAG,OAAO,IAAI,CAAC,GAAG;IACjC,MAAM,YACJ,kBACA;AAEF,QAAI,CAAC,UAAW;AAEhB,mBAAe,QAAQ,UAAU,CAAC,WAAW;KAC3C,MAAM,OAAO,MAAM,IAAI,OAAO;AAC9B,SAAI,KACF,UAAS,cAAc,MAAM,UAAe,CAAQ;MAEtD;KACF;AAEF,UAAO;;AAGT,SAAO;;AAIT,KAAI,OACF,gBAAe,UAAU,OAAO,CAAC,WAAW;AAC1C,SAAO,QAAQ;GACf;AAGJ,QAAO"}
@@ -7,28 +7,17 @@ import { getIntlayerClient } from "./installIntlayer.mjs";
7
7
  * locale changes via the chainable `.onChange()` method — mirroring the API
8
8
  * of `react-intlayer`'s `useIntlayer`.
9
9
  *
10
- * Unlike React (where the hook system automatically re-runs on re-render),
11
- * vanilla JS has no component lifecycle. The `.onChange()` callback is the
12
- * explicit equivalent: it is called with fresh content whenever the active
13
- * locale changes. Use it to patch your DOM or trigger a full re-render.
10
+ * The second argument is either a locale or a selector object:
11
+ * - `{ item: 2 }` collection item (omit `item` to get every item as array)
12
+ * - `{ variant: 'black-friday' }` named variant (omit for the `default` one)
13
+ * - `{ id: 'prod_abc', ...metaFields }` meta record
14
+ * - `locale` composes with any selector and overrides the current locale
14
15
  *
15
16
  * The function returns the current content **directly** (same shape as
16
- * `getIntlayer(key)`), plus the `.onChange()` helper:
17
+ * `getIntlayer(key)`), plus the `.onChange()` helper.
17
18
  *
18
- * ```ts
19
- * // React
20
- * const content = useIntlayer('app');
21
- *
22
- * // Vanilla — identical surface API, opt-in reactivity via .onChange()
23
- * const content = useIntlayer('app');
24
- * const content = useIntlayer('app').onChange((c) => render(c));
25
- * ```
26
- *
27
- * For cleanup (e.g. Vite HMR), subscribe via `getIntlayerClient().subscribe()`
28
- * and hold the returned unsubscribe function.
29
- *
30
- * @param key - Dictionary key registered in your intlayer content files.
31
- * @param locale - Optional locale override (defaults to the current app locale).
19
+ * @param key - Dictionary key registered in your intlayer content files.
20
+ * @param localeOrSelector - Optional locale or selector.
32
21
  * @returns The current translated content with an `.onChange()` method.
33
22
  *
34
23
  * @example
@@ -37,22 +26,22 @@ import { getIntlayerClient } from "./installIntlayer.mjs";
37
26
  *
38
27
  * installIntlayer();
39
28
  *
40
- * // Static read (no subscription)
41
29
  * const content = useIntlayer('homepage');
42
- * document.querySelector('h1').textContent = String(content.title);
43
- *
44
- * // Reactive read — onChange is called on every locale change
45
- * useIntlayer('homepage').onChange((c) => {
46
- * document.querySelector('h1').textContent = String(c.title);
47
- * });
30
+ * const faq2 = useIntlayer('faq', { item: 2 });
48
31
  * ```
49
32
  */
50
- const useIntlayer = (key, locale) => {
33
+ const useIntlayer = (key, localeOrSelector) => {
51
34
  const client = getIntlayerClient();
52
- const content = getIntlayer(key, locale ?? client.locale);
53
- content.onChange = (callback) => {
35
+ const isSelector = typeof localeOrSelector === "object" && localeOrSelector !== null;
36
+ const explicitLocale = isSelector ? localeOrSelector.locale : localeOrSelector;
37
+ const read = (locale) => isSelector ? getIntlayer(key, {
38
+ ...localeOrSelector,
39
+ locale
40
+ }) : getIntlayer(key, locale);
41
+ const content = read(explicitLocale ?? client.locale);
42
+ if (content != null && typeof content === "object") content.onChange = (callback) => {
54
43
  client.subscribe((newLocale) => {
55
- callback(getIntlayer(key, locale ?? newLocale));
44
+ callback(read(explicitLocale ?? newLocale));
56
45
  });
57
46
  return content;
58
47
  };
@@ -1 +1 @@
1
- {"version":3,"file":"useIntlayer.mjs","names":[],"sources":["../../../src/client/useIntlayer.ts"],"sourcesContent":["import type {\n DictionaryKeys,\n DictionaryRegistryContent,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { getIntlayer } from '../getIntlayer';\nimport type { DeepTransformContent } from '../plugins';\nimport { getIntlayerClient } from './installIntlayer';\nimport type { WithOnChange } from './useDictionary';\n\n/**\n * Get the translated content for the given key and optionally subscribe to\n * locale changes via the chainable `.onChange()` method — mirroring the API\n * of `react-intlayer`'s `useIntlayer`.\n *\n * Unlike React (where the hook system automatically re-runs on re-render),\n * vanilla JS has no component lifecycle. The `.onChange()` callback is the\n * explicit equivalent: it is called with fresh content whenever the active\n * locale changes. Use it to patch your DOM or trigger a full re-render.\n *\n * The function returns the current content **directly** (same shape as\n * `getIntlayer(key)`), plus the `.onChange()` helper:\n *\n * ```ts\n * // React\n * const content = useIntlayer('app');\n *\n * // Vanilla — identical surface API, opt-in reactivity via .onChange()\n * const content = useIntlayer('app');\n * const content = useIntlayer('app').onChange((c) => render(c));\n * ```\n *\n * For cleanup (e.g. Vite HMR), subscribe via `getIntlayerClient().subscribe()`\n * and hold the returned unsubscribe function.\n *\n * @param key - Dictionary key registered in your intlayer content files.\n * @param locale - Optional locale override (defaults to the current app locale).\n * @returns The current translated content with an `.onChange()` method.\n *\n * @example\n * ```ts\n * import { installIntlayer, useIntlayer } from 'vanilla-intlayer';\n *\n * installIntlayer();\n *\n * // Static read (no subscription)\n * const content = useIntlayer('homepage');\n * document.querySelector('h1').textContent = String(content.title);\n *\n * // Reactive read — onChange is called on every locale change\n * useIntlayer('homepage').onChange((c) => {\n * document.querySelector('h1').textContent = String(c.title);\n * });\n * ```\n */\nexport const useIntlayer = <const T extends DictionaryKeys>(\n key: T,\n locale?: LocalesValues\n): WithOnChange<DeepTransformContent<DictionaryRegistryContent<T>>> => {\n const client = getIntlayerClient();\n const content = getIntlayer(key, locale ?? client.locale) as WithOnChange<\n DeepTransformContent<DictionaryRegistryContent<T>>\n >;\n\n content.onChange = (callback) => {\n client.subscribe((newLocale) => {\n callback(getIntlayer(key, (locale ?? newLocale) as typeof locale));\n });\n return content;\n };\n\n return content;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAuDA,MAAa,eACX,KACA,WACqE;CACrE,MAAM,SAAS,mBAAmB;CAClC,MAAM,UAAU,YAAY,KAAK,UAAU,OAAO,OAAO;AAIzD,SAAQ,YAAY,aAAa;AAC/B,SAAO,WAAW,cAAc;AAC9B,YAAS,YAAY,KAAM,UAAU,UAA4B,CAAC;IAClE;AACF,SAAO;;AAGT,QAAO"}
1
+ {"version":3,"file":"useIntlayer.mjs","names":[],"sources":["../../../src/client/useIntlayer.ts"],"sourcesContent":["import type { DictionarySelector } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n DictionaryKeys,\n DictionaryRegistryResult,\n DictionarySelectorForKey,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { getIntlayer } from '../getIntlayer';\nimport type { DeepTransformContent } from '../plugins';\nimport { getIntlayerClient } from './installIntlayer';\nimport type { WithOnChange } from './useDictionary';\n\n/**\n * Get the translated content for the given key and optionally subscribe to\n * locale changes via the chainable `.onChange()` method — mirroring the API\n * of `react-intlayer`'s `useIntlayer`.\n *\n * The second argument is either a locale or a selector object:\n * - `{ item: 2 }` collection item (omit `item` to get every item as array)\n * - `{ variant: 'black-friday' }` named variant (omit for the `default` one)\n * - `{ id: 'prod_abc', ...metaFields }` meta record\n * - `locale` composes with any selector and overrides the current locale\n *\n * The function returns the current content **directly** (same shape as\n * `getIntlayer(key)`), plus the `.onChange()` helper.\n *\n * @param key - Dictionary key registered in your intlayer content files.\n * @param localeOrSelector - Optional locale or selector.\n * @returns The current translated content with an `.onChange()` method.\n *\n * @example\n * ```ts\n * import { installIntlayer, useIntlayer } from 'vanilla-intlayer';\n *\n * installIntlayer();\n *\n * const content = useIntlayer('homepage');\n * const faq2 = useIntlayer('faq', { item: 2 });\n * ```\n */\nexport const useIntlayer = <\n const T extends DictionaryKeys,\n const A extends LocalesValues | DictionarySelectorForKey<T> = DeclaredLocales,\n>(\n key: T,\n localeOrSelector?: A\n): WithOnChange<\n DeepTransformContent<DictionaryRegistryResult<T, A>, ExtractSelectorLocale<A>>\n> => {\n const client = getIntlayerClient();\n\n const isSelector =\n typeof localeOrSelector === 'object' && localeOrSelector !== null;\n const explicitLocale = isSelector\n ? (localeOrSelector as DictionarySelector).locale\n : (localeOrSelector as LocalesValues | undefined);\n\n const read = (locale: LocalesValues | undefined) =>\n isSelector\n ? getIntlayer(key, {\n ...(localeOrSelector as DictionarySelector),\n locale,\n } as A)\n : getIntlayer(key, locale as A);\n\n const content = read(explicitLocale ?? client.locale) as WithOnChange<any>;\n\n // A selector can resolve to null/array; only objects carry `.onChange`.\n if (content != null && typeof content === 'object') {\n content.onChange = (callback) => {\n client.subscribe((newLocale) => {\n callback(read(explicitLocale ?? newLocale));\n });\n return content;\n };\n }\n\n return content;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA0CA,MAAa,eAIX,KACA,qBAGG;CACH,MAAM,SAAS,mBAAmB;CAElC,MAAM,aACJ,OAAO,qBAAqB,YAAY,qBAAqB;CAC/D,MAAM,iBAAiB,aAClB,iBAAwC,SACxC;CAEL,MAAM,QAAQ,WACZ,aACI,YAAY,KAAK;EACf,GAAI;EACJ;EACD,CAAM,GACP,YAAY,KAAK,OAAY;CAEnC,MAAM,UAAU,KAAK,kBAAkB,OAAO,OAAO;AAGrD,KAAI,WAAW,QAAQ,OAAO,YAAY,SACxC,SAAQ,YAAY,aAAa;AAC/B,SAAO,WAAW,cAAc;AAC9B,YAAS,KAAK,kBAAkB,UAAU,CAAC;IAC3C;AACF,SAAO;;AAIX,QAAO"}
@@ -2,7 +2,14 @@ import { getPlugins } from "./plugins.mjs";
2
2
  import { getDictionary as getDictionary$1 } from "@intlayer/core/interpreter";
3
3
 
4
4
  //#region src/getDictionary.ts
5
- const getDictionary = (dictionary, locale) => getDictionary$1(dictionary, locale, getPlugins(locale));
5
+ /**
6
+ * Transforms a dictionary (or qualified dictionary group) and returns its
7
+ * content for the given locale or selector (`{ item }`, `{ variant }`,
8
+ * `{ id, ...meta }`, optionally combined with `locale`).
9
+ */
10
+ const getDictionary = (dictionary, localeOrSelector) => {
11
+ return getDictionary$1(dictionary, localeOrSelector, getPlugins(typeof localeOrSelector === "object" && localeOrSelector !== null ? localeOrSelector.locale : localeOrSelector));
12
+ };
6
13
 
7
14
  //#endregion
8
15
  export { getDictionary };
@@ -1 +1 @@
1
- {"version":3,"file":"getDictionary.mjs","names":["getDictionaryCore"],"sources":["../../src/getDictionary.ts"],"sourcesContent":["import { getDictionary as getDictionaryCore } from '@intlayer/core/interpreter';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { type DeepTransformContent, getPlugins } from './plugins';\n\nexport const getDictionary = <\n const T extends Dictionary,\n const L extends LocalesValues = DeclaredLocales,\n>(\n dictionary: T,\n locale?: L\n): DeepTransformContent<T['content'], L> =>\n getDictionaryCore(dictionary, locale, getPlugins(locale)) as any;\n"],"mappings":";;;;AAQA,MAAa,iBAIX,YACA,WAEAA,gBAAkB,YAAY,QAAQ,WAAW,OAAO,CAAC"}
1
+ {"version":3,"file":"getDictionary.mjs","names":["getDictionaryCore"],"sources":["../../src/getDictionary.ts"],"sourcesContent":["import { getDictionary as getDictionaryCore } from '@intlayer/core/interpreter';\nimport type {\n Dictionary,\n DictionarySelector,\n DictionarySelectorForGroup,\n QualifiedDictionaryGroup,\n ResolveQualifiedDictionaryContent,\n} from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { type DeepTransformContent, getPlugins } from './plugins';\n\n/**\n * Transforms a dictionary (or qualified dictionary group) and returns its\n * content for the given locale or selector (`{ item }`, `{ variant }`,\n * `{ id, ...meta }`, optionally combined with `locale`).\n */\nexport const getDictionary = <\n const T extends Dictionary | QualifiedDictionaryGroup,\n const A extends\n | LocalesValues\n | DictionarySelectorForGroup<T> = DeclaredLocales,\n>(\n dictionary: T,\n localeOrSelector?: A\n): DeepTransformContent<\n ResolveQualifiedDictionaryContent<T, A>,\n ExtractSelectorLocale<A>\n> => {\n const locale = (\n typeof localeOrSelector === 'object' && localeOrSelector !== null\n ? localeOrSelector.locale\n : localeOrSelector\n ) as LocalesValues | undefined;\n\n return getDictionaryCore(\n dictionary,\n localeOrSelector,\n getPlugins(locale)\n ) as any;\n};\n"],"mappings":";;;;;;;;;AAoBA,MAAa,iBAMX,YACA,qBAIG;AAOH,QAAOA,gBACL,YACA,kBACA,WARA,OAAO,qBAAqB,YAAY,qBAAqB,OACzD,iBAAiB,SACjB,iBAMc,CACnB"}
@@ -2,7 +2,14 @@ import { getPlugins } from "./plugins.mjs";
2
2
  import { getIntlayer as getIntlayer$1 } from "@intlayer/core/interpreter";
3
3
 
4
4
  //#region src/getIntlayer.ts
5
- const getIntlayer = (key, locale) => getIntlayer$1(key, locale, getPlugins(locale));
5
+ /**
6
+ * Picks one dictionary by its key and returns its content for the given
7
+ * locale or selector (`{ item }`, `{ variant }`, `{ id, ...meta }`,
8
+ * optionally combined with `locale`).
9
+ */
10
+ const getIntlayer = (key, localeOrSelector) => {
11
+ return getIntlayer$1(key, localeOrSelector, getPlugins(typeof localeOrSelector === "object" && localeOrSelector !== null ? localeOrSelector.locale : localeOrSelector));
12
+ };
6
13
 
7
14
  //#endregion
8
15
  export { getIntlayer };
@@ -1 +1 @@
1
- {"version":3,"file":"getIntlayer.mjs","names":["getIntlayerCore"],"sources":["../../src/getIntlayer.ts"],"sourcesContent":["import { getIntlayer as getIntlayerCore } from '@intlayer/core/interpreter';\nimport type {\n DeclaredLocales,\n DictionaryKeys,\n DictionaryRegistryContent,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { type DeepTransformContent, getPlugins } from './plugins';\n\nexport const getIntlayer = <\n const T extends DictionaryKeys,\n const L extends LocalesValues = DeclaredLocales,\n>(\n key: T,\n locale?: L\n): DeepTransformContent<DictionaryRegistryContent<T>, L> =>\n getIntlayerCore(key, locale, getPlugins(locale)) as any;\n"],"mappings":";;;;AASA,MAAa,eAIX,KACA,WAEAA,cAAgB,KAAK,QAAQ,WAAW,OAAO,CAAC"}
1
+ {"version":3,"file":"getIntlayer.mjs","names":["getIntlayerCore"],"sources":["../../src/getIntlayer.ts"],"sourcesContent":["import { getIntlayer as getIntlayerCore } from '@intlayer/core/interpreter';\nimport type { DictionarySelector } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n DictionaryKeys,\n DictionaryRegistryResult,\n DictionarySelectorForKey,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport { type DeepTransformContent, getPlugins } from './plugins';\n\n/**\n * Picks one dictionary by its key and returns its content for the given\n * locale or selector (`{ item }`, `{ variant }`, `{ id, ...meta }`,\n * optionally combined with `locale`).\n */\nexport const getIntlayer = <\n const T extends DictionaryKeys,\n const A extends LocalesValues | DictionarySelectorForKey<T> = DeclaredLocales,\n>(\n key: T,\n localeOrSelector?: A\n): DeepTransformContent<\n DictionaryRegistryResult<T, A>,\n ExtractSelectorLocale<A>\n> => {\n const locale = (\n typeof localeOrSelector === 'object' && localeOrSelector !== null\n ? localeOrSelector.locale\n : localeOrSelector\n ) as LocalesValues | undefined;\n\n return getIntlayerCore(key, localeOrSelector, getPlugins(locale)) as any;\n};\n"],"mappings":";;;;;;;;;AAiBA,MAAa,eAIX,KACA,qBAIG;AAOH,QAAOA,cAAgB,KAAK,kBAAkB,WAL5C,OAAO,qBAAqB,YAAY,qBAAqB,OACzD,iBAAiB,SACjB,iBAG0D,CAAC"}
@@ -1,6 +1,6 @@
1
1
  import { DeepTransformContent } from "../plugins.js";
2
- import { DeclaredLocales, LocalesValues } from "@intlayer/types/module_augmentation";
3
- import { Dictionary } from "@intlayer/types/dictionary";
2
+ import { DeclaredLocales, ExtractSelectorLocale, LocalesValues } from "@intlayer/types/module_augmentation";
3
+ import { Dictionary, DictionarySelectorForGroup, QualifiedDictionaryGroup, ResolveQualifiedDictionaryContent } from "@intlayer/types/dictionary";
4
4
 
5
5
  //#region src/client/useDictionary.d.ts
6
6
  type WithOnChange<T> = T & {
@@ -15,48 +15,21 @@ type WithOnChange<T> = T & {
15
15
  * });
16
16
  * ```
17
17
  */
18
- onChange: (callback: <T extends Dictionary, L extends LocalesValues>(content: DeepTransformContent<T['content'], L>) => void) => WithOnChange<T>;
18
+ onChange: (callback: (content: any) => void) => WithOnChange<T>;
19
19
  };
20
20
  /**
21
- * Get the translated content for a raw dictionary object and optionally
22
- * subscribe to locale changes via the chainable `.onChange()` method —
23
- * mirroring the API of `react-intlayer`.
21
+ * Get the content for a raw dictionary (or qualified dictionary group) and
22
+ * optionally subscribe to locale changes via the chainable `.onChange()`
23
+ * method — mirroring the API of `react-intlayer`.
24
24
  *
25
- * Unlike `useIntlayer` (which takes a registered key), this function accepts a
26
- * dictionary object directly useful for dictionaries loaded asynchronously
27
- * or defined inline.
25
+ * The second argument is either a locale or a selector object (`{ item }`,
26
+ * `{ variant }`, `{ id, ...meta }`, optionally combined with `locale`).
28
27
  *
29
- * The function returns the current content **directly** (same shape as
30
- * `getDictionary(dict)`), plus the `.onChange()` helper:
31
- *
32
- * ```ts
33
- * // React
34
- * const content = useDictionary(myDict);
35
- *
36
- * // Vanilla — identical surface API, opt-in reactivity via .onChange()
37
- * const content = useDictionary(myDict);
38
- * const content = useDictionary(myDict).onChange((c) => render(c));
39
- * ```
40
- *
41
- * @param dictionary - The raw dictionary object.
42
- * @param locale - Optional locale override.
43
- * @returns The current translated content with an `.onChange()` method.
44
- *
45
- * @example
46
- * ```ts
47
- * import myDict from './myDictionary.content';
48
- * import { installIntlayer, useDictionary } from 'vanilla-intlayer';
49
- *
50
- * installIntlayer();
51
- *
52
- * const content = useDictionary(myDict).onChange((c) => {
53
- * document.querySelector('p').textContent = String(c.greeting);
54
- * });
55
- *
56
- * document.querySelector('p').textContent = String(content.greeting);
57
- * ```
28
+ * @param dictionary - The raw dictionary (or qualified group) object.
29
+ * @param localeOrSelector - Optional locale or selector.
30
+ * @returns The current content with an `.onChange()` method.
58
31
  */
59
- declare const useDictionary: <const T extends Dictionary, const L extends LocalesValues = DeclaredLocales>(dictionary: T, locale?: L) => WithOnChange<DeepTransformContent<T["content"], L>>;
32
+ declare const useDictionary: <const T extends Dictionary | QualifiedDictionaryGroup, const A extends LocalesValues | DictionarySelectorForGroup<T> = DeclaredLocales>(dictionary: T, localeOrSelector?: A) => WithOnChange<DeepTransformContent<ResolveQualifiedDictionaryContent<T, A>, ExtractSelectorLocale<A>>>;
60
33
  //#endregion
61
34
  export { WithOnChange, useDictionary };
62
35
  //# sourceMappingURL=useDictionary.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useDictionary.d.ts","names":[],"sources":["../../../src/client/useDictionary.ts"],"mappings":";;;;;KASY,YAAA,MAAkB,CAAA;;AAA9B;;;;;;;;;;EAYE,QAAA,GACE,QAAA,aAAqB,UAAA,YAAsB,aAAA,EACzC,OAAA,EAAS,oBAAA,CAAqB,CAAA,aAAc,CAAA,eAE3C,YAAA,CAAa,CAAA;AAAA;;;;;;;;;;;;;;;;;;AA0CpB;;;;;;;;;;;;;;;;;;;;;;cAAa,aAAA,mBACK,UAAA,kBACA,aAAA,GAAgB,eAAA,EAEhC,UAAA,EAAY,CAAA,EACZ,MAAA,GAAS,CAAA,KACR,YAAA,CAAa,oBAAA,CAAqB,CAAA,aAAc,CAAA"}
1
+ {"version":3,"file":"useDictionary.d.ts","names":[],"sources":["../../../src/client/useDictionary.ts"],"mappings":";;;;;KAgBY,YAAA,MAAkB,CAAA;;AAA9B;;;;;;;;;;EAYE,QAAA,GAAW,QAAA,GAAW,OAAA,mBAA0B,YAAA,CAAa,CAAA;AAAA;;;;;;AAe/D;;;;;;;cAAa,aAAA,mBACK,UAAA,GAAa,wBAAA,kBAEzB,aAAA,GACA,0BAAA,CAA2B,CAAA,IAAK,eAAA,EAEpC,UAAA,EAAY,CAAA,EACZ,gBAAA,GAAmB,CAAA,KAClB,YAAA,CACD,oBAAA,CACE,iCAAA,CAAkC,CAAA,EAAG,CAAA,GACrC,qBAAA,CAAsB,CAAA"}
@@ -1,40 +1,24 @@
1
1
  import { DeepTransformContent } from "../plugins.js";
2
2
  import { WithOnChange } from "./useDictionary.js";
3
- import { DictionaryKeys, LocalesValues, StrictModeLocaleMap } from "@intlayer/types/module_augmentation";
3
+ import { DictionaryKeys, DictionarySelectorForKey, LocalesValues, StrictModeLocaleMap } from "@intlayer/types/module_augmentation";
4
4
  import { Dictionary } from "@intlayer/types/dictionary";
5
+ import { QualifiedDynamicLoaderMap } from "@intlayer/core/dictionaryManipulator";
5
6
 
6
7
  //#region src/client/useDictionaryDynamic.d.ts
7
8
  /**
8
- * Dynamically load and transform a locale-keyed dictionary.
9
+ * Dynamically load and transform a dictionary (plain or qualified).
9
10
  *
10
- * Works like `useIntlayer` / `useDictionary` but accepts a locale-keyed map
11
- * of lazy loaders instead of a pre-imported dictionary. Call it inside your
12
- * render function the first call triggers a background fetch and returns
13
- * placeholder values; when the load completes the client notifies all
14
- * subscribers (including your render loop) so the render runs again with real
15
- * content. Subsequent calls for the same locale return immediately from cache.
11
+ * For a qualified loader map (collection / variant / meta record, possibly
12
+ * combined), only the chunk(s) the selector targets are loaded. For a plain
13
+ * loader map, the locale chunk is loaded. The first call returns a placeholder
14
+ * and triggers a background load; the client then notifies subscribers so the
15
+ * render runs again with real content.
16
16
  *
17
- * Locale switches follow the same two-phase pattern: placeholder on first
18
- * render, real content after the locale bundle loads.
19
- *
20
- * @param dictionaryLoaders - Locale-keyed map of `() => Promise<Dictionary>`.
17
+ * @param dictionaryLoaders - Locale-keyed loader map, or a qualified loader map.
21
18
  * @param key - Dictionary key (used for cache namespacing).
22
- * @param locale - Optional locale override.
23
- *
24
- * @example
25
- * ```ts
26
- * import dynDic from '../.intlayer/dynamic_dictionary/app.mjs';
27
- *
28
- * const render = () => {
29
- * const content = useDictionaryDynamic(dynDic, 'app');
30
- * document.querySelector('h1')!.textContent = String(content.title);
31
- * };
32
- *
33
- * render();
34
- * getIntlayerClient().subscribe(() => render());
35
- * ```
19
+ * @param localeOrSelector - Optional locale or selector.
36
20
  */
37
- declare const useDictionaryDynamic: <const T extends Dictionary, const K extends DictionaryKeys, const L extends LocalesValues = LocalesValues>(dictionaryLoaders: StrictModeLocaleMap<() => Promise<T>>, key: K, locale?: L) => WithOnChange<DeepTransformContent<T["content"], L>>;
21
+ declare const useDictionaryDynamic: <const T extends Dictionary, const K extends DictionaryKeys, const A extends LocalesValues | DictionarySelectorForKey<K> = LocalesValues>(dictionaryLoaders: StrictModeLocaleMap<() => Promise<T>> | QualifiedDynamicLoaderMap, key: K, localeOrSelector?: A) => WithOnChange<DeepTransformContent<T["content"]>>;
38
22
  //#endregion
39
23
  export { useDictionaryDynamic };
40
24
  //# sourceMappingURL=useDictionaryDynamic.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useDictionaryDynamic.d.ts","names":[],"sources":["../../../src/client/useDictionaryDynamic.ts"],"mappings":";;;;;;;;AAgEA;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,oBAAA,mBACK,UAAA,kBACA,cAAA,kBACA,aAAA,GAAgB,aAAA,EAEhC,iBAAA,EAAmB,mBAAA,OAA0B,OAAA,CAAQ,CAAA,IACrD,GAAA,EAAK,CAAA,EACL,MAAA,GAAS,CAAA,KACR,YAAA,CAAa,oBAAA,CAAqB,CAAA,aAAc,CAAA"}
1
+ {"version":3,"file":"useDictionaryDynamic.d.ts","names":[],"sources":["../../../src/client/useDictionaryDynamic.ts"],"mappings":";;;;;;;;;AA2EA;;;;;;;;;;;cAAa,oBAAA,mBACK,UAAA,kBACA,cAAA,kBACA,aAAA,GAAgB,wBAAA,CAAyB,CAAA,IAAK,aAAA,EAE9D,iBAAA,EACI,mBAAA,OAA0B,OAAA,CAAQ,CAAA,KAClC,yBAAA,EACJ,GAAA,EAAK,CAAA,EACL,gBAAA,GAAmB,CAAA,KAClB,YAAA,CAAa,oBAAA,CAAqB,CAAA"}
@@ -1,6 +1,6 @@
1
1
  import { DeepTransformContent } from "../plugins.js";
2
2
  import { WithOnChange } from "./useDictionary.js";
3
- import { DictionaryKeys, DictionaryRegistryContent, LocalesValues } from "@intlayer/types/module_augmentation";
3
+ import { DeclaredLocales, DictionaryKeys, DictionaryRegistryResult, DictionarySelectorForKey, ExtractSelectorLocale, LocalesValues } from "@intlayer/types/module_augmentation";
4
4
 
5
5
  //#region src/client/useIntlayer.d.ts
6
6
  /**
@@ -8,28 +8,17 @@ import { DictionaryKeys, DictionaryRegistryContent, LocalesValues } from "@intla
8
8
  * locale changes via the chainable `.onChange()` method — mirroring the API
9
9
  * of `react-intlayer`'s `useIntlayer`.
10
10
  *
11
- * Unlike React (where the hook system automatically re-runs on re-render),
12
- * vanilla JS has no component lifecycle. The `.onChange()` callback is the
13
- * explicit equivalent: it is called with fresh content whenever the active
14
- * locale changes. Use it to patch your DOM or trigger a full re-render.
11
+ * The second argument is either a locale or a selector object:
12
+ * - `{ item: 2 }` collection item (omit `item` to get every item as array)
13
+ * - `{ variant: 'black-friday' }` named variant (omit for the `default` one)
14
+ * - `{ id: 'prod_abc', ...metaFields }` meta record
15
+ * - `locale` composes with any selector and overrides the current locale
15
16
  *
16
17
  * The function returns the current content **directly** (same shape as
17
- * `getIntlayer(key)`), plus the `.onChange()` helper:
18
+ * `getIntlayer(key)`), plus the `.onChange()` helper.
18
19
  *
19
- * ```ts
20
- * // React
21
- * const content = useIntlayer('app');
22
- *
23
- * // Vanilla — identical surface API, opt-in reactivity via .onChange()
24
- * const content = useIntlayer('app');
25
- * const content = useIntlayer('app').onChange((c) => render(c));
26
- * ```
27
- *
28
- * For cleanup (e.g. Vite HMR), subscribe via `getIntlayerClient().subscribe()`
29
- * and hold the returned unsubscribe function.
30
- *
31
- * @param key - Dictionary key registered in your intlayer content files.
32
- * @param locale - Optional locale override (defaults to the current app locale).
20
+ * @param key - Dictionary key registered in your intlayer content files.
21
+ * @param localeOrSelector - Optional locale or selector.
33
22
  * @returns The current translated content with an `.onChange()` method.
34
23
  *
35
24
  * @example
@@ -38,17 +27,11 @@ import { DictionaryKeys, DictionaryRegistryContent, LocalesValues } from "@intla
38
27
  *
39
28
  * installIntlayer();
40
29
  *
41
- * // Static read (no subscription)
42
30
  * const content = useIntlayer('homepage');
43
- * document.querySelector('h1').textContent = String(content.title);
44
- *
45
- * // Reactive read — onChange is called on every locale change
46
- * useIntlayer('homepage').onChange((c) => {
47
- * document.querySelector('h1').textContent = String(c.title);
48
- * });
31
+ * const faq2 = useIntlayer('faq', { item: 2 });
49
32
  * ```
50
33
  */
51
- declare const useIntlayer: <const T extends DictionaryKeys>(key: T, locale?: LocalesValues) => WithOnChange<DeepTransformContent<DictionaryRegistryContent<T>>>;
34
+ declare const useIntlayer: <const T extends DictionaryKeys, const A extends LocalesValues | DictionarySelectorForKey<T> = DeclaredLocales>(key: T, localeOrSelector?: A) => WithOnChange<DeepTransformContent<DictionaryRegistryResult<T, A>, ExtractSelectorLocale<A>>>;
52
35
  //#endregion
53
36
  export { useIntlayer };
54
37
  //# sourceMappingURL=useIntlayer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"useIntlayer.d.ts","names":[],"sources":["../../../src/client/useIntlayer.ts"],"mappings":";;;;;;;AAuDA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,WAAA,mBAA+B,cAAA,EAC1C,GAAA,EAAK,CAAA,EACL,MAAA,GAAS,aAAA,KACR,YAAA,CAAa,oBAAA,CAAqB,yBAAA,CAA0B,CAAA"}
1
+ {"version":3,"file":"useIntlayer.d.ts","names":[],"sources":["../../../src/client/useIntlayer.ts"],"mappings":";;;;;;;AA0CA;;;;;;;;;;;;;;;;;;;;;;;;;;cAAa,WAAA,mBACK,cAAA,kBACA,aAAA,GAAgB,wBAAA,CAAyB,CAAA,IAAK,eAAA,EAE9D,GAAA,EAAK,CAAA,EACL,gBAAA,GAAmB,CAAA,KAClB,YAAA,CACD,oBAAA,CAAqB,wBAAA,CAAyB,CAAA,EAAG,CAAA,GAAI,qBAAA,CAAsB,CAAA"}
@@ -1,9 +1,14 @@
1
1
  import { DeepTransformContent } from "./plugins.js";
2
- import { DeclaredLocales, LocalesValues } from "@intlayer/types/module_augmentation";
3
- import { Dictionary } from "@intlayer/types/dictionary";
2
+ import { DeclaredLocales, ExtractSelectorLocale, LocalesValues } from "@intlayer/types/module_augmentation";
3
+ import { Dictionary, DictionarySelectorForGroup, QualifiedDictionaryGroup, ResolveQualifiedDictionaryContent } from "@intlayer/types/dictionary";
4
4
 
5
5
  //#region src/getDictionary.d.ts
6
- declare const getDictionary: <const T extends Dictionary, const L extends LocalesValues = DeclaredLocales>(dictionary: T, locale?: L) => DeepTransformContent<T["content"], L>;
6
+ /**
7
+ * Transforms a dictionary (or qualified dictionary group) and returns its
8
+ * content for the given locale or selector (`{ item }`, `{ variant }`,
9
+ * `{ id, ...meta }`, optionally combined with `locale`).
10
+ */
11
+ declare const getDictionary: <const T extends Dictionary | QualifiedDictionaryGroup, const A extends LocalesValues | DictionarySelectorForGroup<T> = DeclaredLocales>(dictionary: T, localeOrSelector?: A) => DeepTransformContent<ResolveQualifiedDictionaryContent<T, A>, ExtractSelectorLocale<A>>;
7
12
  //#endregion
8
13
  export { getDictionary };
9
14
  //# sourceMappingURL=getDictionary.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getDictionary.d.ts","names":[],"sources":["../../src/getDictionary.ts"],"mappings":";;;;;cAQa,aAAA,mBACK,UAAA,kBACA,aAAA,GAAgB,eAAA,EAEhC,UAAA,EAAY,CAAA,EACZ,MAAA,GAAS,CAAA,KACR,oBAAA,CAAqB,CAAA,aAAc,CAAA"}
1
+ {"version":3,"file":"getDictionary.d.ts","names":[],"sources":["../../src/getDictionary.ts"],"mappings":";;;;;;;AAoBA;;;cAAa,aAAA,mBACK,UAAA,GAAa,wBAAA,kBAEzB,aAAA,GACA,0BAAA,CAA2B,CAAA,IAAK,eAAA,EAEpC,UAAA,EAAY,CAAA,EACZ,gBAAA,GAAmB,CAAA,KAClB,oBAAA,CACD,iCAAA,CAAkC,CAAA,EAAG,CAAA,GACrC,qBAAA,CAAsB,CAAA"}
@@ -1,8 +1,13 @@
1
1
  import { DeepTransformContent } from "./plugins.js";
2
- import { DeclaredLocales, DictionaryKeys, DictionaryRegistryContent, LocalesValues } from "@intlayer/types/module_augmentation";
2
+ import { DeclaredLocales, DictionaryKeys, DictionaryRegistryResult, DictionarySelectorForKey, ExtractSelectorLocale, LocalesValues } from "@intlayer/types/module_augmentation";
3
3
 
4
4
  //#region src/getIntlayer.d.ts
5
- declare const getIntlayer: <const T extends DictionaryKeys, const L extends LocalesValues = DeclaredLocales>(key: T, locale?: L) => DeepTransformContent<DictionaryRegistryContent<T>, L>;
5
+ /**
6
+ * Picks one dictionary by its key and returns its content for the given
7
+ * locale or selector (`{ item }`, `{ variant }`, `{ id, ...meta }`,
8
+ * optionally combined with `locale`).
9
+ */
10
+ declare const getIntlayer: <const T extends DictionaryKeys, const A extends LocalesValues | DictionarySelectorForKey<T> = DeclaredLocales>(key: T, localeOrSelector?: A) => DeepTransformContent<DictionaryRegistryResult<T, A>, ExtractSelectorLocale<A>>;
6
11
  //#endregion
7
12
  export { getIntlayer };
8
13
  //# sourceMappingURL=getIntlayer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"getIntlayer.d.ts","names":[],"sources":["../../src/getIntlayer.ts"],"mappings":";;;;cASa,WAAA,mBACK,cAAA,kBACA,aAAA,GAAgB,eAAA,EAEhC,GAAA,EAAK,CAAA,EACL,MAAA,GAAS,CAAA,KACR,oBAAA,CAAqB,yBAAA,CAA0B,CAAA,GAAI,CAAA"}
1
+ {"version":3,"file":"getIntlayer.d.ts","names":[],"sources":["../../src/getIntlayer.ts"],"mappings":";;;;;;AAiBA;;;cAAa,WAAA,mBACK,cAAA,kBACA,aAAA,GAAgB,wBAAA,CAAyB,CAAA,IAAK,eAAA,EAE9D,GAAA,EAAK,CAAA,EACL,gBAAA,GAAmB,CAAA,KAClB,oBAAA,CACD,wBAAA,CAAyB,CAAA,EAAG,CAAA,GAC5B,qBAAA,CAAsB,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vanilla-intlayer",
3
- "version": "8.12.4",
3
+ "version": "9.0.0-canary.0",
4
4
  "private": false,
5
5
  "description": "Easily internationalize i18n your Vanilla JS, HTML, and PHP applications with type-safe multilingual content management.",
6
6
  "keywords": [
@@ -101,14 +101,14 @@
101
101
  "typecheck": "tsc --noEmit --project tsconfig.types.json"
102
102
  },
103
103
  "dependencies": {
104
- "@intlayer/api": "8.12.4",
105
- "@intlayer/config": "8.12.4",
106
- "@intlayer/core": "8.12.4",
107
- "@intlayer/editor": "8.12.4",
108
- "@intlayer/types": "8.12.4"
104
+ "@intlayer/api": "9.0.0-canary.0",
105
+ "@intlayer/config": "9.0.0-canary.0",
106
+ "@intlayer/core": "9.0.0-canary.0",
107
+ "@intlayer/editor": "9.0.0-canary.0",
108
+ "@intlayer/types": "9.0.0-canary.0"
109
109
  },
110
110
  "devDependencies": {
111
- "@types/node": "25.9.2",
111
+ "@types/node": "25.9.3",
112
112
  "@utils/ts-config": "1.0.4",
113
113
  "@utils/ts-config-types": "1.0.4",
114
114
  "@utils/tsdown-config": "1.0.4",
@@ -118,7 +118,7 @@
118
118
  "vitest": "4.1.8"
119
119
  },
120
120
  "peerDependencies": {
121
- "intlayer": "8.12.4"
121
+ "intlayer": "9.0.0-canary.0"
122
122
  },
123
123
  "peerDependenciesMeta": {
124
124
  "intlayer": {