vanilla-intlayer 9.1.1 → 9.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/client/installIntlayer.cjs +25 -5
- package/dist/cjs/client/installIntlayer.cjs.map +1 -1
- package/dist/cjs/client/useDictionary.cjs +8 -6
- package/dist/cjs/client/useDictionary.cjs.map +1 -1
- package/dist/cjs/client/useDictionaryDynamic.cjs +6 -1
- package/dist/cjs/client/useDictionaryDynamic.cjs.map +1 -1
- package/dist/cjs/client/useIntlayer.cjs +8 -6
- package/dist/cjs/client/useIntlayer.cjs.map +1 -1
- package/dist/esm/client/installIntlayer.mjs +25 -5
- package/dist/esm/client/installIntlayer.mjs.map +1 -1
- package/dist/esm/client/useDictionary.mjs +8 -6
- package/dist/esm/client/useDictionary.mjs.map +1 -1
- package/dist/esm/client/useDictionaryDynamic.mjs +7 -2
- package/dist/esm/client/useDictionaryDynamic.mjs.map +1 -1
- package/dist/esm/client/useIntlayer.mjs +8 -6
- package/dist/esm/client/useIntlayer.mjs.map +1 -1
- package/dist/types/client/installIntlayer.d.ts +19 -4
- package/dist/types/client/installIntlayer.d.ts.map +1 -1
- package/dist/types/client/useDictionary.d.ts.map +1 -1
- package/dist/types/client/useDictionaryDynamic.d.ts.map +1 -1
- package/dist/types/client/useIntlayer.d.ts.map +1 -1
- package/package.json +7 -7
|
@@ -6,21 +6,34 @@ let _intlayer_config_client = require("@intlayer/config/client");
|
|
|
6
6
|
//#region src/client/installIntlayer.ts
|
|
7
7
|
var IntlayerClient = class {
|
|
8
8
|
_locale;
|
|
9
|
+
_variant;
|
|
9
10
|
_listeners = /* @__PURE__ */ new Set();
|
|
10
11
|
isCookieEnabled;
|
|
11
|
-
constructor(locale, isCookieEnabled = true) {
|
|
12
|
+
constructor(locale, isCookieEnabled = true, variant) {
|
|
12
13
|
const { defaultLocale } = _intlayer_config_built.internationalization ?? {};
|
|
13
14
|
(0, _intlayer_config_client.setIntlayerIdentifier)();
|
|
14
15
|
this._locale = locale ?? defaultLocale;
|
|
16
|
+
this._variant = variant;
|
|
15
17
|
this.isCookieEnabled = isCookieEnabled;
|
|
16
18
|
}
|
|
17
19
|
get locale() {
|
|
18
20
|
return this._locale;
|
|
19
21
|
}
|
|
22
|
+
/**
|
|
23
|
+
* Ambient variant applied to every dictionary read, the same way `locale` is.
|
|
24
|
+
* Overridden per call by an explicit selector.
|
|
25
|
+
*/
|
|
26
|
+
get variant() {
|
|
27
|
+
return this._variant;
|
|
28
|
+
}
|
|
20
29
|
setLocale(newLocale) {
|
|
21
30
|
this._locale = newLocale;
|
|
22
31
|
for (const listener of this._listeners) listener(newLocale);
|
|
23
32
|
}
|
|
33
|
+
setVariant(newVariant) {
|
|
34
|
+
this._variant = newVariant;
|
|
35
|
+
this.notify();
|
|
36
|
+
}
|
|
24
37
|
subscribe(listener) {
|
|
25
38
|
this._listeners.add(listener);
|
|
26
39
|
return () => {
|
|
@@ -42,9 +55,9 @@ let instance = null;
|
|
|
42
55
|
/**
|
|
43
56
|
* Create and return a single IntlayerClient instance.
|
|
44
57
|
*/
|
|
45
|
-
const createIntlayerClient = (locale, isCookieEnabled = true) => {
|
|
58
|
+
const createIntlayerClient = (locale, isCookieEnabled = true, variant) => {
|
|
46
59
|
if (instance) return instance;
|
|
47
|
-
instance = new IntlayerClient(locale, isCookieEnabled);
|
|
60
|
+
instance = new IntlayerClient(locale, isCookieEnabled, variant);
|
|
48
61
|
return instance;
|
|
49
62
|
};
|
|
50
63
|
/**
|
|
@@ -65,6 +78,13 @@ const getIntlayerClient = () => {
|
|
|
65
78
|
*
|
|
66
79
|
* @param locale - Initial locale (defaults to config defaultLocale).
|
|
67
80
|
* @param isCookieEnabled - Whether to persist locale in cookies/localStorage.
|
|
81
|
+
* @param variant - Ambient variant applied to every dictionary read — for a
|
|
82
|
+
* dimension fixed for the whole session (tenant, school type, plan tier…)
|
|
83
|
+
* that no call site should have to pass by hand. Accepts a name
|
|
84
|
+
* (`'school1'`), an ordered preference chain (`['school1', 'default']`), or a
|
|
85
|
+
* per-key map (`{ key1: 'school1', default: 'base' }`). A plain object is
|
|
86
|
+
* always the per-key map; nest a structured variant as
|
|
87
|
+
* `{ default: { id: 'prod_abc' } }`. A call-site selector always wins.
|
|
68
88
|
* @param config - Optional Intlayer configuration. When provided, sets
|
|
69
89
|
* `window.INTLAYER_CONFIG` with the browser-safe subset of the config so
|
|
70
90
|
* the application works without a Vite/webpack plugin alias.
|
|
@@ -83,8 +103,8 @@ const getIntlayerClient = () => {
|
|
|
83
103
|
* installIntlayer('en');
|
|
84
104
|
* ```
|
|
85
105
|
*/
|
|
86
|
-
const installIntlayer = ({ locale, isCookieEnabled } = {}) => {
|
|
87
|
-
const client = createIntlayerClient(locale, isCookieEnabled);
|
|
106
|
+
const installIntlayer = ({ locale, isCookieEnabled, variant } = {}) => {
|
|
107
|
+
const client = createIntlayerClient(locale, isCookieEnabled, variant);
|
|
88
108
|
require_editor_useEditor.useEditor();
|
|
89
109
|
return client;
|
|
90
110
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installIntlayer.cjs","names":["internationalization"],"sources":["../../../src/client/installIntlayer.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\nimport { setIntlayerIdentifier } from '@intlayer/config/client';\nimport type {
|
|
1
|
+
{"version":3,"file":"installIntlayer.cjs","names":["internationalization"],"sources":["../../../src/client/installIntlayer.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\nimport { setIntlayerIdentifier } from '@intlayer/config/client';\nimport type {\n LocalesValues,\n ProviderVariant,\n} from '@intlayer/types/module_augmentation';\nimport { useEditor } from '../editor/useEditor';\n\ntype LocaleListener = (locale: LocalesValues) => void;\n\nexport class IntlayerClient {\n private _locale: LocalesValues;\n private _variant: ProviderVariant | undefined;\n private _listeners: Set<LocaleListener> = new Set();\n isCookieEnabled: boolean;\n\n constructor(\n locale?: LocalesValues,\n isCookieEnabled = true,\n variant?: ProviderVariant\n ) {\n const { defaultLocale } = internationalization ?? {};\n\n setIntlayerIdentifier();\n\n this._locale =\n (locale as LocalesValues) ?? (defaultLocale as LocalesValues);\n this._variant = variant;\n this.isCookieEnabled = isCookieEnabled;\n }\n\n get locale(): LocalesValues {\n return this._locale;\n }\n\n /**\n * Ambient variant applied to every dictionary read, the same way `locale` is.\n * Overridden per call by an explicit selector.\n */\n get variant(): ProviderVariant | undefined {\n return this._variant;\n }\n\n setLocale(newLocale: LocalesValues): void {\n this._locale = newLocale;\n for (const listener of this._listeners) {\n listener(newLocale);\n }\n }\n\n setVariant(newVariant: ProviderVariant | undefined): void {\n this._variant = newVariant;\n this.notify();\n }\n\n subscribe(listener: LocaleListener): () => void {\n this._listeners.add(listener);\n return () => {\n this._listeners.delete(listener);\n };\n }\n\n /**\n * Fire all listeners with the current locale without changing it.\n * Used to trigger re-renders after async data loads.\n */\n notify(): void {\n for (const listener of this._listeners) {\n listener(this._locale);\n }\n }\n}\n\n/**\n * Singleton instance\n */\nlet instance: IntlayerClient | null = null;\n\n/**\n * Create and return a single IntlayerClient instance.\n */\nexport const createIntlayerClient = (\n locale?: LocalesValues,\n isCookieEnabled = true,\n variant?: ProviderVariant\n): IntlayerClient => {\n if (instance) return instance;\n\n instance = new IntlayerClient(locale, isCookieEnabled, variant);\n return instance;\n};\n\n/**\n * Get the current IntlayerClient instance, creating one with defaults if needed.\n */\nexport const getIntlayerClient = (): IntlayerClient => {\n if (!instance) {\n instance = new IntlayerClient();\n }\n return instance;\n};\n\n/**\n * Install Intlayer into your vanilla JS application.\n *\n * Call this once at application startup, before any Intlayer APIs are used.\n *\n * When called with a `config` argument, `window.INTLAYER_CONFIG` is set so\n * that `@intlayer/config/built` resolves the correct values in the browser\n * without requiring module aliasing in the bundler configuration.\n *\n * @param locale - Initial locale (defaults to config defaultLocale).\n * @param isCookieEnabled - Whether to persist locale in cookies/localStorage.\n * @param variant - Ambient variant applied to every dictionary read — for a\n * dimension fixed for the whole session (tenant, school type, plan tier…)\n * that no call site should have to pass by hand. Accepts a name\n * (`'school1'`), an ordered preference chain (`['school1', 'default']`), or a\n * per-key map (`{ key1: 'school1', default: 'base' }`). A plain object is\n * always the per-key map; nest a structured variant as\n * `{ default: { id: 'prod_abc' } }`. A call-site selector always wins.\n * @param config - Optional Intlayer configuration. When provided, sets\n * `window.INTLAYER_CONFIG` with the browser-safe subset of the config so\n * the application works without a Vite/webpack plugin alias.\n * @returns The IntlayerClient singleton.\n *\n * @example\n * ```ts\n * import { installIntlayer } from 'vanilla-intlayer';\n *\n * // With explicit config (no build plugin alias needed):\n * installIntlayer('en', true, {\n * internationalization: { locales: ['en', 'fr'], defaultLocale: 'en' },\n * });\n *\n * // With build plugin (alias handles config automatically):\n * installIntlayer('en');\n * ```\n */\nexport const installIntlayer = ({\n locale,\n isCookieEnabled,\n variant,\n}: {\n locale?: LocalesValues;\n isCookieEnabled?: boolean;\n variant?: ProviderVariant;\n} = {}): IntlayerClient => {\n const client = createIntlayerClient(locale, isCookieEnabled, variant);\n\n useEditor();\n\n return client;\n};\n"],"mappings":";;;;;;AAUA,IAAa,iBAAb,MAA4B;CAC1B,AAAQ;CACR,AAAQ;CACR,AAAQ,6BAAkC,IAAI,IAAI;CAClD;CAEA,YACE,QACA,kBAAkB,MAClB,SACA;EACA,MAAM,EAAE,kBAAkBA,+CAAwB,CAAC;EAEnD,mDAAsB;EAEtB,KAAK,UACF,UAA6B;EAChC,KAAK,WAAW;EAChB,KAAK,kBAAkB;CACzB;CAEA,IAAI,SAAwB;EAC1B,OAAO,KAAK;CACd;;;;;CAMA,IAAI,UAAuC;EACzC,OAAO,KAAK;CACd;CAEA,UAAU,WAAgC;EACxC,KAAK,UAAU;EACf,KAAK,MAAM,YAAY,KAAK,YAC1B,SAAS,SAAS;CAEtB;CAEA,WAAW,YAA+C;EACxD,KAAK,WAAW;EAChB,KAAK,OAAO;CACd;CAEA,UAAU,UAAsC;EAC9C,KAAK,WAAW,IAAI,QAAQ;EAC5B,aAAa;GACX,KAAK,WAAW,OAAO,QAAQ;EACjC;CACF;;;;;CAMA,SAAe;EACb,KAAK,MAAM,YAAY,KAAK,YAC1B,SAAS,KAAK,OAAO;CAEzB;AACF;;;;AAKA,IAAI,WAAkC;;;;AAKtC,MAAa,wBACX,QACA,kBAAkB,MAClB,YACmB;CACnB,IAAI,UAAU,OAAO;CAErB,WAAW,IAAI,eAAe,QAAQ,iBAAiB,OAAO;CAC9D,OAAO;AACT;;;;AAKA,MAAa,0BAA0C;CACrD,IAAI,CAAC,UACH,WAAW,IAAI,eAAe;CAEhC,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,MAAa,mBAAmB,EAC9B,QACA,iBACA,YAKE,CAAC,MAAsB;CACzB,MAAM,SAAS,qBAAqB,QAAQ,iBAAiB,OAAO;CAEpE,mCAAU;CAEV,OAAO;AACT"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_getDictionary = require('../getDictionary.cjs');
|
|
3
3
|
const require_client_installIntlayer = require('./installIntlayer.cjs');
|
|
4
|
+
let _intlayer_core_dictionaryManipulator = require("@intlayer/core/dictionaryManipulator");
|
|
4
5
|
|
|
5
6
|
//#region src/client/useDictionary.ts
|
|
6
7
|
/**
|
|
@@ -17,12 +18,13 @@ const require_client_installIntlayer = require('./installIntlayer.cjs');
|
|
|
17
18
|
*/
|
|
18
19
|
const useDictionary = (dictionary, localeOrSelector) => {
|
|
19
20
|
const client = require_client_installIntlayer.getIntlayerClient();
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
21
|
+
const explicitLocale = process.env.INTLAYER_DICTIONARY_SELECTOR !== "false" && typeof localeOrSelector === "object" && localeOrSelector !== null ? localeOrSelector.locale : localeOrSelector;
|
|
22
|
+
const read = (locale) => process.env.INTLAYER_DICTIONARY_SELECTOR !== "false" ? require_getDictionary.getDictionary(dictionary, (0, _intlayer_core_dictionaryManipulator.resolveDictionaryArgument)({
|
|
23
|
+
localeOrSelector,
|
|
24
|
+
contextLocale: locale,
|
|
25
|
+
contextVariant: client.variant,
|
|
26
|
+
dictionaryKey: dictionary.key
|
|
27
|
+
})) : require_getDictionary.getDictionary(dictionary, locale);
|
|
26
28
|
const content = read(explicitLocale ?? client.locale);
|
|
27
29
|
if (content != null && typeof content === "object") content.onChange = (callback) => {
|
|
28
30
|
client.subscribe((newLocale) => {
|
|
@@ -1 +1 @@
|
|
|
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 }`, 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 | DeclaredLocales\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 process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false' &&\n typeof localeOrSelector === 'object' &&\n localeOrSelector !== null;\n const explicitLocale = isSelector\n ? (localeOrSelector as DictionarySelector).locale\n : (localeOrSelector as LocalesValues | undefined);\n\n const read = (locale: LocalesValues | undefined) =>\n
|
|
1
|
+
{"version":3,"file":"useDictionary.cjs","names":["getIntlayerClient","getDictionary"],"sources":["../../../src/client/useDictionary.ts"],"sourcesContent":["import { resolveDictionaryArgument } from '@intlayer/core/dictionaryManipulator';\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 { 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 }`, 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 | DeclaredLocales\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 process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false' &&\n typeof localeOrSelector === 'object' &&\n localeOrSelector !== null;\n const explicitLocale = isSelector\n ? (localeOrSelector as DictionarySelector).locale\n : (localeOrSelector as LocalesValues | undefined);\n\n // Layers the client's ambient variant under the call-site argument. Resolved\n // per read so a `setVariant` between reads is picked up.\n const read = (locale: LocalesValues | undefined) =>\n process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false'\n ? getDictionary(\n dictionary,\n resolveDictionaryArgument({\n localeOrSelector,\n contextLocale: locale,\n contextVariant: client.variant,\n dictionaryKey: dictionary.key,\n }) as A\n )\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":";;;;;;;;;;;;;;;;;;AA4CA,MAAa,iBAMX,YACA,qBAMG;CACH,MAAM,SAASA,iDAAkB;CAMjC,MAAM,iBAHJ,QAAQ,IAAI,iCAAiC,WAC7C,OAAO,qBAAqB,YAC5B,qBAAqB,OAElB,iBAAwC,SACxC;CAIL,MAAM,QAAQ,WACZ,QAAQ,IAAI,iCAAiC,UACzCC,oCACE,gFAC0B;EACxB;EACA,eAAe;EACf,gBAAgB,OAAO;EACvB,eAAe,WAAW;CAC5B,CAAC,CACH,IACAA,oCAAc,YAAY,MAAW;CAE3C,MAAM,UAAU,KAAK,kBAAkB,OAAO,MAAM;CAGpD,IAAI,WAAW,QAAQ,OAAO,YAAY,UACxC,QAAQ,YAAY,aAAa;EAC/B,OAAO,WAAW,cAAc;GAC9B,SAAS,KAAK,kBAAkB,SAAS,CAAC;EAC5C,CAAC;EACD,OAAO;CACT;CAGF,OAAO;AACT"}
|
|
@@ -52,7 +52,12 @@ const useDictionaryDynamic = (dictionaryLoaders, key, localeOrSelector) => {
|
|
|
52
52
|
const defaultLocale = _intlayer_config_built.internationalization.defaultLocale;
|
|
53
53
|
if (process.env.INTLAYER_DICTIONARY_SELECTOR !== "false" && (0, _intlayer_core_dictionaryManipulator.isQualifiedDynamicLoaderMap)(dictionaryLoaders)) {
|
|
54
54
|
const loaderMap = dictionaryLoaders;
|
|
55
|
-
const { locale: selectorLocale, selector } = (0, _intlayer_core_dictionaryManipulator.parseDictionarySelector)(
|
|
55
|
+
const { locale: selectorLocale, selector } = (0, _intlayer_core_dictionaryManipulator.parseDictionarySelector)((0, _intlayer_core_dictionaryManipulator.resolveDictionaryArgument)({
|
|
56
|
+
localeOrSelector,
|
|
57
|
+
contextLocale: client.locale,
|
|
58
|
+
contextVariant: client.variant,
|
|
59
|
+
dictionaryKey: String(key)
|
|
60
|
+
}));
|
|
56
61
|
const selectorId = (0, _intlayer_core_dictionaryManipulator.getDictionarySelectorCacheKey)(selector);
|
|
57
62
|
const buildKey = (locale) => `${String(key)}.${locale}.${selectorId}`;
|
|
58
63
|
const resolveContent = (locale) => (0, _intlayer_core_dictionaryManipulator.resolveQualifiedDynamicContentAsync)({
|
|
@@ -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 {\n getDictionarySelectorCacheKey,\n isQualifiedDynamicLoaderMap,\n parseDictionarySelector,\n type QualifiedDynamicLoaderMap,\n resolveQualifiedDynamicContentAsync,\n} from '@intlayer/core/dictionaryManipulator';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\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, 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\n | DeclaredLocales\n | DictionarySelectorForKey<K> = DeclaredLocales,\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) ---\n if (\n process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false' &&\n isQualifiedDynamicLoaderMap(dictionaryLoaders)\n ) {\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) =>\n getDictionary(dictionary, locale as DeclaredLocales),\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":";;;;;;;;
|
|
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 resolveDictionaryArgument,\n resolveQualifiedDynamicContentAsync,\n} from '@intlayer/core/dictionaryManipulator';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\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, 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\n | DeclaredLocales\n | DictionarySelectorForKey<K> = DeclaredLocales,\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) ---\n if (\n process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false' &&\n isQualifiedDynamicLoaderMap(dictionaryLoaders)\n ) {\n const loaderMap = dictionaryLoaders;\n // Layers the client's ambient variant under the call-site argument before\n // the chunk walk, so only the targeted chunk is loaded.\n const { locale: selectorLocale, selector } =\n parseDictionarySelector<LocalesValues>(\n resolveDictionaryArgument({\n localeOrSelector,\n contextLocale: client.locale,\n contextVariant: client.variant,\n dictionaryKey: String(key),\n })\n );\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) =>\n getDictionary(dictionary, locale as DeclaredLocales),\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":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_getIntlayer = require('../getIntlayer.cjs');
|
|
3
3
|
const require_client_installIntlayer = require('./installIntlayer.cjs');
|
|
4
|
+
let _intlayer_core_dictionaryManipulator = require("@intlayer/core/dictionaryManipulator");
|
|
4
5
|
|
|
5
6
|
//#region src/client/useIntlayer.ts
|
|
6
7
|
/**
|
|
@@ -32,12 +33,13 @@ const require_client_installIntlayer = require('./installIntlayer.cjs');
|
|
|
32
33
|
*/
|
|
33
34
|
const useIntlayer = (key, localeOrSelector) => {
|
|
34
35
|
const client = require_client_installIntlayer.getIntlayerClient();
|
|
35
|
-
const
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
const explicitLocale = process.env.INTLAYER_DICTIONARY_SELECTOR !== "false" && typeof localeOrSelector === "object" && localeOrSelector !== null ? localeOrSelector.locale : localeOrSelector;
|
|
37
|
+
const read = (locale) => process.env.INTLAYER_DICTIONARY_SELECTOR !== "false" ? require_getIntlayer.getIntlayer(key, (0, _intlayer_core_dictionaryManipulator.resolveDictionaryArgument)({
|
|
38
|
+
localeOrSelector,
|
|
39
|
+
contextLocale: locale,
|
|
40
|
+
contextVariant: client.variant,
|
|
41
|
+
dictionaryKey: key
|
|
42
|
+
})) : require_getIntlayer.getIntlayer(key, locale);
|
|
41
43
|
const content = read(explicitLocale ?? client.locale);
|
|
42
44
|
if (content != null && typeof content === "object") content.onChange = (callback) => {
|
|
43
45
|
client.subscribe((newLocale) => {
|
|
@@ -1 +1 @@
|
|
|
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 * - `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\n | DeclaredLocales\n | 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 process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false' &&\n typeof localeOrSelector === 'object' &&\n localeOrSelector !== null;\n const explicitLocale = isSelector\n ? (localeOrSelector as DictionarySelector).locale\n : (localeOrSelector as LocalesValues | undefined);\n\n const read = (locale: LocalesValues | undefined) =>\n
|
|
1
|
+
{"version":3,"file":"useIntlayer.cjs","names":["getIntlayerClient","getIntlayer"],"sources":["../../../src/client/useIntlayer.ts"],"sourcesContent":["import { resolveDictionaryArgument } from '@intlayer/core/dictionaryManipulator';\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 { 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 * - `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\n | DeclaredLocales\n | 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 process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false' &&\n typeof localeOrSelector === 'object' &&\n localeOrSelector !== null;\n const explicitLocale = isSelector\n ? (localeOrSelector as DictionarySelector).locale\n : (localeOrSelector as LocalesValues | undefined);\n\n // Layers the client's ambient variant under the call-site argument. Resolved\n // per read so a `setVariant` between reads is picked up.\n const read = (locale: LocalesValues | undefined) =>\n process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false'\n ? getIntlayer(\n key,\n resolveDictionaryArgument({\n localeOrSelector,\n contextLocale: locale,\n contextVariant: client.variant,\n dictionaryKey: key as string,\n }) as A\n )\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,eAMX,KACA,qBAGG;CACH,MAAM,SAASA,iDAAkB;CAMjC,MAAM,iBAHJ,QAAQ,IAAI,iCAAiC,WAC7C,OAAO,qBAAqB,YAC5B,qBAAqB,OAElB,iBAAwC,SACxC;CAIL,MAAM,QAAQ,WACZ,QAAQ,IAAI,iCAAiC,UACzCC,gCACE,yEAC0B;EACxB;EACA,eAAe;EACf,gBAAgB,OAAO;EACvB,eAAe;CACjB,CAAC,CACH,IACAA,gCAAY,KAAK,MAAW;CAElC,MAAM,UAAU,KAAK,kBAAkB,OAAO,MAAM;CAGpD,IAAI,WAAW,QAAQ,OAAO,YAAY,UACxC,QAAQ,YAAY,aAAa;EAC/B,OAAO,WAAW,cAAc;GAC9B,SAAS,KAAK,kBAAkB,SAAS,CAAC;EAC5C,CAAC;EACD,OAAO;CACT;CAGF,OAAO;AACT"}
|
|
@@ -5,21 +5,34 @@ import { setIntlayerIdentifier } from "@intlayer/config/client";
|
|
|
5
5
|
//#region src/client/installIntlayer.ts
|
|
6
6
|
var IntlayerClient = class {
|
|
7
7
|
_locale;
|
|
8
|
+
_variant;
|
|
8
9
|
_listeners = /* @__PURE__ */ new Set();
|
|
9
10
|
isCookieEnabled;
|
|
10
|
-
constructor(locale, isCookieEnabled = true) {
|
|
11
|
+
constructor(locale, isCookieEnabled = true, variant) {
|
|
11
12
|
const { defaultLocale } = internationalization ?? {};
|
|
12
13
|
setIntlayerIdentifier();
|
|
13
14
|
this._locale = locale ?? defaultLocale;
|
|
15
|
+
this._variant = variant;
|
|
14
16
|
this.isCookieEnabled = isCookieEnabled;
|
|
15
17
|
}
|
|
16
18
|
get locale() {
|
|
17
19
|
return this._locale;
|
|
18
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* Ambient variant applied to every dictionary read, the same way `locale` is.
|
|
23
|
+
* Overridden per call by an explicit selector.
|
|
24
|
+
*/
|
|
25
|
+
get variant() {
|
|
26
|
+
return this._variant;
|
|
27
|
+
}
|
|
19
28
|
setLocale(newLocale) {
|
|
20
29
|
this._locale = newLocale;
|
|
21
30
|
for (const listener of this._listeners) listener(newLocale);
|
|
22
31
|
}
|
|
32
|
+
setVariant(newVariant) {
|
|
33
|
+
this._variant = newVariant;
|
|
34
|
+
this.notify();
|
|
35
|
+
}
|
|
23
36
|
subscribe(listener) {
|
|
24
37
|
this._listeners.add(listener);
|
|
25
38
|
return () => {
|
|
@@ -41,9 +54,9 @@ let instance = null;
|
|
|
41
54
|
/**
|
|
42
55
|
* Create and return a single IntlayerClient instance.
|
|
43
56
|
*/
|
|
44
|
-
const createIntlayerClient = (locale, isCookieEnabled = true) => {
|
|
57
|
+
const createIntlayerClient = (locale, isCookieEnabled = true, variant) => {
|
|
45
58
|
if (instance) return instance;
|
|
46
|
-
instance = new IntlayerClient(locale, isCookieEnabled);
|
|
59
|
+
instance = new IntlayerClient(locale, isCookieEnabled, variant);
|
|
47
60
|
return instance;
|
|
48
61
|
};
|
|
49
62
|
/**
|
|
@@ -64,6 +77,13 @@ const getIntlayerClient = () => {
|
|
|
64
77
|
*
|
|
65
78
|
* @param locale - Initial locale (defaults to config defaultLocale).
|
|
66
79
|
* @param isCookieEnabled - Whether to persist locale in cookies/localStorage.
|
|
80
|
+
* @param variant - Ambient variant applied to every dictionary read — for a
|
|
81
|
+
* dimension fixed for the whole session (tenant, school type, plan tier…)
|
|
82
|
+
* that no call site should have to pass by hand. Accepts a name
|
|
83
|
+
* (`'school1'`), an ordered preference chain (`['school1', 'default']`), or a
|
|
84
|
+
* per-key map (`{ key1: 'school1', default: 'base' }`). A plain object is
|
|
85
|
+
* always the per-key map; nest a structured variant as
|
|
86
|
+
* `{ default: { id: 'prod_abc' } }`. A call-site selector always wins.
|
|
67
87
|
* @param config - Optional Intlayer configuration. When provided, sets
|
|
68
88
|
* `window.INTLAYER_CONFIG` with the browser-safe subset of the config so
|
|
69
89
|
* the application works without a Vite/webpack plugin alias.
|
|
@@ -82,8 +102,8 @@ const getIntlayerClient = () => {
|
|
|
82
102
|
* installIntlayer('en');
|
|
83
103
|
* ```
|
|
84
104
|
*/
|
|
85
|
-
const installIntlayer = ({ locale, isCookieEnabled } = {}) => {
|
|
86
|
-
const client = createIntlayerClient(locale, isCookieEnabled);
|
|
105
|
+
const installIntlayer = ({ locale, isCookieEnabled, variant } = {}) => {
|
|
106
|
+
const client = createIntlayerClient(locale, isCookieEnabled, variant);
|
|
87
107
|
useEditor();
|
|
88
108
|
return client;
|
|
89
109
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installIntlayer.mjs","names":[],"sources":["../../../src/client/installIntlayer.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\nimport { setIntlayerIdentifier } from '@intlayer/config/client';\nimport type {
|
|
1
|
+
{"version":3,"file":"installIntlayer.mjs","names":[],"sources":["../../../src/client/installIntlayer.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\nimport { setIntlayerIdentifier } from '@intlayer/config/client';\nimport type {\n LocalesValues,\n ProviderVariant,\n} from '@intlayer/types/module_augmentation';\nimport { useEditor } from '../editor/useEditor';\n\ntype LocaleListener = (locale: LocalesValues) => void;\n\nexport class IntlayerClient {\n private _locale: LocalesValues;\n private _variant: ProviderVariant | undefined;\n private _listeners: Set<LocaleListener> = new Set();\n isCookieEnabled: boolean;\n\n constructor(\n locale?: LocalesValues,\n isCookieEnabled = true,\n variant?: ProviderVariant\n ) {\n const { defaultLocale } = internationalization ?? {};\n\n setIntlayerIdentifier();\n\n this._locale =\n (locale as LocalesValues) ?? (defaultLocale as LocalesValues);\n this._variant = variant;\n this.isCookieEnabled = isCookieEnabled;\n }\n\n get locale(): LocalesValues {\n return this._locale;\n }\n\n /**\n * Ambient variant applied to every dictionary read, the same way `locale` is.\n * Overridden per call by an explicit selector.\n */\n get variant(): ProviderVariant | undefined {\n return this._variant;\n }\n\n setLocale(newLocale: LocalesValues): void {\n this._locale = newLocale;\n for (const listener of this._listeners) {\n listener(newLocale);\n }\n }\n\n setVariant(newVariant: ProviderVariant | undefined): void {\n this._variant = newVariant;\n this.notify();\n }\n\n subscribe(listener: LocaleListener): () => void {\n this._listeners.add(listener);\n return () => {\n this._listeners.delete(listener);\n };\n }\n\n /**\n * Fire all listeners with the current locale without changing it.\n * Used to trigger re-renders after async data loads.\n */\n notify(): void {\n for (const listener of this._listeners) {\n listener(this._locale);\n }\n }\n}\n\n/**\n * Singleton instance\n */\nlet instance: IntlayerClient | null = null;\n\n/**\n * Create and return a single IntlayerClient instance.\n */\nexport const createIntlayerClient = (\n locale?: LocalesValues,\n isCookieEnabled = true,\n variant?: ProviderVariant\n): IntlayerClient => {\n if (instance) return instance;\n\n instance = new IntlayerClient(locale, isCookieEnabled, variant);\n return instance;\n};\n\n/**\n * Get the current IntlayerClient instance, creating one with defaults if needed.\n */\nexport const getIntlayerClient = (): IntlayerClient => {\n if (!instance) {\n instance = new IntlayerClient();\n }\n return instance;\n};\n\n/**\n * Install Intlayer into your vanilla JS application.\n *\n * Call this once at application startup, before any Intlayer APIs are used.\n *\n * When called with a `config` argument, `window.INTLAYER_CONFIG` is set so\n * that `@intlayer/config/built` resolves the correct values in the browser\n * without requiring module aliasing in the bundler configuration.\n *\n * @param locale - Initial locale (defaults to config defaultLocale).\n * @param isCookieEnabled - Whether to persist locale in cookies/localStorage.\n * @param variant - Ambient variant applied to every dictionary read — for a\n * dimension fixed for the whole session (tenant, school type, plan tier…)\n * that no call site should have to pass by hand. Accepts a name\n * (`'school1'`), an ordered preference chain (`['school1', 'default']`), or a\n * per-key map (`{ key1: 'school1', default: 'base' }`). A plain object is\n * always the per-key map; nest a structured variant as\n * `{ default: { id: 'prod_abc' } }`. A call-site selector always wins.\n * @param config - Optional Intlayer configuration. When provided, sets\n * `window.INTLAYER_CONFIG` with the browser-safe subset of the config so\n * the application works without a Vite/webpack plugin alias.\n * @returns The IntlayerClient singleton.\n *\n * @example\n * ```ts\n * import { installIntlayer } from 'vanilla-intlayer';\n *\n * // With explicit config (no build plugin alias needed):\n * installIntlayer('en', true, {\n * internationalization: { locales: ['en', 'fr'], defaultLocale: 'en' },\n * });\n *\n * // With build plugin (alias handles config automatically):\n * installIntlayer('en');\n * ```\n */\nexport const installIntlayer = ({\n locale,\n isCookieEnabled,\n variant,\n}: {\n locale?: LocalesValues;\n isCookieEnabled?: boolean;\n variant?: ProviderVariant;\n} = {}): IntlayerClient => {\n const client = createIntlayerClient(locale, isCookieEnabled, variant);\n\n useEditor();\n\n return client;\n};\n"],"mappings":";;;;;AAUA,IAAa,iBAAb,MAA4B;CAC1B,AAAQ;CACR,AAAQ;CACR,AAAQ,6BAAkC,IAAI,IAAI;CAClD;CAEA,YACE,QACA,kBAAkB,MAClB,SACA;EACA,MAAM,EAAE,kBAAkB,wBAAwB,CAAC;EAEnD,sBAAsB;EAEtB,KAAK,UACF,UAA6B;EAChC,KAAK,WAAW;EAChB,KAAK,kBAAkB;CACzB;CAEA,IAAI,SAAwB;EAC1B,OAAO,KAAK;CACd;;;;;CAMA,IAAI,UAAuC;EACzC,OAAO,KAAK;CACd;CAEA,UAAU,WAAgC;EACxC,KAAK,UAAU;EACf,KAAK,MAAM,YAAY,KAAK,YAC1B,SAAS,SAAS;CAEtB;CAEA,WAAW,YAA+C;EACxD,KAAK,WAAW;EAChB,KAAK,OAAO;CACd;CAEA,UAAU,UAAsC;EAC9C,KAAK,WAAW,IAAI,QAAQ;EAC5B,aAAa;GACX,KAAK,WAAW,OAAO,QAAQ;EACjC;CACF;;;;;CAMA,SAAe;EACb,KAAK,MAAM,YAAY,KAAK,YAC1B,SAAS,KAAK,OAAO;CAEzB;AACF;;;;AAKA,IAAI,WAAkC;;;;AAKtC,MAAa,wBACX,QACA,kBAAkB,MAClB,YACmB;CACnB,IAAI,UAAU,OAAO;CAErB,WAAW,IAAI,eAAe,QAAQ,iBAAiB,OAAO;CAC9D,OAAO;AACT;;;;AAKA,MAAa,0BAA0C;CACrD,IAAI,CAAC,UACH,WAAW,IAAI,eAAe;CAEhC,OAAO;AACT;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAsCA,MAAa,mBAAmB,EAC9B,QACA,iBACA,YAKE,CAAC,MAAsB;CACzB,MAAM,SAAS,qBAAqB,QAAQ,iBAAiB,OAAO;CAEpE,UAAU;CAEV,OAAO;AACT"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getDictionary } from "../getDictionary.mjs";
|
|
2
2
|
import { getIntlayerClient } from "./installIntlayer.mjs";
|
|
3
|
+
import { resolveDictionaryArgument } from "@intlayer/core/dictionaryManipulator";
|
|
3
4
|
|
|
4
5
|
//#region src/client/useDictionary.ts
|
|
5
6
|
/**
|
|
@@ -16,12 +17,13 @@ import { getIntlayerClient } from "./installIntlayer.mjs";
|
|
|
16
17
|
*/
|
|
17
18
|
const useDictionary = (dictionary, localeOrSelector) => {
|
|
18
19
|
const client = getIntlayerClient();
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
const explicitLocale = process.env.INTLAYER_DICTIONARY_SELECTOR !== "false" && typeof localeOrSelector === "object" && localeOrSelector !== null ? localeOrSelector.locale : localeOrSelector;
|
|
21
|
+
const read = (locale) => process.env.INTLAYER_DICTIONARY_SELECTOR !== "false" ? getDictionary(dictionary, resolveDictionaryArgument({
|
|
22
|
+
localeOrSelector,
|
|
23
|
+
contextLocale: locale,
|
|
24
|
+
contextVariant: client.variant,
|
|
25
|
+
dictionaryKey: dictionary.key
|
|
26
|
+
})) : getDictionary(dictionary, locale);
|
|
25
27
|
const content = read(explicitLocale ?? client.locale);
|
|
26
28
|
if (content != null && typeof content === "object") content.onChange = (callback) => {
|
|
27
29
|
client.subscribe((newLocale) => {
|
|
@@ -1 +1 @@
|
|
|
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 }`, 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 | DeclaredLocales\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 process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false' &&\n typeof localeOrSelector === 'object' &&\n localeOrSelector !== null;\n const explicitLocale = isSelector\n ? (localeOrSelector as DictionarySelector).locale\n : (localeOrSelector as LocalesValues | undefined);\n\n const read = (locale: LocalesValues | undefined) =>\n
|
|
1
|
+
{"version":3,"file":"useDictionary.mjs","names":[],"sources":["../../../src/client/useDictionary.ts"],"sourcesContent":["import { resolveDictionaryArgument } from '@intlayer/core/dictionaryManipulator';\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 { 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 }`, 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 | DeclaredLocales\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 process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false' &&\n typeof localeOrSelector === 'object' &&\n localeOrSelector !== null;\n const explicitLocale = isSelector\n ? (localeOrSelector as DictionarySelector).locale\n : (localeOrSelector as LocalesValues | undefined);\n\n // Layers the client's ambient variant under the call-site argument. Resolved\n // per read so a `setVariant` between reads is picked up.\n const read = (locale: LocalesValues | undefined) =>\n process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false'\n ? getDictionary(\n dictionary,\n resolveDictionaryArgument({\n localeOrSelector,\n contextLocale: locale,\n contextVariant: client.variant,\n dictionaryKey: dictionary.key,\n }) as A\n )\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":";;;;;;;;;;;;;;;;;AA4CA,MAAa,iBAMX,YACA,qBAMG;CACH,MAAM,SAAS,kBAAkB;CAMjC,MAAM,iBAHJ,QAAQ,IAAI,iCAAiC,WAC7C,OAAO,qBAAqB,YAC5B,qBAAqB,OAElB,iBAAwC,SACxC;CAIL,MAAM,QAAQ,WACZ,QAAQ,IAAI,iCAAiC,UACzC,cACE,YACA,0BAA0B;EACxB;EACA,eAAe;EACf,gBAAgB,OAAO;EACvB,eAAe,WAAW;CAC5B,CAAC,CACH,IACA,cAAc,YAAY,MAAW;CAE3C,MAAM,UAAU,KAAK,kBAAkB,OAAO,MAAM;CAGpD,IAAI,WAAW,QAAQ,OAAO,YAAY,UACxC,QAAQ,YAAY,aAAa;EAC/B,OAAO,WAAW,cAAc;GAC9B,SAAS,KAAK,kBAAkB,SAAS,CAAC;EAC5C,CAAC;EACD,OAAO;CACT;CAGF,OAAO;AACT"}
|
|
@@ -1,7 +1,7 @@
|
|
|
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
|
+
import { getDictionarySelectorCacheKey, isQualifiedDynamicLoaderMap, parseDictionarySelector, resolveDictionaryArgument, resolveQualifiedDynamicContentAsync } from "@intlayer/core/dictionaryManipulator";
|
|
5
5
|
|
|
6
6
|
//#region src/client/useDictionaryDynamic.ts
|
|
7
7
|
/** Simple in-memory cache shared across all calls in the same page. */
|
|
@@ -51,7 +51,12 @@ const useDictionaryDynamic = (dictionaryLoaders, key, localeOrSelector) => {
|
|
|
51
51
|
const defaultLocale = internationalization.defaultLocale;
|
|
52
52
|
if (process.env.INTLAYER_DICTIONARY_SELECTOR !== "false" && isQualifiedDynamicLoaderMap(dictionaryLoaders)) {
|
|
53
53
|
const loaderMap = dictionaryLoaders;
|
|
54
|
-
const { locale: selectorLocale, selector } = parseDictionarySelector(
|
|
54
|
+
const { locale: selectorLocale, selector } = parseDictionarySelector(resolveDictionaryArgument({
|
|
55
|
+
localeOrSelector,
|
|
56
|
+
contextLocale: client.locale,
|
|
57
|
+
contextVariant: client.variant,
|
|
58
|
+
dictionaryKey: String(key)
|
|
59
|
+
}));
|
|
55
60
|
const selectorId = getDictionarySelectorCacheKey(selector);
|
|
56
61
|
const buildKey = (locale) => `${String(key)}.${locale}.${selectorId}`;
|
|
57
62
|
const resolveContent = (locale) => resolveQualifiedDynamicContentAsync({
|
|
@@ -1 +1 @@
|
|
|
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 { Dictionary } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\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, 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\n | DeclaredLocales\n | DictionarySelectorForKey<K> = DeclaredLocales,\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) ---\n if (\n process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false' &&\n isQualifiedDynamicLoaderMap(dictionaryLoaders)\n ) {\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) =>\n getDictionary(dictionary, locale as DeclaredLocales),\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":";;;;;;;
|
|
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 resolveDictionaryArgument,\n resolveQualifiedDynamicContentAsync,\n} from '@intlayer/core/dictionaryManipulator';\nimport type { Dictionary } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\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, 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\n | DeclaredLocales\n | DictionarySelectorForKey<K> = DeclaredLocales,\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) ---\n if (\n process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false' &&\n isQualifiedDynamicLoaderMap(dictionaryLoaders)\n ) {\n const loaderMap = dictionaryLoaders;\n // Layers the client's ambient variant under the call-site argument before\n // the chunk walk, so only the targeted chunk is loaded.\n const { locale: selectorLocale, selector } =\n parseDictionarySelector<LocalesValues>(\n resolveDictionaryArgument({\n localeOrSelector,\n contextLocale: client.locale,\n contextVariant: client.variant,\n dictionaryKey: String(key),\n })\n );\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) =>\n getDictionary(dictionary, locale as DeclaredLocales),\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":"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"}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { getIntlayer } from "../getIntlayer.mjs";
|
|
2
2
|
import { getIntlayerClient } from "./installIntlayer.mjs";
|
|
3
|
+
import { resolveDictionaryArgument } from "@intlayer/core/dictionaryManipulator";
|
|
3
4
|
|
|
4
5
|
//#region src/client/useIntlayer.ts
|
|
5
6
|
/**
|
|
@@ -31,12 +32,13 @@ import { getIntlayerClient } from "./installIntlayer.mjs";
|
|
|
31
32
|
*/
|
|
32
33
|
const useIntlayer = (key, localeOrSelector) => {
|
|
33
34
|
const client = getIntlayerClient();
|
|
34
|
-
const
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
const explicitLocale = process.env.INTLAYER_DICTIONARY_SELECTOR !== "false" && typeof localeOrSelector === "object" && localeOrSelector !== null ? localeOrSelector.locale : localeOrSelector;
|
|
36
|
+
const read = (locale) => process.env.INTLAYER_DICTIONARY_SELECTOR !== "false" ? getIntlayer(key, resolveDictionaryArgument({
|
|
37
|
+
localeOrSelector,
|
|
38
|
+
contextLocale: locale,
|
|
39
|
+
contextVariant: client.variant,
|
|
40
|
+
dictionaryKey: key
|
|
41
|
+
})) : getIntlayer(key, locale);
|
|
40
42
|
const content = read(explicitLocale ?? client.locale);
|
|
41
43
|
if (content != null && typeof content === "object") content.onChange = (callback) => {
|
|
42
44
|
client.subscribe((newLocale) => {
|
|
@@ -1 +1 @@
|
|
|
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 * - `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\n | DeclaredLocales\n | 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 process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false' &&\n typeof localeOrSelector === 'object' &&\n localeOrSelector !== null;\n const explicitLocale = isSelector\n ? (localeOrSelector as DictionarySelector).locale\n : (localeOrSelector as LocalesValues | undefined);\n\n const read = (locale: LocalesValues | undefined) =>\n
|
|
1
|
+
{"version":3,"file":"useIntlayer.mjs","names":[],"sources":["../../../src/client/useIntlayer.ts"],"sourcesContent":["import { resolveDictionaryArgument } from '@intlayer/core/dictionaryManipulator';\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 { 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 * - `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\n | DeclaredLocales\n | 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 process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false' &&\n typeof localeOrSelector === 'object' &&\n localeOrSelector !== null;\n const explicitLocale = isSelector\n ? (localeOrSelector as DictionarySelector).locale\n : (localeOrSelector as LocalesValues | undefined);\n\n // Layers the client's ambient variant under the call-site argument. Resolved\n // per read so a `setVariant` between reads is picked up.\n const read = (locale: LocalesValues | undefined) =>\n process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false'\n ? getIntlayer(\n key,\n resolveDictionaryArgument({\n localeOrSelector,\n contextLocale: locale,\n contextVariant: client.variant,\n dictionaryKey: key as string,\n }) as A\n )\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,eAMX,KACA,qBAGG;CACH,MAAM,SAAS,kBAAkB;CAMjC,MAAM,iBAHJ,QAAQ,IAAI,iCAAiC,WAC7C,OAAO,qBAAqB,YAC5B,qBAAqB,OAElB,iBAAwC,SACxC;CAIL,MAAM,QAAQ,WACZ,QAAQ,IAAI,iCAAiC,UACzC,YACE,KACA,0BAA0B;EACxB;EACA,eAAe;EACf,gBAAgB,OAAO;EACvB,eAAe;CACjB,CAAC,CACH,IACA,YAAY,KAAK,MAAW;CAElC,MAAM,UAAU,KAAK,kBAAkB,OAAO,MAAM;CAGpD,IAAI,WAAW,QAAQ,OAAO,YAAY,UACxC,QAAQ,YAAY,aAAa;EAC/B,OAAO,WAAW,cAAc;GAC9B,SAAS,KAAK,kBAAkB,SAAS,CAAC;EAC5C,CAAC;EACD,OAAO;CACT;CAGF,OAAO;AACT"}
|
|
@@ -1,13 +1,20 @@
|
|
|
1
|
-
import { LocalesValues } from "@intlayer/types/module_augmentation";
|
|
1
|
+
import { LocalesValues, ProviderVariant } from "@intlayer/types/module_augmentation";
|
|
2
2
|
//#region src/client/installIntlayer.d.ts
|
|
3
3
|
type LocaleListener = (locale: LocalesValues) => void;
|
|
4
4
|
declare class IntlayerClient {
|
|
5
5
|
private _locale;
|
|
6
|
+
private _variant;
|
|
6
7
|
private _listeners;
|
|
7
8
|
isCookieEnabled: boolean;
|
|
8
|
-
constructor(locale?: LocalesValues, isCookieEnabled?: boolean);
|
|
9
|
+
constructor(locale?: LocalesValues, isCookieEnabled?: boolean, variant?: ProviderVariant);
|
|
9
10
|
get locale(): LocalesValues;
|
|
11
|
+
/**
|
|
12
|
+
* Ambient variant applied to every dictionary read, the same way `locale` is.
|
|
13
|
+
* Overridden per call by an explicit selector.
|
|
14
|
+
*/
|
|
15
|
+
get variant(): ProviderVariant | undefined;
|
|
10
16
|
setLocale(newLocale: LocalesValues): void;
|
|
17
|
+
setVariant(newVariant: ProviderVariant | undefined): void;
|
|
11
18
|
subscribe(listener: LocaleListener): () => void;
|
|
12
19
|
/**
|
|
13
20
|
* Fire all listeners with the current locale without changing it.
|
|
@@ -18,7 +25,7 @@ declare class IntlayerClient {
|
|
|
18
25
|
/**
|
|
19
26
|
* Create and return a single IntlayerClient instance.
|
|
20
27
|
*/
|
|
21
|
-
declare const createIntlayerClient: (locale?: LocalesValues, isCookieEnabled?: boolean) => IntlayerClient;
|
|
28
|
+
declare const createIntlayerClient: (locale?: LocalesValues, isCookieEnabled?: boolean, variant?: ProviderVariant) => IntlayerClient;
|
|
22
29
|
/**
|
|
23
30
|
* Get the current IntlayerClient instance, creating one with defaults if needed.
|
|
24
31
|
*/
|
|
@@ -34,6 +41,13 @@ declare const getIntlayerClient: () => IntlayerClient;
|
|
|
34
41
|
*
|
|
35
42
|
* @param locale - Initial locale (defaults to config defaultLocale).
|
|
36
43
|
* @param isCookieEnabled - Whether to persist locale in cookies/localStorage.
|
|
44
|
+
* @param variant - Ambient variant applied to every dictionary read — for a
|
|
45
|
+
* dimension fixed for the whole session (tenant, school type, plan tier…)
|
|
46
|
+
* that no call site should have to pass by hand. Accepts a name
|
|
47
|
+
* (`'school1'`), an ordered preference chain (`['school1', 'default']`), or a
|
|
48
|
+
* per-key map (`{ key1: 'school1', default: 'base' }`). A plain object is
|
|
49
|
+
* always the per-key map; nest a structured variant as
|
|
50
|
+
* `{ default: { id: 'prod_abc' } }`. A call-site selector always wins.
|
|
37
51
|
* @param config - Optional Intlayer configuration. When provided, sets
|
|
38
52
|
* `window.INTLAYER_CONFIG` with the browser-safe subset of the config so
|
|
39
53
|
* the application works without a Vite/webpack plugin alias.
|
|
@@ -52,9 +66,10 @@ declare const getIntlayerClient: () => IntlayerClient;
|
|
|
52
66
|
* installIntlayer('en');
|
|
53
67
|
* ```
|
|
54
68
|
*/
|
|
55
|
-
declare const installIntlayer: ({ locale, isCookieEnabled }?: {
|
|
69
|
+
declare const installIntlayer: ({ locale, isCookieEnabled, variant }?: {
|
|
56
70
|
locale?: LocalesValues;
|
|
57
71
|
isCookieEnabled?: boolean;
|
|
72
|
+
variant?: ProviderVariant;
|
|
58
73
|
}) => IntlayerClient;
|
|
59
74
|
//#endregion
|
|
60
75
|
export { IntlayerClient, createIntlayerClient, getIntlayerClient, installIntlayer };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installIntlayer.d.ts","names":[],"sources":["../../../src/client/installIntlayer.ts"],"mappings":";;
|
|
1
|
+
{"version":3,"file":"installIntlayer.d.ts","names":[],"sources":["../../../src/client/installIntlayer.ts"],"mappings":";;KAQK,kBAAkB,QAAQ;cAElB;UACH;UACA;UACA;EACR;EAEA,YACE,SAAS,eACT,2BACA,UAAU;MAYR,UAAU;;;;;MAQV,WAAW;EAIf,UAAU,WAAW;EAOrB,WAAW,YAAY;EAKvB,UAAU,UAAU;;;;;EAWpB;;;;;cAeW,uBAAoB,SACtB,eAAa,2BAAA,UAEZ,oBACT;;;;cAUU,yBAAwB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA2CxB,oBAAe,QAAA,iBAAA;EAK1B,SAAS;EACT;EACA,UAAU;MACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDictionary.d.ts","names":[],"sources":["../../../src/client/useDictionary.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"useDictionary.d.ts","names":[],"sources":["../../../src/client/useDictionary.ts"],"mappings":";;;;KAiBY,aAAa,KAAK;;;;;;;;;;;;EAY5B,WAAW,WAAW,0BAA0B,aAAa;;;;;;;;;;;;;;cAelD,sBACL,UAAU,aAAa,gCACvB,UACF,kBACA,2BAA2B,KAAK,iBAAe,YAEvC,GAAC,mBACM,MAClB,aACD,qBACE,kCAAkC,GAAG,IACrC,sBAAsB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useDictionaryDynamic.d.ts","names":[],"sources":["../../../src/client/useDictionaryDynamic.ts"],"mappings":";;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"useDictionaryDynamic.d.ts","names":[],"sources":["../../../src/client/useDictionaryDynamic.ts"],"mappings":";;;;;;;;;;;;;;;;;;;cA0Ea,6BACL,UAAU,kBACV,UAAU,sBACV,UACF,kBACA,yBAAyB,KAAK,iBAAe,mBAG7C,0BAA0B,QAAQ,MAClC,2BAAyB,KACxB,GAAC,mBACa,MAClB,aAAa,qBAAqB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useIntlayer.d.ts","names":[],"sources":["../../../src/client/useIntlayer.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"useIntlayer.d.ts","names":[],"sources":["../../../src/client/useIntlayer.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA0Ca,oBACL,UAAU,sBACV,UACF,kBACA,yBAAyB,KAAK,iBAAe,KAE5C,GAAC,mBACa,MAClB,aACD,qBAAqB,yBAAyB,GAAG,IAAI,sBAAsB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vanilla-intlayer",
|
|
3
|
-
"version": "9.1.
|
|
3
|
+
"version": "9.1.2",
|
|
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,11 +101,11 @@
|
|
|
101
101
|
"typecheck": "tsc --noEmit --project tsconfig.types.json"
|
|
102
102
|
},
|
|
103
103
|
"dependencies": {
|
|
104
|
-
"@intlayer/api": "9.1.
|
|
105
|
-
"@intlayer/config": "9.1.
|
|
106
|
-
"@intlayer/core": "9.1.
|
|
107
|
-
"@intlayer/editor": "9.1.
|
|
108
|
-
"@intlayer/types": "9.1.
|
|
104
|
+
"@intlayer/api": "9.1.2",
|
|
105
|
+
"@intlayer/config": "9.1.2",
|
|
106
|
+
"@intlayer/core": "9.1.2",
|
|
107
|
+
"@intlayer/editor": "9.1.2",
|
|
108
|
+
"@intlayer/types": "9.1.2"
|
|
109
109
|
},
|
|
110
110
|
"devDependencies": {
|
|
111
111
|
"@types/node": "26.1.2",
|
|
@@ -118,7 +118,7 @@
|
|
|
118
118
|
"vitest": "4.1.10"
|
|
119
119
|
},
|
|
120
120
|
"peerDependencies": {
|
|
121
|
-
"intlayer": "9.1.
|
|
121
|
+
"intlayer": "9.1.2"
|
|
122
122
|
},
|
|
123
123
|
"peerDependenciesMeta": {
|
|
124
124
|
"intlayer": {
|