@intlayer/core 9.5.11 → 9.6.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.
- package/dist/cjs/index.cjs +7 -2
- package/dist/cjs/interpreter/getDictionary.cjs +4 -3
- package/dist/cjs/interpreter/getDictionary.cjs.map +1 -1
- package/dist/cjs/interpreter/getDictionaryAsync.cjs +2 -2
- package/dist/cjs/interpreter/getDictionaryAsync.cjs.map +1 -1
- package/dist/cjs/interpreter/getIntlayer.cjs.map +1 -1
- package/dist/cjs/interpreter/getIntlayerAsync.cjs +10 -1
- package/dist/cjs/interpreter/getIntlayerAsync.cjs.map +1 -1
- package/dist/cjs/interpreter/index.cjs +5 -0
- package/dist/cjs/interpreter/resolveInterpreterLocale.cjs +101 -0
- package/dist/cjs/interpreter/resolveInterpreterLocale.cjs.map +1 -0
- package/dist/cjs/localization/getLocaleFromRequest.cjs +1 -1
- package/dist/cjs/localization/localeDetector.cjs +60 -22
- package/dist/cjs/localization/localeDetector.cjs.map +1 -1
- package/dist/cjs/localization/resolveBrowserLocale.cjs +1 -1
- package/dist/cjs/localization/rewriteMatch.cjs +11 -1
- package/dist/cjs/localization/rewriteMatch.cjs.map +1 -1
- package/dist/cjs/markdown/compiler.cjs.map +1 -1
- package/dist/cjs/markdown/constants.cjs.map +1 -1
- package/dist/cjs/transpiler/markdown/getMarkdownMetadata.cjs +29 -9
- package/dist/cjs/transpiler/markdown/getMarkdownMetadata.cjs.map +1 -1
- package/dist/cjs/transpiler/markdown/markdown.cjs.map +1 -1
- package/dist/cjs/utils/index.cjs +2 -2
- package/dist/cjs/utils/intl.cjs.map +1 -1
- package/dist/esm/index.mjs +4 -3
- package/dist/esm/interpreter/getDictionary.mjs +4 -3
- package/dist/esm/interpreter/getDictionary.mjs.map +1 -1
- package/dist/esm/interpreter/getDictionaryAsync.mjs +2 -2
- package/dist/esm/interpreter/getDictionaryAsync.mjs.map +1 -1
- package/dist/esm/interpreter/getIntlayer.mjs +1 -1
- package/dist/esm/interpreter/getIntlayer.mjs.map +1 -1
- package/dist/esm/interpreter/getIntlayerAsync.mjs +10 -1
- package/dist/esm/interpreter/getIntlayerAsync.mjs.map +1 -1
- package/dist/esm/interpreter/index.mjs +2 -1
- package/dist/esm/interpreter/resolveInterpreterLocale.mjs +97 -0
- package/dist/esm/interpreter/resolveInterpreterLocale.mjs.map +1 -0
- package/dist/esm/localization/getLocaleFromRequest.mjs +1 -1
- package/dist/esm/localization/localeDetector.mjs +60 -22
- package/dist/esm/localization/localeDetector.mjs.map +1 -1
- package/dist/esm/localization/resolveBrowserLocale.mjs +1 -1
- package/dist/esm/localization/rewriteMatch.mjs +11 -1
- package/dist/esm/localization/rewriteMatch.mjs.map +1 -1
- package/dist/esm/markdown/compiler.mjs +1 -1
- package/dist/esm/markdown/compiler.mjs.map +1 -1
- package/dist/esm/markdown/constants.mjs +3 -0
- package/dist/esm/markdown/constants.mjs.map +1 -1
- package/dist/esm/transpiler/markdown/getMarkdownMetadata.mjs +29 -9
- package/dist/esm/transpiler/markdown/getMarkdownMetadata.mjs.map +1 -1
- package/dist/esm/transpiler/markdown/markdown.mjs +1 -1
- package/dist/esm/transpiler/markdown/markdown.mjs.map +1 -1
- package/dist/esm/utils/index.mjs +2 -2
- package/dist/esm/utils/intl.mjs +1 -0
- package/dist/esm/utils/intl.mjs.map +1 -1
- package/dist/types/index.d.ts +2 -1
- package/dist/types/interpreter/getIntlayerAsync.d.ts.map +1 -1
- package/dist/types/interpreter/index.d.ts +2 -1
- package/dist/types/interpreter/resolveInterpreterLocale.d.ts +63 -0
- package/dist/types/interpreter/resolveInterpreterLocale.d.ts.map +1 -0
- package/dist/types/localization/localeDetector.d.ts +2 -0
- package/dist/types/localization/localeDetector.d.ts.map +1 -1
- package/dist/types/localization/rewriteMatch.d.ts.map +1 -1
- package/dist/types/transpiler/markdown/getMarkdownMetadata.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/cjs/index.cjs
CHANGED
|
@@ -7,6 +7,9 @@ const require_interpreter_getEnumeration = require('./interpreter/getEnumeration
|
|
|
7
7
|
const require_interpreter_getGender = require('./interpreter/getGender.cjs');
|
|
8
8
|
const require_interpreter_getInsertion = require('./interpreter/getInsertion.cjs');
|
|
9
9
|
const require_dictionaryManipulator_qualifiedDictionary = require('./dictionaryManipulator/qualifiedDictionary.cjs');
|
|
10
|
+
const require_utils_getCookie = require('./utils/getCookie.cjs');
|
|
11
|
+
const require_utils_localeStorage = require('./utils/localeStorage.cjs');
|
|
12
|
+
const require_interpreter_resolveInterpreterLocale = require('./interpreter/resolveInterpreterLocale.cjs');
|
|
10
13
|
const require_interpreter_getDictionary = require('./interpreter/getDictionary.cjs');
|
|
11
14
|
const require_interpreter_getIntlayer = require('./interpreter/getIntlayer.cjs');
|
|
12
15
|
const require_interpreter_getNesting = require('./interpreter/getNesting.cjs');
|
|
@@ -86,8 +89,6 @@ const require_localization_getLocalizedPath = require('./localization/getLocaliz
|
|
|
86
89
|
const require_localization_getLocalizedUrl = require('./localization/getLocalizedUrl.cjs');
|
|
87
90
|
const require_localization_getMultilingualUrls = require('./localization/getMultilingualUrls.cjs');
|
|
88
91
|
const require_localization_generateSitemap = require('./localization/generateSitemap.cjs');
|
|
89
|
-
const require_utils_getCookie = require('./utils/getCookie.cjs');
|
|
90
|
-
const require_utils_localeStorage = require('./utils/localeStorage.cjs');
|
|
91
92
|
const require_localization_localeResolver = require('./localization/localeResolver.cjs');
|
|
92
93
|
const require_localization_localeDetector = require('./localization/localeDetector.cjs');
|
|
93
94
|
const require_localization_getBrowserLocale = require('./localization/getBrowserLocale.cjs');
|
|
@@ -386,6 +387,8 @@ exports.qualifies = require_markdown_utils.qualifies;
|
|
|
386
387
|
exports.readTransformCache = require_interpreter_dictionaryTransformCache.readTransformCache;
|
|
387
388
|
exports.rebuildInterpolableContent = require_interpreter_interpolableNode.rebuildInterpolableContent;
|
|
388
389
|
exports.reconstructQualifiedEntry = require_dictionaryManipulator_qualifiedDictionary.reconstructQualifiedEntry;
|
|
390
|
+
exports.registerAmbientLocaleResolver = require_interpreter_resolveInterpreterLocale.registerAmbientLocaleResolver;
|
|
391
|
+
exports.registerAsyncAmbientLocaleResolver = require_interpreter_resolveInterpreterLocale.registerAsyncAmbientLocaleResolver;
|
|
389
392
|
exports.relativeTime = require_formatters_relativeTime.relativeTime;
|
|
390
393
|
exports.removeContentNodeByKeyPath = require_dictionaryManipulator_removeContentNodeByKeyPath.removeContentNodeByKeyPath;
|
|
391
394
|
exports.renameContentNodeByKeyPath = require_dictionaryManipulator_renameContentNodeByKeyPath.renameContentNodeByKeyPath;
|
|
@@ -396,6 +399,8 @@ exports.resolveBrowserLocale = require_localization_resolveBrowserLocale.resolve
|
|
|
396
399
|
exports.resolveDictionaryArgument = require_dictionaryManipulator_qualifiedDictionary.resolveDictionaryArgument;
|
|
397
400
|
exports.resolveEffectiveVariantId = require_dictionaryManipulator_qualifiedDictionary.resolveEffectiveVariantId;
|
|
398
401
|
exports.resolveInsertedSelector = require_interpreter_getContent_plugins.resolveInsertedSelector;
|
|
402
|
+
exports.resolveInterpreterLocale = require_interpreter_resolveInterpreterLocale.resolveInterpreterLocale;
|
|
403
|
+
exports.resolveInterpreterLocaleAsync = require_interpreter_resolveInterpreterLocale.resolveInterpreterLocaleAsync;
|
|
399
404
|
exports.resolveLocalizedPath = require_localization_rewriteUtils.resolveLocalizedPath;
|
|
400
405
|
exports.resolveMessage = require_messageFormat_resolveMessage.resolveMessage;
|
|
401
406
|
exports.resolveMessageNode = require_messageFormat_resolveMessage.resolveMessageNode;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_interpreter_dictionaryTransformCache = require('./dictionaryTransformCache.cjs');
|
|
3
3
|
const require_dictionaryManipulator_qualifiedDictionary = require('../dictionaryManipulator/qualifiedDictionary.cjs');
|
|
4
|
+
const require_interpreter_resolveInterpreterLocale = require('./resolveInterpreterLocale.cjs');
|
|
4
5
|
const require_interpreter_getContent_getContent = require('./getContent/getContent.cjs');
|
|
5
|
-
let _intlayer_config_built = require("@intlayer/config/built");
|
|
6
6
|
|
|
7
7
|
//#region src/interpreter/getDictionary.ts
|
|
8
8
|
/**
|
|
@@ -21,11 +21,12 @@ const transformsInProgress = /* @__PURE__ */ new WeakSet();
|
|
|
21
21
|
* If omitted, we’ll use a default set of plugins.
|
|
22
22
|
*/
|
|
23
23
|
const getDictionary = (dictionary, localeOrSelector, plugins) => {
|
|
24
|
-
const { locale, selector } = process.env.INTLAYER_DICTIONARY_SELECTOR !== "false" ? require_dictionaryManipulator_qualifiedDictionary.parseDictionarySelector(localeOrSelector) : {
|
|
24
|
+
const { locale: requestedLocale, selector } = process.env.INTLAYER_DICTIONARY_SELECTOR !== "false" ? require_dictionaryManipulator_qualifiedDictionary.parseDictionarySelector(localeOrSelector) : {
|
|
25
25
|
locale: localeOrSelector,
|
|
26
26
|
selector: void 0
|
|
27
27
|
};
|
|
28
|
-
const
|
|
28
|
+
const locale = require_interpreter_resolveInterpreterLocale.resolveInterpreterLocale(requestedLocale);
|
|
29
|
+
const cacheKey = require_interpreter_dictionaryTransformCache.getDictionaryTransformCacheKey(locale, process.env.INTLAYER_DICTIONARY_SELECTOR !== "false" ? require_dictionaryManipulator_qualifiedDictionary.getDictionarySelectorCacheKey(selector) : "", plugins);
|
|
29
30
|
const cached = require_interpreter_dictionaryTransformCache.readTransformCache(dictionary, cacheKey);
|
|
30
31
|
if (cached.hit) return cached.content;
|
|
31
32
|
const appliedPlugins = plugins ?? require_interpreter_getContent_getContent.getBasePlugins(locale);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getDictionary.cjs","names":["parseDictionarySelector","
|
|
1
|
+
{"version":3,"file":"getDictionary.cjs","names":["parseDictionarySelector","resolveInterpreterLocale","getDictionaryTransformCacheKey","getDictionarySelectorCacheKey","readTransformCache","getBasePlugins","resolveQualifiedDictionary","getContent","writeTransformCache"],"sources":["../../../src/interpreter/getDictionary.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionarySelector,\n QualifiedDictionaryGroup,\n ResolveQualifiedDictionaryContent,\n} from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport {\n getDictionarySelectorCacheKey,\n parseDictionarySelector,\n resolveQualifiedDictionary,\n} from '../dictionaryManipulator/qualifiedDictionary';\nimport {\n getDictionaryTransformCacheKey,\n readTransformCache,\n writeTransformCache,\n} from './dictionaryTransformCache';\nimport type {\n DeepTransformContent,\n IInterpreterPluginState,\n NodeProps,\n Plugins,\n} from './getContent';\nimport { getBasePlugins, getContent } from './getContent/getContent';\nimport { resolveInterpreterLocale } from './resolveInterpreterLocale';\n\n/**\n * Dictionaries whose transform is running. Content is transformed eagerly, so a\n * dictionary reached again through a `nest()` cycle is transformed lazily\n * instead: its nodes then resolve on read, and the cycle unwinds.\n */\nconst transformsInProgress = new WeakSet<object>();\n\n/**\n * Transforms a dictionary in a single pass, applying each plugin as needed.\n *\n * @param dictionary The dictionary (or qualified dictionary group) to transform.\n * @param localeOrSelector The locale, or a selector object (`{ item }`,\n * `{ variant }`, optionally with `locale`).\n * @param plugins An array of NodeTransformer that define how to transform recognized nodes.\n * If omitted, we’ll use a default set of plugins.\n */\nexport const getDictionary = <\n const T extends Dictionary | QualifiedDictionaryGroup,\n const A extends LocalesValues | DictionarySelector = DeclaredLocales,\n>(\n dictionary: T,\n localeOrSelector?: A,\n plugins?: Plugins[]\n): DeepTransformContent<\n ResolveQualifiedDictionaryContent<T, A>,\n IInterpreterPluginState,\n ExtractSelectorLocale<A>\n> => {\n // The selector checks stay inline so bundlers can drop the whole\n // qualified-dictionary module when no dictionary declares a qualifier.\n const { locale: requestedLocale, selector } =\n process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false'\n ? parseDictionarySelector(localeOrSelector)\n : {\n locale: localeOrSelector as LocalesValues | undefined,\n selector: undefined,\n };\n const locale = resolveInterpreterLocale(requestedLocale);\n\n // The base plugins are rebuilt on every call, so they cannot identify\n // themselves — but they are fully determined by the locale, which the key\n // already carries. Only an explicitly passed array needs its own identity.\n const cacheKey = getDictionaryTransformCacheKey(\n locale,\n process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false'\n ? getDictionarySelectorCacheKey(selector)\n : '',\n plugins\n );\n\n const cached = readTransformCache<any>(dictionary, cacheKey);\n if (cached.hit) return cached.content;\n\n const appliedPlugins = plugins ?? getBasePlugins(locale);\n\n const resolved =\n process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false'\n ? resolveQualifiedDictionary(dictionary, selector)\n : (dictionary as Dictionary);\n\n const transformDictionary = (resolvedDictionary: Dictionary) => {\n const props: NodeProps = {\n dictionaryKey: resolvedDictionary.key,\n dictionaryPath: resolvedDictionary.filePath,\n keyPath: [],\n plugins: appliedPlugins,\n // Attached by the build optimization so `nest()` resolves without the\n // global registry. Undefined in unoptimized builds, where the\n // registry-based resolver is used instead.\n nestedDictionaries: resolvedDictionary.nestedDictionaries,\n eager: !transformsInProgress.has(resolvedDictionary),\n };\n\n transformsInProgress.add(resolvedDictionary);\n\n try {\n return getContent(resolvedDictionary.content, props, appliedPlugins);\n } finally {\n if (props.eager) transformsInProgress.delete(resolvedDictionary);\n }\n };\n\n if (resolved === null)\n return writeTransformCache(dictionary, cacheKey, null)!;\n\n if (Array.isArray(resolved)) {\n return writeTransformCache(\n dictionary,\n cacheKey,\n resolved.map(transformDictionary)\n ) as any;\n }\n\n return writeTransformCache(\n dictionary,\n cacheKey,\n transformDictionary(resolved)\n ) as any;\n};\n"],"mappings":";;;;;;;;;;;;AAmCA,MAAM,uCAAuB,IAAI,QAAgB;;;;;;;;;;AAWjD,MAAa,iBAIX,YACA,kBACA,YAKG;CAGH,MAAM,EAAE,QAAQ,iBAAiB,aAC/B,QAAQ,IAAI,iCAAiC,UACzCA,0EAAwB,gBAAgB,IACxC;EACE,QAAQ;EACR,UAAU;CACZ;CACN,MAAM,SAASC,sEAAyB,eAAe;CAKvD,MAAM,WAAWC,4EACf,QACA,QAAQ,IAAI,iCAAiC,UACzCC,gFAA8B,QAAQ,IACtC,IACJ,OACF;CAEA,MAAM,SAASC,gEAAwB,YAAY,QAAQ;CAC3D,IAAI,OAAO,KAAK,OAAO,OAAO;CAE9B,MAAM,iBAAiB,WAAWC,yDAAe,MAAM;CAEvD,MAAM,WACJ,QAAQ,IAAI,iCAAiC,UACzCC,6EAA2B,YAAY,QAAQ,IAC9C;CAEP,MAAM,uBAAuB,uBAAmC;EAC9D,MAAM,QAAmB;GACvB,eAAe,mBAAmB;GAClC,gBAAgB,mBAAmB;GACnC,SAAS,CAAC;GACV,SAAS;GAIT,oBAAoB,mBAAmB;GACvC,OAAO,CAAC,qBAAqB,IAAI,kBAAkB;EACrD;EAEA,qBAAqB,IAAI,kBAAkB;EAE3C,IAAI;GACF,OAAOC,qDAAW,mBAAmB,SAAS,OAAO,cAAc;EACrE,UAAU;GACR,IAAI,MAAM,OAAO,qBAAqB,OAAO,kBAAkB;EACjE;CACF;CAEA,IAAI,aAAa,MACf,OAAOC,iEAAoB,YAAY,UAAU,IAAI;CAEvD,IAAI,MAAM,QAAQ,QAAQ,GACxB,OAAOA,iEACL,YACA,UACA,SAAS,IAAI,mBAAmB,CAClC;CAGF,OAAOA,iEACL,YACA,UACA,oBAAoB,QAAQ,CAC9B;AACF"}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
2
|
const require_interpreter_dictionaryTransformCache = require('./dictionaryTransformCache.cjs');
|
|
3
3
|
const require_dictionaryManipulator_qualifiedDictionary = require('../dictionaryManipulator/qualifiedDictionary.cjs');
|
|
4
|
+
const require_interpreter_resolveInterpreterLocale = require('./resolveInterpreterLocale.cjs');
|
|
4
5
|
const require_interpreter_getDictionary = require('./getDictionary.cjs');
|
|
5
6
|
const require_dictionaryManipulator_qualifiedDynamicLoader = require('../dictionaryManipulator/qualifiedDynamicLoader.cjs');
|
|
6
7
|
const require_dictionaryManipulator_plainDynamicLoader = require('../dictionaryManipulator/plainDynamicLoader.cjs');
|
|
7
|
-
let _intlayer_config_built = require("@intlayer/config/built");
|
|
8
8
|
|
|
9
9
|
//#region src/interpreter/getDictionaryAsync.ts
|
|
10
10
|
/**
|
|
@@ -57,7 +57,7 @@ const getDictionaryAsync = async (dictionaryLoaders, key, localeOrSelector, plug
|
|
|
57
57
|
locale = parsed.locale;
|
|
58
58
|
selector = parsed.selector;
|
|
59
59
|
} else locale = localeOrSelector;
|
|
60
|
-
const localeTarget = locale ??
|
|
60
|
+
const localeTarget = locale ?? await require_interpreter_resolveInterpreterLocale.resolveInterpreterLocaleAsync();
|
|
61
61
|
const cacheKey = `${key}_${localeTarget}_${require_dictionaryManipulator_qualifiedDictionary.getDictionarySelectorCacheKey(selector)}_${require_interpreter_dictionaryTransformCache.getPluginsCacheKey(plugins)}`;
|
|
62
62
|
const cachedContent = contentPromiseCache.get(cacheKey);
|
|
63
63
|
if (cachedContent) return cachedContent;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getDictionaryAsync.cjs","names":["findPlainDynamicLoader","parseDictionarySelector","
|
|
1
|
+
{"version":3,"file":"getDictionaryAsync.cjs","names":["findPlainDynamicLoader","parseDictionarySelector","resolveInterpreterLocaleAsync","getDictionarySelectorCacheKey","getPluginsCacheKey","getDictionary","isQualifiedDynamicLoaderMap","resolveQualifiedDynamicContentAsync"],"sources":["../../../src/interpreter/getDictionaryAsync.ts"],"sourcesContent":["import type {\n Dictionary,\n DictionarySelector,\n} from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport {\n findPlainDynamicLoader,\n type PlainDynamicLoaderMap,\n} from '../dictionaryManipulator/plainDynamicLoader';\nimport {\n getDictionarySelectorCacheKey,\n parseDictionarySelector,\n} from '../dictionaryManipulator/qualifiedDictionary';\nimport {\n isQualifiedDynamicLoaderMap,\n type QualifiedDynamicLoaderMap,\n resolveQualifiedDynamicContentAsync,\n} from '../dictionaryManipulator/qualifiedDynamicLoader';\nimport { getPluginsCacheKey } from './dictionaryTransformCache';\nimport type {\n DeepTransformContent,\n IInterpreterPluginState,\n Plugins,\n} from './getContent';\nimport { getDictionary } from './getDictionary';\nimport { resolveInterpreterLocaleAsync } from './resolveInterpreterLocale';\n\n/**\n * Content already resolved for a `key + locale + selector` triple, or the\n * in-flight promise resolving it. Storing the promise itself makes the cache\n * double as an in-flight deduplication table: concurrent calls for the same\n * chunk await one single load.\n */\nconst contentPromiseCache = new Map<string, Promise<unknown>>();\n\n/**\n * Awaits the chunk a plain loader map holds for `locale` — walking the same\n * fallback chain as static mode — and transforms it.\n *\n * Returns `null` when the map emitted no chunk for the locale nor for any of\n * its fallbacks, mirroring how a missing qualified coordinate resolves.\n */\nconst loadPlainChunk = async <T extends Dictionary, Content>(\n dictionaryLoaders: PlainDynamicLoaderMap<T>,\n locale: LocalesValues,\n transform: (dictionary: Dictionary) => Content\n): Promise<Content | null> => {\n const resolved = findPlainDynamicLoader<T>(dictionaryLoaders, locale);\n\n if (!resolved) return null;\n\n return transform(await resolved.loader());\n};\n\n/**\n * Loads a single locale chunk of a dictionary and returns its transformed\n * content.\n *\n * Counterpart of {@link getDictionary} for the per-locale loader maps emitted\n * in `.intlayer/dynamic_dictionaries/`: instead of receiving a dictionary\n * holding every locale, it receives the loader map and awaits only the chunk\n * the requested locale needs.\n *\n * The loader map is either plain (`locale → loader`) or qualified\n * (`locale → qualifierId → loader`, for collections and variants). For a\n * qualified map, only the chunk(s) the selector targets are loaded.\n *\n * Build plugins rewrite every `getIntlayerAsync('key', locale)` call into a\n * `getDictionaryAsync(loaderMap, 'key', locale)` one, so applications call\n * {@link getIntlayerAsync} and never this function directly.\n *\n * @param dictionaryLoaders - Plain or qualified per-locale loader map.\n * @param key - Dictionary key, used to namespace the chunk cache.\n * @param localeOrSelector - The locale, or a selector object (`{ item }`,\n * `{ variant }`, optionally with `locale`).\n * @param plugins - Node transformers. Defaults to the base interpreter set.\n */\nexport const getDictionaryAsync = async <\n const T extends Dictionary,\n const A extends LocalesValues | DictionarySelector = DeclaredLocales,\n>(\n dictionaryLoaders: PlainDynamicLoaderMap<T> | QualifiedDynamicLoaderMap,\n key: string,\n localeOrSelector?: A,\n plugins?: Plugins[]\n): Promise<\n DeepTransformContent<\n T['content'],\n IInterpreterPluginState,\n ExtractSelectorLocale<A>\n >\n> => {\n let locale: LocalesValues | undefined;\n let selector: DictionarySelector | undefined;\n\n if (process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false') {\n const parsed = parseDictionarySelector<LocalesValues>(localeOrSelector);\n locale = parsed.locale;\n selector = parsed.selector;\n } else {\n // Selectors are unused in this project (build-time flag): the argument can\n // only be a locale, so the selector parsing is dead code.\n locale = localeOrSelector as LocalesValues | undefined;\n }\n\n const localeTarget = locale ?? (await resolveInterpreterLocaleAsync());\n\n const cacheKey = `${key}_${localeTarget}_${getDictionarySelectorCacheKey(\n selector\n )}_${getPluginsCacheKey(plugins)}`;\n\n const cachedContent = contentPromiseCache.get(cacheKey);\n if (cachedContent) return cachedContent as Promise<any>;\n\n const transform = (dictionary: Dictionary) =>\n getDictionary(dictionary, localeTarget, plugins);\n\n const contentPromise =\n process.env.INTLAYER_DICTIONARY_SELECTOR !== 'false' &&\n isQualifiedDynamicLoaderMap(dictionaryLoaders)\n ? resolveQualifiedDynamicContentAsync({\n loaderMap: dictionaryLoaders,\n key,\n locale: localeTarget,\n selector,\n transform,\n })\n : loadPlainChunk(\n dictionaryLoaders as PlainDynamicLoaderMap<T>,\n localeTarget,\n transform\n );\n\n contentPromiseCache.set(cacheKey, contentPromise);\n\n // A rejected load must not stay cached, or every later call replays the\n // same failure without ever retrying the chunk.\n contentPromise.catch(() => contentPromiseCache.delete(cacheKey));\n\n return contentPromise as Promise<any>;\n};\n"],"mappings":";;;;;;;;;;;;;;;AAqCA,MAAM,sCAAsB,IAAI,IAA8B;;;;;;;;AAS9D,MAAM,iBAAiB,OACrB,mBACA,QACA,cAC4B;CAC5B,MAAM,WAAWA,wEAA0B,mBAAmB,MAAM;CAEpE,IAAI,CAAC,UAAU,OAAO;CAEtB,OAAO,UAAU,MAAM,SAAS,OAAO,CAAC;AAC1C;;;;;;;;;;;;;;;;;;;;;;;;AAyBA,MAAa,qBAAqB,OAIhC,mBACA,KACA,kBACA,YAOG;CACH,IAAI;CACJ,IAAI;CAEJ,IAAI,QAAQ,IAAI,iCAAiC,SAAS;EACxD,MAAM,SAASC,0EAAuC,gBAAgB;EACtE,SAAS,OAAO;EAChB,WAAW,OAAO;CACpB,OAGE,SAAS;CAGX,MAAM,eAAe,UAAW,MAAMC,2EAA8B;CAEpE,MAAM,WAAW,GAAG,IAAI,GAAG,aAAa,GAAGC,gFACzC,QACF,EAAE,GAAGC,gEAAmB,OAAO;CAE/B,MAAM,gBAAgB,oBAAoB,IAAI,QAAQ;CACtD,IAAI,eAAe,OAAO;CAE1B,MAAM,aAAa,eACjBC,gDAAc,YAAY,cAAc,OAAO;CAEjD,MAAM,iBACJ,QAAQ,IAAI,iCAAiC,WAC7CC,iFAA4B,iBAAiB,IACzCC,yFAAoC;EAClC,WAAW;EACX;EACA,QAAQ;EACR;EACA;CACF,CAAC,IACD,eACE,mBACA,cACA,SACF;CAEN,oBAAoB,IAAI,UAAU,cAAc;CAIhD,eAAe,YAAY,oBAAoB,OAAO,QAAQ,CAAC;CAE/D,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getIntlayer.cjs","names":["getDictionaries","getAppLogger","colorizeKey","getDictionary"],"sources":["../../../src/interpreter/getIntlayer.ts"],"sourcesContent":["import { log } from '@intlayer/config/built';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport type { DictionarySelector } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n DictionaryKeys,\n DictionaryRegistryResult,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport type {\n DeepTransformContent,\n IInterpreterPluginState,\n Plugins,\n} from './getContent';\nimport { getDictionary } from './getDictionary';\n\n/**\n * Object.prototype methods libraries call on arbitrary values while inspecting\n * them — TanStack Router's `isPlainObject` reaches for `hasOwnProperty` on\n * every value it deep-compares. Answering them with another path proxy turns\n * a missing dictionary into `hasOwnProperty is not a function`, thrown deep\n * inside the consumer, so they are forwarded to the real implementations.\n */\nconst PROTOTYPE_METHOD_NAMES = new Set<string | symbol>([\n 'hasOwnProperty',\n 'isPrototypeOf',\n 'propertyIsEnumerable',\n 'toLocaleString',\n]);\n\n/**\n * Creates a Recursive Proxy that returns the path of the accessed key\n * stringified. This prevents the app from crashing on undefined access.\n *\n * The proxy wraps a function so that calling any leaf — `keywords.join(', ')`,\n * `title.trim()` — resolves to the path too, instead of throwing\n * `join is not a function`. Being callable also keeps structural checks\n * (`Object.prototype.toString`) from mistaking it for a plain object worth\n * walking.\n */\nconst createSafeFallback = (path = ''): any => {\n return new Proxy((() => path) as Record<string | symbol, any>, {\n get: (target, prop) => {\n if (\n prop === 'toJSON' ||\n prop === Symbol.toPrimitive ||\n prop === 'toString' ||\n prop === 'valueOf'\n ) {\n return () => path;\n }\n if (prop === 'then') {\n return undefined; // Prevent it from being treated as a Promise\n }\n if (PROTOTYPE_METHOD_NAMES.has(prop)) {\n return (Object.prototype as Record<string | symbol, any>)[prop].bind(\n target\n );\n }\n if (prop === Symbol.iterator) {\n return function* () {\n yield path;\n };\n }\n\n // Recursively build the path (e.g., \"myDictionary.home.title\")\n const nextPath = path ? `${path}.${String(prop)}` : String(prop);\n return createSafeFallback(nextPath);\n },\n });\n};\n\nconst warnedMissingDictionaries = new Set<string>();\n\n/**\n * Picks one dictionary by its key and returns its content for the given\n * locale or selector.\n *\n * The second argument is either a locale (`'fr'`) 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 * - `{ variant: { id: 'prod_abc', userId: '123' } }` — structured variant\n * - `locale` can be combined with any selector: `{ item: 2, locale: 'fr' }`\n */\nexport const getIntlayer = <\n const T extends DictionaryKeys,\n const A extends LocalesValues | DictionarySelector = DeclaredLocales,\n>(\n key: T,\n localeOrSelector?: A,\n plugins?: Plugins[]\n): DeepTransformContent<\n DictionaryRegistryResult<T, A>,\n IInterpreterPluginState,\n ExtractSelectorLocale<A>\n> => {\n const dictionaries = getDictionaries();\n const dictionary = dictionaries[key as T]!;\n\n if (!dictionary && process.env.NODE_ENV === 'development') {\n if (!warnedMissingDictionaries.has(key as string)) {\n // Log a warning instead of throwing (so developers know it's missing)\n const logger = getAppLogger({ log });\n logger(\n typeof window === 'undefined'\n ? `Dictionary ${colorizeKey(key)} was not found. Using fallback proxy.`\n : `Dictionary ${key} was not found. Using fallback proxy.`,\n {\n level: 'warn',\n }\n );\n warnedMissingDictionaries.add(key as string);\n }\n\n return createSafeFallback(key as string);\n }\n\n return getDictionary(dictionary, localeOrSelector, plugins) as any;\n};\n"],"mappings":";;;;;;;;;;;;;;AAyBA,MAAM,yCAAyB,IAAI,IAAqB;CACtD;CACA;CACA;CACA;AACF,CAAC;;;;;;;;;;;AAYD,MAAM,sBAAsB,OAAO,OAAY;CAC7C,OAAO,IAAI,aAAa,OAAuC,EAC7D,MAAM,QAAQ,SAAS;EACrB,IACE,SAAS,YACT,SAAS,OAAO,eAChB,SAAS,cACT,SAAS,WAET,aAAa;EAEf,IAAI,SAAS,QACX;EAEF,IAAI,uBAAuB,IAAI,IAAI,GACjC,OAAQ,OAAO,UAA2C,KAAK,CAAC,KAC9D,MACF;EAEF,IAAI,SAAS,OAAO,UAClB,OAAO,aAAa;GAClB,MAAM;EACR;EAIF,MAAM,WAAW,OAAO,GAAG,KAAK,GAAG,OAAO,IAAI,MAAM,OAAO,IAAI;EAC/D,OAAO,mBAAmB,QAAQ;CACpC,EACF,CAAC;AACH;AAEA,MAAM,4CAA4B,IAAI,IAAY;;;;;;;;;;;AAYlD,MAAa,eAIX,KACA,kBACA,YAKG;CAEH,MAAM,iBADeA,8CACS,CAAC,CAAC;CAEhC,IAAI,CAAC,
|
|
1
|
+
{"version":3,"file":"getIntlayer.cjs","names":["getDictionaries","getAppLogger","colorizeKey","getDictionary"],"sources":["../../../src/interpreter/getIntlayer.ts"],"sourcesContent":["import { log } from '@intlayer/config/built';\nimport { colorizeKey, getAppLogger } from '@intlayer/config/logger';\nimport { getDictionaries } from '@intlayer/dictionaries-entry';\nimport type { DictionarySelector } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n DictionaryKeys,\n DictionaryRegistryResult,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport type {\n DeepTransformContent,\n IInterpreterPluginState,\n Plugins,\n} from './getContent';\nimport { getDictionary } from './getDictionary';\n\n/**\n * Object.prototype methods libraries call on arbitrary values while inspecting\n * them — TanStack Router's `isPlainObject` reaches for `hasOwnProperty` on\n * every value it deep-compares. Answering them with another path proxy turns\n * a missing dictionary into `hasOwnProperty is not a function`, thrown deep\n * inside the consumer, so they are forwarded to the real implementations.\n */\nconst PROTOTYPE_METHOD_NAMES = new Set<string | symbol>([\n 'hasOwnProperty',\n 'isPrototypeOf',\n 'propertyIsEnumerable',\n 'toLocaleString',\n]);\n\n/**\n * Creates a Recursive Proxy that returns the path of the accessed key\n * stringified. This prevents the app from crashing on undefined access.\n *\n * The proxy wraps a function so that calling any leaf — `keywords.join(', ')`,\n * `title.trim()` — resolves to the path too, instead of throwing\n * `join is not a function`. Being callable also keeps structural checks\n * (`Object.prototype.toString`) from mistaking it for a plain object worth\n * walking.\n */\nconst createSafeFallback = (path = ''): any => {\n return new Proxy((() => path) as Record<string | symbol, any>, {\n get: (target, prop) => {\n if (\n prop === 'toJSON' ||\n prop === Symbol.toPrimitive ||\n prop === 'toString' ||\n prop === 'valueOf'\n ) {\n return () => path;\n }\n if (prop === 'then') {\n return undefined; // Prevent it from being treated as a Promise\n }\n if (PROTOTYPE_METHOD_NAMES.has(prop)) {\n return (Object.prototype as Record<string | symbol, any>)[prop].bind(\n target\n );\n }\n if (prop === Symbol.iterator) {\n return function* () {\n yield path;\n };\n }\n\n // Recursively build the path (e.g., \"myDictionary.home.title\")\n const nextPath = path ? `${path}.${String(prop)}` : String(prop);\n return createSafeFallback(nextPath);\n },\n });\n};\n\nconst warnedMissingDictionaries = new Set<string>();\n\n/**\n * Picks one dictionary by its key and returns its content for the given\n * locale or selector.\n *\n * The second argument is either a locale (`'fr'`) 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 * - `{ variant: { id: 'prod_abc', userId: '123' } }` — structured variant\n * - `locale` can be combined with any selector: `{ item: 2, locale: 'fr' }`\n */\nexport const getIntlayer = <\n const T extends DictionaryKeys,\n const A extends LocalesValues | DictionarySelector = DeclaredLocales,\n>(\n key: T,\n localeOrSelector?: A,\n plugins?: Plugins[]\n): DeepTransformContent<\n DictionaryRegistryResult<T, A>,\n IInterpreterPluginState,\n ExtractSelectorLocale<A>\n> => {\n const dictionaries = getDictionaries();\n const dictionary = dictionaries[key as T]!;\n\n if (!dictionary && process.env.NODE_ENV === 'development') {\n if (!warnedMissingDictionaries.has(key as string)) {\n // Log a warning instead of throwing (so developers know it's missing)\n const logger = getAppLogger({ log });\n logger(\n typeof window === 'undefined'\n ? `Dictionary ${colorizeKey(key)} was not found. Using fallback proxy.`\n : `Dictionary ${key} was not found. Using fallback proxy.`,\n {\n level: 'warn',\n }\n );\n warnedMissingDictionaries.add(key as string);\n }\n\n return createSafeFallback(key as string);\n }\n\n return getDictionary(dictionary, localeOrSelector, plugins) as any;\n};\n"],"mappings":";;;;;;;;;;;;;;AAyBA,MAAM,yCAAyB,IAAI,IAAqB;CACtD;CACA;CACA;CACA;AACF,CAAC;;;;;;;;;;;AAYD,MAAM,sBAAsB,OAAO,OAAY;CAC7C,OAAO,IAAI,aAAa,OAAuC,EAC7D,MAAM,QAAQ,SAAS;EACrB,IACE,SAAS,YACT,SAAS,OAAO,eAChB,SAAS,cACT,SAAS,WAET,aAAa;EAEf,IAAI,SAAS,QACX;EAEF,IAAI,uBAAuB,IAAI,IAAI,GACjC,OAAQ,OAAO,UAA2C,KAAK,CAAC,KAC9D,MACF;EAEF,IAAI,SAAS,OAAO,UAClB,OAAO,aAAa;GAClB,MAAM;EACR;EAIF,MAAM,WAAW,OAAO,GAAG,KAAK,GAAG,OAAO,IAAI,MAAM,OAAO,IAAI;EAC/D,OAAO,mBAAmB,QAAQ;CACpC,EACF,CAAC;AACH;AAEA,MAAM,4CAA4B,IAAI,IAAY;;;;;;;;;;;AAYlD,MAAa,eAIX,KACA,kBACA,YAKG;CAEH,MAAM,iBADeA,8CACS,CAAC,CAAC;CAEhC,IAAI,CAAC,uCAAuC,eAAe;EACzD,IAAI,CAAC,0BAA0B,IAAI,GAAa,GAAG;GAGjD,IADeC,sCAAa,EAAE,gCAAI,CAC7B,CAAC,CACJ,OAAO,WAAW,cACd,kBAAcC,qCAAY,GAAG,EAAE,yCAC/B,cAAc,IAAI,wCACtB,EACE,OAAO,OACT,CACF;GACA,0BAA0B,IAAI,GAAa;EAC7C;EAEA,OAAO,mBAAmB,GAAa;CACzC;CAEA,OAAOC,gDAAc,YAAY,kBAAkB,OAAO;AAC5D"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_interpreter_resolveInterpreterLocale = require('./resolveInterpreterLocale.cjs');
|
|
2
3
|
const require_interpreter_getIntlayer = require('./getIntlayer.cjs');
|
|
3
4
|
|
|
4
5
|
//#region src/interpreter/getIntlayerAsync.ts
|
|
@@ -29,7 +30,15 @@ const require_interpreter_getIntlayer = require('./getIntlayer.cjs');
|
|
|
29
30
|
* const { title } = await getIntlayerAsync('home-metadata', locale);
|
|
30
31
|
* ```
|
|
31
32
|
*/
|
|
32
|
-
const getIntlayerAsync = async (key, localeOrSelector, plugins) =>
|
|
33
|
+
const getIntlayerAsync = async (key, localeOrSelector, plugins) => {
|
|
34
|
+
const isSelector = typeof localeOrSelector === "object" && localeOrSelector !== null;
|
|
35
|
+
if (isSelector ? localeOrSelector.locale : localeOrSelector) return require_interpreter_getIntlayer.getIntlayer(key, localeOrSelector, plugins);
|
|
36
|
+
const locale = await require_interpreter_resolveInterpreterLocale.resolveInterpreterLocaleAsync();
|
|
37
|
+
return require_interpreter_getIntlayer.getIntlayer(key, isSelector ? {
|
|
38
|
+
...localeOrSelector,
|
|
39
|
+
locale
|
|
40
|
+
} : locale, plugins);
|
|
41
|
+
};
|
|
33
42
|
|
|
34
43
|
//#endregion
|
|
35
44
|
exports.getIntlayerAsync = getIntlayerAsync;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"getIntlayerAsync.cjs","names":["getIntlayer"],"sources":["../../../src/interpreter/getIntlayerAsync.ts"],"sourcesContent":["import type { DictionarySelector } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n DictionaryKeys,\n DictionaryRegistryResult,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport type {\n DeepTransformContent,\n IInterpreterPluginState,\n Plugins,\n} from './getContent';\nimport { getIntlayer } from './getIntlayer';\n\n/**\n * Picks one dictionary by its key and resolves its content for the given\n * locale or selector, loading that locale alone.\n *\n * Async counterpart of {@link getIntlayer}, meant for the places a dictionary\n * is read outside of rendering — route `head` / metadata builders, loaders,\n * server functions. Where `getIntlayer` pulls in the merged dictionary holding\n * every locale, the build plugins (`@intlayer/babel`, `@intlayer/swc`) rewrite\n * this call into `getDictionaryAsync(loaderMap, key, locale)`, pointing it at\n * the per-locale chunks in `.intlayer/dynamic_dictionaries/`, so the bundle\n * only ever carries the locale actually requested.\n *\n * Without those plugins — an unoptimized build — the call resolves through the\n * synchronous dictionary registry instead: the same content, without the\n * per-locale split.\n *\n * The second argument is either a locale (`'fr'`) 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 * - `{ variant: { id: 'prod_abc', userId: '123' } }` — structured variant\n * - `locale` can be combined with any selector: `{ item: 2, locale: 'fr' }`\n *\n * @example\n * ```ts\n * const { title } = await getIntlayerAsync('home-metadata', locale);\n * ```\n */\nexport const getIntlayerAsync = async <\n const T extends DictionaryKeys,\n const A extends LocalesValues | DictionarySelector = DeclaredLocales,\n>(\n key: T,\n localeOrSelector?: A,\n plugins?: Plugins[]\n): Promise<\n DeepTransformContent<\n DictionaryRegistryResult<T, A>,\n IInterpreterPluginState,\n ExtractSelectorLocale<A>\n >\n> => getIntlayer<T, A>(key, localeOrSelector, plugins);\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"getIntlayerAsync.cjs","names":["getIntlayer","resolveInterpreterLocaleAsync"],"sources":["../../../src/interpreter/getIntlayerAsync.ts"],"sourcesContent":["import type { DictionarySelector } from '@intlayer/types/dictionary';\nimport type {\n DeclaredLocales,\n DictionaryKeys,\n DictionaryRegistryResult,\n ExtractSelectorLocale,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport type {\n DeepTransformContent,\n IInterpreterPluginState,\n Plugins,\n} from './getContent';\nimport { getIntlayer } from './getIntlayer';\nimport { resolveInterpreterLocaleAsync } from './resolveInterpreterLocale';\n\n/**\n * Picks one dictionary by its key and resolves its content for the given\n * locale or selector, loading that locale alone.\n *\n * Async counterpart of {@link getIntlayer}, meant for the places a dictionary\n * is read outside of rendering — route `head` / metadata builders, loaders,\n * server functions. Where `getIntlayer` pulls in the merged dictionary holding\n * every locale, the build plugins (`@intlayer/babel`, `@intlayer/swc`) rewrite\n * this call into `getDictionaryAsync(loaderMap, key, locale)`, pointing it at\n * the per-locale chunks in `.intlayer/dynamic_dictionaries/`, so the bundle\n * only ever carries the locale actually requested.\n *\n * Without those plugins — an unoptimized build — the call resolves through the\n * synchronous dictionary registry instead: the same content, without the\n * per-locale split.\n *\n * The second argument is either a locale (`'fr'`) 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 * - `{ variant: { id: 'prod_abc', userId: '123' } }` — structured variant\n * - `locale` can be combined with any selector: `{ item: 2, locale: 'fr' }`\n *\n * @example\n * ```ts\n * const { title } = await getIntlayerAsync('home-metadata', locale);\n * ```\n */\nexport const getIntlayerAsync = async <\n const T extends DictionaryKeys,\n const A extends LocalesValues | DictionarySelector = DeclaredLocales,\n>(\n key: T,\n localeOrSelector?: A,\n plugins?: Plugins[]\n): Promise<\n DeepTransformContent<\n DictionaryRegistryResult<T, A>,\n IInterpreterPluginState,\n ExtractSelectorLocale<A>\n >\n> => {\n const isSelector =\n typeof localeOrSelector === 'object' && localeOrSelector !== null;\n const requestedLocale = isSelector\n ? (localeOrSelector as DictionarySelector).locale\n : (localeOrSelector as LocalesValues | undefined);\n\n if (requestedLocale) return getIntlayer<T, A>(key, localeOrSelector, plugins);\n\n // Only the async read can await request-bound locale sources\n const locale = await resolveInterpreterLocaleAsync();\n\n return getIntlayer<T, A>(\n key,\n (isSelector ? { ...localeOrSelector, locale } : locale) as A,\n plugins\n );\n};\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAa,mBAAmB,OAI9B,KACA,kBACA,YAOG;CACH,MAAM,aACJ,OAAO,qBAAqB,YAAY,qBAAqB;CAK/D,IAJwB,aACnB,iBAAwC,SACxC,kBAEgB,OAAOA,4CAAkB,KAAK,kBAAkB,OAAO;CAG5E,MAAM,SAAS,MAAMC,2EAA8B;CAEnD,OAAOD,4CACL,KACC,aAAa;EAAE,GAAG;EAAkB;CAAO,IAAI,QAChD,OACF;AACF"}
|
|
@@ -5,6 +5,7 @@ const require_interpreter_getContent_deepTransform = require('./getContent/deepT
|
|
|
5
5
|
const require_interpreter_getEnumeration = require('./getEnumeration.cjs');
|
|
6
6
|
const require_interpreter_getGender = require('./getGender.cjs');
|
|
7
7
|
const require_interpreter_getInsertion = require('./getInsertion.cjs');
|
|
8
|
+
const require_interpreter_resolveInterpreterLocale = require('./resolveInterpreterLocale.cjs');
|
|
8
9
|
const require_interpreter_getDictionary = require('./getDictionary.cjs');
|
|
9
10
|
const require_interpreter_getIntlayer = require('./getIntlayer.cjs');
|
|
10
11
|
const require_interpreter_getNesting = require('./getNesting.cjs');
|
|
@@ -51,7 +52,11 @@ exports.nestedPlugin = require_interpreter_getContent_plugins.nestedPlugin;
|
|
|
51
52
|
exports.pluralPlugin = require_interpreter_getContent_plugins.pluralPlugin;
|
|
52
53
|
exports.readTransformCache = require_interpreter_dictionaryTransformCache.readTransformCache;
|
|
53
54
|
exports.rebuildInterpolableContent = require_interpreter_interpolableNode.rebuildInterpolableContent;
|
|
55
|
+
exports.registerAmbientLocaleResolver = require_interpreter_resolveInterpreterLocale.registerAmbientLocaleResolver;
|
|
56
|
+
exports.registerAsyncAmbientLocaleResolver = require_interpreter_resolveInterpreterLocale.registerAsyncAmbientLocaleResolver;
|
|
54
57
|
exports.resolveInsertedSelector = require_interpreter_getContent_plugins.resolveInsertedSelector;
|
|
58
|
+
exports.resolveInterpreterLocale = require_interpreter_resolveInterpreterLocale.resolveInterpreterLocale;
|
|
59
|
+
exports.resolveInterpreterLocaleAsync = require_interpreter_resolveInterpreterLocale.resolveInterpreterLocaleAsync;
|
|
55
60
|
exports.selectPlugin = require_interpreter_getContent_plugins.selectPlugin;
|
|
56
61
|
exports.splitInsertionTemplate = require_interpreter_splitAndJoinInsertion.splitInsertionTemplate;
|
|
57
62
|
exports.transformInterpolableNode = require_interpreter_interpolableNode.transformInterpolableNode;
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_utils_localeStorage = require('../utils/localeStorage.cjs');
|
|
3
|
+
let _intlayer_config_built = require("@intlayer/config/built");
|
|
4
|
+
|
|
5
|
+
//#region src/interpreter/resolveInterpreterLocale.ts
|
|
6
|
+
/**
|
|
7
|
+
* Held on `globalThis` so that every copy of this module (CJS and ESM builds,
|
|
8
|
+
* duplicated installs) shares the resolvers an integration registered.
|
|
9
|
+
*/
|
|
10
|
+
const getGlobalSet = (key) => {
|
|
11
|
+
const globalScope = globalThis;
|
|
12
|
+
globalScope[key] ??= /* @__PURE__ */ new Set();
|
|
13
|
+
return globalScope[key];
|
|
14
|
+
};
|
|
15
|
+
const getAmbientLocaleResolvers = () => getGlobalSet(Symbol.for("intlayer.ambientLocaleResolvers"));
|
|
16
|
+
const getAsyncAmbientLocaleResolvers = () => getGlobalSet(Symbol.for("intlayer.asyncAmbientLocaleResolvers"));
|
|
17
|
+
/**
|
|
18
|
+
* Registers a resolver giving the locale of the current context, consulted by
|
|
19
|
+
* `getIntlayer` / `getDictionary` when the call site passes no locale.
|
|
20
|
+
*
|
|
21
|
+
* Integrations owning a request scope (server middlewares keeping the request
|
|
22
|
+
* in `AsyncLocalStorage`) register one so that a bare `getIntlayer('key')`
|
|
23
|
+
* answers in the request locale.
|
|
24
|
+
*
|
|
25
|
+
* @param resolver - Returns the current locale, or `undefined` when unknown.
|
|
26
|
+
* @returns A function unregistering the resolver.
|
|
27
|
+
*/
|
|
28
|
+
const registerAmbientLocaleResolver = (resolver) => {
|
|
29
|
+
const resolvers = getAmbientLocaleResolvers();
|
|
30
|
+
resolvers.add(resolver);
|
|
31
|
+
return () => {
|
|
32
|
+
resolvers.delete(resolver);
|
|
33
|
+
};
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* Registers an {@link AsyncAmbientLocaleResolver}, consulted only by the
|
|
37
|
+
* asynchronous reads (`getIntlayerAsync` / `getDictionaryAsync`), after the
|
|
38
|
+
* synchronous resolvers.
|
|
39
|
+
*
|
|
40
|
+
* Unlike synchronous resolvers, its errors propagate: frameworks rely on them
|
|
41
|
+
* for control flow (Next.js marks a route dynamic by throwing from
|
|
42
|
+
* `headers()`). A resolver returns `undefined` for errors meaning "unknown".
|
|
43
|
+
*
|
|
44
|
+
* @param resolver - Resolves the current locale, or `undefined` when unknown.
|
|
45
|
+
* @returns A function unregistering the resolver.
|
|
46
|
+
*/
|
|
47
|
+
const registerAsyncAmbientLocaleResolver = (resolver) => {
|
|
48
|
+
const resolvers = getAsyncAmbientLocaleResolvers();
|
|
49
|
+
resolvers.add(resolver);
|
|
50
|
+
return () => {
|
|
51
|
+
resolvers.delete(resolver);
|
|
52
|
+
};
|
|
53
|
+
};
|
|
54
|
+
/**
|
|
55
|
+
* Returns the first locale a registered resolver knows, if any.
|
|
56
|
+
*/
|
|
57
|
+
const getAmbientLocale = () => {
|
|
58
|
+
for (const resolver of getAmbientLocaleResolvers()) try {
|
|
59
|
+
const locale = resolver();
|
|
60
|
+
if (locale) return locale;
|
|
61
|
+
} catch {}
|
|
62
|
+
};
|
|
63
|
+
/**
|
|
64
|
+
* Resolves the locale a dictionary is read in when the call site gives none:
|
|
65
|
+
*
|
|
66
|
+
* 1. the locale of the current context, from the registered resolvers
|
|
67
|
+
* (e.g. the request being handled by a server middleware);
|
|
68
|
+
* 2. in the browser, the locale persisted in storage (cookie, localStorage,
|
|
69
|
+
* sessionStorage), cached until the next `setLocaleInStorageClient`;
|
|
70
|
+
* 3. the default locale.
|
|
71
|
+
*
|
|
72
|
+
* Browser storage is never read off-browser: the server shares module state
|
|
73
|
+
* across requests, and the request cookie is only known to its integration.
|
|
74
|
+
*
|
|
75
|
+
* @param locale - Locale passed by the call site, if any.
|
|
76
|
+
* @returns The resolved locale.
|
|
77
|
+
*/
|
|
78
|
+
const resolveInterpreterLocale = (locale) => locale ?? getAmbientLocale() ?? (typeof window === "undefined" ? void 0 : require_utils_localeStorage.getCachedLocaleFromStorageClient()) ?? _intlayer_config_built.internationalization.defaultLocale;
|
|
79
|
+
/**
|
|
80
|
+
* Asynchronous twin of {@link resolveInterpreterLocale}: the asynchronous
|
|
81
|
+
* resolvers are awaited after the synchronous ones, before browser storage.
|
|
82
|
+
*
|
|
83
|
+
* @param locale - Locale passed by the call site, if any.
|
|
84
|
+
* @returns The resolved locale.
|
|
85
|
+
*/
|
|
86
|
+
const resolveInterpreterLocaleAsync = async (locale) => {
|
|
87
|
+
const knownLocale = locale ?? getAmbientLocale();
|
|
88
|
+
if (knownLocale) return knownLocale;
|
|
89
|
+
for (const resolver of getAsyncAmbientLocaleResolvers()) {
|
|
90
|
+
const resolvedLocale = await resolver();
|
|
91
|
+
if (resolvedLocale) return resolvedLocale;
|
|
92
|
+
}
|
|
93
|
+
return resolveInterpreterLocale();
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
//#endregion
|
|
97
|
+
exports.registerAmbientLocaleResolver = registerAmbientLocaleResolver;
|
|
98
|
+
exports.registerAsyncAmbientLocaleResolver = registerAsyncAmbientLocaleResolver;
|
|
99
|
+
exports.resolveInterpreterLocale = resolveInterpreterLocale;
|
|
100
|
+
exports.resolveInterpreterLocaleAsync = resolveInterpreterLocaleAsync;
|
|
101
|
+
//# sourceMappingURL=resolveInterpreterLocale.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolveInterpreterLocale.cjs","names":["getCachedLocaleFromStorageClient","internationalization"],"sources":["../../../src/interpreter/resolveInterpreterLocale.ts"],"sourcesContent":["import { internationalization } from '@intlayer/config/built';\nimport type { LocalesValues } from '@intlayer/types/module_augmentation';\nimport { getCachedLocaleFromStorageClient } from '../utils/localeStorage';\n\n/**\n * Returns the locale of the context the code runs in (typically the request\n * being handled on the server), or `undefined` outside of such a context.\n */\nexport type AmbientLocaleResolver = () => LocalesValues | undefined;\n\n/**\n * Asynchronous {@link AmbientLocaleResolver}, for sources that can only be read\n * by awaiting them (e.g. Next.js `headers()` / `cookies()`).\n */\nexport type AsyncAmbientLocaleResolver = () => Promise<\n LocalesValues | undefined\n>;\n\n/**\n * Held on `globalThis` so that every copy of this module (CJS and ESM builds,\n * duplicated installs) shares the resolvers an integration registered.\n */\nconst getGlobalSet = <T>(key: symbol): Set<T> => {\n const globalScope = globalThis as typeof globalThis & {\n [key: symbol]: Set<T> | undefined;\n };\n\n globalScope[key] ??= new Set<T>();\n\n return globalScope[key];\n};\n\nconst getAmbientLocaleResolvers = (): Set<AmbientLocaleResolver> =>\n getGlobalSet(Symbol.for('intlayer.ambientLocaleResolvers'));\n\nconst getAsyncAmbientLocaleResolvers = (): Set<AsyncAmbientLocaleResolver> =>\n getGlobalSet(Symbol.for('intlayer.asyncAmbientLocaleResolvers'));\n\n/**\n * Registers a resolver giving the locale of the current context, consulted by\n * `getIntlayer` / `getDictionary` when the call site passes no locale.\n *\n * Integrations owning a request scope (server middlewares keeping the request\n * in `AsyncLocalStorage`) register one so that a bare `getIntlayer('key')`\n * answers in the request locale.\n *\n * @param resolver - Returns the current locale, or `undefined` when unknown.\n * @returns A function unregistering the resolver.\n */\nexport const registerAmbientLocaleResolver = (\n resolver: AmbientLocaleResolver\n): (() => void) => {\n const resolvers = getAmbientLocaleResolvers();\n\n resolvers.add(resolver);\n\n return () => {\n resolvers.delete(resolver);\n };\n};\n\n/**\n * Registers an {@link AsyncAmbientLocaleResolver}, consulted only by the\n * asynchronous reads (`getIntlayerAsync` / `getDictionaryAsync`), after the\n * synchronous resolvers.\n *\n * Unlike synchronous resolvers, its errors propagate: frameworks rely on them\n * for control flow (Next.js marks a route dynamic by throwing from\n * `headers()`). A resolver returns `undefined` for errors meaning \"unknown\".\n *\n * @param resolver - Resolves the current locale, or `undefined` when unknown.\n * @returns A function unregistering the resolver.\n */\nexport const registerAsyncAmbientLocaleResolver = (\n resolver: AsyncAmbientLocaleResolver\n): (() => void) => {\n const resolvers = getAsyncAmbientLocaleResolvers();\n\n resolvers.add(resolver);\n\n return () => {\n resolvers.delete(resolver);\n };\n};\n\n/**\n * Returns the first locale a registered resolver knows, if any.\n */\nconst getAmbientLocale = (): LocalesValues | undefined => {\n for (const resolver of getAmbientLocaleResolvers()) {\n try {\n const locale = resolver();\n if (locale) return locale;\n } catch {}\n }\n};\n\n/**\n * Resolves the locale a dictionary is read in when the call site gives none:\n *\n * 1. the locale of the current context, from the registered resolvers\n * (e.g. the request being handled by a server middleware);\n * 2. in the browser, the locale persisted in storage (cookie, localStorage,\n * sessionStorage), cached until the next `setLocaleInStorageClient`;\n * 3. the default locale.\n *\n * Browser storage is never read off-browser: the server shares module state\n * across requests, and the request cookie is only known to its integration.\n *\n * @param locale - Locale passed by the call site, if any.\n * @returns The resolved locale.\n */\nexport const resolveInterpreterLocale = (\n locale?: LocalesValues\n): LocalesValues =>\n locale ??\n getAmbientLocale() ??\n (typeof window === 'undefined'\n ? undefined\n : getCachedLocaleFromStorageClient()) ??\n internationalization.defaultLocale;\n\n/**\n * Asynchronous twin of {@link resolveInterpreterLocale}: the asynchronous\n * resolvers are awaited after the synchronous ones, before browser storage.\n *\n * @param locale - Locale passed by the call site, if any.\n * @returns The resolved locale.\n */\nexport const resolveInterpreterLocaleAsync = async (\n locale?: LocalesValues\n): Promise<LocalesValues> => {\n const knownLocale = locale ?? getAmbientLocale();\n if (knownLocale) return knownLocale;\n\n for (const resolver of getAsyncAmbientLocaleResolvers()) {\n const resolvedLocale = await resolver();\n if (resolvedLocale) return resolvedLocale;\n }\n\n return resolveInterpreterLocale();\n};\n"],"mappings":";;;;;;;;;AAsBA,MAAM,gBAAmB,QAAwB;CAC/C,MAAM,cAAc;CAIpB,YAAY,yBAAS,IAAI,IAAO;CAEhC,OAAO,YAAY;AACrB;AAEA,MAAM,kCACJ,aAAa,OAAO,IAAI,iCAAiC,CAAC;AAE5D,MAAM,uCACJ,aAAa,OAAO,IAAI,sCAAsC,CAAC;;;;;;;;;;;;AAajE,MAAa,iCACX,aACiB;CACjB,MAAM,YAAY,0BAA0B;CAE5C,UAAU,IAAI,QAAQ;CAEtB,aAAa;EACX,UAAU,OAAO,QAAQ;CAC3B;AACF;;;;;;;;;;;;;AAcA,MAAa,sCACX,aACiB;CACjB,MAAM,YAAY,+BAA+B;CAEjD,UAAU,IAAI,QAAQ;CAEtB,aAAa;EACX,UAAU,OAAO,QAAQ;CAC3B;AACF;;;;AAKA,MAAM,yBAAoD;CACxD,KAAK,MAAM,YAAY,0BAA0B,GAC/C,IAAI;EACF,MAAM,SAAS,SAAS;EACxB,IAAI,QAAQ,OAAO;CACrB,QAAQ,CAAC;AAEb;;;;;;;;;;;;;;;;AAiBA,MAAa,4BACX,WAEA,UACA,iBAAiB,MAChB,OAAO,WAAW,cACf,SACAA,6DAAiC,MACrCC,4CAAqB;;;;;;;;AASvB,MAAa,gCAAgC,OAC3C,WAC2B;CAC3B,MAAM,cAAc,UAAU,iBAAiB;CAC/C,IAAI,aAAa,OAAO;CAExB,KAAK,MAAM,YAAY,+BAA+B,GAAG;EACvD,MAAM,iBAAiB,MAAM,SAAS;EACtC,IAAI,gBAAgB,OAAO;CAC7B;CAEA,OAAO,yBAAyB;AAClC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
-
const require_localization_getPrefix = require('./getPrefix.cjs');
|
|
3
2
|
const require_utils_getCookie = require('../utils/getCookie.cjs');
|
|
3
|
+
const require_localization_getPrefix = require('./getPrefix.cjs');
|
|
4
4
|
const require_localization_getLocale = require('./getLocale.cjs');
|
|
5
5
|
const require_localization_getLocaleFromPath = require('./getLocaleFromPath.cjs');
|
|
6
6
|
let _intlayer_config_built = require("@intlayer/config/built");
|
|
@@ -8,6 +8,24 @@ const require_localization_localeResolver = require('./localeResolver.cjs');
|
|
|
8
8
|
const LANGUAGE_FORMAT_REGULAR_EXPRESSION = /^\s*([^\s\-;]+)(?:-([^\s;]+))?\s*(?:;(.*))?$/;
|
|
9
9
|
const DEFAULT_QUALITY_SCORE = 1;
|
|
10
10
|
/**
|
|
11
|
+
* Upper bound of the memoization caches below. Browsers send a handful of
|
|
12
|
+
* distinct `Accept-Language` values, so a small cache holds the working set.
|
|
13
|
+
*/
|
|
14
|
+
const MAX_CACHE_ENTRIES = 512;
|
|
15
|
+
/**
|
|
16
|
+
* Longer headers are resolved without being memoized, so a client sending
|
|
17
|
+
* arbitrary headers cannot grow the cache memory.
|
|
18
|
+
*/
|
|
19
|
+
const MAX_CACHEABLE_HEADER_LENGTH = 128;
|
|
20
|
+
/**
|
|
21
|
+
* Inserts an entry in a bounded cache, evicting the oldest entry when full.
|
|
22
|
+
*/
|
|
23
|
+
const setBoundedCacheEntry = (cache, key, value) => {
|
|
24
|
+
if (cache.size >= MAX_CACHE_ENTRIES) cache.delete(cache.keys().next().value);
|
|
25
|
+
cache.set(key, value);
|
|
26
|
+
return value;
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
11
29
|
* Parses a single language tag string from the Accept-Language header.
|
|
12
30
|
* Example input: "en-US;q=0.8"
|
|
13
31
|
*/
|
|
@@ -31,11 +49,26 @@ const parseLanguageTag = (tagString, index) => {
|
|
|
31
49
|
regionCode,
|
|
32
50
|
qualityScore,
|
|
33
51
|
originalIndex: index,
|
|
34
|
-
fullLocale
|
|
52
|
+
fullLocale,
|
|
53
|
+
fullLocaleLower: fullLocale.toLowerCase(),
|
|
54
|
+
languageCodeLower: languageCode.toLowerCase()
|
|
35
55
|
};
|
|
36
56
|
};
|
|
37
57
|
/**
|
|
38
|
-
*
|
|
58
|
+
* Parsed available languages. They come from the configuration, so the same
|
|
59
|
+
* few tags are parsed on every request: parse each one once.
|
|
60
|
+
*/
|
|
61
|
+
const availableLanguageCache = /* @__PURE__ */ new Map();
|
|
62
|
+
/**
|
|
63
|
+
* Parses an available language, reusing the previous parse of the same tag.
|
|
64
|
+
*/
|
|
65
|
+
const parseAvailableLanguage = (language) => {
|
|
66
|
+
const cachedLanguage = availableLanguageCache.get(language);
|
|
67
|
+
if (cachedLanguage !== void 0) return cachedLanguage;
|
|
68
|
+
return setBoundedCacheEntry(availableLanguageCache, language, parseLanguageTag(language, 0));
|
|
69
|
+
};
|
|
70
|
+
/**
|
|
71
|
+
* Parses the full Accept-Language header into a list of language preferences.
|
|
39
72
|
*/
|
|
40
73
|
const parseAcceptLanguageHeader = (headerValue) => {
|
|
41
74
|
const rawTags = headerValue.split(",");
|
|
@@ -50,19 +83,13 @@ const parseAcceptLanguageHeader = (headerValue) => {
|
|
|
50
83
|
return preferences;
|
|
51
84
|
};
|
|
52
85
|
/**
|
|
53
|
-
*
|
|
86
|
+
* Determines how well a specific available language matches a requested language preference.
|
|
54
87
|
*/
|
|
55
|
-
const calculateMatchSpecificity = (
|
|
56
|
-
const parsedProvided = parseLanguageTag(providedLanguage, providedIndex);
|
|
57
|
-
if (!parsedProvided) return null;
|
|
88
|
+
const calculateMatchSpecificity = (parsedProvided, preference, providedIndex) => {
|
|
58
89
|
let specificityScore = 0;
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const providedPrefixLower = parsedProvided.languageCode.toLowerCase();
|
|
63
|
-
if (preferenceFullLower === providedFullLower) specificityScore |= 4;
|
|
64
|
-
else if (preferencePrefixLower === providedFullLower) specificityScore |= 2;
|
|
65
|
-
else if (preferenceFullLower === providedPrefixLower) specificityScore |= 1;
|
|
90
|
+
if (preference.fullLocaleLower === parsedProvided.fullLocaleLower) specificityScore |= 4;
|
|
91
|
+
else if (preference.languageCodeLower === parsedProvided.fullLocaleLower) specificityScore |= 2;
|
|
92
|
+
else if (preference.fullLocaleLower === parsedProvided.languageCodeLower) specificityScore |= 1;
|
|
66
93
|
else if (preference.fullLocale !== "*") return null;
|
|
67
94
|
return {
|
|
68
95
|
providedIndex,
|
|
@@ -72,7 +99,7 @@ const calculateMatchSpecificity = (providedLanguage, preference, providedIndex)
|
|
|
72
99
|
};
|
|
73
100
|
};
|
|
74
101
|
/**
|
|
75
|
-
*
|
|
102
|
+
* Finds the best matching preference from the header for a specific available language.
|
|
76
103
|
*/
|
|
77
104
|
const getBestMatchForLanguage = (providedLanguage, acceptedPreferences, providedIndex) => {
|
|
78
105
|
let bestMatch = {
|
|
@@ -81,8 +108,10 @@ const getBestMatchForLanguage = (providedLanguage, acceptedPreferences, provided
|
|
|
81
108
|
specificityScore: 0,
|
|
82
109
|
providedIndex
|
|
83
110
|
};
|
|
111
|
+
const parsedProvided = parseAvailableLanguage(providedLanguage);
|
|
112
|
+
if (!parsedProvided) return bestMatch;
|
|
84
113
|
for (const preference of acceptedPreferences) {
|
|
85
|
-
const matchSpec = calculateMatchSpecificity(
|
|
114
|
+
const matchSpec = calculateMatchSpecificity(parsedProvided, preference, providedIndex);
|
|
86
115
|
if (matchSpec) {
|
|
87
116
|
if ((bestMatch.specificityScore - matchSpec.specificityScore || bestMatch.qualityScore - matchSpec.qualityScore || bestMatch.headerIndex - matchSpec.headerIndex) < 0) bestMatch = matchSpec;
|
|
88
117
|
}
|
|
@@ -90,12 +119,8 @@ const getBestMatchForLanguage = (providedLanguage, acceptedPreferences, provided
|
|
|
90
119
|
return bestMatch;
|
|
91
120
|
};
|
|
92
121
|
/**
|
|
93
|
-
* Comparator function to sort
|
|
94
|
-
*
|
|
95
|
-
* 1. Quality Score (Descending)
|
|
96
|
-
* 2. Specificity Score (Descending)
|
|
97
|
-
* 3. Order in Header (Ascending - lower index is better)
|
|
98
|
-
* 4. Order in Provided List (Ascending)
|
|
122
|
+
* Comparator function to sort MatchResults.
|
|
123
|
+
* Order: Quality (desc) -> Specificity (desc) -> Header Order (asc) -> Provided Order (asc)
|
|
99
124
|
*/
|
|
100
125
|
const compareMatchResults = (a, b) => {
|
|
101
126
|
return b.qualityScore - a.qualityScore || b.specificityScore - a.specificityScore || a.headerIndex - b.headerIndex || a.providedIndex - b.providedIndex || 0;
|
|
@@ -110,15 +135,28 @@ const getPreferredLanguages = (acceptHeader, availableLanguages) => {
|
|
|
110
135
|
return availableLanguages.map((language, index) => getBestMatchForLanguage(language, acceptedPreferences, index)).filter((result) => result.qualityScore > 0).sort(compareMatchResults).map((result) => availableLanguages[result.providedIndex]);
|
|
111
136
|
};
|
|
112
137
|
/**
|
|
138
|
+
* Detected locales, keyed by header, available locales and default locale.
|
|
139
|
+
*/
|
|
140
|
+
const detectedLocaleCache = /* @__PURE__ */ new Map();
|
|
141
|
+
/**
|
|
113
142
|
* Detects the locale from the request headers.
|
|
114
143
|
*
|
|
115
144
|
* Headers are provided by the browser/client and can be used to determine the user's preferred language.
|
|
116
145
|
* This function intersects the user's `Accept-Language` header with the application's available locales.
|
|
146
|
+
*
|
|
147
|
+
* The result is memoized, as it only depends on its arguments.
|
|
117
148
|
*/
|
|
118
149
|
const localeDetector = (headers, availableLocales, defaultLocale) => {
|
|
119
150
|
const acceptLanguageHeader = headers["accept-language"];
|
|
151
|
+
const cacheKey = (acceptLanguageHeader?.length ?? 0) <= MAX_CACHEABLE_HEADER_LENGTH ? `${acceptLanguageHeader === void 0 ? "\0" : `=${acceptLanguageHeader}`}\u0001${availableLocales?.join(",") ?? "\0"}\u0001${defaultLocale ?? "\0"}` : void 0;
|
|
152
|
+
if (cacheKey !== void 0) {
|
|
153
|
+
const cachedLocale = detectedLocaleCache.get(cacheKey);
|
|
154
|
+
if (cachedLocale !== void 0) return cachedLocale;
|
|
155
|
+
}
|
|
120
156
|
const preferredLocaleStrings = getPreferredLanguages(acceptLanguageHeader, availableLocales);
|
|
121
|
-
|
|
157
|
+
const detectedLocale = require_localization_localeResolver.localeResolver(preferredLocaleStrings, availableLocales, defaultLocale);
|
|
158
|
+
if (cacheKey !== void 0) setBoundedCacheEntry(detectedLocaleCache, cacheKey, detectedLocale);
|
|
159
|
+
return detectedLocale;
|
|
122
160
|
};
|
|
123
161
|
|
|
124
162
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"localeDetector.cjs","names":["localeResolver"],"sources":["../../../src/localization/localeDetector.ts"],"sourcesContent":["import type { Locale } from '@intlayer/types/allLocales';\nimport type { DeclaredLocales } from '@intlayer/types/module_augmentation';\nimport { localeResolver } from './localeResolver';\n\n/**\n * Constants\n */\nconst LANGUAGE_FORMAT_REGULAR_EXPRESSION =\n /^\\s*([^\\s\\-;]+)(?:-([^\\s;]+))?\\s*(?:;(.*))?$/;\nconst DEFAULT_QUALITY_SCORE = 1;\n\n/**\n * Enumeration for specificity weights.\n * Higher values indicate a more precise match.\n */\nenum SpecificityWeight {\n None = 0,\n Broad = 1, // Matches prefix (e.g., 'en' matches 'en-US')\n Prefix = 2, // Matches prefix in reverse (e.g., 'en-US' matches 'en')\n Exact = 4, // Matches exact string (e.g., 'en-US' matches 'en-US')\n}\n\n/**\n * Represents a parsed language tag from the header.\n */\ntype LanguagePreference = {\n languageCode: string;\n regionCode?: string;\n fullLocale: string;\n qualityScore: number;\n originalIndex: number;\n};\n\n/**\n * Represents the result of matching a requested language against an available language.\n */\ntype MatchResult = {\n providedIndex: number;\n headerIndex: number;\n qualityScore: number;\n specificityScore: number;\n};\n\n/**\n * Parses a single language tag string from the Accept-Language header.\n * Example input: \"en-US;q=0.8\"\n */\nconst parseLanguageTag = (\n tagString: string,\n index: number\n): LanguagePreference | null => {\n const match = LANGUAGE_FORMAT_REGULAR_EXPRESSION.exec(tagString);\n if (!match) {\n return null;\n }\n\n const languageCode = match[1] as string;\n const regionCode = match[2];\n const parameters = match[3];\n\n // Construct the full locale string (e.g., \"en-US\" or \"en\")\n const fullLocale = regionCode\n ? `${languageCode}-${regionCode}`\n : languageCode;\n\n let qualityScore = DEFAULT_QUALITY_SCORE;\n\n // Parse parameters to find the quality score (\"q\")\n if (parameters) {\n const parameterList = parameters.split(';');\n\n for (const parameter of parameterList) {\n const [key, value] = parameter.split('=');\n\n if (key === 'q') {\n qualityScore = parseFloat(value as string);\n }\n }\n }\n\n return {\n languageCode,\n regionCode,\n qualityScore,\n originalIndex: index,\n fullLocale,\n };\n};\n\n/**\n * Parses the entire Accept-Language header string into a list of preferences.\n */\nconst parseAcceptLanguageHeader = (\n headerValue: string\n): LanguagePreference[] => {\n const rawTags = headerValue.split(',');\n const preferences: LanguagePreference[] = [];\n\n for (let index = 0; index < rawTags.length; index++) {\n const tag = rawTags[index]?.trim();\n\n if (tag) {\n const parsedLanguage = parseLanguageTag(tag, index);\n\n if (parsedLanguage) {\n preferences.push(parsedLanguage);\n }\n }\n }\n\n return preferences;\n};\n\n/**\n * Calculates the specificity of a match between a provided language and a requested preference.\n */\nconst calculateMatchSpecificity = (\n providedLanguage: string,\n preference: LanguagePreference,\n providedIndex: number\n): MatchResult | null => {\n const parsedProvided = parseLanguageTag(providedLanguage, providedIndex);\n if (!parsedProvided) {\n return null;\n }\n\n let specificityScore = SpecificityWeight.None;\n\n const preferenceFullLower = preference.fullLocale.toLowerCase();\n const preferencePrefixLower = preference.languageCode.toLowerCase();\n const providedFullLower = parsedProvided.fullLocale.toLowerCase();\n const providedPrefixLower = parsedProvided.languageCode.toLowerCase();\n\n if (preferenceFullLower === providedFullLower) {\n specificityScore |= SpecificityWeight.Exact;\n } else if (preferencePrefixLower === providedFullLower) {\n specificityScore |= SpecificityWeight.Prefix;\n } else if (preferenceFullLower === providedPrefixLower) {\n specificityScore |= SpecificityWeight.Broad;\n } else if (preference.fullLocale !== '*') {\n return null;\n }\n\n return {\n providedIndex,\n headerIndex: preference.originalIndex,\n qualityScore: preference.qualityScore,\n specificityScore,\n };\n};\n\n/**\n * Determines the best match for a specific available language against the list of user accepted languages.\n */\nconst getBestMatchForLanguage = (\n providedLanguage: string,\n acceptedPreferences: LanguagePreference[],\n providedIndex: number\n): MatchResult => {\n // Initialize with a non-match priority\n let bestMatch: MatchResult = {\n headerIndex: -1,\n qualityScore: 0,\n specificityScore: 0,\n providedIndex,\n };\n\n for (const preference of acceptedPreferences) {\n const matchSpec = calculateMatchSpecificity(\n providedLanguage,\n preference,\n providedIndex\n );\n\n if (matchSpec) {\n // Compare current best match with new match\n const scoreDifference =\n bestMatch.specificityScore - matchSpec.specificityScore ||\n bestMatch.qualityScore - matchSpec.qualityScore ||\n bestMatch.headerIndex - matchSpec.headerIndex;\n\n // If the new match is better (difference < 0), update priority\n if (scoreDifference < 0) {\n bestMatch = matchSpec;\n }\n }\n }\n\n return bestMatch;\n};\n\n/**\n * Comparator function to sort language matches.\n * Sorting order:\n * 1. Quality Score (Descending)\n * 2. Specificity Score (Descending)\n * 3. Order in Header (Ascending - lower index is better)\n * 4. Order in Provided List (Ascending)\n */\nconst compareMatchResults = (a: MatchResult, b: MatchResult): number => {\n return (\n b.qualityScore - a.qualityScore ||\n b.specificityScore - a.specificityScore ||\n a.headerIndex - b.headerIndex ||\n a.providedIndex - b.providedIndex ||\n 0\n );\n};\n\n/**\n * Derives the list of preferred languages based on the Accept-Language header\n * and an optional list of available languages.\n */\nexport const getPreferredLanguages = (\n acceptHeader: string | undefined,\n availableLanguages?: string[]\n): string[] => {\n // RFC 2616 sec 14.4: no header implies '*'\n const headerValue = acceptHeader === undefined ? '*' : acceptHeader || '';\n const acceptedPreferences = parseAcceptLanguageHeader(headerValue);\n\n // If no specific languages are provided to filter against, return the header languages sorted by quality\n if (!availableLanguages) {\n return acceptedPreferences\n .filter((preference) => preference.qualityScore > 0)\n .sort((a, b) => b.qualityScore - a.qualityScore) // Simple sort by quality\n .map((preference) => preference.fullLocale);\n }\n\n // Map available languages to their match priority against the header\n const matchResults = availableLanguages.map((language, index) =>\n getBestMatchForLanguage(language, acceptedPreferences, index)\n );\n\n return matchResults\n .filter((result) => result.qualityScore > 0)\n .sort(compareMatchResults)\n .map((result) => availableLanguages[result.providedIndex]) as string[];\n};\n\n/**\n * Detects the locale from the request headers.\n *\n * Headers are provided by the browser/client and can be used to determine the user's preferred language.\n * This function intersects the user's `Accept-Language` header with the application's available locales.\n */\nexport const localeDetector = (\n headers: Record<string, string | undefined>,\n availableLocales?: Locale[],\n defaultLocale?: Locale\n): DeclaredLocales => {\n const acceptLanguageHeader = headers['accept-language'];\n\n const preferredLocaleStrings = getPreferredLanguages(\n acceptLanguageHeader,\n availableLocales as string[]\n );\n\n return localeResolver(\n preferredLocaleStrings as Locale[],\n availableLocales,\n defaultLocale\n );\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"localeDetector.cjs","names":["localeResolver"],"sources":["../../../src/localization/localeDetector.ts"],"sourcesContent":["import type { Locale } from '@intlayer/types/allLocales';\nimport type { DeclaredLocales } from '@intlayer/types/module_augmentation';\nimport { localeResolver } from './localeResolver';\n\n/**\n * Constants\n */\nconst LANGUAGE_FORMAT_REGULAR_EXPRESSION =\n /^\\s*([^\\s\\-;]+)(?:-([^\\s;]+))?\\s*(?:;(.*))?$/;\nconst DEFAULT_QUALITY_SCORE = 1;\n\n/**\n * Upper bound of the memoization caches below. Browsers send a handful of\n * distinct `Accept-Language` values, so a small cache holds the working set.\n */\nconst MAX_CACHE_ENTRIES = 512;\n\n/**\n * Longer headers are resolved without being memoized, so a client sending\n * arbitrary headers cannot grow the cache memory.\n */\nconst MAX_CACHEABLE_HEADER_LENGTH = 128;\n\n/**\n * Enumeration for specificity weights.\n * Higher values indicate a more precise match.\n */\nenum SpecificityWeight {\n None = 0,\n Broad = 1, // Matches prefix (e.g., 'en' matches 'en-US')\n Prefix = 2, // Matches prefix in reverse (e.g., 'en-US' matches 'en')\n Exact = 4, // Matches exact string (e.g., 'en-US' matches 'en-US')\n}\n\n/**\n * Represents a parsed language tag from the header.\n */\ntype LanguagePreference = {\n languageCode: string;\n regionCode?: string;\n fullLocale: string;\n /** `fullLocale` lower-cased once, for case-insensitive matching */\n fullLocaleLower: string;\n /** `languageCode` lower-cased once, for case-insensitive matching */\n languageCodeLower: string;\n qualityScore: number;\n originalIndex: number;\n};\n\n/**\n * Represents the result of matching a requested language against an available language.\n */\ntype MatchResult = {\n providedIndex: number;\n headerIndex: number;\n qualityScore: number;\n specificityScore: number;\n};\n\n/**\n * Inserts an entry in a bounded cache, evicting the oldest entry when full.\n */\nconst setBoundedCacheEntry = <Value>(\n cache: Map<string, Value>,\n key: string,\n value: Value\n): Value => {\n if (cache.size >= MAX_CACHE_ENTRIES) {\n cache.delete(cache.keys().next().value as string);\n }\n\n cache.set(key, value);\n\n return value;\n};\n\n/**\n * Parses a single language tag string from the Accept-Language header.\n * Example input: \"en-US;q=0.8\"\n */\nconst parseLanguageTag = (\n tagString: string,\n index: number\n): LanguagePreference | null => {\n const match = LANGUAGE_FORMAT_REGULAR_EXPRESSION.exec(tagString);\n if (!match) {\n return null;\n }\n\n const languageCode = match[1] as string;\n const regionCode = match[2];\n const parameters = match[3];\n\n // Construct the full locale string (e.g., \"en-US\" or \"en\")\n const fullLocale = regionCode\n ? `${languageCode}-${regionCode}`\n : languageCode;\n\n let qualityScore = DEFAULT_QUALITY_SCORE;\n\n // Parse parameters to find the quality score (\"q\")\n if (parameters) {\n const parameterList = parameters.split(';');\n\n for (const parameter of parameterList) {\n const [key, value] = parameter.split('=');\n\n if (key === 'q') {\n qualityScore = parseFloat(value as string);\n }\n }\n }\n\n return {\n languageCode,\n regionCode,\n qualityScore,\n originalIndex: index,\n fullLocale,\n fullLocaleLower: fullLocale.toLowerCase(),\n languageCodeLower: languageCode.toLowerCase(),\n };\n};\n\n/**\n * Parsed available languages. They come from the configuration, so the same\n * few tags are parsed on every request: parse each one once.\n */\nconst availableLanguageCache = new Map<string, LanguagePreference | null>();\n\n/**\n * Parses an available language, reusing the previous parse of the same tag.\n */\nconst parseAvailableLanguage = (\n language: string\n): LanguagePreference | null => {\n const cachedLanguage = availableLanguageCache.get(language);\n\n if (cachedLanguage !== undefined) return cachedLanguage;\n\n return setBoundedCacheEntry(\n availableLanguageCache,\n language,\n parseLanguageTag(language, 0)\n );\n};\n\n/**\n * Parses the full Accept-Language header into a list of language preferences.\n */\nconst parseAcceptLanguageHeader = (\n headerValue: string\n): LanguagePreference[] => {\n const rawTags = headerValue.split(',');\n const preferences: LanguagePreference[] = [];\n\n for (let index = 0; index < rawTags.length; index++) {\n const tag = rawTags[index]?.trim();\n\n if (tag) {\n const parsedLanguage = parseLanguageTag(tag, index);\n\n if (parsedLanguage) {\n preferences.push(parsedLanguage);\n }\n }\n }\n\n return preferences;\n};\n\n/**\n * Determines how well a specific available language matches a requested language preference.\n */\nconst calculateMatchSpecificity = (\n parsedProvided: LanguagePreference,\n preference: LanguagePreference,\n providedIndex: number\n): MatchResult | null => {\n let specificityScore = SpecificityWeight.None;\n\n if (preference.fullLocaleLower === parsedProvided.fullLocaleLower) {\n specificityScore |= SpecificityWeight.Exact;\n } else if (preference.languageCodeLower === parsedProvided.fullLocaleLower) {\n specificityScore |= SpecificityWeight.Prefix;\n } else if (preference.fullLocaleLower === parsedProvided.languageCodeLower) {\n specificityScore |= SpecificityWeight.Broad;\n } else if (preference.fullLocale !== '*') {\n return null;\n }\n\n return {\n providedIndex,\n headerIndex: preference.originalIndex,\n qualityScore: preference.qualityScore,\n specificityScore,\n };\n};\n\n/**\n * Finds the best matching preference from the header for a specific available language.\n */\nconst getBestMatchForLanguage = (\n providedLanguage: string,\n acceptedPreferences: LanguagePreference[],\n providedIndex: number\n): MatchResult => {\n // Initialize with a non-match priority\n let bestMatch: MatchResult = {\n headerIndex: -1,\n qualityScore: 0,\n specificityScore: 0,\n providedIndex,\n };\n\n const parsedProvided = parseAvailableLanguage(providedLanguage);\n\n if (!parsedProvided) {\n return bestMatch;\n }\n\n for (const preference of acceptedPreferences) {\n const matchSpec = calculateMatchSpecificity(\n parsedProvided,\n preference,\n providedIndex\n );\n\n if (matchSpec) {\n // Compare current best match with new match\n const scoreDifference =\n bestMatch.specificityScore - matchSpec.specificityScore ||\n bestMatch.qualityScore - matchSpec.qualityScore ||\n bestMatch.headerIndex - matchSpec.headerIndex;\n\n // If the new match is better (difference < 0), update priority\n if (scoreDifference < 0) {\n bestMatch = matchSpec;\n }\n }\n }\n\n return bestMatch;\n};\n\n/**\n * Comparator function to sort MatchResults.\n * Order: Quality (desc) -> Specificity (desc) -> Header Order (asc) -> Provided Order (asc)\n */\nconst compareMatchResults = (a: MatchResult, b: MatchResult): number => {\n return (\n b.qualityScore - a.qualityScore ||\n b.specificityScore - a.specificityScore ||\n a.headerIndex - b.headerIndex ||\n a.providedIndex - b.providedIndex ||\n 0\n );\n};\n\n/**\n * Derives the list of preferred languages based on the Accept-Language header\n * and an optional list of available languages.\n */\nexport const getPreferredLanguages = (\n acceptHeader: string | undefined,\n availableLanguages?: string[]\n): string[] => {\n // RFC 2616 sec 14.4: no header implies '*'\n const headerValue = acceptHeader === undefined ? '*' : acceptHeader || '';\n const acceptedPreferences = parseAcceptLanguageHeader(headerValue);\n\n // If no specific languages are provided to filter against, return the header languages sorted by quality\n if (!availableLanguages) {\n return acceptedPreferences\n .filter((preference) => preference.qualityScore > 0)\n .sort((a, b) => b.qualityScore - a.qualityScore) // Simple sort by quality\n .map((preference) => preference.fullLocale);\n }\n\n // Map available languages to their match priority against the header\n const matchResults = availableLanguages.map((language, index) =>\n getBestMatchForLanguage(language, acceptedPreferences, index)\n );\n\n return matchResults\n .filter((result) => result.qualityScore > 0)\n .sort(compareMatchResults)\n .map((result) => availableLanguages[result.providedIndex]) as string[];\n};\n\n/**\n * Detected locales, keyed by header, available locales and default locale.\n */\nconst detectedLocaleCache = new Map<string, DeclaredLocales>();\n\n/**\n * Detects the locale from the request headers.\n *\n * Headers are provided by the browser/client and can be used to determine the user's preferred language.\n * This function intersects the user's `Accept-Language` header with the application's available locales.\n *\n * The result is memoized, as it only depends on its arguments.\n */\nexport const localeDetector = (\n headers: Record<string, string | undefined>,\n availableLocales?: Locale[],\n defaultLocale?: Locale\n): DeclaredLocales => {\n const acceptLanguageHeader = headers['accept-language'];\n\n const isCacheable =\n (acceptLanguageHeader?.length ?? 0) <= MAX_CACHEABLE_HEADER_LENGTH;\n\n // A missing header (`*`) and an empty header resolve differently\n const cacheKey = isCacheable\n ? `${acceptLanguageHeader === undefined ? '\\u0000' : `=${acceptLanguageHeader}`}\\u0001${availableLocales?.join(',') ?? '\\u0000'}\\u0001${defaultLocale ?? '\\u0000'}`\n : undefined;\n\n if (cacheKey !== undefined) {\n const cachedLocale = detectedLocaleCache.get(cacheKey);\n\n if (cachedLocale !== undefined) return cachedLocale;\n }\n\n const preferredLocaleStrings = getPreferredLanguages(\n acceptLanguageHeader,\n availableLocales as string[]\n );\n\n const detectedLocale = localeResolver(\n preferredLocaleStrings as Locale[],\n availableLocales,\n defaultLocale\n );\n\n if (cacheKey !== undefined) {\n setBoundedCacheEntry(detectedLocaleCache, cacheKey, detectedLocale);\n }\n\n return detectedLocale;\n};\n"],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
const require_utils_localeStorage = require('../utils/localeStorage.cjs');
|
|
2
3
|
const require_localization_domainUtils = require('./domainUtils.cjs');
|
|
3
4
|
const require_localization_getPrefix = require('./getPrefix.cjs');
|
|
4
|
-
const require_utils_localeStorage = require('../utils/localeStorage.cjs');
|
|
5
5
|
const require_localization_getLocaleFromPath = require('./getLocaleFromPath.cjs');
|
|
6
6
|
|
|
7
7
|
//#region src/localization/resolveBrowserLocale.ts
|
|
@@ -18,6 +18,12 @@ const getRewriteRules = (rewrite, context = "url") => {
|
|
|
18
18
|
})) };
|
|
19
19
|
};
|
|
20
20
|
/**
|
|
21
|
+
* Compiled regexes by pattern. Patterns come from the finite set of rewrite
|
|
22
|
+
* rules, while URL localization runs per link, so compiling once pays off.
|
|
23
|
+
* The regexes have no `g` flag, so sharing them is stateless.
|
|
24
|
+
*/
|
|
25
|
+
const patternRegexCache = /* @__PURE__ */ new Map();
|
|
26
|
+
/**
|
|
21
27
|
* Converts normalized pattern to Regex.
|
|
22
28
|
* Internal syntax supports:
|
|
23
29
|
* - :param -> ([^/]+) (one segment)
|
|
@@ -26,8 +32,12 @@ const getRewriteRules = (rewrite, context = "url") => {
|
|
|
26
32
|
* - :param? -> ([^/]*) (zero or one segment)
|
|
27
33
|
*/
|
|
28
34
|
const patternToRegex = (pattern) => {
|
|
35
|
+
const cachedRegex = patternRegexCache.get(pattern);
|
|
36
|
+
if (cachedRegex) return cachedRegex;
|
|
29
37
|
const regexString = pattern.replace(/\//g, "\\/").replace(/\\\/:(?:[^/\\*+?]+)\*/g, "(?:\\/(.*))?").replace(/\\\/:(?:[^/\\*+?]+)\?/g, "(?:\\/([^\\/]+))?").replace(/:([^/\\*+?]+)\*/g, "(.*)").replace(/:([^/\\*+?]+)\?/g, "([^\\/]*)").replace(/:([^/\\*+?]+)\+/g, "(.+)").replace(/:([^/\\*+?]+)/g, "([^\\/]+)");
|
|
30
|
-
|
|
38
|
+
const regex = new RegExp(`^${regexString}$`);
|
|
39
|
+
patternRegexCache.set(pattern, regex);
|
|
40
|
+
return regex;
|
|
31
41
|
};
|
|
32
42
|
/**
|
|
33
43
|
* Replaces route parameters in a path with provided values.
|