@intlayer/i18next 8.12.5-canary.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,41 @@
1
+ import { getInterpolationValues, resolveTranslation } from "./resolveTranslation.mjs";
2
+ import { createInstance } from "./createInstance.mjs";
3
+ import { initReactI18next } from "react-i18next";
4
+
5
+ //#region src/index.ts
6
+ const i18next = createInstance();
7
+ const dir = i18next.dir.bind(i18next);
8
+ const init = i18next.init.bind(i18next);
9
+ const loadResources = i18next.loadResources.bind(i18next);
10
+ const reloadResources = i18next.reloadResources.bind(i18next);
11
+ const use = i18next.use.bind(i18next);
12
+ const changeLanguage = i18next.changeLanguage.bind(i18next);
13
+ /**
14
+ * Returns a `t()` function bound to a fixed locale and namespace.
15
+ * When `ns` matches a registered intlayer dictionary key, the returned
16
+ * function's `key` parameter is typed to only accept valid dot-notation
17
+ * paths for that dictionary.
18
+ *
19
+ * @example
20
+ * ```ts
21
+ * const tAbout = getFixedT(null, 'about');
22
+ * tAbout('counter.label'); // ✓ typed
23
+ * ```
24
+ */
25
+ const getFixedT = i18next.getFixedT.bind(i18next);
26
+ const t = i18next.t.bind(i18next);
27
+ const exists = i18next.exists.bind(i18next);
28
+ const setDefaultNamespace = i18next.setDefaultNamespace.bind(i18next);
29
+ const hasLoadedNamespace = i18next.hasLoadedNamespace.bind(i18next);
30
+ const loadNamespaces = i18next.loadNamespaces.bind(i18next);
31
+ const loadLanguages = i18next.loadLanguages.bind(i18next);
32
+ /**
33
+ * No-op shim for i18next's `keyFromSelector`. This helper is not exposed by
34
+ * every supported i18next version, so a permissive identity function is
35
+ * provided to keep `.keyFromSelector(...)` call-sites working.
36
+ */
37
+ const keyFromSelector = (selector) => selector;
38
+
39
+ //#endregion
40
+ export { changeLanguage, createInstance, i18next as default, i18next, dir, exists, getFixedT, getInterpolationValues, hasLoadedNamespace, init, initReactI18next, keyFromSelector, loadLanguages, loadNamespaces, loadResources, reloadResources, resolveTranslation, setDefaultNamespace, t, use };
41
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../src/index.ts"],"sourcesContent":["import type {\n changeLanguage as _changeLanguage,\n dir as _dir,\n exists as _exists,\n hasLoadedNamespace as _hasLoadedNamespace,\n init as _init,\n loadLanguages as _loadLanguages,\n loadNamespaces as _loadNamespaces,\n loadResources as _loadResources,\n reloadResources as _reloadResources,\n setDefaultNamespace as _setDefaultNamespace,\n t as _t,\n use as _use,\n i18n,\n} from 'i18next';\nimport { createInstance, type TypedGetFixedT } from './createInstance';\n\nexport {\n getInterpolationValues,\n type ResolveTranslationParams,\n resolveTranslation,\n} from './resolveTranslation';\n\nconst i18next: i18n = createInstance();\n\nexport { createInstance };\nexport default i18next;\nexport { i18next };\n\nexport const dir: typeof _dir = i18next.dir.bind(i18next);\nexport const init: typeof _init = i18next.init.bind(i18next);\nexport const loadResources: typeof _loadResources =\n i18next.loadResources.bind(i18next);\nexport const reloadResources: typeof _reloadResources =\n i18next.reloadResources.bind(i18next);\nexport const use: typeof _use = i18next.use.bind(i18next);\nexport const changeLanguage: typeof _changeLanguage =\n i18next.changeLanguage.bind(i18next);\n\n/**\n * Returns a `t()` function bound to a fixed locale and namespace.\n * When `ns` matches a registered intlayer dictionary key, the returned\n * function's `key` parameter is typed to only accept valid dot-notation\n * paths for that dictionary.\n *\n * @example\n * ```ts\n * const tAbout = getFixedT(null, 'about');\n * tAbout('counter.label'); // ✓ typed\n * ```\n */\nexport const getFixedT: TypedGetFixedT = i18next.getFixedT.bind(\n i18next\n) as TypedGetFixedT;\n\nexport const t: typeof _t = i18next.t.bind(i18next);\nexport const exists: typeof _exists = i18next.exists.bind(i18next);\nexport const setDefaultNamespace: typeof _setDefaultNamespace =\n i18next.setDefaultNamespace.bind(i18next);\nexport const hasLoadedNamespace: typeof _hasLoadedNamespace =\n i18next.hasLoadedNamespace.bind(i18next);\nexport const loadNamespaces: typeof _loadNamespaces =\n i18next.loadNamespaces.bind(i18next);\nexport const loadLanguages: typeof _loadLanguages =\n i18next.loadLanguages.bind(i18next);\n\n/**\n * No-op shim for i18next's `keyFromSelector`. This helper is not exposed by\n * every supported i18next version, so a permissive identity function is\n * provided to keep `.keyFromSelector(...)` call-sites working.\n */\nexport const keyFromSelector = (selector: unknown): unknown => selector;\n\nexport type { InitOptions, i18n, TFunction, TOptions } from 'i18next';\nexport { initReactI18next } from 'react-i18next';\n"],"mappings":";;;;;AAuBA,MAAM,UAAgB,eAAe;AAMrC,MAAa,MAAmB,QAAQ,IAAI,KAAK,OAAO;AACxD,MAAa,OAAqB,QAAQ,KAAK,KAAK,OAAO;AAC3D,MAAa,gBACX,QAAQ,cAAc,KAAK,OAAO;AACpC,MAAa,kBACX,QAAQ,gBAAgB,KAAK,OAAO;AACtC,MAAa,MAAmB,QAAQ,IAAI,KAAK,OAAO;AACxD,MAAa,iBACX,QAAQ,eAAe,KAAK,OAAO;;;;;;;;;;;;;AAcrC,MAAa,YAA4B,QAAQ,UAAU,KACzD,OACF;AAEA,MAAa,IAAe,QAAQ,EAAE,KAAK,OAAO;AAClD,MAAa,SAAyB,QAAQ,OAAO,KAAK,OAAO;AACjE,MAAa,sBACX,QAAQ,oBAAoB,KAAK,OAAO;AAC1C,MAAa,qBACX,QAAQ,mBAAmB,KAAK,OAAO;AACzC,MAAa,iBACX,QAAQ,eAAe,KAAK,OAAO;AACrC,MAAa,gBACX,QAAQ,cAAc,KAAK,OAAO;;;;;;AAOpC,MAAa,mBAAmB,aAA+B"}
@@ -0,0 +1,70 @@
1
+ import * as ANSIColors from "@intlayer/config/colors";
2
+ import { colorize, getAppLogger } from "@intlayer/config/logger";
3
+ import { join } from "node:path";
4
+ import { runOnce } from "@intlayer/chokidar/utils";
5
+ import { getConfiguration } from "@intlayer/config/node";
6
+ import { intlayer } from "vite-intlayer";
7
+
8
+ //#region src/plugin/index.ts
9
+ /**
10
+ * A Vite plugin for the i18next compat adapter that wraps `vite-intlayer`
11
+ * and registers a resolve alias mapping `i18next` to `@intlayer/i18next`.
12
+ *
13
+ * This lets an existing i18next codebase migrate to intlayer without
14
+ * rewriting any `import ... from 'i18next'` statements — they are
15
+ * transparently redirected to the compat adapter at build time.
16
+ *
17
+ * @example
18
+ * ```ts
19
+ * // vite.config.ts
20
+ * import i18nextVitePlugin from '@intlayer/i18next/plugin';
21
+ *
22
+ * export default defineConfig({
23
+ * plugins: [i18nextVitePlugin()],
24
+ * });
25
+ * ```
26
+ */
27
+ /**
28
+ * Caller configurations for i18next's `getFixedT` method.
29
+ *
30
+ * Tells the intlayer field-usage analyser how to extract the dictionary key
31
+ * (namespace) and optional key prefix from `i18n.getFixedT(lng, ns, prefix)`
32
+ * call sites, enabling accurate dictionary pruning for projects using
33
+ * `@intlayer/i18next`.
34
+ */
35
+ const I18NEXT_COMPAT_CALLERS = [{
36
+ callerName: "getFixedT",
37
+ importSources: ["i18next", "@intlayer/i18next"],
38
+ matchAsMethod: true,
39
+ namespace: {
40
+ from: "argument",
41
+ index: 1
42
+ },
43
+ keyPrefix: {
44
+ from: "argument",
45
+ index: 2
46
+ },
47
+ translationFunction: "return-value"
48
+ }];
49
+ const i18nextVitePlugin = (options) => {
50
+ const intlayerConfig = getConfiguration();
51
+ const appLogger = getAppLogger(intlayerConfig);
52
+ runOnce(join(intlayerConfig.system.baseDir, ".intlayer", "cache", "intlayer-issues-invitation.lock"), () => {
53
+ appLogger([colorize("Please report any issues you met on GitHub:", ANSIColors.GREY), colorize("https://github.com/aymericzip/intlayer/issues", ANSIColors.GREY_LIGHT)]);
54
+ }, { cacheTimeoutMs: 1e3 * 60 * 60 });
55
+ const basePlugins = intlayer({
56
+ ...options,
57
+ compatCallers: [...options?.compatCallers ?? [], ...I18NEXT_COMPAT_CALLERS]
58
+ });
59
+ const compatPlugin = {
60
+ name: "vite-i18next-compat-plugin",
61
+ config: () => {
62
+ return { resolve: { alias: { i18next: "@intlayer/i18next" } } };
63
+ }
64
+ };
65
+ return [...Array.isArray(basePlugins) ? basePlugins : [basePlugins], compatPlugin];
66
+ };
67
+
68
+ //#endregion
69
+ export { i18nextVitePlugin as default, i18nextVitePlugin };
70
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/plugin/index.ts"],"sourcesContent":["import { join } from 'node:path';\nimport { runOnce } from '@intlayer/chokidar/utils';\nimport * as ANSIColors from '@intlayer/config/colors';\nimport { colorize, getAppLogger } from '@intlayer/config/logger';\nimport { getConfiguration } from '@intlayer/config/node';\nimport type { PluginOption } from 'vite';\nimport { type CompatCallerConfig, intlayer } from 'vite-intlayer';\n\n/**\n * A Vite plugin for the i18next compat adapter that wraps `vite-intlayer`\n * and registers a resolve alias mapping `i18next` to `@intlayer/i18next`.\n *\n * This lets an existing i18next codebase migrate to intlayer without\n * rewriting any `import ... from 'i18next'` statements — they are\n * transparently redirected to the compat adapter at build time.\n *\n * @example\n * ```ts\n * // vite.config.ts\n * import i18nextVitePlugin from '@intlayer/i18next/plugin';\n *\n * export default defineConfig({\n * plugins: [i18nextVitePlugin()],\n * });\n * ```\n */\n\n/**\n * Caller configurations for i18next's `getFixedT` method.\n *\n * Tells the intlayer field-usage analyser how to extract the dictionary key\n * (namespace) and optional key prefix from `i18n.getFixedT(lng, ns, prefix)`\n * call sites, enabling accurate dictionary pruning for projects using\n * `@intlayer/i18next`.\n */\nconst I18NEXT_COMPAT_CALLERS: CompatCallerConfig[] = [\n {\n callerName: 'getFixedT',\n importSources: ['i18next', '@intlayer/i18next'],\n matchAsMethod: true,\n namespace: { from: 'argument', index: 1 },\n keyPrefix: { from: 'argument', index: 2 },\n translationFunction: 'return-value',\n },\n];\n\nexport const i18nextVitePlugin = (\n options?: Parameters<typeof intlayer>[0]\n): PluginOption[] => {\n const intlayerConfig = getConfiguration();\n const appLogger = getAppLogger(intlayerConfig);\n\n runOnce(\n join(\n intlayerConfig.system.baseDir,\n '.intlayer',\n 'cache',\n 'intlayer-issues-invitation.lock'\n ),\n () => {\n appLogger([\n colorize(\n 'Please report any issues you met on GitHub:',\n ANSIColors.GREY\n ),\n colorize(\n 'https://github.com/aymericzip/intlayer/issues',\n ANSIColors.GREY_LIGHT\n ),\n ]);\n },\n {\n cacheTimeoutMs: 1000 * 60 * 60, // 1 hour\n }\n );\n\n const basePlugins = intlayer({\n ...options,\n compatCallers: [\n ...(options?.compatCallers ?? []),\n ...I18NEXT_COMPAT_CALLERS,\n ],\n });\n\n const compatPlugin: PluginOption = {\n name: 'vite-i18next-compat-plugin',\n config: () => {\n return {\n resolve: {\n alias: {\n i18next: '@intlayer/i18next',\n },\n },\n };\n },\n };\n\n return [\n ...(Array.isArray(basePlugins) ? basePlugins : [basePlugins]),\n compatPlugin,\n ];\n};\n\nexport default i18nextVitePlugin;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,MAAM,yBAA+C,CACnD;CACE,YAAY;CACZ,eAAe,CAAC,WAAW,mBAAmB;CAC9C,eAAe;CACf,WAAW;EAAE,MAAM;EAAY,OAAO;CAAE;CACxC,WAAW;EAAE,MAAM;EAAY,OAAO;CAAE;CACxC,qBAAqB;AACvB,CACF;AAEA,MAAa,qBACX,YACmB;CACnB,MAAM,iBAAiB,iBAAiB;CACxC,MAAM,YAAY,aAAa,cAAc;CAE7C,QACE,KACE,eAAe,OAAO,SACtB,aACA,SACA,iCACF,SACM;EACJ,UAAU,CACR,SACE,+CACA,WAAW,IACb,GACA,SACE,iDACA,WAAW,UACb,CACF,CAAC;CACH,GACA,EACE,gBAAgB,MAAO,KAAK,GAC9B,CACF;CAEA,MAAM,cAAc,SAAS;EAC3B,GAAG;EACH,eAAe,CACb,GAAI,SAAS,iBAAiB,CAAC,GAC/B,GAAG,sBACL;CACF,CAAC;CAED,MAAM,eAA6B;EACjC,MAAM;EACN,cAAc;GACZ,OAAO,EACL,SAAS,EACP,OAAO,EACL,SAAS,oBACX,EACF,EACF;EACF;CACF;CAEA,OAAO,CACL,GAAI,MAAM,QAAQ,WAAW,IAAI,cAAc,CAAC,WAAW,GAC3D,YACF;AACF"}
@@ -0,0 +1,140 @@
1
+ import { getIntlayer } from "@intlayer/core/interpreter";
2
+ import { resolveMessage } from "@intlayer/core/messageFormat";
3
+
4
+ //#region src/resolveTranslation.ts
5
+ /**
6
+ * Shared i18next-dialect translation resolution.
7
+ *
8
+ * Implements the i18next lookup pipeline on top of intlayer dictionaries:
9
+ * namespace prefix (`ns:key`), `ns` option override, plural suffixes
10
+ * (`key_one`, `key_other`, …) via `Intl.PluralRules`, context suffixes
11
+ * (`key_male`), `$t()` nesting, `defaultValue` and `{{var}}` interpolation.
12
+ *
13
+ * Used by `@intlayer/i18next` (instance `t`) and `@intlayer/react-i18next`
14
+ * (`useTranslation`, `<Trans>`).
15
+ */
16
+ /** Option keys that are control flags, never interpolation values. */
17
+ const CONTROL_OPTION_KEYS = new Set([
18
+ "defaultValue",
19
+ "ns",
20
+ "lng",
21
+ "lngs",
22
+ "fallbackLng",
23
+ "returnObjects",
24
+ "returnDetails",
25
+ "keySeparator",
26
+ "nsSeparator",
27
+ "ordinal",
28
+ "postProcess",
29
+ "postProcessPassResolved",
30
+ "interpolation",
31
+ "replace",
32
+ "joinArrays",
33
+ "nsMode",
34
+ "keyPrefix"
35
+ ]);
36
+ /** Maximum `$t()` nesting recursion depth. */
37
+ const MAX_NESTING_DEPTH = 5;
38
+ const navigatePath = (objectValue, path, keySeparator = ".") => {
39
+ if (!path) return objectValue;
40
+ const parts = keySeparator === false ? [path] : path.split(keySeparator);
41
+ let current = objectValue;
42
+ for (const part of parts) {
43
+ if (current === null || current === void 0 || typeof current !== "object") return;
44
+ current = current[part];
45
+ }
46
+ return current;
47
+ };
48
+ /**
49
+ * Builds the ordered list of key candidates following i18next's resolution
50
+ * order: context + plural → context → plural → exact key.
51
+ */
52
+ const buildKeyCandidates = (path, locale, count, context, ordinal) => {
53
+ const candidates = [];
54
+ const pluralCategory = count === void 0 ? void 0 : new Intl.PluralRules(locale, { type: ordinal ? "ordinal" : "cardinal" }).select(count);
55
+ if (context) {
56
+ if (pluralCategory) {
57
+ if (ordinal) candidates.push(`${path}_${context}_ordinal_${pluralCategory}`);
58
+ candidates.push(`${path}_${context}_${pluralCategory}`);
59
+ if (count !== 1) candidates.push(`${path}_${context}_plural`);
60
+ }
61
+ candidates.push(`${path}_${context}`);
62
+ }
63
+ if (pluralCategory) {
64
+ if (ordinal) candidates.push(`${path}_ordinal_${pluralCategory}`);
65
+ candidates.push(`${path}_${pluralCategory}`);
66
+ if (count !== 1) candidates.push(`${path}_plural`);
67
+ }
68
+ candidates.push(path);
69
+ return candidates;
70
+ };
71
+ /** Extracts interpolation values from i18next `t()` options. */
72
+ const getInterpolationValues = (options) => {
73
+ if (!options || typeof options !== "object") return {};
74
+ const replace = options.replace;
75
+ if (replace) {
76
+ const values = { ...replace };
77
+ if (options.count !== void 0) values.count ??= options.count;
78
+ if (options.context !== void 0) values.context ??= options.context;
79
+ return values;
80
+ }
81
+ const values = {};
82
+ for (const [optionKey, optionValue] of Object.entries(options)) if (!CONTROL_OPTION_KEYS.has(optionKey)) values[optionKey] = optionValue;
83
+ return values;
84
+ };
85
+ /**
86
+ * Resolves a single translation key the i18next way against intlayer
87
+ * dictionaries.
88
+ *
89
+ * Returns the resolved value: a string in the common case, or an
90
+ * object/array when `returnObjects: true`. Returns `undefined` when the key
91
+ * cannot be resolved (caller decides between `defaultValue`, fallback keys
92
+ * and key echo).
93
+ */
94
+ const resolveTranslation = ({ locale, namespace, key, options, keySeparator = ".", nsSeparator = ":", depth = 0 }) => {
95
+ let targetNamespace = namespace;
96
+ let path = key;
97
+ if (nsSeparator !== false && key.includes(nsSeparator)) {
98
+ const separatorIndex = key.indexOf(nsSeparator);
99
+ targetNamespace = key.slice(0, separatorIndex);
100
+ path = key.slice(separatorIndex + nsSeparator.length);
101
+ } else if (options?.ns) targetNamespace = Array.isArray(options.ns) ? options.ns[0] : options.ns;
102
+ const count = typeof options?.count === "number" ? options.count : void 0;
103
+ const context = options?.context !== void 0 ? String(options.context) : void 0;
104
+ const ordinal = options?.ordinal === true;
105
+ let dictionary;
106
+ try {
107
+ dictionary = getIntlayer(targetNamespace, options?.lng ?? locale);
108
+ } catch {
109
+ return;
110
+ }
111
+ let resolvedValue;
112
+ for (const candidate of buildKeyCandidates(path, options?.lng ?? locale, count, context, ordinal)) {
113
+ const value = navigatePath(dictionary, candidate, keySeparator);
114
+ if (value !== null && value !== void 0) {
115
+ resolvedValue = value;
116
+ break;
117
+ }
118
+ }
119
+ if (resolvedValue === null || resolvedValue === void 0) return void 0;
120
+ if (options?.returnObjects && typeof resolvedValue === "object" && resolvedValue !== null) return resolvedValue;
121
+ const values = getInterpolationValues(options);
122
+ let resolved = resolveMessage(resolvedValue, values, options?.lng ?? locale, "i18next");
123
+ if (depth < MAX_NESTING_DEPTH && resolved.includes("$t(")) resolved = resolved.replace(/\$t\(\s*([^),]+?)\s*(?:,[^)]*)?\)/g, (match, nestedKey) => {
124
+ const nestedValue = resolveTranslation({
125
+ locale,
126
+ namespace: targetNamespace,
127
+ key: nestedKey.trim(),
128
+ options,
129
+ keySeparator,
130
+ nsSeparator,
131
+ depth: depth + 1
132
+ });
133
+ return typeof nestedValue === "string" ? nestedValue : match;
134
+ });
135
+ return resolved;
136
+ };
137
+
138
+ //#endregion
139
+ export { getInterpolationValues, resolveTranslation };
140
+ //# sourceMappingURL=resolveTranslation.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveTranslation.mjs","names":[],"sources":["../../src/resolveTranslation.ts"],"sourcesContent":["import { getIntlayer } from '@intlayer/core/interpreter';\nimport {\n type MessageValues,\n resolveMessage,\n} from '@intlayer/core/messageFormat';\nimport type {\n DictionaryKeys,\n LocalesValues,\n} from '@intlayer/types/module_augmentation';\nimport type { TOptions } from 'i18next';\n\n/**\n * Shared i18next-dialect translation resolution.\n *\n * Implements the i18next lookup pipeline on top of intlayer dictionaries:\n * namespace prefix (`ns:key`), `ns` option override, plural suffixes\n * (`key_one`, `key_other`, …) via `Intl.PluralRules`, context suffixes\n * (`key_male`), `$t()` nesting, `defaultValue` and `{{var}}` interpolation.\n *\n * Used by `@intlayer/i18next` (instance `t`) and `@intlayer/react-i18next`\n * (`useTranslation`, `<Trans>`).\n */\n\n/** Option keys that are control flags, never interpolation values. */\nconst CONTROL_OPTION_KEYS = new Set([\n 'defaultValue',\n 'ns',\n 'lng',\n 'lngs',\n 'fallbackLng',\n 'returnObjects',\n 'returnDetails',\n 'keySeparator',\n 'nsSeparator',\n 'ordinal',\n 'postProcess',\n 'postProcessPassResolved',\n 'interpolation',\n 'replace',\n 'joinArrays',\n 'nsMode',\n 'keyPrefix',\n]);\n\n/** Maximum `$t()` nesting recursion depth. */\nconst MAX_NESTING_DEPTH = 5;\n\nconst navigatePath = (\n objectValue: unknown,\n path: string,\n keySeparator: string | false = '.'\n): unknown => {\n if (!path) return objectValue;\n\n const parts = keySeparator === false ? [path] : path.split(keySeparator);\n\n let current: unknown = objectValue;\n for (const part of parts) {\n if (\n current === null ||\n current === undefined ||\n typeof current !== 'object'\n ) {\n return undefined;\n }\n current = (current as Record<string, unknown>)[part];\n }\n return current;\n};\n\n/**\n * Builds the ordered list of key candidates following i18next's resolution\n * order: context + plural → context → plural → exact key.\n */\nconst buildKeyCandidates = (\n path: string,\n locale: string,\n count: number | undefined,\n context: string | undefined,\n ordinal: boolean\n): string[] => {\n const candidates: string[] = [];\n\n const pluralCategory =\n count === undefined\n ? undefined\n : new Intl.PluralRules(locale, {\n type: ordinal ? 'ordinal' : 'cardinal',\n }).select(count);\n\n if (context) {\n if (pluralCategory) {\n if (ordinal) {\n candidates.push(`${path}_${context}_ordinal_${pluralCategory}`);\n }\n candidates.push(`${path}_${context}_${pluralCategory}`);\n if (count !== 1) candidates.push(`${path}_${context}_plural`);\n }\n candidates.push(`${path}_${context}`);\n }\n\n if (pluralCategory) {\n if (ordinal) candidates.push(`${path}_ordinal_${pluralCategory}`);\n candidates.push(`${path}_${pluralCategory}`);\n // Legacy i18next v3 JSON suffix\n if (count !== 1) candidates.push(`${path}_plural`);\n }\n\n candidates.push(path);\n\n return candidates;\n};\n\n/** Extracts interpolation values from i18next `t()` options. */\nexport const getInterpolationValues = (options?: TOptions): MessageValues => {\n if (!options || typeof options !== 'object') return {};\n\n const replace = (options as { replace?: MessageValues }).replace;\n if (replace) {\n // `count` and `context` are always interpolatable, even with `replace`\n const values: MessageValues = { ...replace };\n if (options.count !== undefined) values.count ??= options.count;\n if (options.context !== undefined) values.context ??= options.context;\n return values;\n }\n\n const values: MessageValues = {};\n for (const [optionKey, optionValue] of Object.entries(options)) {\n if (!CONTROL_OPTION_KEYS.has(optionKey)) values[optionKey] = optionValue;\n }\n return values;\n};\n\nexport type ResolveTranslationParams = {\n /** Locale to resolve against. */\n locale: LocalesValues;\n /** Default namespace (dictionary key) when the key has no `ns:` prefix. */\n namespace: string;\n /** The translation key, possibly `ns:path.to.key`. */\n key: string;\n /** i18next `t()` options (interpolation values, count, context, …). */\n options?: TOptions;\n /** Custom key separator (`init({ keySeparator })`). */\n keySeparator?: string | false;\n /** Custom namespace separator (`init({ nsSeparator })`). */\n nsSeparator?: string | false;\n /** Internal `$t()` nesting recursion depth. */\n depth?: number;\n};\n\n/**\n * Resolves a single translation key the i18next way against intlayer\n * dictionaries.\n *\n * Returns the resolved value: a string in the common case, or an\n * object/array when `returnObjects: true`. Returns `undefined` when the key\n * cannot be resolved (caller decides between `defaultValue`, fallback keys\n * and key echo).\n */\nexport const resolveTranslation = ({\n locale,\n namespace,\n key,\n options,\n keySeparator = '.',\n nsSeparator = ':',\n depth = 0,\n}: ResolveTranslationParams): unknown => {\n // Namespace resolution: `ns:` prefix > `ns` option > default namespace\n let targetNamespace = namespace;\n let path = key;\n\n if (nsSeparator !== false && key.includes(nsSeparator)) {\n const separatorIndex = key.indexOf(nsSeparator);\n targetNamespace = key.slice(0, separatorIndex);\n path = key.slice(separatorIndex + nsSeparator.length);\n } else if (options?.ns) {\n targetNamespace = Array.isArray(options.ns)\n ? (options.ns[0] as string)\n : (options.ns as string);\n }\n\n const count = typeof options?.count === 'number' ? options.count : undefined;\n const context =\n options?.context !== undefined ? String(options.context) : undefined;\n const ordinal = options?.ordinal === true;\n\n let dictionary: unknown;\n try {\n dictionary = getIntlayer(\n targetNamespace as DictionaryKeys,\n ((options?.lng as string) ?? locale) as LocalesValues\n );\n } catch {\n return undefined;\n }\n\n let resolvedValue: unknown;\n for (const candidate of buildKeyCandidates(\n path,\n (options?.lng as string) ?? (locale as string),\n count,\n context,\n ordinal\n )) {\n const value = navigatePath(dictionary, candidate, keySeparator);\n if (value !== null && value !== undefined) {\n resolvedValue = value;\n break;\n }\n }\n\n if (resolvedValue === null || resolvedValue === undefined) return undefined;\n\n // `returnObjects: true` — return the raw subtree\n if (\n options?.returnObjects &&\n typeof resolvedValue === 'object' &&\n resolvedValue !== null\n ) {\n return resolvedValue;\n }\n\n const values = getInterpolationValues(options);\n\n let resolved = resolveMessage(\n resolvedValue,\n values,\n ((options?.lng as string) ?? locale) as LocalesValues,\n 'i18next'\n );\n\n // `$t(key)` nesting\n if (depth < MAX_NESTING_DEPTH && resolved.includes('$t(')) {\n resolved = resolved.replace(\n /\\$t\\(\\s*([^),]+?)\\s*(?:,[^)]*)?\\)/g,\n (match, nestedKey: string) => {\n const nestedValue = resolveTranslation({\n locale,\n namespace: targetNamespace,\n key: nestedKey.trim(),\n options,\n keySeparator,\n nsSeparator,\n depth: depth + 1,\n });\n return typeof nestedValue === 'string' ? nestedValue : match;\n }\n );\n }\n\n return resolved;\n};\n"],"mappings":";;;;;;;;;;;;;;;;AAwBA,MAAM,sBAAsB,IAAI,IAAI;CAClC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;;AAGD,MAAM,oBAAoB;AAE1B,MAAM,gBACJ,aACA,MACA,eAA+B,QACnB;CACZ,IAAI,CAAC,MAAM,OAAO;CAElB,MAAM,QAAQ,iBAAiB,QAAQ,CAAC,IAAI,IAAI,KAAK,MAAM,YAAY;CAEvE,IAAI,UAAmB;CACvB,KAAK,MAAM,QAAQ,OAAO;EACxB,IACE,YAAY,QACZ,YAAY,UACZ,OAAO,YAAY,UAEnB;EAEF,UAAW,QAAoC;CACjD;CACA,OAAO;AACT;;;;;AAMA,MAAM,sBACJ,MACA,QACA,OACA,SACA,YACa;CACb,MAAM,aAAuB,CAAC;CAE9B,MAAM,iBACJ,UAAU,SACN,SACA,IAAI,KAAK,YAAY,QAAQ,EAC3B,MAAM,UAAU,YAAY,WAC9B,CAAC,CAAC,CAAC,OAAO,KAAK;CAErB,IAAI,SAAS;EACX,IAAI,gBAAgB;GAClB,IAAI,SACF,WAAW,KAAK,GAAG,KAAK,GAAG,QAAQ,WAAW,gBAAgB;GAEhE,WAAW,KAAK,GAAG,KAAK,GAAG,QAAQ,GAAG,gBAAgB;GACtD,IAAI,UAAU,GAAG,WAAW,KAAK,GAAG,KAAK,GAAG,QAAQ,QAAQ;EAC9D;EACA,WAAW,KAAK,GAAG,KAAK,GAAG,SAAS;CACtC;CAEA,IAAI,gBAAgB;EAClB,IAAI,SAAS,WAAW,KAAK,GAAG,KAAK,WAAW,gBAAgB;EAChE,WAAW,KAAK,GAAG,KAAK,GAAG,gBAAgB;EAE3C,IAAI,UAAU,GAAG,WAAW,KAAK,GAAG,KAAK,QAAQ;CACnD;CAEA,WAAW,KAAK,IAAI;CAEpB,OAAO;AACT;;AAGA,MAAa,0BAA0B,YAAsC;CAC3E,IAAI,CAAC,WAAW,OAAO,YAAY,UAAU,OAAO,CAAC;CAErD,MAAM,UAAW,QAAwC;CACzD,IAAI,SAAS;EAEX,MAAM,SAAwB,EAAE,GAAG,QAAQ;EAC3C,IAAI,QAAQ,UAAU,QAAW,OAAO,UAAU,QAAQ;EAC1D,IAAI,QAAQ,YAAY,QAAW,OAAO,YAAY,QAAQ;EAC9D,OAAO;CACT;CAEA,MAAM,SAAwB,CAAC;CAC/B,KAAK,MAAM,CAAC,WAAW,gBAAgB,OAAO,QAAQ,OAAO,GAC3D,IAAI,CAAC,oBAAoB,IAAI,SAAS,GAAG,OAAO,aAAa;CAE/D,OAAO;AACT;;;;;;;;;;AA4BA,MAAa,sBAAsB,EACjC,QACA,WACA,KACA,SACA,eAAe,KACf,cAAc,KACd,QAAQ,QAC+B;CAEvC,IAAI,kBAAkB;CACtB,IAAI,OAAO;CAEX,IAAI,gBAAgB,SAAS,IAAI,SAAS,WAAW,GAAG;EACtD,MAAM,iBAAiB,IAAI,QAAQ,WAAW;EAC9C,kBAAkB,IAAI,MAAM,GAAG,cAAc;EAC7C,OAAO,IAAI,MAAM,iBAAiB,YAAY,MAAM;CACtD,OAAO,IAAI,SAAS,IAClB,kBAAkB,MAAM,QAAQ,QAAQ,EAAE,IACrC,QAAQ,GAAG,KACX,QAAQ;CAGf,MAAM,QAAQ,OAAO,SAAS,UAAU,WAAW,QAAQ,QAAQ;CACnE,MAAM,UACJ,SAAS,YAAY,SAAY,OAAO,QAAQ,OAAO,IAAI;CAC7D,MAAM,UAAU,SAAS,YAAY;CAErC,IAAI;CACJ,IAAI;EACF,aAAa,YACX,iBACE,SAAS,OAAkB,MAC/B;CACF,QAAQ;EACN;CACF;CAEA,IAAI;CACJ,KAAK,MAAM,aAAa,mBACtB,MACC,SAAS,OAAmB,QAC7B,OACA,SACA,OACF,GAAG;EACD,MAAM,QAAQ,aAAa,YAAY,WAAW,YAAY;EAC9D,IAAI,UAAU,QAAQ,UAAU,QAAW;GACzC,gBAAgB;GAChB;EACF;CACF;CAEA,IAAI,kBAAkB,QAAQ,kBAAkB,QAAW,OAAO;CAGlE,IACE,SAAS,iBACT,OAAO,kBAAkB,YACzB,kBAAkB,MAElB,OAAO;CAGT,MAAM,SAAS,uBAAuB,OAAO;CAE7C,IAAI,WAAW,eACb,eACA,QACE,SAAS,OAAkB,QAC7B,SACF;CAGA,IAAI,QAAQ,qBAAqB,SAAS,SAAS,KAAK,GACtD,WAAW,SAAS,QAClB,uCACC,OAAO,cAAsB;EAC5B,MAAM,cAAc,mBAAmB;GACrC;GACA,WAAW;GACX,KAAK,UAAU,KAAK;GACpB;GACA;GACA;GACA,OAAO,QAAQ;EACjB,CAAC;EACD,OAAO,OAAO,gBAAgB,WAAW,cAAc;CACzD,CACF;CAGF,OAAO;AACT"}
@@ -0,0 +1,11 @@
1
+ import { ValidDotPathsFor } from "@intlayer/core/transpiler";
2
+ import { DictionaryKeys } from "@intlayer/types/module_augmentation";
3
+ import { TOptions, createInstance as createInstance$1 } from "i18next";
4
+
5
+ //#region src/createInstance.d.ts
6
+ declare const createInstance: typeof createInstance$1;
7
+ /** Typed variant of i18next's `getFixedT`, scoped to an intlayer dictionary namespace. */
8
+ type TypedGetFixedT = <N extends DictionaryKeys>(lng: string | readonly string[] | null, ns?: N | null, keyPrefix?: string) => <P extends ValidDotPathsFor<N>>(key: P, opts?: TOptions) => string;
9
+ //#endregion
10
+ export { TypedGetFixedT, createInstance };
11
+ //# sourceMappingURL=createInstance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"createInstance.d.ts","names":[],"sources":["../../src/createInstance.ts"],"mappings":";;;;;cA8Ca,cAAA,SAAuB,gBAyTnC;;KAGW,cAAA,cAA4B,cAAA,EACtC,GAAA,qCACA,EAAA,GAAK,CAAA,SACL,SAAA,yBACc,gBAAA,CAAiB,CAAA,GAAI,GAAA,EAAK,CAAA,EAAG,IAAA,GAAO,QAAA"}
@@ -0,0 +1,41 @@
1
+ import { TypedGetFixedT, createInstance } from "./createInstance.js";
2
+ import { ResolveTranslationParams, getInterpolationValues, resolveTranslation } from "./resolveTranslation.js";
3
+ import { InitOptions, TFunction, TOptions, changeLanguage as changeLanguage$1, dir as dir$1, exists as exists$1, hasLoadedNamespace as hasLoadedNamespace$1, i18n, i18n as i18n$1, init as init$1, loadLanguages as loadLanguages$1, loadNamespaces as loadNamespaces$1, loadResources as loadResources$1, reloadResources as reloadResources$1, setDefaultNamespace as setDefaultNamespace$1, t as t$1, use as use$1 } from "i18next";
4
+ import { initReactI18next } from "react-i18next";
5
+
6
+ //#region src/index.d.ts
7
+ declare const i18next: i18n$1;
8
+ declare const dir: typeof dir$1;
9
+ declare const init: typeof init$1;
10
+ declare const loadResources: typeof loadResources$1;
11
+ declare const reloadResources: typeof reloadResources$1;
12
+ declare const use: typeof use$1;
13
+ declare const changeLanguage: typeof changeLanguage$1;
14
+ /**
15
+ * Returns a `t()` function bound to a fixed locale and namespace.
16
+ * When `ns` matches a registered intlayer dictionary key, the returned
17
+ * function's `key` parameter is typed to only accept valid dot-notation
18
+ * paths for that dictionary.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * const tAbout = getFixedT(null, 'about');
23
+ * tAbout('counter.label'); // ✓ typed
24
+ * ```
25
+ */
26
+ declare const getFixedT: TypedGetFixedT;
27
+ declare const t: typeof t$1;
28
+ declare const exists: typeof exists$1;
29
+ declare const setDefaultNamespace: typeof setDefaultNamespace$1;
30
+ declare const hasLoadedNamespace: typeof hasLoadedNamespace$1;
31
+ declare const loadNamespaces: typeof loadNamespaces$1;
32
+ declare const loadLanguages: typeof loadLanguages$1;
33
+ /**
34
+ * No-op shim for i18next's `keyFromSelector`. This helper is not exposed by
35
+ * every supported i18next version, so a permissive identity function is
36
+ * provided to keep `.keyFromSelector(...)` call-sites working.
37
+ */
38
+ declare const keyFromSelector: (selector: unknown) => unknown;
39
+ //#endregion
40
+ export { type InitOptions, type ResolveTranslationParams, type TFunction, type TOptions, changeLanguage, createInstance, i18next as default, i18next, dir, exists, getFixedT, getInterpolationValues, hasLoadedNamespace, type i18n, init, initReactI18next, keyFromSelector, loadLanguages, loadNamespaces, loadResources, reloadResources, resolveTranslation, setDefaultNamespace, t, use };
41
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/index.ts"],"mappings":";;;;;;cAuBM,OAAA,EAAS,MAAuB;AAAA,cAMzB,GAAA,SAAY,KAAgC;AAAA,cAC5C,IAAA,SAAa,MAAkC;AAAA,cAC/C,aAAA,SAAsB,eACE;AAAA,cACxB,eAAA,SAAwB,iBACE;AAAA,cAC1B,GAAA,SAAY,KAAgC;AAAA,cAC5C,cAAA,SAAuB,gBACE;;;AARmB;AACzD;;;;AAA4D;AAC5D;;;;cAoBa,SAAA,EAAW,cAEL;AAAA,cAEN,CAAA,SAAU,GAA4B;AAAA,cACtC,MAAA,SAAe,QAAsC;AAAA,cACrD,mBAAA,SAA4B,qBACE;AAAA,cAC9B,kBAAA,SAA2B,oBACE;AAAA,cAC7B,cAAA,SAAuB,gBACE;AAAA,cACzB,aAAA,SAAsB,eACE;;;;AA7BoB;AACzD;cAmCa,eAAA,GAAmB,QAAiB"}
@@ -0,0 +1,8 @@
1
+ import { PluginOption } from "vite";
2
+ import { intlayer } from "vite-intlayer";
3
+
4
+ //#region src/plugin/index.d.ts
5
+ declare const i18nextVitePlugin: (options?: Parameters<typeof intlayer>[0]) => PluginOption[];
6
+ //#endregion
7
+ export { i18nextVitePlugin as default, i18nextVitePlugin };
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/plugin/index.ts"],"mappings":";;;;cA8Ca,iBAAA,GACX,OAAA,GAAU,UAAA,QAAkB,QAAA,SAC3B,YAAA"}
@@ -0,0 +1,37 @@
1
+ import { LocalesValues } from "@intlayer/types/module_augmentation";
2
+ import { TOptions } from "i18next";
3
+ import { MessageValues } from "@intlayer/core/messageFormat";
4
+
5
+ //#region src/resolveTranslation.d.ts
6
+ /** Extracts interpolation values from i18next `t()` options. */
7
+ declare const getInterpolationValues: (options?: TOptions) => MessageValues;
8
+ type ResolveTranslationParams = {
9
+ /** Locale to resolve against. */locale: LocalesValues; /** Default namespace (dictionary key) when the key has no `ns:` prefix. */
10
+ namespace: string; /** The translation key, possibly `ns:path.to.key`. */
11
+ key: string; /** i18next `t()` options (interpolation values, count, context, …). */
12
+ options?: TOptions; /** Custom key separator (`init({ keySeparator })`). */
13
+ keySeparator?: string | false; /** Custom namespace separator (`init({ nsSeparator })`). */
14
+ nsSeparator?: string | false; /** Internal `$t()` nesting recursion depth. */
15
+ depth?: number;
16
+ };
17
+ /**
18
+ * Resolves a single translation key the i18next way against intlayer
19
+ * dictionaries.
20
+ *
21
+ * Returns the resolved value: a string in the common case, or an
22
+ * object/array when `returnObjects: true`. Returns `undefined` when the key
23
+ * cannot be resolved (caller decides between `defaultValue`, fallback keys
24
+ * and key echo).
25
+ */
26
+ declare const resolveTranslation: ({
27
+ locale,
28
+ namespace,
29
+ key,
30
+ options,
31
+ keySeparator,
32
+ nsSeparator,
33
+ depth
34
+ }: ResolveTranslationParams) => unknown;
35
+ //#endregion
36
+ export { ResolveTranslationParams, getInterpolationValues, resolveTranslation };
37
+ //# sourceMappingURL=resolveTranslation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveTranslation.d.ts","names":[],"sources":["../../src/resolveTranslation.ts"],"mappings":";;;;;;cAkHa,sBAAA,GAA0B,OAAA,GAAU,QAAA,KAAW,aAiB3D;AAAA,KAEW,wBAAA;EAFX,iCAIC,MAAA,EAAQ,aAAA,EAJT;EAMC,SAAA,UAvBqC;EAyBrC,GAAA,UARD;EAUC,OAAA,GAAU,QAAQ,EARR;EAUV,YAAA;EAEA,WAAA,mBAVA;EAYA,KAAA;AAAA;;;;;;;;AAAK;AAYP;cAAa,kBAAA;EAAsB,MAAA;EAAA,SAAA;EAAA,GAAA;EAAA,OAAA;EAAA,YAAA;EAAA,WAAA;EAAA;AAAA,GAQhC,wBAAA"}
package/package.json ADDED
@@ -0,0 +1,111 @@
1
+ {
2
+ "name": "@intlayer/i18next",
3
+ "version": "8.12.5-canary.0",
4
+ "private": false,
5
+ "description": "i18next API adapter for intlayer — drop-in compatibility layer that exposes the i18next interface while using @intlayer/core under the hood",
6
+ "keywords": [
7
+ "intlayer",
8
+ "i18next",
9
+ "i18n",
10
+ "adapter",
11
+ "compatibility"
12
+ ],
13
+ "homepage": "https://intlayer.org",
14
+ "bugs": {
15
+ "url": "https://github.com/aymericzip/intlayer/issues"
16
+ },
17
+ "repository": {
18
+ "type": "git",
19
+ "url": "git+https://github.com/aymericzip/intlayer.git"
20
+ },
21
+ "license": "Apache-2.0",
22
+ "author": {
23
+ "name": "Aymeric PINEAU",
24
+ "url": "https://github.com/aymericzip"
25
+ },
26
+ "contributors": [
27
+ {
28
+ "name": "Aymeric Pineau",
29
+ "email": "ay.pineau@gmail.com",
30
+ "url": "https://github.com/aymericzip"
31
+ }
32
+ ],
33
+ "sideEffects": false,
34
+ "exports": {
35
+ ".": {
36
+ "types": "./dist/types/index.d.ts",
37
+ "require": "./dist/cjs/index.cjs",
38
+ "import": "./dist/esm/index.mjs"
39
+ },
40
+ "./plugin": {
41
+ "types": "./dist/types/plugin/index.d.ts",
42
+ "require": "./dist/cjs/plugin/index.cjs",
43
+ "import": "./dist/esm/plugin/index.mjs"
44
+ },
45
+ "./package.json": "./package.json"
46
+ },
47
+ "main": "dist/cjs/index.cjs",
48
+ "module": "dist/esm/index.mjs",
49
+ "types": "dist/types/index.d.ts",
50
+ "typesVersions": {
51
+ "*": {
52
+ "plugin": [
53
+ "./dist/types/plugin/index.d.ts"
54
+ ]
55
+ }
56
+ },
57
+ "files": [
58
+ "./dist",
59
+ "./package.json"
60
+ ],
61
+ "scripts": {
62
+ "build": "bun --bun tsdown --config tsdown.config.ts",
63
+ "build:ci": "bun --bun tsdown --config tsdown.config.ts",
64
+ "clean": "bun --bun rimraf ./dist .turbo",
65
+ "dev": "bun --bun tsdown --config tsdown.config.ts --watch",
66
+ "format": "bun --bun biome format . --check",
67
+ "format:fix": "bun --bun biome format --write .",
68
+ "lint": "bun --bun biome lint .",
69
+ "lint:fix": "bun --bun biome lint --write .",
70
+ "prepublish": "cp -f ../../README.md ./README.md",
71
+ "publish": "bun publish || true",
72
+ "publish:canary": "bun publish --access public --tag canary || true",
73
+ "publish:latest": "bun publish --access public --tag latest || true",
74
+ "test": "bun --bun vitest run",
75
+ "test:watch": "bun --bun vitest",
76
+ "typecheck": "tsc --noEmit --project tsconfig.types.json"
77
+ },
78
+ "dependencies": {
79
+ "@intlayer/chokidar": "9.0.0-canary.1",
80
+ "@intlayer/config": "9.0.0-canary.1",
81
+ "@intlayer/core": "9.0.0-canary.1",
82
+ "@intlayer/dictionaries-entry": "9.0.0-canary.1",
83
+ "@intlayer/types": "9.0.0-canary.1",
84
+ "vite-intlayer": "9.0.0-canary.1"
85
+ },
86
+ "devDependencies": {
87
+ "@types/node": "25.9.3",
88
+ "@utils/ts-config": "1.0.4",
89
+ "@utils/ts-config-types": "1.0.4",
90
+ "@utils/tsdown-config": "1.0.4",
91
+ "i18next": "26.3.1",
92
+ "rimraf": "6.1.3",
93
+ "tsdown": "0.22.2",
94
+ "typescript": "6.0.3",
95
+ "vite": "8.0.16",
96
+ "vitest": "4.1.9"
97
+ },
98
+ "peerDependencies": {
99
+ "i18next": ">=20.0.0",
100
+ "react-i18next": ">=11.0.0",
101
+ "vite": ">=4.0.0"
102
+ },
103
+ "peerDependenciesMeta": {
104
+ "vite": {
105
+ "optional": true
106
+ }
107
+ },
108
+ "engines": {
109
+ "node": ">=14.18"
110
+ }
111
+ }