i18next 26.2.0 → 26.3.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.
@@ -15,7 +15,9 @@
15
15
  "Bash(python3 -c \"import json,sys; print\\(json.load\\(sys.stdin\\)['version']\\)\")",
16
16
  "Bash(npm test *)",
17
17
  "Bash(git pull *)",
18
- "Bash(git push *)"
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\")"
19
21
  ],
20
22
  "additionalDirectories": [
21
23
  "/Users/adrai/Projects/i18next/react-i18next",
@@ -1 +1 @@
1
- {"type":"module","version":"26.2.0"}
1
+ {"type":"module","version":"26.3.0"}
package/index.d.ts CHANGED
@@ -584,6 +584,7 @@ export type {
584
584
  InitOptions,
585
585
  TypeOptions,
586
586
  CustomTypeOptions,
587
+ ResourceNamespaceMap,
587
588
  CustomPluginOptions,
588
589
  PluginOptions,
589
590
  FormatFunction,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "i18next",
3
- "version": "26.2.0",
3
+ "version": "26.3.0",
4
4
  "description": "i18next internationalization framework",
5
5
  "main": "./dist/cjs/i18next.js",
6
6
  "module": "./dist/esm/i18next.js",
@@ -27,161 +27,246 @@ import type { $MergeBy, $PreservedValue, $Dictionary } from './helpers.js';
27
27
  */
28
28
  export interface CustomTypeOptions {}
29
29
 
30
+ /**
31
+ * Per-package namespace types for monorepos. Augment this in each package's
32
+ * `i18next.d.ts` instead of `CustomTypeOptions.resources` when several packages
33
+ * need their own namespaces — otherwise TypeScript reports TS2717 on merge.
34
+ *
35
+ * Works alongside the legacy `CustomTypeOptions.resources` field; both end up
36
+ * in `TypeOptions['resources']`.
37
+ *
38
+ * Scalar type options like `defaultNS`, `returnNull`, `enableSelector`, etc.,
39
+ * still belong on `CustomTypeOptions` — this interface is for namespace
40
+ * resource types only.
41
+ *
42
+ * @see https://github.com/i18next/i18next/issues/2409
43
+ *
44
+ * @example
45
+ * ```ts
46
+ * // packages/ui/i18next.d.ts
47
+ * declare module 'i18next' {
48
+ * interface ResourceNamespaceMap {
49
+ * '@repo/ui': typeof uiTranslations;
50
+ * }
51
+ * }
52
+ * ```
53
+ */
54
+ export interface ResourceNamespaceMap {}
55
+
30
56
  /**
31
57
  * This interface can be augmented by users to add types to `i18next` default PluginOptions.
32
58
  */
33
59
  export interface CustomPluginOptions {}
34
60
 
35
- export type TypeOptions = $MergeBy<
36
- {
37
- /** @see {InitOptions.returnNull} */
38
- returnNull: false;
39
-
40
- /** @see {InitOptions.returnEmptyString} */
41
- returnEmptyString: true;
42
-
43
- /** @see {InitOptions.returnObjects} */
44
- returnObjects: false;
45
-
46
- /** @see {InitOptions.keySeparator} */
47
- keySeparator: '.';
48
-
49
- /** @see {InitOptions.nsSeparator} */
50
- nsSeparator: ':';
51
-
52
- /** @see {InitOptions.pluralSeparator} */
53
- pluralSeparator: '_';
54
-
55
- /** @see {InitOptions.contextSeparator} */
56
- contextSeparator: '_';
57
-
58
- /** @see {InitOptions.defaultNS} */
59
- defaultNS: 'translation';
60
-
61
- /** @see {InitOptions.fallbackNS} */
62
- fallbackNS: false;
63
-
64
- /** @see {InitOptions.compatibilityJSON} */
65
- compatibilityJSON: 'v4';
66
-
67
- /** @see {InitOptions.resources} */
68
- resources: object;
61
+ type _LegacyResources = CustomTypeOptions extends { resources: infer R } ? R : object;
62
+
63
+ // Per-property merge of two object types. Unlike `L & R`, which TypeScript
64
+ // collapses to `never` whenever ANY property has incompatible literals (so a
65
+ // single same-key/different-literal conflict wipes out the whole namespace),
66
+ // this iterates keys and intersects them individually — the conflicting key
67
+ // becomes `never`, the rest survive.
68
+ type _PerPropMerge<L, R> = {
69
+ [K in keyof L | keyof R]: K extends keyof L
70
+ ? K extends keyof R
71
+ ? L[K] & R[K]
72
+ : L[K]
73
+ : K extends keyof R
74
+ ? R[K]
75
+ : never;
76
+ };
77
+
78
+ // Drop properties whose value resolved to `never`. Without this, the
79
+ // conflict key would leak into `keyof Resources[ns]`, then poison
80
+ // `KeysBuilder` recursion (it tries to walk a `never` value) and break
81
+ // `t()` overload resolution for the entire namespace.
82
+ // NOTE: must use the `Pick<T, NonNeverKeys>` form. A `[K in keyof T as ...]`
83
+ // remap does NOT eagerly evaluate `T[K]` for intersection types, so conflict
84
+ // keys would survive the filter.
85
+ type _NonNeverKeys<T> = {
86
+ [K in keyof T]: [T[K]] extends [never] ? never : K;
87
+ }[keyof T];
88
+ type _DropConflictKeys<T> = Pick<T, _NonNeverKeys<T>>;
89
+
90
+ // When the same namespace exists on both sides, deep-merge per property and
91
+ // strip same-key/different-literal conflicts. Otherwise pick from whichever
92
+ // side has it.
93
+ type _MergeNamespaces<L, R> = {
94
+ [K in keyof L | keyof R]: K extends keyof L
95
+ ? K extends keyof R
96
+ ? _DropConflictKeys<_PerPropMerge<L[K], R[K]>>
97
+ : L[K]
98
+ : K extends keyof R
99
+ ? R[K]
100
+ : never;
101
+ };
102
+
103
+ // NOTE: empty legacy + empty registry must stay `object` so unconfigured projects
104
+ // still get `$IsResourcesDefined === false`.
105
+ type _MergedResources = [keyof _LegacyResources] extends [never]
106
+ ? [keyof ResourceNamespaceMap] extends [never]
107
+ ? object
108
+ : ResourceNamespaceMap
109
+ : [keyof ResourceNamespaceMap] extends [never]
110
+ ? _LegacyResources
111
+ : _MergeNamespaces<_LegacyResources, ResourceNamespaceMap>;
69
112
 
70
- /**
71
- * Flag that allows HTML elements to receive objects. This is only useful for React applications
72
- * where you pass objects to HTML elements so they can be replaced to their respective interpolation
73
- * values (mostly with Trans component)
74
- */
75
- allowObjectInHTMLChildren: false;
76
-
77
- /**
78
- * Flag that enables strict key checking even if a `defaultValue` has been provided.
79
- * This ensures all calls of `t` function don't accidentally use implicitly missing keys.
80
- */
81
- strictKeyChecks: false;
82
-
83
- /**
84
- * Prefix for interpolation
85
- */
86
- interpolationPrefix: '{{';
87
-
88
- /**
89
- * Suffix for interpolation
90
- */
91
- interpolationSuffix: '}}';
92
-
93
- /** @see {InterpolationOptions.unescapePrefix} */
94
- unescapePrefix: '-';
95
-
96
- /** @see {InterpolationOptions.unescapeSuffix} */
97
- unescapeSuffix: '';
98
-
99
- /**
100
- * Whether to extract interpolation variables from translation strings at
101
- * the type level. When `true` (default), the type system parses each
102
- * resource value for `{{variable}}` patterns and types the `t()` options
103
- * accordingly.
104
- *
105
- * Set to `false` when your translation strings use a different
106
- * interpolation syntax that the i18next type extractor cannot understand
107
- * e.g. ICU MessageFormat plurals like `{count, plural, one {{count} row}
108
- * other {{count} rows}}` would otherwise produce phantom variable names
109
- * because the default extractor naively matches the outermost `{{` …
110
- * `}}`. This flag is type-only; runtime interpolation is governed by
111
- * `InterpolationOptions` and is unaffected.
112
- *
113
- * Required by `i18next-icu` users — see that package's docs for the
114
- * recommended `CustomTypeOptions` augmentation.
115
- *
116
- * @default true
117
- */
118
- parseInterpolation: true;
119
-
120
- /**
121
- * Use a proxy-based selector to select a translation.
122
- *
123
- * Enables features like go-to definition, and better DX/faster autocompletion
124
- * for TypeScript developers.
125
- *
126
- * If you're working with an especially large set of translations and aren't
127
- * using context, you set `enableSelector` to `"optimize"` and i18next won't do
128
- * any type-level processing of your translations at all.
129
- *
130
- * With `enableSelector` set to `"optimize"`, i18next is capable of supporting
131
- * arbitrarily large/deep translation sets without causing any IDE slowdown
132
- * whatsoever.
133
- *
134
- * Set `enableSelector` to `"strict"` to require an explicit namespace as the
135
- * first selector path segment in every call. The selector proxy stops
136
- * exposing the primary namespace's keys flat on `$` — even
137
- * `useTranslation('only')` must use `$.only.foo`, never `$.foo`. At runtime,
138
- * a leading segment matching the scope's namespace list (primary included)
139
- * is always rewritten as a namespace prefix, fully decoupling selector
140
- * shape from resolution scope. Use this when you want a single mental model
141
- * for selector paths regardless of how many namespaces a hook was created
142
- * with, and to remove the silent miss that flat-primary paths can otherwise
143
- * cause in multi-ns hooks (see [#2429](https://github.com/i18next/i18next/issues/2429)).
144
- *
145
- * `"strict"` mode is incompatible with the `"optimize"` shortcut. If you
146
- * have keys whose names match sibling namespaces (the
147
- * [#2405](https://github.com/i18next/i18next/issues/2405) pattern), do not
148
- * enable strict mode — the leading-segment rewrite would route those keys
149
- * into the wrong namespace.
150
- *
151
- * @default false
152
- */
153
- enableSelector: false;
154
-
155
- /**
156
- * Maps interpolation format specifiers to their expected value types.
157
- *
158
- * By default, i18next infers types from built-in formatter names:
159
- * - `number`, `currency` `number`
160
- * - `datetime` → `Date`
161
- * - `relativetime` → `number`
162
- * - `list` `readonly string[]`
163
- * - No format specifier `string | number` (since i18next stringifies values at runtime)
164
- *
165
- * Use this option to add mappings for custom formatters or to override
166
- * the built-in defaults.
167
- *
168
- * @default {} (empty — built-in defaults apply)
169
- *
170
- * @example
171
- * ```ts
172
- * interface CustomTypeOptions {
173
- * interpolationFormatTypeMap: {
174
- * // custom formatter
175
- * uppercase: string;
176
- * // override built-in
177
- * currency: string;
178
- * };
179
- * }
180
- * ```
181
- */
182
- interpolationFormatTypeMap: {};
183
- },
184
- CustomTypeOptions
113
+ export type TypeOptions = $MergeBy<
114
+ $MergeBy<
115
+ {
116
+ /** @see {InitOptions.returnNull} */
117
+ returnNull: false;
118
+
119
+ /** @see {InitOptions.returnEmptyString} */
120
+ returnEmptyString: true;
121
+
122
+ /** @see {InitOptions.returnObjects} */
123
+ returnObjects: false;
124
+
125
+ /** @see {InitOptions.keySeparator} */
126
+ keySeparator: '.';
127
+
128
+ /** @see {InitOptions.nsSeparator} */
129
+ nsSeparator: ':';
130
+
131
+ /** @see {InitOptions.pluralSeparator} */
132
+ pluralSeparator: '_';
133
+
134
+ /** @see {InitOptions.contextSeparator} */
135
+ contextSeparator: '_';
136
+
137
+ /** @see {InitOptions.defaultNS} */
138
+ defaultNS: 'translation';
139
+
140
+ /** @see {InitOptions.fallbackNS} */
141
+ fallbackNS: false;
142
+
143
+ /** @see {InitOptions.compatibilityJSON} */
144
+ compatibilityJSON: 'v4';
145
+
146
+ /** @see {InitOptions.resources} */
147
+ resources: object;
148
+
149
+ /**
150
+ * Flag that allows HTML elements to receive objects. This is only useful for React applications
151
+ * where you pass objects to HTML elements so they can be replaced to their respective interpolation
152
+ * values (mostly with Trans component)
153
+ */
154
+ allowObjectInHTMLChildren: false;
155
+
156
+ /**
157
+ * Flag that enables strict key checking even if a `defaultValue` has been provided.
158
+ * This ensures all calls of `t` function don't accidentally use implicitly missing keys.
159
+ */
160
+ strictKeyChecks: false;
161
+
162
+ /**
163
+ * Prefix for interpolation
164
+ */
165
+ interpolationPrefix: '{{';
166
+
167
+ /**
168
+ * Suffix for interpolation
169
+ */
170
+ interpolationSuffix: '}}';
171
+
172
+ /** @see {InterpolationOptions.unescapePrefix} */
173
+ unescapePrefix: '-';
174
+
175
+ /** @see {InterpolationOptions.unescapeSuffix} */
176
+ unescapeSuffix: '';
177
+
178
+ /**
179
+ * Whether to extract interpolation variables from translation strings at
180
+ * the type level. When `true` (default), the type system parses each
181
+ * resource value for `{{variable}}` patterns and types the `t()` options
182
+ * accordingly.
183
+ *
184
+ * Set to `false` when your translation strings use a different
185
+ * interpolation syntax that the i18next type extractor cannot understand
186
+ * e.g. ICU MessageFormat plurals like `{count, plural, one {{count} row}
187
+ * other {{count} rows}}` would otherwise produce phantom variable names
188
+ * because the default extractor naively matches the outermost `{{`
189
+ * `}}`. This flag is type-only; runtime interpolation is governed by
190
+ * `InterpolationOptions` and is unaffected.
191
+ *
192
+ * Required by `i18next-icu` users — see that package's docs for the
193
+ * recommended `CustomTypeOptions` augmentation.
194
+ *
195
+ * @default true
196
+ */
197
+ parseInterpolation: true;
198
+
199
+ /**
200
+ * Use a proxy-based selector to select a translation.
201
+ *
202
+ * Enables features like go-to definition, and better DX/faster autocompletion
203
+ * for TypeScript developers.
204
+ *
205
+ * If you're working with an especially large set of translations and aren't
206
+ * using context, you set `enableSelector` to `"optimize"` and i18next won't do
207
+ * any type-level processing of your translations at all.
208
+ *
209
+ * With `enableSelector` set to `"optimize"`, i18next is capable of supporting
210
+ * arbitrarily large/deep translation sets without causing any IDE slowdown
211
+ * whatsoever.
212
+ *
213
+ * Set `enableSelector` to `"strict"` to require an explicit namespace as the
214
+ * first selector path segment in every call. The selector proxy stops
215
+ * exposing the primary namespace's keys flat on `$` — even
216
+ * `useTranslation('only')` must use `$.only.foo`, never `$.foo`. At runtime,
217
+ * a leading segment matching the scope's namespace list (primary included)
218
+ * is always rewritten as a namespace prefix, fully decoupling selector
219
+ * shape from resolution scope. Use this when you want a single mental model
220
+ * for selector paths regardless of how many namespaces a hook was created
221
+ * with, and to remove the silent miss that flat-primary paths can otherwise
222
+ * cause in multi-ns hooks (see [#2429](https://github.com/i18next/i18next/issues/2429)).
223
+ *
224
+ * `"strict"` mode is incompatible with the `"optimize"` shortcut. If you
225
+ * have keys whose names match sibling namespaces (the
226
+ * [#2405](https://github.com/i18next/i18next/issues/2405) pattern), do not
227
+ * enable strict mode — the leading-segment rewrite would route those keys
228
+ * into the wrong namespace.
229
+ *
230
+ * @default false
231
+ */
232
+ enableSelector: false;
233
+
234
+ /**
235
+ * Maps interpolation format specifiers to their expected value types.
236
+ *
237
+ * By default, i18next infers types from built-in formatter names:
238
+ * - `number`, `currency` → `number`
239
+ * - `datetime` → `Date`
240
+ * - `relativetime` → `number`
241
+ * - `list` → `readonly string[]`
242
+ * - No format specifier → `string | number` (since i18next stringifies values at runtime)
243
+ *
244
+ * Use this option to add mappings for custom formatters or to override
245
+ * the built-in defaults.
246
+ *
247
+ * @default {} (empty — built-in defaults apply)
248
+ *
249
+ * @example
250
+ * ```ts
251
+ * interface CustomTypeOptions {
252
+ * interpolationFormatTypeMap: {
253
+ * // custom formatter
254
+ * uppercase: string;
255
+ * // override built-in
256
+ * currency: string;
257
+ * };
258
+ * }
259
+ * ```
260
+ */
261
+ interpolationFormatTypeMap: {};
262
+ },
263
+ CustomTypeOptions
264
+ >,
265
+ // HACK: apply merged resources *after* CustomTypeOptions so registry contributions
266
+ // survive when CustomTypeOptions.resources is also defined. Without this
267
+ // second step, the inner $MergeBy would replace the default `resources: object`
268
+ // with CustomTypeOptions['resources'] alone, dropping the registry namespaces.
269
+ { resources: _MergedResources }
185
270
  >;
186
271
 
187
272
  export type PluginOptions<T> = $MergeBy<
package/typescript/t.d.ts CHANGED
@@ -77,19 +77,23 @@ interface Branded<Ns extends Namespace> {
77
77
  /** ****************************************************
78
78
  * Build all keys and key prefixes based on Resources *
79
79
  ***************************************************** */
80
- type KeysBuilderWithReturnObjects<Res, Key = keyof Res> = Key extends keyof Res
81
- ? Res[Key] extends $Dictionary | readonly unknown[]
82
- ?
83
- | JoinKeys<Key, WithOrWithoutPlural<keyof $OmitArrayKeys<Res[Key]>>>
84
- | JoinKeys<Key, KeysBuilderWithReturnObjects<Res[Key]>>
85
- : never
86
- : never;
80
+ type KeysBuilderWithReturnObjects<Res, Key = keyof Res> = [Res] extends [never]
81
+ ? never
82
+ : Key extends keyof Res
83
+ ? Res[Key] extends $Dictionary | readonly unknown[]
84
+ ?
85
+ | JoinKeys<Key, WithOrWithoutPlural<keyof $OmitArrayKeys<Res[Key]>>>
86
+ | JoinKeys<Key, KeysBuilderWithReturnObjects<Res[Key]>>
87
+ : never
88
+ : never;
87
89
 
88
- type KeysBuilderWithoutReturnObjects<Res, Key = keyof $OmitArrayKeys<Res>> = Key extends keyof Res
89
- ? Res[Key] extends $Dictionary | readonly unknown[]
90
- ? JoinKeys<Key, KeysBuilderWithoutReturnObjects<Res[Key]>>
91
- : Key
92
- : never;
90
+ type KeysBuilderWithoutReturnObjects<Res, Key = keyof $OmitArrayKeys<Res>> = [Res] extends [never]
91
+ ? never
92
+ : Key extends keyof Res
93
+ ? Res[Key] extends $Dictionary | readonly unknown[]
94
+ ? JoinKeys<Key, KeysBuilderWithoutReturnObjects<Res[Key]>>
95
+ : Key
96
+ : never;
93
97
 
94
98
  type KeysBuilder<Res, WithReturnObjects> = $IsResourcesDefined extends true
95
99
  ? WithReturnObjects extends true