react-i18next 16.1.6 → 16.2.1

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 CHANGED
@@ -1,3 +1,11 @@
1
+ ### 16.2.1
2
+
3
+ - fix regression in v16.2.0: bindI18nStore does not work correctly [1879](https://github.com/i18next/react-i18next/issues/1879)
4
+
5
+ ### 16.2.0
6
+
7
+ - try to address: useTranslation hook violates React's rules of hooks by conditionally calling inner hooks [1863](https://github.com/i18next/react-i18next/issues/1863)
8
+
1
9
  ### 16.1.6
2
10
 
3
11
  - fix: fix: handle spread props for inner components in Trans (icu) [1877](https://github.com/i18next/react-i18next/pull/1877)
@@ -471,7 +471,12 @@ define(['exports', 'react'], (function (exports, React) { 'use strict';
471
471
  };
472
472
  if (key == null) return false;
473
473
  const resolved = this.resolve(key, opt);
474
- return resolved?.res !== undefined;
474
+ if (resolved?.res === undefined) return false;
475
+ const isObject = shouldHandleAsObject(resolved.res);
476
+ if (opt.returnObjects === false && isObject) {
477
+ return false;
478
+ }
479
+ return true;
475
480
  }
476
481
  extractFromKey(key, opt) {
477
482
  let nsSeparator = opt.nsSeparator !== undefined ? opt.nsSeparator : this.options.nsSeparator;
@@ -3266,15 +3271,102 @@ define(['exports', 'react'], (function (exports, React) { 'use strict';
3266
3271
  }
3267
3272
  IcuTrans.displayName = 'IcuTrans';
3268
3273
 
3269
- const usePrevious = (value, ignore) => {
3270
- const ref = React.useRef();
3271
- React.useEffect(() => {
3272
- ref.current = value;
3273
- }, [value, ignore]);
3274
- return ref.current;
3274
+ var shim = {exports: {}};
3275
+
3276
+ var useSyncExternalStoreShim_development = {};
3277
+
3278
+ /**
3279
+ * @license React
3280
+ * use-sync-external-store-shim.development.js
3281
+ *
3282
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
3283
+ *
3284
+ * This source code is licensed under the MIT license found in the
3285
+ * LICENSE file in the root directory of this source tree.
3286
+ */
3287
+
3288
+ (function () {
3289
+ function is(x, y) {
3290
+ return x === y && (0 !== x || 1 / x === 1 / y) || x !== x && y !== y;
3291
+ }
3292
+ function useSyncExternalStore$2(subscribe, getSnapshot) {
3293
+ didWarnOld18Alpha || void 0 === React$1.startTransition || (didWarnOld18Alpha = true, console.error("You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."));
3294
+ var value = getSnapshot();
3295
+ if (!didWarnUncachedGetSnapshot) {
3296
+ var cachedValue = getSnapshot();
3297
+ objectIs(value, cachedValue) || (console.error("The result of getSnapshot should be cached to avoid an infinite loop"), didWarnUncachedGetSnapshot = true);
3298
+ }
3299
+ cachedValue = useState({
3300
+ inst: {
3301
+ value: value,
3302
+ getSnapshot: getSnapshot
3303
+ }
3304
+ });
3305
+ var inst = cachedValue[0].inst,
3306
+ forceUpdate = cachedValue[1];
3307
+ useLayoutEffect(function () {
3308
+ inst.value = value;
3309
+ inst.getSnapshot = getSnapshot;
3310
+ checkIfSnapshotChanged(inst) && forceUpdate({
3311
+ inst: inst
3312
+ });
3313
+ }, [subscribe, value, getSnapshot]);
3314
+ useEffect(function () {
3315
+ checkIfSnapshotChanged(inst) && forceUpdate({
3316
+ inst: inst
3317
+ });
3318
+ return subscribe(function () {
3319
+ checkIfSnapshotChanged(inst) && forceUpdate({
3320
+ inst: inst
3321
+ });
3322
+ });
3323
+ }, [subscribe]);
3324
+ useDebugValue(value);
3325
+ return value;
3326
+ }
3327
+ function checkIfSnapshotChanged(inst) {
3328
+ var latestGetSnapshot = inst.getSnapshot;
3329
+ inst = inst.value;
3330
+ try {
3331
+ var nextValue = latestGetSnapshot();
3332
+ return !objectIs(inst, nextValue);
3333
+ } catch (error) {
3334
+ return true;
3335
+ }
3336
+ }
3337
+ function useSyncExternalStore$1(subscribe, getSnapshot) {
3338
+ return getSnapshot();
3339
+ }
3340
+ "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
3341
+ var React$1 = React,
3342
+ objectIs = "function" === typeof Object.is ? Object.is : is,
3343
+ useState = React$1.useState,
3344
+ useEffect = React$1.useEffect,
3345
+ useLayoutEffect = React$1.useLayoutEffect,
3346
+ useDebugValue = React$1.useDebugValue,
3347
+ didWarnOld18Alpha = false,
3348
+ didWarnUncachedGetSnapshot = false,
3349
+ shim = "undefined" === typeof window || "undefined" === typeof window.document || "undefined" === typeof window.document.createElement ? useSyncExternalStore$1 : useSyncExternalStore$2;
3350
+ useSyncExternalStoreShim_development.useSyncExternalStore = void 0 !== React$1.useSyncExternalStore ? React$1.useSyncExternalStore : shim;
3351
+ "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ && "function" === typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop && __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
3352
+ })();
3353
+
3354
+ {
3355
+ shim.exports = useSyncExternalStoreShim_development;
3356
+ }
3357
+
3358
+ var shimExports = shim.exports;
3359
+
3360
+ const notReadyT = (k, optsOrDefaultValue) => {
3361
+ if (isString(optsOrDefaultValue)) return optsOrDefaultValue;
3362
+ if (isObject(optsOrDefaultValue) && isString(optsOrDefaultValue.defaultValue)) return optsOrDefaultValue.defaultValue;
3363
+ return Array.isArray(k) ? k[k.length - 1] : k;
3364
+ };
3365
+ const notReadySnapshot = {
3366
+ t: notReadyT,
3367
+ ready: false
3275
3368
  };
3276
- const alwaysNewT = (i18n, language, namespace, keyPrefix) => i18n.getFixedT(language, namespace, keyPrefix);
3277
- const useMemoizedT = (i18n, language, namespace, keyPrefix) => React.useCallback(alwaysNewT(i18n, language, namespace, keyPrefix), [i18n, language, namespace, keyPrefix]);
3369
+ const dummySubscribe = () => () => {};
3278
3370
  const useTranslation = (ns, props = {}) => {
3279
3371
  const {
3280
3372
  i18n: i18nFromProps
@@ -3287,88 +3379,96 @@ define(['exports', 'react'], (function (exports, React) { 'use strict';
3287
3379
  if (i18n && !i18n.reportNamespaces) i18n.reportNamespaces = new ReportNamespaces();
3288
3380
  if (!i18n) {
3289
3381
  warnOnce(i18n, 'NO_I18NEXT_INSTANCE', 'useTranslation: You will need to pass in an i18next instance by using initReactI18next');
3290
- const notReadyT = (k, optsOrDefaultValue) => {
3291
- if (isString(optsOrDefaultValue)) return optsOrDefaultValue;
3292
- if (isObject(optsOrDefaultValue) && isString(optsOrDefaultValue.defaultValue)) return optsOrDefaultValue.defaultValue;
3293
- return Array.isArray(k) ? k[k.length - 1] : k;
3294
- };
3295
- const retNotReady = [notReadyT, {}, false];
3296
- retNotReady.t = notReadyT;
3297
- retNotReady.i18n = {};
3298
- retNotReady.ready = false;
3299
- return retNotReady;
3300
- }
3301
- if (i18n.options.react?.wait) warnOnce(i18n, 'DEPRECATED_OPTION', 'useTranslation: It seems you are still using the old wait option, you may migrate to the new useSuspense behaviour.');
3302
- const i18nOptions = {
3382
+ }
3383
+ const i18nOptions = React.useMemo(() => ({
3303
3384
  ...getDefaults(),
3304
- ...i18n.options.react,
3385
+ ...i18n?.options?.react,
3305
3386
  ...props
3306
- };
3387
+ }), [i18n, props]);
3307
3388
  const {
3308
3389
  useSuspense,
3309
3390
  keyPrefix
3310
3391
  } = i18nOptions;
3311
- let namespaces = ns || defaultNSFromContext || i18n.options?.defaultNS;
3312
- namespaces = isString(namespaces) ? [namespaces] : namespaces || ['translation'];
3313
- i18n.reportNamespaces.addUsedNamespaces?.(namespaces);
3314
- const ready = (i18n.isInitialized || i18n.initializedStoreOnce) && namespaces.every(n => hasLoadedNamespace(n, i18n, i18nOptions));
3315
- const memoGetT = useMemoizedT(i18n, props.lng || null, i18nOptions.nsMode === 'fallback' ? namespaces : namespaces[0], keyPrefix);
3316
- const getT = () => memoGetT;
3317
- const getNewT = () => alwaysNewT(i18n, props.lng || null, i18nOptions.nsMode === 'fallback' ? namespaces : namespaces[0], keyPrefix);
3318
- const [t, setT] = React.useState(getT);
3319
- let joinedNS = namespaces.join();
3320
- if (props.lng) joinedNS = `${props.lng}${joinedNS}`;
3321
- const previousJoinedNS = usePrevious(joinedNS);
3322
- const isMounted = React.useRef(true);
3323
- React.useEffect(() => {
3392
+ const namespaces = React.useMemo(() => {
3393
+ const nsOrContext = ns || defaultNSFromContext || i18n?.options?.defaultNS;
3394
+ return isString(nsOrContext) ? [nsOrContext] : nsOrContext || ['translation'];
3395
+ }, [ns, defaultNSFromContext, i18n]);
3396
+ i18n?.reportNamespaces?.addUsedNamespaces?.(namespaces);
3397
+ const revisionRef = React.useRef(0);
3398
+ const subscribe = React.useCallback(callback => {
3399
+ if (!i18n) return dummySubscribe;
3324
3400
  const {
3325
3401
  bindI18n,
3326
3402
  bindI18nStore
3327
3403
  } = i18nOptions;
3328
- isMounted.current = true;
3329
- if (!ready && !useSuspense) {
3330
- if (props.lng) {
3331
- loadLanguages(i18n, props.lng, namespaces, () => {
3332
- if (isMounted.current) setT(getNewT);
3333
- });
3334
- } else {
3335
- loadNamespaces(i18n, namespaces, () => {
3336
- if (isMounted.current) setT(getNewT);
3337
- });
3338
- }
3339
- }
3340
- if (ready && previousJoinedNS && previousJoinedNS !== joinedNS && isMounted.current) {
3341
- setT(getNewT);
3342
- }
3343
- const boundReset = () => {
3344
- if (isMounted.current) setT(getNewT);
3404
+ const wrappedCallback = () => {
3405
+ revisionRef.current += 1;
3406
+ callback();
3345
3407
  };
3346
- if (bindI18n) i18n?.on(bindI18n, boundReset);
3347
- if (bindI18nStore) i18n?.store.on(bindI18nStore, boundReset);
3408
+ if (bindI18n) i18n.on(bindI18n, wrappedCallback);
3409
+ if (bindI18nStore) i18n.store.on(bindI18nStore, wrappedCallback);
3348
3410
  return () => {
3349
- isMounted.current = false;
3350
- if (i18n && bindI18n) bindI18n?.split(' ').forEach(e => i18n.off(e, boundReset));
3351
- if (bindI18nStore && i18n) bindI18nStore.split(' ').forEach(e => i18n.store.off(e, boundReset));
3411
+ if (bindI18n) bindI18n.split(' ').forEach(e => i18n.off(e, wrappedCallback));
3412
+ if (bindI18nStore) bindI18nStore.split(' ').forEach(e => i18n.store.off(e, wrappedCallback));
3413
+ };
3414
+ }, [i18n, i18nOptions]);
3415
+ const snapshotRef = React.useRef();
3416
+ const getSnapshot = React.useCallback(() => {
3417
+ if (!i18n) {
3418
+ return notReadySnapshot;
3419
+ }
3420
+ const calculatedReady = !!(i18n.isInitialized || i18n.initializedStoreOnce) && namespaces.every(n => hasLoadedNamespace(n, i18n, i18nOptions));
3421
+ const currentLng = props.lng || i18n.language;
3422
+ const currentRevision = revisionRef.current;
3423
+ const lastSnapshot = snapshotRef.current;
3424
+ if (lastSnapshot && lastSnapshot.ready === calculatedReady && lastSnapshot.lng === currentLng && lastSnapshot.keyPrefix === keyPrefix && lastSnapshot.revision === currentRevision) {
3425
+ return lastSnapshot;
3426
+ }
3427
+ const calculatedT = i18n.getFixedT(currentLng, i18nOptions.nsMode === 'fallback' ? namespaces : namespaces[0], keyPrefix);
3428
+ const newSnapshot = {
3429
+ t: calculatedT,
3430
+ ready: calculatedReady,
3431
+ lng: currentLng,
3432
+ keyPrefix,
3433
+ revision: currentRevision
3352
3434
  };
3353
- }, [i18n, joinedNS]);
3435
+ snapshotRef.current = newSnapshot;
3436
+ return newSnapshot;
3437
+ }, [i18n, namespaces, keyPrefix, i18nOptions, props.lng]);
3438
+ const [loadCount, setLoadCount] = React.useState(0);
3439
+ const {
3440
+ t,
3441
+ ready
3442
+ } = shimExports.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
3354
3443
  React.useEffect(() => {
3355
- if (isMounted.current && ready) {
3356
- setT(getT);
3357
- }
3358
- }, [i18n, keyPrefix, ready]);
3359
- const ret = [t, i18n, ready];
3360
- ret.t = t;
3361
- ret.i18n = i18n;
3362
- ret.ready = ready;
3363
- if (ready) return ret;
3364
- if (!ready && !useSuspense) return ret;
3365
- throw new Promise(resolve => {
3366
- if (props.lng) {
3367
- loadLanguages(i18n, props.lng, namespaces, () => resolve());
3368
- } else {
3369
- loadNamespaces(i18n, namespaces, () => resolve());
3444
+ if (i18n && !ready && !useSuspense) {
3445
+ const onLoaded = () => setLoadCount(c => c + 1);
3446
+ if (props.lng) {
3447
+ loadLanguages(i18n, props.lng, namespaces, onLoaded);
3448
+ } else {
3449
+ loadNamespaces(i18n, namespaces, onLoaded);
3450
+ }
3370
3451
  }
3371
- });
3452
+ }, [i18n, props.lng, namespaces, ready, useSuspense, loadCount]);
3453
+ const finalI18n = i18n || {};
3454
+ const ret = React.useMemo(() => {
3455
+ const arr = [t, finalI18n, ready];
3456
+ arr.t = t;
3457
+ arr.i18n = finalI18n;
3458
+ arr.ready = ready;
3459
+ return arr;
3460
+ }, [t, finalI18n, ready]);
3461
+ if (i18n && useSuspense && !ready) {
3462
+ throw new Promise(resolve => {
3463
+ const onLoaded = () => resolve();
3464
+ if (props.lng) {
3465
+ loadLanguages(i18n, props.lng, namespaces, onLoaded);
3466
+ } else {
3467
+ loadNamespaces(i18n, namespaces, onLoaded);
3468
+ }
3469
+ });
3470
+ }
3471
+ return ret;
3372
3472
  };
3373
3473
 
3374
3474
  const withTranslation = (ns, options = {}) => function Extend(WrappedComponent) {