@depup/i18next 26.0.4-depup.0 → 26.3.6-depup.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/.claude/settings.local.json +29 -0
- package/README.md +2 -2
- package/changes.json +1 -1
- package/dist/cjs/i18next.js +38 -19
- package/dist/esm/i18next.js +38 -19
- package/dist/esm/package.json +1 -1
- package/dist/umd/i18next.js +38 -19
- package/dist/umd/i18next.min.js +1 -1
- package/i18next.js +38 -19
- package/i18next.min.js +1 -1
- package/index.d.ts +17 -3
- package/package.json +14 -18
- package/typescript/options.d.ts +247 -111
- package/typescript/t.d.ts +125 -69
- package/dist/esm/i18next.bundled.js +0 -2237
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(gh pr *)",
|
|
5
|
+
"Read(//Users/adrai/Projects/i18next/react-i18next/**)",
|
|
6
|
+
"Bash(git add *)",
|
|
7
|
+
"Bash(git commit *)",
|
|
8
|
+
"Read(//Users/adrai/Projects/i18next/**)",
|
|
9
|
+
"Bash(gh issue *)",
|
|
10
|
+
"Read(//tmp/**)",
|
|
11
|
+
"WebFetch(domain:github.com)",
|
|
12
|
+
"Bash(grep -n '$PreservedValue\\\\b' /Users/adrai/Projects/i18next/i18next/typescript/helpers.d.ts)",
|
|
13
|
+
"Bash(npm run *)",
|
|
14
|
+
"Bash(cat > *)",
|
|
15
|
+
"Bash(python3 -c \"import json,sys; print\\(json.load\\(sys.stdin\\)['version']\\)\")",
|
|
16
|
+
"Bash(npm test *)",
|
|
17
|
+
"Bash(git pull *)",
|
|
18
|
+
"Bash(git push *)",
|
|
19
|
+
"Bash(gh api *)",
|
|
20
|
+
"Bash(curl -s \"https://raw.githubusercontent.com/GabenGar/todos/d6d77cef716fcdc5a3991f84605176f85a69cdc8/apps/frontend/src/lib/internationalization/augs.d.ts\")"
|
|
21
|
+
],
|
|
22
|
+
"additionalDirectories": [
|
|
23
|
+
"/Users/adrai/Projects/i18next/react-i18next",
|
|
24
|
+
"/tmp",
|
|
25
|
+
"/Users/adrai/Projects/i18next/i18next-gitbook/overview",
|
|
26
|
+
"/Users/adrai/Projects/i18next/i18next-icu"
|
|
27
|
+
]
|
|
28
|
+
}
|
|
29
|
+
}
|
package/README.md
CHANGED
|
@@ -13,8 +13,8 @@ npm install @depup/i18next
|
|
|
13
13
|
|
|
14
14
|
| Field | Value |
|
|
15
15
|
|-------|-------|
|
|
16
|
-
| Original | [i18next](https://www.npmjs.com/package/i18next) @ 26.
|
|
17
|
-
| Processed | 2026-
|
|
16
|
+
| Original | [i18next](https://www.npmjs.com/package/i18next) @ 26.3.6 |
|
|
17
|
+
| Processed | 2026-07-21 |
|
|
18
18
|
| Smoke test | passed |
|
|
19
19
|
| Deps updated | 0 |
|
|
20
20
|
|
package/changes.json
CHANGED
package/dist/cjs/i18next.js
CHANGED
|
@@ -93,7 +93,7 @@ const getPathWithDefaults = (data, defaultData, key) => {
|
|
|
93
93
|
const deepExtend = (target, source, overwrite) => {
|
|
94
94
|
for (const prop in source) {
|
|
95
95
|
if (prop !== '__proto__' && prop !== 'constructor') {
|
|
96
|
-
if (prop
|
|
96
|
+
if (Object.prototype.hasOwnProperty.call(target, prop)) {
|
|
97
97
|
if (isString(target[prop]) || target[prop] instanceof String || isString(source[prop]) || source[prop] instanceof String) {
|
|
98
98
|
if (overwrite) target[prop] = source[prop];
|
|
99
99
|
} else {
|
|
@@ -231,6 +231,7 @@ class Logger {
|
|
|
231
231
|
}
|
|
232
232
|
forward(args, lvl, prefix, debugOnly) {
|
|
233
233
|
if (debugOnly && !this.debug) return null;
|
|
234
|
+
args = args.map(a => isString(a) ? a.replace(/[\r\n\x00-\x1F\x7F]/g, ' ') : a);
|
|
234
235
|
if (isString(args[0])) args[0] = `${prefix}${this.prefix} ${args[0]}`;
|
|
235
236
|
return this.logger[lvl](args);
|
|
236
237
|
}
|
|
@@ -461,11 +462,15 @@ function keysFromSelector(selector, opts) {
|
|
|
461
462
|
} = selector(createProxy());
|
|
462
463
|
const keySeparator = opts?.keySeparator ?? '.';
|
|
463
464
|
const nsSeparator = opts?.nsSeparator ?? ':';
|
|
465
|
+
const strict = opts?.enableSelector === 'strict';
|
|
464
466
|
if (path.length > 1 && nsSeparator) {
|
|
465
467
|
const ns = opts?.ns;
|
|
466
|
-
const
|
|
467
|
-
if (
|
|
468
|
-
|
|
468
|
+
const nsList = strict ? Array.isArray(ns) ? ns : ns ? [ns] : null : Array.isArray(ns) ? ns : null;
|
|
469
|
+
if (nsList) {
|
|
470
|
+
const candidates = strict ? nsList : nsList.length > 1 ? nsList.slice(1) : [];
|
|
471
|
+
if (candidates.includes(path[0])) {
|
|
472
|
+
return `${path[0]}${nsSeparator}${path.slice(1).join(keySeparator)}`;
|
|
473
|
+
}
|
|
469
474
|
}
|
|
470
475
|
}
|
|
471
476
|
return path.join(keySeparator);
|
|
@@ -668,7 +673,7 @@ class Translator extends EventEmitter {
|
|
|
668
673
|
const resForMissing = missingKeyNoValueFallbackToKey && usedKey ? undefined : res;
|
|
669
674
|
const updateMissing = hasDefaultValue && defaultValue !== res && this.options.updateMissing;
|
|
670
675
|
if (usedKey || usedDefault || updateMissing) {
|
|
671
|
-
this.logger.log(updateMissing ? 'updateKey' : 'missingKey', lng, namespace, key, updateMissing ? defaultValue : res);
|
|
676
|
+
this.logger.log(updateMissing ? 'updateKey' : 'missingKey', lng, namespace, needsPluralHandling && !updateMissing ? `${key}${this.pluralResolver.getSuffix(lng, opt.count, opt)}` : key, updateMissing ? defaultValue : res);
|
|
672
677
|
if (keySeparator) {
|
|
673
678
|
const fk = this.resolve(key, {
|
|
674
679
|
...opt,
|
|
@@ -878,7 +883,10 @@ class Translator extends EventEmitter {
|
|
|
878
883
|
const useOptionsReplaceForData = options.replace && !isString(options.replace);
|
|
879
884
|
let data = useOptionsReplaceForData ? options.replace : options;
|
|
880
885
|
if (useOptionsReplaceForData && typeof options.count !== 'undefined') {
|
|
881
|
-
data
|
|
886
|
+
data = {
|
|
887
|
+
...data,
|
|
888
|
+
count: options.count
|
|
889
|
+
};
|
|
882
890
|
}
|
|
883
891
|
if (this.options.interpolation.defaultVariables) {
|
|
884
892
|
data = {
|
|
@@ -1136,8 +1144,8 @@ class Interpolator {
|
|
|
1136
1144
|
this.prefix = prefix ? regexEscape(prefix) : prefixEscaped || '{{';
|
|
1137
1145
|
this.suffix = suffix ? regexEscape(suffix) : suffixEscaped || '}}';
|
|
1138
1146
|
this.formatSeparator = formatSeparator || ',';
|
|
1139
|
-
this.unescapePrefix = unescapeSuffix ? '' : unescapePrefix
|
|
1140
|
-
this.unescapeSuffix = this.unescapePrefix ? '' : unescapeSuffix
|
|
1147
|
+
this.unescapePrefix = unescapeSuffix ? '' : unescapePrefix ? regexEscape(unescapePrefix) : '-';
|
|
1148
|
+
this.unescapeSuffix = this.unescapePrefix ? '' : unescapeSuffix ? regexEscape(unescapeSuffix) : '';
|
|
1141
1149
|
this.nestingPrefix = nestingPrefix ? regexEscape(nestingPrefix) : nestingPrefixEscaped || regexEscape('$t(');
|
|
1142
1150
|
this.nestingSuffix = nestingSuffix ? regexEscape(nestingSuffix) : nestingSuffixEscaped || regexEscape(')');
|
|
1143
1151
|
this.nestingOptionsSeparator = nestingOptionsSeparator || ',';
|
|
@@ -1184,14 +1192,17 @@ class Interpolator {
|
|
|
1184
1192
|
});
|
|
1185
1193
|
};
|
|
1186
1194
|
this.resetRegExp();
|
|
1195
|
+
if (!this.escapeValue && typeof str === 'string' && /\$t\([^)]*\{[^}]*\{\{/.test(str)) {
|
|
1196
|
+
this.logger.warn('nesting options string contains interpolated variables with escapeValue: false — ' + 'if any of those values are attacker-controlled they can inject additional ' + 'nesting options (e.g. redirect lng/ns). Sanitise untrusted input before passing ' + 'it to t(), or keep escapeValue: true.');
|
|
1197
|
+
}
|
|
1187
1198
|
const missingInterpolationHandler = options?.missingInterpolationHandler || this.options.missingInterpolationHandler;
|
|
1188
1199
|
const skipOnVariables = options?.interpolation?.skipOnVariables !== undefined ? options.interpolation.skipOnVariables : this.options.interpolation.skipOnVariables;
|
|
1189
1200
|
const todos = [{
|
|
1190
1201
|
regex: this.regexpUnescape,
|
|
1191
|
-
safeValue: val =>
|
|
1202
|
+
safeValue: val => val
|
|
1192
1203
|
}, {
|
|
1193
1204
|
regex: this.regexp,
|
|
1194
|
-
safeValue: val => this.escapeValue ?
|
|
1205
|
+
safeValue: val => this.escapeValue ? this.escape(val) : val
|
|
1195
1206
|
}];
|
|
1196
1207
|
todos.forEach(todo => {
|
|
1197
1208
|
replaces = 0;
|
|
@@ -1215,9 +1226,9 @@ class Interpolator {
|
|
|
1215
1226
|
value = makeString(value);
|
|
1216
1227
|
}
|
|
1217
1228
|
const safeValue = todo.safeValue(value);
|
|
1218
|
-
str = str.replace(match[0], safeValue);
|
|
1229
|
+
str = str.replace(match[0], regexSafe(safeValue));
|
|
1219
1230
|
if (skipOnVariables) {
|
|
1220
|
-
todo.regex.lastIndex +=
|
|
1231
|
+
todo.regex.lastIndex += safeValue.length;
|
|
1221
1232
|
todo.regex.lastIndex -= match[0].length;
|
|
1222
1233
|
} else {
|
|
1223
1234
|
todo.regex.lastIndex = 0;
|
|
@@ -1267,7 +1278,7 @@ class Interpolator {
|
|
|
1267
1278
|
clonedOptions = clonedOptions.replace && !isString(clonedOptions.replace) ? clonedOptions.replace : clonedOptions;
|
|
1268
1279
|
clonedOptions.applyPostProcessor = false;
|
|
1269
1280
|
delete clonedOptions.defaultValue;
|
|
1270
|
-
const keyEndIndex = /{.*}
|
|
1281
|
+
const keyEndIndex = /{.*}/s.test(match[1]) ? match[1].lastIndexOf('}') + 1 : match[1].indexOf(this.formatSeparator);
|
|
1271
1282
|
if (keyEndIndex !== -1) {
|
|
1272
1283
|
formatters = match[1].slice(keyEndIndex).split(this.formatSeparator).map(elem => elem.trim()).filter(Boolean);
|
|
1273
1284
|
match[1] = match[1].slice(0, keyEndIndex);
|
|
@@ -1397,10 +1408,14 @@ class Formatter {
|
|
|
1397
1408
|
format(value, format, lng, options = {}) {
|
|
1398
1409
|
if (!format) return value;
|
|
1399
1410
|
if (value == null) return value;
|
|
1400
|
-
const
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1411
|
+
const rawFormats = format.split(this.formatSeparator);
|
|
1412
|
+
const formats = [];
|
|
1413
|
+
for (let i = 0; i < rawFormats.length; i++) {
|
|
1414
|
+
let f = rawFormats[i];
|
|
1415
|
+
while (f.indexOf('(') > -1 && !f.includes(')') && i + 1 < rawFormats.length) {
|
|
1416
|
+
f = `${f}${this.formatSeparator}${rawFormats[++i]}`;
|
|
1417
|
+
}
|
|
1418
|
+
formats.push(f);
|
|
1404
1419
|
}
|
|
1405
1420
|
const result = formats.reduce((mem, f) => {
|
|
1406
1421
|
const {
|
|
@@ -1661,6 +1676,7 @@ const get = () => ({
|
|
|
1661
1676
|
nsSeparator: ':',
|
|
1662
1677
|
pluralSeparator: '_',
|
|
1663
1678
|
contextSeparator: '_',
|
|
1679
|
+
enableSelector: false,
|
|
1664
1680
|
partialBundledLanguages: false,
|
|
1665
1681
|
saveMissing: false,
|
|
1666
1682
|
updateMissing: false,
|
|
@@ -1862,7 +1878,7 @@ class I18n extends EventEmitter {
|
|
|
1862
1878
|
deferred.resolve(t);
|
|
1863
1879
|
callback(err, t);
|
|
1864
1880
|
};
|
|
1865
|
-
if (this.languages && !this.isInitialized) return finish(null, this.t.bind(this));
|
|
1881
|
+
if ((this.languages || this.isLanguageChangingTo) && !this.isInitialized) return finish(null, this.t.bind(this));
|
|
1866
1882
|
this.changeLanguage(this.options.lng, finish);
|
|
1867
1883
|
};
|
|
1868
1884
|
if (this.options.resources || !this.options.initAsync) {
|
|
@@ -2017,7 +2033,8 @@ class I18n extends EventEmitter {
|
|
|
2017
2033
|
}
|
|
2018
2034
|
return deferred;
|
|
2019
2035
|
}
|
|
2020
|
-
getFixedT(lng, ns, keyPrefix) {
|
|
2036
|
+
getFixedT(lng, ns, keyPrefix, fixedOpts) {
|
|
2037
|
+
const scopeNs = fixedOpts?.scopeNs;
|
|
2021
2038
|
const fixedT = (key, opts, ...rest) => {
|
|
2022
2039
|
let o;
|
|
2023
2040
|
if (typeof opts !== 'object') {
|
|
@@ -2029,12 +2046,14 @@ class I18n extends EventEmitter {
|
|
|
2029
2046
|
}
|
|
2030
2047
|
o.lng = o.lng || fixedT.lng;
|
|
2031
2048
|
o.lngs = o.lngs || fixedT.lngs;
|
|
2049
|
+
const explicitCallNs = o.ns !== undefined && o.ns !== null;
|
|
2032
2050
|
o.ns = o.ns || fixedT.ns;
|
|
2033
2051
|
if (o.keyPrefix !== '') o.keyPrefix = o.keyPrefix || keyPrefix || fixedT.keyPrefix;
|
|
2034
2052
|
const selectorOpts = {
|
|
2035
2053
|
...this.options,
|
|
2036
2054
|
...o
|
|
2037
2055
|
};
|
|
2056
|
+
if (Array.isArray(scopeNs) && !explicitCallNs) selectorOpts.ns = scopeNs;
|
|
2038
2057
|
if (typeof o.keyPrefix === 'function') o.keyPrefix = keysFromSelector(o.keyPrefix, selectorOpts);
|
|
2039
2058
|
const keySeparator = this.options.keySeparator || '.';
|
|
2040
2059
|
let resultKey;
|
package/dist/esm/i18next.js
CHANGED
|
@@ -91,7 +91,7 @@ const getPathWithDefaults = (data, defaultData, key) => {
|
|
|
91
91
|
const deepExtend = (target, source, overwrite) => {
|
|
92
92
|
for (const prop in source) {
|
|
93
93
|
if (prop !== '__proto__' && prop !== 'constructor') {
|
|
94
|
-
if (prop
|
|
94
|
+
if (Object.prototype.hasOwnProperty.call(target, prop)) {
|
|
95
95
|
if (isString(target[prop]) || target[prop] instanceof String || isString(source[prop]) || source[prop] instanceof String) {
|
|
96
96
|
if (overwrite) target[prop] = source[prop];
|
|
97
97
|
} else {
|
|
@@ -229,6 +229,7 @@ class Logger {
|
|
|
229
229
|
}
|
|
230
230
|
forward(args, lvl, prefix, debugOnly) {
|
|
231
231
|
if (debugOnly && !this.debug) return null;
|
|
232
|
+
args = args.map(a => isString(a) ? a.replace(/[\r\n\x00-\x1F\x7F]/g, ' ') : a);
|
|
232
233
|
if (isString(args[0])) args[0] = `${prefix}${this.prefix} ${args[0]}`;
|
|
233
234
|
return this.logger[lvl](args);
|
|
234
235
|
}
|
|
@@ -459,11 +460,15 @@ function keysFromSelector(selector, opts) {
|
|
|
459
460
|
} = selector(createProxy());
|
|
460
461
|
const keySeparator = opts?.keySeparator ?? '.';
|
|
461
462
|
const nsSeparator = opts?.nsSeparator ?? ':';
|
|
463
|
+
const strict = opts?.enableSelector === 'strict';
|
|
462
464
|
if (path.length > 1 && nsSeparator) {
|
|
463
465
|
const ns = opts?.ns;
|
|
464
|
-
const
|
|
465
|
-
if (
|
|
466
|
-
|
|
466
|
+
const nsList = strict ? Array.isArray(ns) ? ns : ns ? [ns] : null : Array.isArray(ns) ? ns : null;
|
|
467
|
+
if (nsList) {
|
|
468
|
+
const candidates = strict ? nsList : nsList.length > 1 ? nsList.slice(1) : [];
|
|
469
|
+
if (candidates.includes(path[0])) {
|
|
470
|
+
return `${path[0]}${nsSeparator}${path.slice(1).join(keySeparator)}`;
|
|
471
|
+
}
|
|
467
472
|
}
|
|
468
473
|
}
|
|
469
474
|
return path.join(keySeparator);
|
|
@@ -666,7 +671,7 @@ class Translator extends EventEmitter {
|
|
|
666
671
|
const resForMissing = missingKeyNoValueFallbackToKey && usedKey ? undefined : res;
|
|
667
672
|
const updateMissing = hasDefaultValue && defaultValue !== res && this.options.updateMissing;
|
|
668
673
|
if (usedKey || usedDefault || updateMissing) {
|
|
669
|
-
this.logger.log(updateMissing ? 'updateKey' : 'missingKey', lng, namespace, key, updateMissing ? defaultValue : res);
|
|
674
|
+
this.logger.log(updateMissing ? 'updateKey' : 'missingKey', lng, namespace, needsPluralHandling && !updateMissing ? `${key}${this.pluralResolver.getSuffix(lng, opt.count, opt)}` : key, updateMissing ? defaultValue : res);
|
|
670
675
|
if (keySeparator) {
|
|
671
676
|
const fk = this.resolve(key, {
|
|
672
677
|
...opt,
|
|
@@ -876,7 +881,10 @@ class Translator extends EventEmitter {
|
|
|
876
881
|
const useOptionsReplaceForData = options.replace && !isString(options.replace);
|
|
877
882
|
let data = useOptionsReplaceForData ? options.replace : options;
|
|
878
883
|
if (useOptionsReplaceForData && typeof options.count !== 'undefined') {
|
|
879
|
-
data
|
|
884
|
+
data = {
|
|
885
|
+
...data,
|
|
886
|
+
count: options.count
|
|
887
|
+
};
|
|
880
888
|
}
|
|
881
889
|
if (this.options.interpolation.defaultVariables) {
|
|
882
890
|
data = {
|
|
@@ -1134,8 +1142,8 @@ class Interpolator {
|
|
|
1134
1142
|
this.prefix = prefix ? regexEscape(prefix) : prefixEscaped || '{{';
|
|
1135
1143
|
this.suffix = suffix ? regexEscape(suffix) : suffixEscaped || '}}';
|
|
1136
1144
|
this.formatSeparator = formatSeparator || ',';
|
|
1137
|
-
this.unescapePrefix = unescapeSuffix ? '' : unescapePrefix
|
|
1138
|
-
this.unescapeSuffix = this.unescapePrefix ? '' : unescapeSuffix
|
|
1145
|
+
this.unescapePrefix = unescapeSuffix ? '' : unescapePrefix ? regexEscape(unescapePrefix) : '-';
|
|
1146
|
+
this.unescapeSuffix = this.unescapePrefix ? '' : unescapeSuffix ? regexEscape(unescapeSuffix) : '';
|
|
1139
1147
|
this.nestingPrefix = nestingPrefix ? regexEscape(nestingPrefix) : nestingPrefixEscaped || regexEscape('$t(');
|
|
1140
1148
|
this.nestingSuffix = nestingSuffix ? regexEscape(nestingSuffix) : nestingSuffixEscaped || regexEscape(')');
|
|
1141
1149
|
this.nestingOptionsSeparator = nestingOptionsSeparator || ',';
|
|
@@ -1182,14 +1190,17 @@ class Interpolator {
|
|
|
1182
1190
|
});
|
|
1183
1191
|
};
|
|
1184
1192
|
this.resetRegExp();
|
|
1193
|
+
if (!this.escapeValue && typeof str === 'string' && /\$t\([^)]*\{[^}]*\{\{/.test(str)) {
|
|
1194
|
+
this.logger.warn('nesting options string contains interpolated variables with escapeValue: false — ' + 'if any of those values are attacker-controlled they can inject additional ' + 'nesting options (e.g. redirect lng/ns). Sanitise untrusted input before passing ' + 'it to t(), or keep escapeValue: true.');
|
|
1195
|
+
}
|
|
1185
1196
|
const missingInterpolationHandler = options?.missingInterpolationHandler || this.options.missingInterpolationHandler;
|
|
1186
1197
|
const skipOnVariables = options?.interpolation?.skipOnVariables !== undefined ? options.interpolation.skipOnVariables : this.options.interpolation.skipOnVariables;
|
|
1187
1198
|
const todos = [{
|
|
1188
1199
|
regex: this.regexpUnescape,
|
|
1189
|
-
safeValue: val =>
|
|
1200
|
+
safeValue: val => val
|
|
1190
1201
|
}, {
|
|
1191
1202
|
regex: this.regexp,
|
|
1192
|
-
safeValue: val => this.escapeValue ?
|
|
1203
|
+
safeValue: val => this.escapeValue ? this.escape(val) : val
|
|
1193
1204
|
}];
|
|
1194
1205
|
todos.forEach(todo => {
|
|
1195
1206
|
replaces = 0;
|
|
@@ -1213,9 +1224,9 @@ class Interpolator {
|
|
|
1213
1224
|
value = makeString(value);
|
|
1214
1225
|
}
|
|
1215
1226
|
const safeValue = todo.safeValue(value);
|
|
1216
|
-
str = str.replace(match[0], safeValue);
|
|
1227
|
+
str = str.replace(match[0], regexSafe(safeValue));
|
|
1217
1228
|
if (skipOnVariables) {
|
|
1218
|
-
todo.regex.lastIndex +=
|
|
1229
|
+
todo.regex.lastIndex += safeValue.length;
|
|
1219
1230
|
todo.regex.lastIndex -= match[0].length;
|
|
1220
1231
|
} else {
|
|
1221
1232
|
todo.regex.lastIndex = 0;
|
|
@@ -1265,7 +1276,7 @@ class Interpolator {
|
|
|
1265
1276
|
clonedOptions = clonedOptions.replace && !isString(clonedOptions.replace) ? clonedOptions.replace : clonedOptions;
|
|
1266
1277
|
clonedOptions.applyPostProcessor = false;
|
|
1267
1278
|
delete clonedOptions.defaultValue;
|
|
1268
|
-
const keyEndIndex = /{.*}
|
|
1279
|
+
const keyEndIndex = /{.*}/s.test(match[1]) ? match[1].lastIndexOf('}') + 1 : match[1].indexOf(this.formatSeparator);
|
|
1269
1280
|
if (keyEndIndex !== -1) {
|
|
1270
1281
|
formatters = match[1].slice(keyEndIndex).split(this.formatSeparator).map(elem => elem.trim()).filter(Boolean);
|
|
1271
1282
|
match[1] = match[1].slice(0, keyEndIndex);
|
|
@@ -1395,10 +1406,14 @@ class Formatter {
|
|
|
1395
1406
|
format(value, format, lng, options = {}) {
|
|
1396
1407
|
if (!format) return value;
|
|
1397
1408
|
if (value == null) return value;
|
|
1398
|
-
const
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1409
|
+
const rawFormats = format.split(this.formatSeparator);
|
|
1410
|
+
const formats = [];
|
|
1411
|
+
for (let i = 0; i < rawFormats.length; i++) {
|
|
1412
|
+
let f = rawFormats[i];
|
|
1413
|
+
while (f.indexOf('(') > -1 && !f.includes(')') && i + 1 < rawFormats.length) {
|
|
1414
|
+
f = `${f}${this.formatSeparator}${rawFormats[++i]}`;
|
|
1415
|
+
}
|
|
1416
|
+
formats.push(f);
|
|
1402
1417
|
}
|
|
1403
1418
|
const result = formats.reduce((mem, f) => {
|
|
1404
1419
|
const {
|
|
@@ -1659,6 +1674,7 @@ const get = () => ({
|
|
|
1659
1674
|
nsSeparator: ':',
|
|
1660
1675
|
pluralSeparator: '_',
|
|
1661
1676
|
contextSeparator: '_',
|
|
1677
|
+
enableSelector: false,
|
|
1662
1678
|
partialBundledLanguages: false,
|
|
1663
1679
|
saveMissing: false,
|
|
1664
1680
|
updateMissing: false,
|
|
@@ -1860,7 +1876,7 @@ class I18n extends EventEmitter {
|
|
|
1860
1876
|
deferred.resolve(t);
|
|
1861
1877
|
callback(err, t);
|
|
1862
1878
|
};
|
|
1863
|
-
if (this.languages && !this.isInitialized) return finish(null, this.t.bind(this));
|
|
1879
|
+
if ((this.languages || this.isLanguageChangingTo) && !this.isInitialized) return finish(null, this.t.bind(this));
|
|
1864
1880
|
this.changeLanguage(this.options.lng, finish);
|
|
1865
1881
|
};
|
|
1866
1882
|
if (this.options.resources || !this.options.initAsync) {
|
|
@@ -2015,7 +2031,8 @@ class I18n extends EventEmitter {
|
|
|
2015
2031
|
}
|
|
2016
2032
|
return deferred;
|
|
2017
2033
|
}
|
|
2018
|
-
getFixedT(lng, ns, keyPrefix) {
|
|
2034
|
+
getFixedT(lng, ns, keyPrefix, fixedOpts) {
|
|
2035
|
+
const scopeNs = fixedOpts?.scopeNs;
|
|
2019
2036
|
const fixedT = (key, opts, ...rest) => {
|
|
2020
2037
|
let o;
|
|
2021
2038
|
if (typeof opts !== 'object') {
|
|
@@ -2027,12 +2044,14 @@ class I18n extends EventEmitter {
|
|
|
2027
2044
|
}
|
|
2028
2045
|
o.lng = o.lng || fixedT.lng;
|
|
2029
2046
|
o.lngs = o.lngs || fixedT.lngs;
|
|
2047
|
+
const explicitCallNs = o.ns !== undefined && o.ns !== null;
|
|
2030
2048
|
o.ns = o.ns || fixedT.ns;
|
|
2031
2049
|
if (o.keyPrefix !== '') o.keyPrefix = o.keyPrefix || keyPrefix || fixedT.keyPrefix;
|
|
2032
2050
|
const selectorOpts = {
|
|
2033
2051
|
...this.options,
|
|
2034
2052
|
...o
|
|
2035
2053
|
};
|
|
2054
|
+
if (Array.isArray(scopeNs) && !explicitCallNs) selectorOpts.ns = scopeNs;
|
|
2036
2055
|
if (typeof o.keyPrefix === 'function') o.keyPrefix = keysFromSelector(o.keyPrefix, selectorOpts);
|
|
2037
2056
|
const keySeparator = this.options.keySeparator || '.';
|
|
2038
2057
|
let resultKey;
|
package/dist/esm/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"26.
|
|
1
|
+
{"type":"module","version":"26.3.6"}
|
package/dist/umd/i18next.js
CHANGED
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
const deepExtend = (target, source, overwrite) => {
|
|
98
98
|
for (const prop in source) {
|
|
99
99
|
if (prop !== '__proto__' && prop !== 'constructor') {
|
|
100
|
-
if (prop
|
|
100
|
+
if (Object.prototype.hasOwnProperty.call(target, prop)) {
|
|
101
101
|
if (isString(target[prop]) || target[prop] instanceof String || isString(source[prop]) || source[prop] instanceof String) {
|
|
102
102
|
if (overwrite) target[prop] = source[prop];
|
|
103
103
|
} else {
|
|
@@ -235,6 +235,7 @@
|
|
|
235
235
|
}
|
|
236
236
|
forward(args, lvl, prefix, debugOnly) {
|
|
237
237
|
if (debugOnly && !this.debug) return null;
|
|
238
|
+
args = args.map(a => isString(a) ? a.replace(/[\r\n\x00-\x1F\x7F]/g, ' ') : a);
|
|
238
239
|
if (isString(args[0])) args[0] = `${prefix}${this.prefix} ${args[0]}`;
|
|
239
240
|
return this.logger[lvl](args);
|
|
240
241
|
}
|
|
@@ -465,11 +466,15 @@
|
|
|
465
466
|
} = selector(createProxy());
|
|
466
467
|
const keySeparator = opts?.keySeparator ?? '.';
|
|
467
468
|
const nsSeparator = opts?.nsSeparator ?? ':';
|
|
469
|
+
const strict = opts?.enableSelector === 'strict';
|
|
468
470
|
if (path.length > 1 && nsSeparator) {
|
|
469
471
|
const ns = opts?.ns;
|
|
470
|
-
const
|
|
471
|
-
if (
|
|
472
|
-
|
|
472
|
+
const nsList = strict ? Array.isArray(ns) ? ns : ns ? [ns] : null : Array.isArray(ns) ? ns : null;
|
|
473
|
+
if (nsList) {
|
|
474
|
+
const candidates = strict ? nsList : nsList.length > 1 ? nsList.slice(1) : [];
|
|
475
|
+
if (candidates.includes(path[0])) {
|
|
476
|
+
return `${path[0]}${nsSeparator}${path.slice(1).join(keySeparator)}`;
|
|
477
|
+
}
|
|
473
478
|
}
|
|
474
479
|
}
|
|
475
480
|
return path.join(keySeparator);
|
|
@@ -672,7 +677,7 @@
|
|
|
672
677
|
const resForMissing = missingKeyNoValueFallbackToKey && usedKey ? undefined : res;
|
|
673
678
|
const updateMissing = hasDefaultValue && defaultValue !== res && this.options.updateMissing;
|
|
674
679
|
if (usedKey || usedDefault || updateMissing) {
|
|
675
|
-
this.logger.log(updateMissing ? 'updateKey' : 'missingKey', lng, namespace, key, updateMissing ? defaultValue : res);
|
|
680
|
+
this.logger.log(updateMissing ? 'updateKey' : 'missingKey', lng, namespace, needsPluralHandling && !updateMissing ? `${key}${this.pluralResolver.getSuffix(lng, opt.count, opt)}` : key, updateMissing ? defaultValue : res);
|
|
676
681
|
if (keySeparator) {
|
|
677
682
|
const fk = this.resolve(key, {
|
|
678
683
|
...opt,
|
|
@@ -882,7 +887,10 @@
|
|
|
882
887
|
const useOptionsReplaceForData = options.replace && !isString(options.replace);
|
|
883
888
|
let data = useOptionsReplaceForData ? options.replace : options;
|
|
884
889
|
if (useOptionsReplaceForData && typeof options.count !== 'undefined') {
|
|
885
|
-
data
|
|
890
|
+
data = {
|
|
891
|
+
...data,
|
|
892
|
+
count: options.count
|
|
893
|
+
};
|
|
886
894
|
}
|
|
887
895
|
if (this.options.interpolation.defaultVariables) {
|
|
888
896
|
data = {
|
|
@@ -1140,8 +1148,8 @@
|
|
|
1140
1148
|
this.prefix = prefix ? regexEscape(prefix) : prefixEscaped || '{{';
|
|
1141
1149
|
this.suffix = suffix ? regexEscape(suffix) : suffixEscaped || '}}';
|
|
1142
1150
|
this.formatSeparator = formatSeparator || ',';
|
|
1143
|
-
this.unescapePrefix = unescapeSuffix ? '' : unescapePrefix
|
|
1144
|
-
this.unescapeSuffix = this.unescapePrefix ? '' : unescapeSuffix
|
|
1151
|
+
this.unescapePrefix = unescapeSuffix ? '' : unescapePrefix ? regexEscape(unescapePrefix) : '-';
|
|
1152
|
+
this.unescapeSuffix = this.unescapePrefix ? '' : unescapeSuffix ? regexEscape(unescapeSuffix) : '';
|
|
1145
1153
|
this.nestingPrefix = nestingPrefix ? regexEscape(nestingPrefix) : nestingPrefixEscaped || regexEscape('$t(');
|
|
1146
1154
|
this.nestingSuffix = nestingSuffix ? regexEscape(nestingSuffix) : nestingSuffixEscaped || regexEscape(')');
|
|
1147
1155
|
this.nestingOptionsSeparator = nestingOptionsSeparator || ',';
|
|
@@ -1188,14 +1196,17 @@
|
|
|
1188
1196
|
});
|
|
1189
1197
|
};
|
|
1190
1198
|
this.resetRegExp();
|
|
1199
|
+
if (!this.escapeValue && typeof str === 'string' && /\$t\([^)]*\{[^}]*\{\{/.test(str)) {
|
|
1200
|
+
this.logger.warn('nesting options string contains interpolated variables with escapeValue: false — ' + 'if any of those values are attacker-controlled they can inject additional ' + 'nesting options (e.g. redirect lng/ns). Sanitise untrusted input before passing ' + 'it to t(), or keep escapeValue: true.');
|
|
1201
|
+
}
|
|
1191
1202
|
const missingInterpolationHandler = options?.missingInterpolationHandler || this.options.missingInterpolationHandler;
|
|
1192
1203
|
const skipOnVariables = options?.interpolation?.skipOnVariables !== undefined ? options.interpolation.skipOnVariables : this.options.interpolation.skipOnVariables;
|
|
1193
1204
|
const todos = [{
|
|
1194
1205
|
regex: this.regexpUnescape,
|
|
1195
|
-
safeValue: val =>
|
|
1206
|
+
safeValue: val => val
|
|
1196
1207
|
}, {
|
|
1197
1208
|
regex: this.regexp,
|
|
1198
|
-
safeValue: val => this.escapeValue ?
|
|
1209
|
+
safeValue: val => this.escapeValue ? this.escape(val) : val
|
|
1199
1210
|
}];
|
|
1200
1211
|
todos.forEach(todo => {
|
|
1201
1212
|
replaces = 0;
|
|
@@ -1219,9 +1230,9 @@
|
|
|
1219
1230
|
value = makeString(value);
|
|
1220
1231
|
}
|
|
1221
1232
|
const safeValue = todo.safeValue(value);
|
|
1222
|
-
str = str.replace(match[0], safeValue);
|
|
1233
|
+
str = str.replace(match[0], regexSafe(safeValue));
|
|
1223
1234
|
if (skipOnVariables) {
|
|
1224
|
-
todo.regex.lastIndex +=
|
|
1235
|
+
todo.regex.lastIndex += safeValue.length;
|
|
1225
1236
|
todo.regex.lastIndex -= match[0].length;
|
|
1226
1237
|
} else {
|
|
1227
1238
|
todo.regex.lastIndex = 0;
|
|
@@ -1271,7 +1282,7 @@
|
|
|
1271
1282
|
clonedOptions = clonedOptions.replace && !isString(clonedOptions.replace) ? clonedOptions.replace : clonedOptions;
|
|
1272
1283
|
clonedOptions.applyPostProcessor = false;
|
|
1273
1284
|
delete clonedOptions.defaultValue;
|
|
1274
|
-
const keyEndIndex = /{.*}
|
|
1285
|
+
const keyEndIndex = /{.*}/s.test(match[1]) ? match[1].lastIndexOf('}') + 1 : match[1].indexOf(this.formatSeparator);
|
|
1275
1286
|
if (keyEndIndex !== -1) {
|
|
1276
1287
|
formatters = match[1].slice(keyEndIndex).split(this.formatSeparator).map(elem => elem.trim()).filter(Boolean);
|
|
1277
1288
|
match[1] = match[1].slice(0, keyEndIndex);
|
|
@@ -1401,10 +1412,14 @@
|
|
|
1401
1412
|
format(value, format, lng, options = {}) {
|
|
1402
1413
|
if (!format) return value;
|
|
1403
1414
|
if (value == null) return value;
|
|
1404
|
-
const
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1415
|
+
const rawFormats = format.split(this.formatSeparator);
|
|
1416
|
+
const formats = [];
|
|
1417
|
+
for (let i = 0; i < rawFormats.length; i++) {
|
|
1418
|
+
let f = rawFormats[i];
|
|
1419
|
+
while (f.indexOf('(') > -1 && !f.includes(')') && i + 1 < rawFormats.length) {
|
|
1420
|
+
f = `${f}${this.formatSeparator}${rawFormats[++i]}`;
|
|
1421
|
+
}
|
|
1422
|
+
formats.push(f);
|
|
1408
1423
|
}
|
|
1409
1424
|
const result = formats.reduce((mem, f) => {
|
|
1410
1425
|
const {
|
|
@@ -1665,6 +1680,7 @@
|
|
|
1665
1680
|
nsSeparator: ':',
|
|
1666
1681
|
pluralSeparator: '_',
|
|
1667
1682
|
contextSeparator: '_',
|
|
1683
|
+
enableSelector: false,
|
|
1668
1684
|
partialBundledLanguages: false,
|
|
1669
1685
|
saveMissing: false,
|
|
1670
1686
|
updateMissing: false,
|
|
@@ -1866,7 +1882,7 @@
|
|
|
1866
1882
|
deferred.resolve(t);
|
|
1867
1883
|
callback(err, t);
|
|
1868
1884
|
};
|
|
1869
|
-
if (this.languages && !this.isInitialized) return finish(null, this.t.bind(this));
|
|
1885
|
+
if ((this.languages || this.isLanguageChangingTo) && !this.isInitialized) return finish(null, this.t.bind(this));
|
|
1870
1886
|
this.changeLanguage(this.options.lng, finish);
|
|
1871
1887
|
};
|
|
1872
1888
|
if (this.options.resources || !this.options.initAsync) {
|
|
@@ -2021,7 +2037,8 @@
|
|
|
2021
2037
|
}
|
|
2022
2038
|
return deferred;
|
|
2023
2039
|
}
|
|
2024
|
-
getFixedT(lng, ns, keyPrefix) {
|
|
2040
|
+
getFixedT(lng, ns, keyPrefix, fixedOpts) {
|
|
2041
|
+
const scopeNs = fixedOpts?.scopeNs;
|
|
2025
2042
|
const fixedT = (key, opts, ...rest) => {
|
|
2026
2043
|
let o;
|
|
2027
2044
|
if (typeof opts !== 'object') {
|
|
@@ -2033,12 +2050,14 @@
|
|
|
2033
2050
|
}
|
|
2034
2051
|
o.lng = o.lng || fixedT.lng;
|
|
2035
2052
|
o.lngs = o.lngs || fixedT.lngs;
|
|
2053
|
+
const explicitCallNs = o.ns !== undefined && o.ns !== null;
|
|
2036
2054
|
o.ns = o.ns || fixedT.ns;
|
|
2037
2055
|
if (o.keyPrefix !== '') o.keyPrefix = o.keyPrefix || keyPrefix || fixedT.keyPrefix;
|
|
2038
2056
|
const selectorOpts = {
|
|
2039
2057
|
...this.options,
|
|
2040
2058
|
...o
|
|
2041
2059
|
};
|
|
2060
|
+
if (Array.isArray(scopeNs) && !explicitCallNs) selectorOpts.ns = scopeNs;
|
|
2042
2061
|
if (typeof o.keyPrefix === 'function') o.keyPrefix = keysFromSelector(o.keyPrefix, selectorOpts);
|
|
2043
2062
|
const keySeparator = this.options.keySeparator || '.';
|
|
2044
2063
|
let resultKey;
|