i18next 26.3.2 → 26.3.3

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.
@@ -1 +1 @@
1
- {"type":"module","version":"26.3.2"}
1
+ {"type":"module","version":"26.3.3"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18next",
3
- "version": "26.3.2",
3
+ "version": "26.3.3",
4
4
  "description": "i18next internationalization framework",
5
5
  "main": "./dist/cjs/i18next.js",
6
6
  "module": "./dist/esm/i18next.js",
package/typescript/t.d.ts CHANGED
@@ -605,6 +605,24 @@ interface TFunctionSelector<Ns extends Namespace, KPrefix, Source> extends Brand
605
605
  >,
606
606
  ): SelectorReturn<Fns[number] extends (...args: any[]) => infer R ? R : never, Opts>;
607
607
 
608
+ // ── Single selector with context + returnObjects ────────────────────────────
609
+ // More specific than the general context overload below. Captures `Context` as
610
+ // a const generic and uses `const Fn` + `ReturnType<Fn>` so FilterKeys sees the
611
+ // concrete context and the callback's object/array shape is preserved (#2398).
612
+ // Without this, `ConstrainTarget` / `ApplyTarget` collapse to `unknown` when
613
+ // `returnObjects: true`.
614
+ <
615
+ const Context extends string,
616
+ const Fn extends (src: Select<Source, Context>) => any,
617
+ const Opts extends SelectorOptions<Ns[number]> & {
618
+ context: Context;
619
+ returnObjects: true;
620
+ } = SelectorOptions<Ns[number]> & { context: Context; returnObjects: true },
621
+ >(
622
+ selector: Fn,
623
+ options: Opts & InterpolationMap<DeepUnwrapPlural<ReturnType<Fn>>>,
624
+ ): SelectorReturn<ReturnType<Fn>, Opts>;
625
+
608
626
  // ── Single selector with context — bypasses count enforcement ────────────────
609
627
  // When `context` is present in options, `Target` is derived from the
610
628
  // context-filtered source (third mapped type of FilterKeys), which does NOT
@@ -728,12 +746,20 @@ type Select<T, Context> = $IsResourcesDefined extends false
728
746
  ? T
729
747
  : FilterKeys<T, Context>;
730
748
 
749
+ /**
750
+ * Context-variant keys that are actually present (not JSON-union phantoms typed
751
+ * as optional `undefined`, e.g. `transKey1_withContext?: undefined`).
752
+ */
753
+ type _DefinedContextKeys<T, Pattern extends string> = {
754
+ [P in keyof T as P extends Pattern ? ([T[P]] extends [undefined] ? never : P) : never]: true;
755
+ };
756
+
731
757
  type _HasContextVariant<T, K extends string, Context> = [
732
- keyof T &
733
- (
734
- | `${K}${_ContextSeparator}${Context & string}`
735
- | `${K}${_ContextSeparator}${Context & string}${_PluralSeparator}${PluralSuffix}`
736
- ),
758
+ keyof _DefinedContextKeys<
759
+ T,
760
+ | `${K}${_ContextSeparator}${Context & string}`
761
+ | `${K}${_ContextSeparator}${Context & string}${_PluralSeparator}${PluralSuffix}`
762
+ >,
737
763
  ] extends [never]
738
764
  ? false
739
765
  : true;
