react-i18next 16.5.7 → 16.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/CHANGELOG.md +9 -0
- package/TransWithoutContext.d.ts +10 -3
- package/dist/amd/react-i18next.js +75 -20
- package/dist/amd/react-i18next.min.js +1 -1
- package/dist/commonjs/useTranslation.js +12 -3
- package/dist/es/package.json +1 -1
- package/dist/es/useTranslation.js +12 -3
- package/dist/umd/react-i18next.js +75 -20
- package/dist/umd/react-i18next.min.js +1 -1
- package/package.json +20 -20
- package/react-i18next.js +75 -20
- package/react-i18next.min.js +1 -1
- package/src/useTranslation.js +22 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
### 16.6.0
|
|
2
|
+
|
|
3
|
+
- warn when `t` is called before `ready` with `useSuspense: false` [1896](https://github.com/i18next/react-i18next/issues/1896)
|
|
4
|
+
- type-safe `values` prop on `<Trans />` component — interpolation variables are now inferred from the translation string when custom types are configured [1772](https://github.com/i18next/react-i18next/issues/1772)
|
|
5
|
+
|
|
6
|
+
### 16.5.8
|
|
7
|
+
|
|
8
|
+
- A selector function cannot be resolved without an i18n instance... returning empty string is safer than leaking the raw function reference. [1907](https://github.com/i18next/react-i18next/issues/1907)
|
|
9
|
+
|
|
1
10
|
### 16.5.7
|
|
2
11
|
|
|
3
12
|
- fix: `<Trans>` component with `enableSelector: true` does not support multiple selectors for fallbacks [1907](https://github.com/i18next/react-i18next/issues/1907)
|
package/TransWithoutContext.d.ts
CHANGED
|
@@ -4,9 +4,11 @@ import type {
|
|
|
4
4
|
ApplyTarget,
|
|
5
5
|
ConstrainTarget,
|
|
6
6
|
GetSource,
|
|
7
|
+
InterpolationMap,
|
|
7
8
|
ParseKeys,
|
|
8
9
|
Namespace,
|
|
9
10
|
SelectorFn,
|
|
11
|
+
TFunctionReturn,
|
|
10
12
|
TypeOptions,
|
|
11
13
|
TOptions,
|
|
12
14
|
TFunction,
|
|
@@ -15,6 +17,10 @@ import * as React from 'react';
|
|
|
15
17
|
|
|
16
18
|
type _DefaultNamespace = TypeOptions['defaultNS'];
|
|
17
19
|
type _EnableSelector = TypeOptions['enableSelector'];
|
|
20
|
+
type _KeySeparator = TypeOptions['keySeparator'];
|
|
21
|
+
type _AppendKeyPrefix<Key, KPrefix> = KPrefix extends string
|
|
22
|
+
? `${KPrefix}${_KeySeparator}${Key & string}`
|
|
23
|
+
: Key;
|
|
18
24
|
|
|
19
25
|
type TransChild = React.ReactNode | Record<string, unknown>;
|
|
20
26
|
type $NoInfer<T> = [T][T extends T ? 0 : never];
|
|
@@ -37,7 +43,7 @@ export type TransProps<
|
|
|
37
43
|
ns?: Ns;
|
|
38
44
|
parent?: string | React.ComponentType<any> | null; // used in React.createElement if not null
|
|
39
45
|
tOptions?: TOpt;
|
|
40
|
-
values?:
|
|
46
|
+
values?: InterpolationMap<TFunctionReturn<Ns, _AppendKeyPrefix<Key, KPrefix>, TOpt>>;
|
|
41
47
|
shouldUnescape?: boolean;
|
|
42
48
|
t?: TFunction<Ns, KPrefix>;
|
|
43
49
|
};
|
|
@@ -72,7 +78,7 @@ export interface TransSelectorProps<
|
|
|
72
78
|
ns?: Ns;
|
|
73
79
|
parent?: string | React.ComponentType<any> | null; // used in React.createElement if not null
|
|
74
80
|
tOptions?: TOpt;
|
|
75
|
-
values?: {};
|
|
81
|
+
values?: Key extends (...args: any[]) => infer R ? InterpolationMap<R> : {};
|
|
76
82
|
shouldUnescape?: boolean;
|
|
77
83
|
t?: TFunction<Ns, KPrefix>;
|
|
78
84
|
}
|
|
@@ -107,7 +113,8 @@ export type ErrorCode =
|
|
|
107
113
|
| 'TRANS_NULL_VALUE'
|
|
108
114
|
| 'TRANS_INVALID_OBJ'
|
|
109
115
|
| 'TRANS_INVALID_VAR'
|
|
110
|
-
| 'TRANS_INVALID_COMPONENTS'
|
|
116
|
+
| 'TRANS_INVALID_COMPONENTS'
|
|
117
|
+
| 'USE_T_BEFORE_READY';
|
|
111
118
|
|
|
112
119
|
export type ErrorMeta = {
|
|
113
120
|
code: ErrorCode;
|
|
@@ -190,7 +190,7 @@ define(['exports', 'react'], (function (exports, React) { 'use strict';
|
|
|
190
190
|
}
|
|
191
191
|
return current;
|
|
192
192
|
};
|
|
193
|
-
const getCleanedCode = code => code?.replace(
|
|
193
|
+
const getCleanedCode = code => code?.replace(/_/g, '-');
|
|
194
194
|
const consoleLogger = {
|
|
195
195
|
type: 'logger',
|
|
196
196
|
log(args) {
|
|
@@ -446,7 +446,16 @@ define(['exports', 'react'], (function (exports, React) { 'use strict';
|
|
|
446
446
|
const {
|
|
447
447
|
[PATH_KEY]: path
|
|
448
448
|
} = selector(createProxy());
|
|
449
|
-
|
|
449
|
+
const keySeparator = opts?.keySeparator ?? '.';
|
|
450
|
+
const nsSeparator = opts?.nsSeparator ?? ':';
|
|
451
|
+
if (path.length > 1 && nsSeparator) {
|
|
452
|
+
const ns = opts?.ns;
|
|
453
|
+
const nsArray = Array.isArray(ns) ? ns : null;
|
|
454
|
+
if (nsArray && nsArray.length > 1 && nsArray.slice(1).includes(path[0])) {
|
|
455
|
+
return `${path[0]}${nsSeparator}${path.slice(1).join(keySeparator)}`;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
return path.join(keySeparator);
|
|
450
459
|
}
|
|
451
460
|
const checkedLoadedFor = {};
|
|
452
461
|
const shouldHandleAsObject = res => !isString$1(res) && typeof res !== 'boolean' && typeof res !== 'number';
|
|
@@ -519,6 +528,10 @@ define(['exports', 'react'], (function (exports, React) { 'use strict';
|
|
|
519
528
|
...opt
|
|
520
529
|
});
|
|
521
530
|
if (!Array.isArray(keys)) keys = [String(keys)];
|
|
531
|
+
keys = keys.map(k => typeof k === 'function' ? keysFromSelector(k, {
|
|
532
|
+
...this.options,
|
|
533
|
+
...opt
|
|
534
|
+
}) : String(k));
|
|
522
535
|
const returnDetails = opt.returnDetails !== undefined ? opt.returnDetails : this.options.returnDetails;
|
|
523
536
|
const keySeparator = opt.keySeparator !== undefined ? opt.keySeparator : this.options.keySeparator;
|
|
524
537
|
const {
|
|
@@ -765,6 +778,10 @@ define(['exports', 'react'], (function (exports, React) { 'use strict';
|
|
|
765
778
|
let usedLng;
|
|
766
779
|
let usedNS;
|
|
767
780
|
if (isString$1(keys)) keys = [keys];
|
|
781
|
+
if (Array.isArray(keys)) keys = keys.map(k => typeof k === 'function' ? keysFromSelector(k, {
|
|
782
|
+
...this.options,
|
|
783
|
+
...opt
|
|
784
|
+
}) : k);
|
|
768
785
|
keys.forEach(k => {
|
|
769
786
|
if (this.isValidLookup(found)) return;
|
|
770
787
|
const extracted = this.extractFromKey(k, opt);
|
|
@@ -1003,9 +1020,6 @@ define(['exports', 'react'], (function (exports, React) { 'use strict';
|
|
|
1003
1020
|
this.logger = baseLogger.create('pluralResolver');
|
|
1004
1021
|
this.pluralRulesCache = {};
|
|
1005
1022
|
}
|
|
1006
|
-
addRule(lng, obj) {
|
|
1007
|
-
this.rules[lng] = obj;
|
|
1008
|
-
}
|
|
1009
1023
|
clearCache() {
|
|
1010
1024
|
this.pluralRulesCache = {};
|
|
1011
1025
|
}
|
|
@@ -1025,7 +1039,7 @@ define(['exports', 'react'], (function (exports, React) { 'use strict';
|
|
|
1025
1039
|
type
|
|
1026
1040
|
});
|
|
1027
1041
|
} catch (err) {
|
|
1028
|
-
if (
|
|
1042
|
+
if (typeof Intl === 'undefined') {
|
|
1029
1043
|
this.logger.error('No Intl support, please use an Intl polyfill!');
|
|
1030
1044
|
return dummyRule;
|
|
1031
1045
|
}
|
|
@@ -1205,13 +1219,13 @@ define(['exports', 'react'], (function (exports, React) { 'use strict';
|
|
|
1205
1219
|
const handleHasOptions = (key, inheritedOptions) => {
|
|
1206
1220
|
const sep = this.nestingOptionsSeparator;
|
|
1207
1221
|
if (key.indexOf(sep) < 0) return key;
|
|
1208
|
-
const c = key.split(new RegExp(`${sep}[ ]*{`));
|
|
1222
|
+
const c = key.split(new RegExp(`${regexEscape(sep)}[ ]*{`));
|
|
1209
1223
|
let optionsString = `{${c[1]}`;
|
|
1210
1224
|
key = c[0];
|
|
1211
1225
|
optionsString = this.interpolate(optionsString, clonedOptions);
|
|
1212
1226
|
const matchedSingleQuotes = optionsString.match(/'/g);
|
|
1213
1227
|
const matchedDoubleQuotes = optionsString.match(/"/g);
|
|
1214
|
-
if ((matchedSingleQuotes?.length ?? 0) % 2 === 0 && !matchedDoubleQuotes || matchedDoubleQuotes
|
|
1228
|
+
if ((matchedSingleQuotes?.length ?? 0) % 2 === 0 && !matchedDoubleQuotes || (matchedDoubleQuotes?.length ?? 0) % 2 !== 0) {
|
|
1215
1229
|
optionsString = optionsString.replace(/'/g, '"');
|
|
1216
1230
|
}
|
|
1217
1231
|
try {
|
|
@@ -1689,6 +1703,23 @@ define(['exports', 'react'], (function (exports, React) { 'use strict';
|
|
|
1689
1703
|
}
|
|
1690
1704
|
});
|
|
1691
1705
|
};
|
|
1706
|
+
const SUPPORT_NOTICE_KEY = '__i18next_supportNoticeShown';
|
|
1707
|
+
const getSupportNoticeShown = () => typeof globalThis !== 'undefined' && !!globalThis[SUPPORT_NOTICE_KEY];
|
|
1708
|
+
const setSupportNoticeShown = () => {
|
|
1709
|
+
if (typeof globalThis !== 'undefined') globalThis[SUPPORT_NOTICE_KEY] = true;
|
|
1710
|
+
};
|
|
1711
|
+
const usesLocize = inst => {
|
|
1712
|
+
if (inst?.modules?.backend?.name?.indexOf('Locize') > 0) return true;
|
|
1713
|
+
if (inst?.modules?.backend?.constructor?.name?.indexOf('Locize') > 0) return true;
|
|
1714
|
+
if (inst?.options?.backend?.backends) {
|
|
1715
|
+
if (inst.options.backend.backends.some(b => b?.name?.indexOf('Locize') > 0 || b?.constructor?.name?.indexOf('Locize') > 0)) return true;
|
|
1716
|
+
}
|
|
1717
|
+
if (inst?.options?.backend?.projectId) return true;
|
|
1718
|
+
if (inst?.options?.backend?.backendOptions) {
|
|
1719
|
+
if (inst.options.backend.backendOptions.some(b => b?.projectId)) return true;
|
|
1720
|
+
}
|
|
1721
|
+
return false;
|
|
1722
|
+
};
|
|
1692
1723
|
class I18n extends EventEmitter {
|
|
1693
1724
|
constructor(options = {}, callback) {
|
|
1694
1725
|
super();
|
|
@@ -1741,6 +1772,10 @@ define(['exports', 'react'], (function (exports, React) { 'use strict';
|
|
|
1741
1772
|
if (typeof this.options.overloadTranslationOptionHandler !== 'function') {
|
|
1742
1773
|
this.options.overloadTranslationOptionHandler = defOpts.overloadTranslationOptionHandler;
|
|
1743
1774
|
}
|
|
1775
|
+
if (this.options.showSupportNotice !== false && !usesLocize(this) && !getSupportNoticeShown()) {
|
|
1776
|
+
if (typeof console !== 'undefined' && typeof console.info !== 'undefined') console.info('🌐 i18next is made possible by our own product, Locize — consider powering your project with managed localization (AI, CDN, integrations): https://locize.com 💙');
|
|
1777
|
+
setSupportNoticeShown();
|
|
1778
|
+
}
|
|
1744
1779
|
const createClassOnDemand = ClassOrObject => {
|
|
1745
1780
|
if (!ClassOrObject) return null;
|
|
1746
1781
|
if (typeof ClassOrObject === 'function') return new ClassOrObject();
|
|
@@ -2001,21 +2036,20 @@ define(['exports', 'react'], (function (exports, React) { 'use strict';
|
|
|
2001
2036
|
o.lngs = o.lngs || fixedT.lngs;
|
|
2002
2037
|
o.ns = o.ns || fixedT.ns;
|
|
2003
2038
|
if (o.keyPrefix !== '') o.keyPrefix = o.keyPrefix || keyPrefix || fixedT.keyPrefix;
|
|
2039
|
+
const selectorOpts = {
|
|
2040
|
+
...this.options,
|
|
2041
|
+
...o
|
|
2042
|
+
};
|
|
2043
|
+
if (typeof o.keyPrefix === 'function') o.keyPrefix = keysFromSelector(o.keyPrefix, selectorOpts);
|
|
2004
2044
|
const keySeparator = this.options.keySeparator || '.';
|
|
2005
2045
|
let resultKey;
|
|
2006
2046
|
if (o.keyPrefix && Array.isArray(key)) {
|
|
2007
2047
|
resultKey = key.map(k => {
|
|
2008
|
-
if (typeof k === 'function') k = keysFromSelector(k,
|
|
2009
|
-
...this.options,
|
|
2010
|
-
...opts
|
|
2011
|
-
});
|
|
2048
|
+
if (typeof k === 'function') k = keysFromSelector(k, selectorOpts);
|
|
2012
2049
|
return `${o.keyPrefix}${keySeparator}${k}`;
|
|
2013
2050
|
});
|
|
2014
2051
|
} else {
|
|
2015
|
-
if (typeof key === 'function') key = keysFromSelector(key,
|
|
2016
|
-
...this.options,
|
|
2017
|
-
...opts
|
|
2018
|
-
});
|
|
2052
|
+
if (typeof key === 'function') key = keysFromSelector(key, selectorOpts);
|
|
2019
2053
|
resultKey = o.keyPrefix ? `${o.keyPrefix}${keySeparator}${key}` : key;
|
|
2020
2054
|
}
|
|
2021
2055
|
return this.t(resultKey, o);
|
|
@@ -2156,7 +2190,19 @@ define(['exports', 'react'], (function (exports, React) { 'use strict';
|
|
|
2156
2190
|
clone.store = new ResourceStore(clonedData, mergedOptions);
|
|
2157
2191
|
clone.services.resourceStore = clone.store;
|
|
2158
2192
|
}
|
|
2159
|
-
if (options.interpolation)
|
|
2193
|
+
if (options.interpolation) {
|
|
2194
|
+
const defOpts = get();
|
|
2195
|
+
const mergedInterpolation = {
|
|
2196
|
+
...defOpts.interpolation,
|
|
2197
|
+
...this.options.interpolation,
|
|
2198
|
+
...options.interpolation
|
|
2199
|
+
};
|
|
2200
|
+
const mergedForInterpolator = {
|
|
2201
|
+
...mergedOptions,
|
|
2202
|
+
interpolation: mergedInterpolation
|
|
2203
|
+
};
|
|
2204
|
+
clone.services.interpolator = new Interpolator(mergedForInterpolator);
|
|
2205
|
+
}
|
|
2160
2206
|
clone.translator = new Translator(clone.services, mergedOptions);
|
|
2161
2207
|
clone.translator.on('*', (event, ...args) => {
|
|
2162
2208
|
clone.emit(event, ...args);
|
|
@@ -3465,7 +3511,12 @@ define(['exports', 'react'], (function (exports, React) { 'use strict';
|
|
|
3465
3511
|
const notReadyT = (k, optsOrDefaultValue) => {
|
|
3466
3512
|
if (isString(optsOrDefaultValue)) return optsOrDefaultValue;
|
|
3467
3513
|
if (isObject(optsOrDefaultValue) && isString(optsOrDefaultValue.defaultValue)) return optsOrDefaultValue.defaultValue;
|
|
3468
|
-
|
|
3514
|
+
if (typeof k === 'function') return '';
|
|
3515
|
+
if (Array.isArray(k)) {
|
|
3516
|
+
const last = k[k.length - 1];
|
|
3517
|
+
return typeof last === 'function' ? '' : last;
|
|
3518
|
+
}
|
|
3519
|
+
return k;
|
|
3469
3520
|
};
|
|
3470
3521
|
const notReadySnapshot = {
|
|
3471
3522
|
t: notReadyT,
|
|
@@ -3592,8 +3643,12 @@ define(['exports', 'react'], (function (exports, React) { 'use strict';
|
|
|
3592
3643
|
wrapperLangRef.current = lang;
|
|
3593
3644
|
}
|
|
3594
3645
|
}
|
|
3595
|
-
const
|
|
3596
|
-
|
|
3646
|
+
const effectiveT = !ready && !useSuspense ? (...args) => {
|
|
3647
|
+
warnOnce(i18n, 'USE_T_BEFORE_READY', 'useTranslation: t was called before ready. When using useSuspense: false, make sure to check the ready flag before using t.');
|
|
3648
|
+
return t(...args);
|
|
3649
|
+
} : t;
|
|
3650
|
+
const arr = [effectiveT, i18nWrapper, ready];
|
|
3651
|
+
arr.t = effectiveT;
|
|
3597
3652
|
arr.i18n = i18nWrapper;
|
|
3598
3653
|
arr.ready = ready;
|
|
3599
3654
|
return arr;
|