|
97 | 97 | const deepExtend = (target, source, overwrite) => { |
98 | 98 | for (const prop in source) { |
99 | 99 | if (prop !== '__proto__' && prop !== 'constructor') { |
100 | | - if (prop in target) { |
| 100 | + if (Object.prototype.hasOwnProperty.call(target, prop)) { |
101 | 101 | if (isString$1(target[prop]) || target[prop] instanceof String || isString$1(source[prop]) || source[prop] instanceof String) { |
102 | 102 | if (overwrite) target[prop] = source[prop]; |
103 | 103 | } else { |
|
461 | 461 | } = selector(createProxy()); |
462 | 462 | const keySeparator = opts?.keySeparator ?? '.'; |
463 | 463 | const nsSeparator = opts?.nsSeparator ?? ':'; |
| 464 | + const strict = opts?.enableSelector === 'strict'; |
464 | 465 | if (path.length > 1 && nsSeparator) { |
465 | 466 | const ns = opts?.ns; |
466 | | - const nsArray = Array.isArray(ns) ? ns : null; |
467 | | - if (nsArray && nsArray.length > 1 && nsArray.slice(1).includes(path[0])) { |
468 | | - return `${path[0]}${nsSeparator}${path.slice(1).join(keySeparator)}`; |
| 467 | + const nsList = strict ? Array.isArray(ns) ? ns : ns ? [ns] : null : Array.isArray(ns) ? ns : null; |
| 468 | + if (nsList) { |
| 469 | + const candidates = strict ? nsList : nsList.length > 1 ? nsList.slice(1) : []; |
| 470 | + if (candidates.includes(path[0])) { |
| 471 | + return `${path[0]}${nsSeparator}${path.slice(1).join(keySeparator)}`; |
| 472 | + } |
469 | 473 | } |
470 | 474 | } |
471 | 475 | return path.join(keySeparator); |
|
877 | 881 | const useOptionsReplaceForData = options.replace && !isString$1(options.replace); |
878 | 882 | let data = useOptionsReplaceForData ? options.replace : options; |
879 | 883 | if (useOptionsReplaceForData && typeof options.count !== 'undefined') { |
880 | | - data.count = options.count; |
| 884 | + data = { |
| 885 | + ...data, |
| 886 | + count: options.count |
| 887 | + }; |
881 | 888 | } |
882 | 889 | if (this.options.interpolation.defaultVariables) { |
883 | 890 | data = { |
|
1187 | 1194 | const skipOnVariables = options?.interpolation?.skipOnVariables !== undefined ? options.interpolation.skipOnVariables : this.options.interpolation.skipOnVariables; |
1188 | 1195 | const todos = [{ |
1189 | 1196 | regex: this.regexpUnescape, |
1190 | | - safeValue: val => regexSafe(val) |
| 1197 | + safeValue: val => val |
1191 | 1198 | }, { |
1192 | 1199 | regex: this.regexp, |
1193 | | - safeValue: val => this.escapeValue ? regexSafe(this.escape(val)) : regexSafe(val) |
| 1200 | + safeValue: val => this.escapeValue ? this.escape(val) : val |
1194 | 1201 | }]; |
1195 | 1202 | todos.forEach(todo => { |
1196 | 1203 | replaces = 0; |
|
1214 | 1221 | value = makeString(value); |
1215 | 1222 | } |
1216 | 1223 | const safeValue = todo.safeValue(value); |
1217 | | - str = str.replace(match[0], safeValue); |
| 1224 | + str = str.replace(match[0], regexSafe(safeValue)); |
1218 | 1225 | if (skipOnVariables) { |
1219 | | - todo.regex.lastIndex += value.length; |
| 1226 | + todo.regex.lastIndex += safeValue.length; |
1220 | 1227 | todo.regex.lastIndex -= match[0].length; |
1221 | 1228 | } else { |
1222 | 1229 | todo.regex.lastIndex = 0; |
|
1266 | 1273 | clonedOptions = clonedOptions.replace && !isString$1(clonedOptions.replace) ? clonedOptions.replace : clonedOptions; |
1267 | 1274 | clonedOptions.applyPostProcessor = false; |
1268 | 1275 | delete clonedOptions.defaultValue; |
1269 | | - const keyEndIndex = /{.*}/.test(match[1]) ? match[1].lastIndexOf('}') + 1 : match[1].indexOf(this.formatSeparator); |
| 1276 | + const keyEndIndex = /{.*}/s.test(match[1]) ? match[1].lastIndexOf('}') + 1 : match[1].indexOf(this.formatSeparator); |
1270 | 1277 | if (keyEndIndex !== -1) { |
1271 | 1278 | formatters = match[1].slice(keyEndIndex).split(this.formatSeparator).map(elem => elem.trim()).filter(Boolean); |
1272 | 1279 | match[1] = match[1].slice(0, keyEndIndex); |
|
1395 | 1402 | format(value, format, lng, options = {}) { |
1396 | 1403 | if (!format) return value; |
1397 | 1404 | if (value == null) return value; |
1398 | | - const formats = format.split(this.formatSeparator); |
1399 | | - if (formats.length > 1 && formats[0].indexOf('(') > 1 && !formats[0].includes(')') && formats.find(f => f.includes(')'))) { |
1400 | | - const lastIndex = formats.findIndex(f => f.includes(')')); |
1401 | | - formats[0] = [formats[0], ...formats.splice(1, lastIndex)].join(this.formatSeparator); |
| 1405 | + const rawFormats = format.split(this.formatSeparator); |
| 1406 | + const formats = []; |
| 1407 | + for (let i = 0; i < rawFormats.length; i++) { |
| 1408 | + let f = rawFormats[i]; |
| 1409 | + while (f.indexOf('(') > -1 && !f.includes(')') && i + 1 < rawFormats.length) { |
| 1410 | + f = `${f}${this.formatSeparator}${rawFormats[++i]}`; |
| 1411 | + } |
| 1412 | + formats.push(f); |
1402 | 1413 | } |
1403 | 1414 | const result = formats.reduce((mem, f) => { |
1404 | 1415 | const { |
|
1657 | 1668 | nsSeparator: ':', |
1658 | 1669 | pluralSeparator: '_', |
1659 | 1670 | contextSeparator: '_', |
| 1671 | + enableSelector: false, |
1660 | 1672 | partialBundledLanguages: false, |
1661 | 1673 | saveMissing: false, |
1662 | 1674 | updateMissing: false, |
|
2831 | 2843 | }) { |
2832 | 2844 | const i18n = i18nFromProps || getI18n(); |
2833 | 2845 | if (!i18n) { |
2834 | | - warnOnce(i18n, 'NO_I18NEXT_INSTANCE', `Trans: You need to pass in an i18next instance using i18nextReactModule`, { |
| 2846 | + warnOnce(i18n, 'NO_I18NEXT_INSTANCE', `Trans: You need to pass in an i18next instance using initReactI18next or by passing it via props or context. In monorepo setups, make sure there is only one instance of react-i18next.`, { |
2835 | 2847 | i18nKey |
2836 | 2848 | }); |
2837 | 2849 | return children; |
|
3579 | 3591 | const i18n = i18nFromProps || i18nFromContext || getI18n(); |
3580 | 3592 | if (i18n && !i18n.reportNamespaces) i18n.reportNamespaces = new ReportNamespaces(); |
3581 | 3593 | if (!i18n) { |
3582 | | - warnOnce(i18n, 'NO_I18NEXT_INSTANCE', 'useTranslation: You will need to pass in an i18next instance by using initReactI18next'); |
| 3594 | + warnOnce(i18n, 'NO_I18NEXT_INSTANCE', 'useTranslation: You will need to pass in an i18next instance by using initReactI18next or by passing it via props or context. In monorepo setups, make sure there is only one instance of react-i18next.'); |
3583 | 3595 | } |
3584 | 3596 | const i18nOptions = React.useMemo(() => ({ |
3585 | 3597 | ...getDefaults(), |
|
3701 | 3713 | return arr; |
3702 | 3714 | }, [t, finalI18n, ready, finalI18n.resolvedLanguage, finalI18n.language, finalI18n.languages]); |
3703 | 3715 | if (i18n && useSuspense && !ready) { |
| 3716 | + let inDevelopment = false; |
| 3717 | + try { |
| 3718 | + inDevelopment = "development" !== 'production'; |
| 3719 | + } catch (e) {} |
| 3720 | + if (inDevelopment) { |
| 3721 | + warnOnce(i18n, 'SUSPENDED_WHILE_LOADING', 'useTranslation: suspended while translations are loading (useSuspense is true by default). Add a <Suspense> boundary above this component, or set react.useSuspense: false in the i18next init options. https://react.i18next.com/latest/usetranslation-hook'); |
| 3722 | + } |
3704 | 3723 | throw new Promise(resolve => { |
3705 | 3724 | const onLoaded = () => resolve(); |
3706 | 3725 | if (props.lng) { |
|
0 commit comments