@@ -741,7 +767,7 @@ type _HasContextVariant<T, K extends string, Context> = [
741
767
  /** Checks whether key K has **any** context variant in T (excluding pure plural suffixes). */
742
768
  type _IsContextualKey<T, K extends string> = [
743
769
  Exclude<
744
- keyof T & `${K}${_ContextSeparator}${string}`,
770
+ keyof _DefinedContextKeys<T, `${K}${_ContextSeparator}${string}`>,
745
771
  | `${K}${_PluralSeparator}${PluralSuffix}`
746
772
  | `${K}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`
747
773
  >,
@@ -749,61 +775,72 @@ type _IsContextualKey<T, K extends string> = [
749
775
  ? false
750
776
  : true;
751
777
 
752
- type FilterKeys<T, Context> = never | T extends readonly any[]
778
+ /**
779
+ * Distributes {@link FilterKeysObject} over unions so heterogeneous JSON array
780
+ * element types (each object literal in the array) are filtered independently,
781
+ * then re-unified. Without distribution, `keyof (A | B)` is only the common keys
782
+ * and context variants that exist on a single element are invisible, which
783
+ * produced partial element unions for `returnObjects` + `context` (#2398).
784
+ */
785
+ type FilterKeys<T, Context> = [T] extends [readonly any[]]
753
786
  ? { [I in keyof T]: FilterKeys<T[I], Context> }
754
- : $Prune<
755
- {
756
- // Mapped type 1: object-valued keys (recurse) + plain leaf keys (non-plural, non-context)
757
- [K in keyof T as T[K] extends object
758
- ? K
759
- : [Context] extends [string]
760
- ? K extends
761
- | `${string}${_ContextSeparator}${Context}`
762
- | `${string}${_ContextSeparator}${Context}${_PluralSeparator}${PluralSuffix}`
763
- ? never // context keys handled by mapped type 3
764
- : K extends `${string}${_PluralSeparator}${PluralSuffix}`
765
- ? never // plural keys handled by mapped type 2
766
- : K extends string
767
- ? _HasContextVariant<T, K, Context> extends true
768
- ? never // context variant exists, drop base key (type 3 handles it)
769
- : _IsContextualKey<T, K> extends true
770
- ? never // key has context variants but not for this context
771
- : K // no context variants at all, keep base key
772
- : K
773
- : K extends `${string}${_PluralSeparator}${PluralSuffix}`
774
- ? never
775
- : K]: T[K] extends object ? FilterKeys<T[K], Context> : T[K];
776
- } & {
777
- // Mapped type 2: plural collapsing (active regardless of context)
778
- [K in keyof T as T[K] extends object
779
- ? never
780
- : [Context] extends [string]
781
- ? K extends
782
- | `${string}${_ContextSeparator}${Context}`
783
- | `${string}${_ContextSeparator}${Context}${_PluralSeparator}${PluralSuffix}`
784
- ? never // context keys handled by mapped type 3
785
- : K extends
786
- | `${infer Prefix}${_PluralSeparator}${PluralSuffix}`
787
- | `${infer Prefix}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`
788
- ? Prefix
789
- : never
790
- : K extends
791
- | `${infer Prefix}${_PluralSeparator}${PluralSuffix}`
792
- | `${infer Prefix}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`
793
- ? Prefix
794
- : never]: T[K] extends object
795
- ? FilterKeys<T[K], Context>
796
- : PluralValue<T[K] & string>;
797
- } & {
798
- // Mapped type 3: context key collapsing
799
- [K in keyof T as T[K] extends object
787
+ : T extends any
788
+ ? FilterKeysObject<T, Context>
789
+ : never;
790
+
791
+ type FilterKeysObject<T, Context> = $Prune<
792
+ {
793
+ // Mapped type 1: object-valued keys (recurse) + plain leaf keys (non-plural, non-context)
794
+ [K in keyof T as T[K] extends object
795
+ ? K
796
+ : [Context] extends [string]
797
+ ? K extends
798
+ | `${string}${_ContextSeparator}${Context}`
799
+ | `${string}${_ContextSeparator}${Context}${_PluralSeparator}${PluralSuffix}`
800
+ ? never // context keys handled by mapped type 3
801
+ : K extends `${string}${_PluralSeparator}${PluralSuffix}`
802
+ ? never // plural keys handled by mapped type 2
803
+ : K extends string
804
+ ? _HasContextVariant<T, K, Context> extends true
805
+ ? never // context variant exists, drop base key (type 3 handles it)
806
+ : _IsContextualKey<T, K> extends true
807
+ ? never // key has context variants but not for this context
808
+ : K // no context variants at all, keep base key
809
+ : K
810
+ : K extends `${string}${_PluralSeparator}${PluralSuffix}`
800
811
  ? never
801
- : [Context] extends [string]
802
- ? K extends
803
- | `${infer Prefix}${_ContextSeparator}${Context}`
804
- | `${infer Prefix}${_ContextSeparator}${Context}${_PluralSeparator}${PluralSuffix}`
805
- ? Prefix
806
- : never
807
- : never]: T[K] extends object ? FilterKeys<T[K], Context> : T[K];
808
- }
809
- >;
812
+ : K]: T[K] extends object ? FilterKeys<T[K], Context> : T[K];
813
+ } & {
814
+ // Mapped type 2: plural collapsing (active regardless of context)
815
+ [K in keyof T as T[K] extends object
816
+ ? never
817
+ : [Context] extends [string]
818
+ ? K extends
819
+ | `${string}${_ContextSeparator}${Context}`
820
+ | `${string}${_ContextSeparator}${Context}${_PluralSeparator}${PluralSuffix}`
821
+ ? never // context keys handled by mapped type 3
822
+ : K extends
823
+ | `${infer Prefix}${_PluralSeparator}${PluralSuffix}`
824
+ | `${infer Prefix}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`
825
+ ? Prefix
826
+ : never
827
+ : K extends
828
+ | `${infer Prefix}${_PluralSeparator}${PluralSuffix}`
829
+ | `${infer Prefix}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`
830
+ ? Prefix
831
+ : never]: T[K] extends object ? FilterKeys<T[K], Context> : PluralValue<T[K] & string>;
832
+ } & {
833
+ // Mapped type 3: context key collapsing (skip JSON-union phantoms typed as `undefined`)
834
+ [K in keyof T as T[K] extends object
835
+ ? never
836
+ : [T[K]] extends [undefined]
837
+ ? never
838
+ : [Context] extends [string]
839
+ ? K extends
840
+ | `${infer Prefix}${_ContextSeparator}${Context}`
841
+ | `${infer Prefix}${_ContextSeparator}${Context}${_PluralSeparator}${PluralSuffix}`
842
+ ? Prefix
843
+ : never
844
+ : never]: T[K] extends object ? FilterKeys<T[K], Context> : T[K];
845
+ }
846
+ >;