i18next 26.4.7 → 26.5.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/dist/esm/package.json +1 -1
- package/package.json +1 -1
- package/typescript/options.d.ts +18 -2
- package/typescript/t.d.ts +357 -42
package/dist/esm/package.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"type":"module","version":"26.
|
|
1
|
+
{"type":"module","version":"26.5.1"}
|
package/package.json
CHANGED
package/typescript/options.d.ts
CHANGED
|
@@ -189,8 +189,24 @@ export type TypeOptions = $MergeBy<
|
|
|
189
189
|
* `}}`. This flag is type-only; runtime interpolation is governed by
|
|
190
190
|
* `InterpolationOptions` and is unaffected.
|
|
191
191
|
*
|
|
192
|
-
*
|
|
193
|
-
*
|
|
192
|
+
* Set to `'icu'` with `i18next-icu` to type `t()` options from the ICU
|
|
193
|
+
* MessageFormat arguments instead: `{name}` takes `string | number`,
|
|
194
|
+
* `{n, number}`, `plural` and `selectordinal` take `number`, `{d, date}`
|
|
195
|
+
* and `{t, time}` take `Date`, `select` takes its case keys or any string
|
|
196
|
+
* (for `other`), and arguments nested in cases are required too.
|
|
197
|
+
* `interpolationFormatTypeMap` overrides a type by its format name (e.g.
|
|
198
|
+
* `date: Date | number`). Options become required for a message with
|
|
199
|
+
* arguments.
|
|
200
|
+
*
|
|
201
|
+
* Set to `'mf2'` with `i18next-mf2` to type `t()` options from the
|
|
202
|
+
* MessageFormat 2 variables: `{$name}` takes `string | number`, a
|
|
203
|
+
* variable annotated with `:number`, `:integer`, `:currency`, `:percent`
|
|
204
|
+
* or `:unit` (directly or through `.input`/`.local`) takes `number`,
|
|
205
|
+
* `:datetime`, `:date` and `:time` take `Date`, `:string` takes `string`,
|
|
206
|
+
* option and markup variables (`minimumFractionDigits=$digits`,
|
|
207
|
+
* `{#link href=$url}`) take `string | number`, and `.local` variables are
|
|
208
|
+
* not arguments. `interpolationFormatTypeMap` overrides a type by its
|
|
209
|
+
* function name.
|
|
194
210
|
*
|
|
195
211
|
* @default true
|
|
196
212
|
*/
|
package/typescript/t.d.ts
CHANGED
|
@@ -74,6 +74,26 @@ interface Branded<Ns extends Namespace> {
|
|
|
74
74
|
: never;
|
|
75
75
|
}
|
|
76
76
|
|
|
77
|
+
/**
|
|
78
|
+
* `keyof Res`, built once per resource object. TypeScript does not cache
|
|
79
|
+
* `keyof` of an object type, so `keyof Res` inside a conditional that runs per
|
|
80
|
+
* key (or per `t()` call) rebuilt the union of all keys every time: quadratic
|
|
81
|
+
* in the namespace size. Conditional type instantiations are cached.
|
|
82
|
+
*/
|
|
83
|
+
type _KeysOf<Res> = [Res] extends [unknown] ? keyof Res : never;
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* `Res[Key & keyof Res]`: a literal key is looked up directly instead of being
|
|
87
|
+
* intersected with every key; a wide key (`string`, `${string}_suffix` from a
|
|
88
|
+
* string context) still matches all keys it covers.
|
|
89
|
+
*/
|
|
90
|
+
type _ValueOf<Res, Key> =
|
|
91
|
+
Key extends _KeysOf<Res>
|
|
92
|
+
? Res[Key]
|
|
93
|
+
: _IsLiteral<Key & PropertyKey> extends true
|
|
94
|
+
? never
|
|
95
|
+
: Res[Key & _KeysOf<Res>];
|
|
96
|
+
|
|
77
97
|
/** ****************************************************
|
|
78
98
|
* Build all keys and key prefixes based on Resources *
|
|
79
99
|
***************************************************** */
|
|
@@ -81,18 +101,20 @@ interface Branded<Ns extends Namespace> {
|
|
|
81
101
|
// conditional types, so `KeyPrefix<Ns>` no longer resolves to a literal union
|
|
82
102
|
// and `keyPrefix` inference widens to the whole namespace, polluting `t()`
|
|
83
103
|
// return types with sibling keys (#2434 regression). Guard `never` in the merge.
|
|
84
|
-
type KeysBuilderWithReturnObjects<Res, Key = keyof Res> =
|
|
85
|
-
|
|
86
|
-
?
|
|
87
|
-
| JoinKeys<Key,
|
|
88
|
-
|
|
89
|
-
|
|
104
|
+
type KeysBuilderWithReturnObjects<Res, Key = keyof Res> =
|
|
105
|
+
Key extends _KeysOf<Res>
|
|
106
|
+
? Res[Key] extends $Dictionary | readonly unknown[]
|
|
107
|
+
? | JoinKeys<Key, WithOrWithoutPlural<keyof $OmitArrayKeys<Res[Key]>>>
|
|
108
|
+
| JoinKeys<Key, KeysBuilderWithReturnObjects<Res[Key]>>
|
|
109
|
+
: never
|
|
110
|
+
: never;
|
|
90
111
|
|
|
91
|
-
type KeysBuilderWithoutReturnObjects<Res, Key = keyof $OmitArrayKeys<Res>> =
|
|
92
|
-
|
|
93
|
-
?
|
|
94
|
-
|
|
95
|
-
|
|
112
|
+
type KeysBuilderWithoutReturnObjects<Res, Key = keyof $OmitArrayKeys<Res>> =
|
|
113
|
+
Key extends _KeysOf<Res>
|
|
114
|
+
? Res[Key] extends $Dictionary | readonly unknown[]
|
|
115
|
+
? JoinKeys<Key, KeysBuilderWithoutReturnObjects<Res[Key]>>
|
|
116
|
+
: Key
|
|
117
|
+
: never;
|
|
96
118
|
|
|
97
119
|
type KeysBuilder<Res, WithReturnObjects> = $IsResourcesDefined extends true
|
|
98
120
|
? WithReturnObjects extends true
|
|
@@ -172,7 +194,7 @@ type ParseActualValue<Ret> = Ret extends `${_UnescapePrefix}${infer ActualValue}
|
|
|
172
194
|
: Ret;
|
|
173
195
|
|
|
174
196
|
/** Parses interpolation entries as `[variableName, formatSpecifier | never]` tuples. */
|
|
175
|
-
type ParseInterpolationEntries<Ret> = [_ParseInterpolation] extends [false]
|
|
197
|
+
type ParseInterpolationEntries<Ret> = [_ParseInterpolation] extends [false | 'icu' | 'mf2']
|
|
176
198
|
? never
|
|
177
199
|
: Ret extends `${string}${_InterpolationPrefix}${infer Value}${_InterpolationSuffix}${infer Rest}`
|
|
178
200
|
? | (Value extends `${infer ActualValue},${infer Format}`
|
|
@@ -225,23 +247,213 @@ type _InterpolationMapFromEntries<E> = _UnionToIntersection<
|
|
|
225
247
|
: never
|
|
226
248
|
>;
|
|
227
249
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
250
|
+
/** *********************************************************************
|
|
251
|
+
* ICU MessageFormat arguments (`parseInterpolation: 'icu'`) *
|
|
252
|
+
*********************************************************************** */
|
|
253
|
+
type _IcuWhitespace = ' ' | '\n' | '\t' | '\r';
|
|
254
|
+
type _IcuTrim<S> = S extends `${_IcuWhitespace}${infer R}`
|
|
255
|
+
? _IcuTrim<R>
|
|
256
|
+
: S extends `${infer R}${_IcuWhitespace}`
|
|
257
|
+
? _IcuTrim<R>
|
|
258
|
+
: S;
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Drops ICU apostrophe quoting as intl-messageformat (i18next-icu) reads it:
|
|
262
|
+
* `''` is a literal apostrophe, `'{...}'` (also `'}'`, `'<'`, `'>'`, `'#'`) is
|
|
263
|
+
* literal text up to the next single apostrophe.
|
|
264
|
+
* Simplification: `'#` quotes everywhere here, at runtime only inside plurals.
|
|
265
|
+
*/
|
|
266
|
+
type _IcuStripQuotes<S, Acc extends string = ''> = S extends `${infer L}'${infer R}`
|
|
267
|
+
? R extends `'${infer R2}`
|
|
268
|
+
? _IcuStripQuotes<R2, `${Acc}${L}`>
|
|
269
|
+
: R extends `${'{' | '}' | '<' | '>' | '#'}${string}`
|
|
270
|
+
? _IcuStripQuotes<_IcuSkipQuoted<R>, `${Acc}${L}`>
|
|
271
|
+
: _IcuStripQuotes<R, `${Acc}${L}'`>
|
|
272
|
+
: `${Acc}${S & string}`;
|
|
273
|
+
type _IcuSkipQuoted<S> = S extends `${string}'${infer R}`
|
|
274
|
+
? R extends `'${infer R2}`
|
|
275
|
+
? _IcuSkipQuoted<R2>
|
|
276
|
+
: R
|
|
277
|
+
: '';
|
|
278
|
+
|
|
279
|
+
/** Reads up to the `}` closing an already opened `{`; returns `[content, rest]`. */
|
|
280
|
+
type _IcuBlock<
|
|
281
|
+
S,
|
|
282
|
+
Acc extends string = '',
|
|
283
|
+
Depth extends 0[] = [],
|
|
284
|
+
> = S extends `${infer L}}${infer R}`
|
|
285
|
+
? L extends `${infer L2}{${infer R2}`
|
|
286
|
+
? _IcuBlock<`${R2}}${R}`, `${Acc}${L2}{`, [...Depth, 0]>
|
|
287
|
+
: Depth extends [0, ...infer D extends 0[]]
|
|
288
|
+
? _IcuBlock<R, `${Acc}${L}}`, D>
|
|
289
|
+
: [`${Acc}${L}`, R]
|
|
290
|
+
: never;
|
|
291
|
+
|
|
292
|
+
/** `[name, type]` entries of every argument in a message, nested cases included. */
|
|
293
|
+
type _IcuEntries<S, Acc = never> = S extends `${string}{${infer R}`
|
|
294
|
+
? _IcuBlock<R> extends [infer Arg, infer Rest]
|
|
295
|
+
? _IcuEntries<Rest, Acc | _IcuArg<Arg>>
|
|
296
|
+
: Acc
|
|
297
|
+
: Acc;
|
|
298
|
+
|
|
299
|
+
/** Walks plural/select cases: `[selector keys, entries of all case messages]`. */
|
|
300
|
+
type _IcuCases<S, Keys = never, Acc = never> = S extends `${infer L}{${infer R}`
|
|
301
|
+
? _IcuBlock<R> extends [infer Msg, infer Rest]
|
|
302
|
+
? _IcuCases<Rest, Keys | _IcuTrim<L>, Acc | _IcuEntries<Msg>>
|
|
303
|
+
: [Keys, Acc]
|
|
304
|
+
: [Keys, Acc];
|
|
305
|
+
|
|
306
|
+
type _IcuArg<A> = A extends `${infer Name},${infer Type},${infer Cases}`
|
|
307
|
+
? _IcuTrim<Type> extends 'plural' | 'selectordinal'
|
|
308
|
+
? [_IcuTrim<Name>, _IcuFormatType<_IcuTrim<Type>>] | _IcuCases<Cases>[1]
|
|
309
|
+
: _IcuTrim<Type> extends 'select'
|
|
310
|
+
? _IcuCases<Cases> extends [infer Keys, infer E]
|
|
311
|
+
? [_IcuTrim<Name>, 'other' extends Keys ? Exclude<Keys, 'other'> | (string & {}) : Keys] | E
|
|
312
|
+
: never
|
|
313
|
+
: [_IcuTrim<Name>, _IcuFormatType<_IcuTrim<Type>>] // `{n, number, ::currency/EUR}`
|
|
314
|
+
: A extends `${infer Name},${infer Type}`
|
|
315
|
+
? [_IcuTrim<Name>, _IcuFormatType<_IcuTrim<Type>>]
|
|
316
|
+
: [_IcuTrim<A>, _IcuFormatType<never>];
|
|
317
|
+
|
|
318
|
+
/** `interpolationFormatTypeMap` wins, then ICU's built-in argument types. */
|
|
319
|
+
type _IcuFormatType<F> = [F] extends [never]
|
|
320
|
+
? string | number
|
|
321
|
+
: F extends keyof _InterpolationFormatTypeMap
|
|
322
|
+
? _InterpolationFormatTypeMap[F]
|
|
323
|
+
: F extends 'number' | 'plural' | 'selectordinal'
|
|
324
|
+
? number
|
|
325
|
+
: F extends 'date' | 'time'
|
|
326
|
+
? Date
|
|
327
|
+
: string | number;
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Merges `[name, type]` entries into one record: a name used twice gets both
|
|
331
|
+
* types, a dotted MF2 name (`$user.name`) becomes a nested object.
|
|
332
|
+
*/
|
|
333
|
+
type _ArgsFromEntries<E> =
|
|
334
|
+
_UnionToIntersection<
|
|
335
|
+
E extends [infer Name extends string, infer Type]
|
|
336
|
+
? Name extends `${string}.${string}`
|
|
337
|
+
? $StringKeyPathToRecord<Name, Type>
|
|
338
|
+
: { [K in Name]: Type }
|
|
339
|
+
: never
|
|
340
|
+
> extends infer M
|
|
341
|
+
? [E] extends [never]
|
|
342
|
+
? never
|
|
343
|
+
: { [K in keyof M]: M[K] }
|
|
344
|
+
: never;
|
|
345
|
+
|
|
346
|
+
type _IcuArgsMap<Ret> = Ret extends string
|
|
347
|
+
? string extends Ret
|
|
348
|
+
? never
|
|
349
|
+
: _ArgsFromEntries<_IcuEntries<_IcuStripQuotes<Ret>>>
|
|
350
|
+
: never;
|
|
351
|
+
|
|
352
|
+
/** *********************************************************************
|
|
353
|
+
* MessageFormat 2 variables (`parseInterpolation: 'mf2'`) *
|
|
354
|
+
*********************************************************************** */
|
|
355
|
+
/** Drops `\`-escapes (`\{`, `\}`, `\|`, `\\`), so they are neither braces nor quotes. */
|
|
356
|
+
type _Mf2StripEscapes<S, Acc extends string = ''> = S extends `${infer L}\\${string}${infer R}`
|
|
357
|
+
? _Mf2StripEscapes<R, `${Acc}${L}`>
|
|
358
|
+
: `${Acc}${S & string}`;
|
|
359
|
+
|
|
360
|
+
/** Reads an expression up to its `}`, skipping `|quoted literals|`; returns `[body, rest]`. */
|
|
361
|
+
type _Mf2Expression<S, Acc extends string = ''> = S extends `${infer B}}${infer Rest}`
|
|
362
|
+
? B extends `${infer Before}|${infer Lit}`
|
|
363
|
+
? `${Lit}}${Rest}` extends `${string}|${infer After}`
|
|
364
|
+
? _Mf2Expression<After, `${Acc}${Before} `>
|
|
365
|
+
: never
|
|
366
|
+
: [`${Acc}${B}`, Rest]
|
|
367
|
+
: never;
|
|
368
|
+
|
|
369
|
+
/** `[name, type]` entries of every variable in a message's expressions; `{{` opens a quoted pattern. */
|
|
370
|
+
type _Mf2Entries<S, Acc = never> = S extends `${string}{${infer R}`
|
|
371
|
+
? R extends `{${infer Pattern}`
|
|
372
|
+
? _Mf2Entries<Pattern, Acc>
|
|
373
|
+
: _Mf2Expression<R> extends [infer Body, infer Rest]
|
|
374
|
+
? _Mf2Entries<Rest, Acc | _Mf2ExprEntries<_Mf2Spaces<Body>>>
|
|
375
|
+
: Acc
|
|
376
|
+
: Acc;
|
|
377
|
+
|
|
378
|
+
type _Mf2Space<S, C extends string> = S extends `${infer L}${C}${infer R}`
|
|
379
|
+
? _Mf2Space<`${L} ${R}`, C>
|
|
380
|
+
: S;
|
|
381
|
+
type _Mf2Spaces<S> = _IcuTrim<_Mf2Space<_Mf2Space<_Mf2Space<S, '\n'>, '\t'>, '\r'>>;
|
|
382
|
+
type _Mf2Name<S> = S extends `${infer N} ${string}`
|
|
383
|
+
? _Mf2Name<N>
|
|
384
|
+
: S extends `${infer N}/${string}`
|
|
385
|
+
? N
|
|
386
|
+
: S;
|
|
387
|
+
|
|
388
|
+
/** `{$x :fn opt=$y}`: `$x` typed by its function, option and markup variables `string | number`. */
|
|
389
|
+
type _Mf2ExprEntries<Body> = Body extends `$${infer R}`
|
|
390
|
+
? R extends `${infer Name} ${infer Tail}`
|
|
391
|
+
? | [Name, _Mf2FunctionType<_IcuTrim<Tail> extends `:${infer F}` ? _Mf2Name<F> : never>]
|
|
392
|
+
| _Mf2Vars<Tail>
|
|
393
|
+
: [_Mf2Name<R>, _Mf2FunctionType<never>]
|
|
394
|
+
: _Mf2Vars<Body>;
|
|
395
|
+
|
|
396
|
+
/** Every `$name` in a string, typed `string | number`. */
|
|
397
|
+
type _Mf2Vars<S, Acc = never> = S extends `${string}$${infer R}`
|
|
398
|
+
? _Mf2Vars<R, Acc | [_Mf2Name<R>, string | number]>
|
|
399
|
+
: Acc;
|
|
400
|
+
|
|
401
|
+
/** `interpolationFormatTypeMap` wins, then messageformat's built-in functions. */
|
|
402
|
+
type _Mf2FunctionType<F> = [F] extends [never]
|
|
403
|
+
? string | number
|
|
404
|
+
: F extends keyof _InterpolationFormatTypeMap
|
|
405
|
+
? _InterpolationFormatTypeMap[F]
|
|
406
|
+
: F extends 'number' | 'integer' | 'currency' | 'percent' | 'offset' | 'unit' | 'math'
|
|
407
|
+
? number
|
|
408
|
+
: F extends 'datetime' | 'date' | 'time'
|
|
409
|
+
? Date
|
|
410
|
+
: F extends 'string'
|
|
411
|
+
? string
|
|
412
|
+
: string | number;
|
|
413
|
+
|
|
414
|
+
/** Names declared by `.local $name = ...` (local, not passed in). */
|
|
415
|
+
type _Mf2Locals<S, Acc = never> = S extends `${string}.local${infer R}`
|
|
416
|
+
? _Mf2Locals<R, Acc | (_IcuTrim<R> extends `$${infer N}=${string}` ? _IcuTrim<N> : never)>
|
|
417
|
+
: Acc;
|
|
418
|
+
/** `.match $a $b`: selectors, normally also declared by `.input` or `.local`. */
|
|
419
|
+
type _Mf2Selectors<S> = S extends `${string}.match${infer R}`
|
|
420
|
+
? _Mf2Vars<_Mf2Spaces<R extends `${infer Sel}{${string}` ? Sel : R>>
|
|
421
|
+
: never;
|
|
422
|
+
|
|
423
|
+
type _Mf2ArgsMap<Ret> = Ret extends string
|
|
424
|
+
? string extends Ret
|
|
425
|
+
? never
|
|
426
|
+
: _Mf2StripEscapes<Ret> extends infer S
|
|
427
|
+
? _ArgsFromEntries<
|
|
428
|
+
Exclude<
|
|
429
|
+
_Mf2Entries<S> | _Mf2Selectors<S>,
|
|
430
|
+
[_IcuTrim<S> extends `.${string}` ? _Mf2Locals<S> : never, unknown]
|
|
431
|
+
>
|
|
432
|
+
>
|
|
433
|
+
: never
|
|
434
|
+
: never;
|
|
435
|
+
|
|
436
|
+
export type InterpolationMap<Ret> = [_ParseInterpolation] extends ['icu']
|
|
437
|
+
? $PreservedValue<_IcuArgsMap<Ret>, Record<string, unknown>>
|
|
438
|
+
: [_ParseInterpolation] extends ['mf2']
|
|
439
|
+
? $PreservedValue<_Mf2ArgsMap<Ret>, Record<string, unknown>>
|
|
440
|
+
: $PreservedValue<
|
|
441
|
+
_InterpolationMapFromEntries<ParseInterpolationEntries<Ret>>,
|
|
442
|
+
Record<string, unknown>
|
|
443
|
+
>;
|
|
232
444
|
|
|
233
445
|
type ParseTReturnPlural<
|
|
234
446
|
Res,
|
|
235
447
|
Key,
|
|
236
448
|
KeyWithPlural = `${Key & string}${_PluralSeparator}${PluralSuffix}`,
|
|
237
|
-
> = Res
|
|
449
|
+
> = _ValueOf<Res, KeyWithPlural | Key>;
|
|
238
450
|
|
|
239
451
|
type ParseTReturnPluralOrdinal<
|
|
240
452
|
Res,
|
|
241
453
|
Key,
|
|
242
454
|
KeyWithOrdinalPlural = `${Key &
|
|
243
455
|
string}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`,
|
|
244
|
-
> = Res
|
|
456
|
+
> = _ValueOf<Res, KeyWithOrdinalPlural | Key>;
|
|
245
457
|
|
|
246
458
|
type ParseTReturnWithFallback<Key, Val> = Val extends ''
|
|
247
459
|
? _ReturnEmptyString extends true
|
|
@@ -256,7 +468,7 @@ type ParseTReturnWithFallback<Key, Val> = Val extends ''
|
|
|
256
468
|
type ParseTReturn<Key, Res, TOpt extends TOptions = {}> = ParseTReturnWithFallback<
|
|
257
469
|
Key,
|
|
258
470
|
Key extends `${infer K1}${_KeySeparator}${infer RestKey}`
|
|
259
|
-
? ParseTReturn<RestKey, Res
|
|
471
|
+
? ParseTReturn<RestKey, _ValueOf<Res, K1>, TOpt>
|
|
260
472
|
: // Process plurals only if count is provided inside options
|
|
261
473
|
TOpt['count'] extends number
|
|
262
474
|
? TOpt['ordinal'] extends boolean
|
|
@@ -267,7 +479,7 @@ type ParseTReturn<Key, Res, TOpt extends TOptions = {}> = ParseTReturnWithFallba
|
|
|
267
479
|
? Key extends `${infer NKey extends number}`
|
|
268
480
|
? Res[NKey]
|
|
269
481
|
: never
|
|
270
|
-
: Res
|
|
482
|
+
: _ValueOf<Res, Key>
|
|
271
483
|
>;
|
|
272
484
|
|
|
273
485
|
type TReturnOptionalNull = _ReturnNull extends true ? null : never;
|
|
@@ -393,6 +605,28 @@ type EffectiveKPrefix<KPrefix, TOpt> = TOpt extends { keyPrefix: infer OptKP ext
|
|
|
393
605
|
? OptKP
|
|
394
606
|
: KPrefix;
|
|
395
607
|
|
|
608
|
+
/** `parseInterpolation: 'icu'` or `'mf2'`: messages are typed by their arguments. */
|
|
609
|
+
type _ParsesMessageFormat = [_ParseInterpolation] extends ['icu'] | ['mf2'] ? true : false;
|
|
610
|
+
|
|
611
|
+
/**
|
|
612
|
+
* `t()` options are optional, except for a message with arguments under
|
|
613
|
+
* `parseInterpolation: 'icu'` or `'mf2'` (on a missing one, i18next-icu renders
|
|
614
|
+
* the raw message and i18next-mf2 a `{$name}` fallback). The conditional
|
|
615
|
+
* resolves eagerly; a generic one here would break inference.
|
|
616
|
+
*/
|
|
617
|
+
type _KeyAndOptions<Key, Ret, Opts> = _ParsesMessageFormat extends true
|
|
618
|
+
? [key: Key | Key[], options: Opts] | [key: (Key | Key[]) & _IfOptionalOptions<Ret>]
|
|
619
|
+
: [key: Key | Key[], options?: Opts];
|
|
620
|
+
/** `unknown` when options may be left out, else a type naming the missing arguments. */
|
|
621
|
+
type _IfOptionalOptions<Ret> = _ParsesMessageFormat extends true
|
|
622
|
+
? {} extends InterpolationMap<Ret>
|
|
623
|
+
? unknown
|
|
624
|
+
: { 'pass these arguments as t() options': InterpolationMap<Ret> }
|
|
625
|
+
: unknown;
|
|
626
|
+
/** Options parameter of a selector overload: required for a message with arguments. */
|
|
627
|
+
type _SelectorOptionsArg<Ret, Opts> =
|
|
628
|
+
unknown extends _IfOptionalOptions<Ret> ? [options?: Opts] : [options: Opts];
|
|
629
|
+
|
|
396
630
|
/** ************************
|
|
397
631
|
* T function declaration *
|
|
398
632
|
************************* */
|
|
@@ -407,7 +641,7 @@ interface TFunctionStrict<
|
|
|
407
641
|
Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, EffectiveKPrefix<KPrefix, TOpt>>, TOpt>,
|
|
408
642
|
>(
|
|
409
643
|
key: Key | Key[],
|
|
410
|
-
options
|
|
644
|
+
options: TOpt &
|
|
411
645
|
InterpolationMap<Ret> & {
|
|
412
646
|
context?: Key extends string
|
|
413
647
|
? unknown extends TOpt['context']
|
|
@@ -416,6 +650,13 @@ interface TFunctionStrict<
|
|
|
416
650
|
: never;
|
|
417
651
|
},
|
|
418
652
|
): TFunctionReturnOptionalDetails<TFunctionProcessReturnValue<$NoInfer<Ret>, never>, TOpt>;
|
|
653
|
+
<
|
|
654
|
+
const Key extends ParseKeys<Ns, TOpt, EffectiveKPrefix<KPrefix, TOpt>> | TemplateStringsArray,
|
|
655
|
+
const TOpt extends TOptions,
|
|
656
|
+
Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, EffectiveKPrefix<KPrefix, TOpt>>, TOpt>,
|
|
657
|
+
>(
|
|
658
|
+
key: (Key | Key[]) & _IfOptionalOptions<$NoInfer<Ret>>,
|
|
659
|
+
): TFunctionReturnOptionalDetails<TFunctionProcessReturnValue<$NoInfer<Ret>, never>, TOpt>;
|
|
419
660
|
<
|
|
420
661
|
const Key extends ParseKeys<Ns, TOpt, EffectiveKPrefix<KPrefix, TOpt>> | TemplateStringsArray,
|
|
421
662
|
const TOpt extends TOptions,
|
|
@@ -460,9 +701,9 @@ interface TFunctionNonStrict<
|
|
|
460
701
|
DefaultValue extends string = never,
|
|
461
702
|
>(
|
|
462
703
|
...args:
|
|
463
|
-
| [key: Key | Key[], options?: ActualOptions]
|
|
464
704
|
| [key: string | string[], options: TOpt & $Dictionary & { defaultValue: DefaultValue }]
|
|
465
705
|
| [key: string | string[], defaultValue: DefaultValue, options?: TOpt & $Dictionary]
|
|
706
|
+
| _KeyAndOptions<Key, $NoInfer<Ret>, ActualOptions>
|
|
466
707
|
): TFunctionReturnOptionalDetails<TFunctionProcessReturnValue<$NoInfer<Ret>, DefaultValue>, TOpt>;
|
|
467
708
|
}
|
|
468
709
|
|
|
@@ -596,10 +837,13 @@ interface TFunctionSelector<Ns extends Namespace, KPrefix, Source> extends Brand
|
|
|
596
837
|
const Opts extends SelectorOptions<Ns[number]> = SelectorOptions<Ns[number]>,
|
|
597
838
|
>(
|
|
598
839
|
selectors: Fns,
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
840
|
+
...args: _SelectorOptionsArg<
|
|
841
|
+
DeepUnwrapPlural<Fns[number] extends (...args: any[]) => infer R ? R : never>,
|
|
842
|
+
Opts &
|
|
843
|
+
InterpolationMap<
|
|
844
|
+
DeepUnwrapPlural<Fns[number] extends (...args: any[]) => infer R ? R : never>
|
|
845
|
+
>
|
|
846
|
+
>
|
|
603
847
|
): SelectorReturn<Fns[number] extends (...args: any[]) => infer R ? R : never, Opts>;
|
|
604
848
|
|
|
605
849
|
// ── Single selector with context + returnObjects ────────────────────────────
|
|
@@ -667,7 +911,7 @@ interface TFunctionSelector<Ns extends Namespace, KPrefix, Source> extends Brand
|
|
|
667
911
|
selector: Fn,
|
|
668
912
|
...args: ReturnType<Fn> extends PluralValue<string>
|
|
669
913
|
? [options: Opts & { count: number } & InterpolationMap<DeepUnwrapPlural<ReturnType<Fn>>>]
|
|
670
|
-
:
|
|
914
|
+
: _SelectorOptionsArg<ReturnType<Fn>, Opts & InterpolationMap<ReturnType<Fn>>>
|
|
671
915
|
): SelectorReturn<ReturnType<Fn>, Opts>;
|
|
672
916
|
}
|
|
673
917
|
|
|
@@ -751,26 +995,97 @@ type _DefinedContextKeys<T, Pattern extends string> = {
|
|
|
751
995
|
[P in keyof T as P extends Pattern ? ([T[P]] extends [undefined] ? never : P) : never]: true;
|
|
752
996
|
};
|
|
753
997
|
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
998
|
+
/**
|
|
999
|
+
* Keys of T whose value is not `undefined` (JSON-union phantoms are typed as
|
|
1000
|
+
* optional `undefined`), built once per object.
|
|
1001
|
+
*/
|
|
1002
|
+
type _DefinedKeys<T> = [T] extends [unknown]
|
|
1003
|
+
? keyof { [P in keyof T as [T[P]] extends [undefined] ? never : P]: true }
|
|
1004
|
+
: never;
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* For every defined key, the prefixes K before each context separator, as
|
|
1008
|
+
* `[all, bases]`: `all` has every K with a defined key `${K}${_ContextSeparator}...`,
|
|
1009
|
+
* `bases` only those where the rest is not just a plural suffix. Jumps from one
|
|
1010
|
+
* separator occurrence to the next (so the recursion depth is the number of
|
|
1011
|
+
* separators, not the key length, which can be a whole sentence) and resumes one
|
|
1012
|
+
* character after the match, so overlapping occurrences (`a___b` with `__`) count.
|
|
1013
|
+
*/
|
|
1014
|
+
type _ContextPrefixesOf<
|
|
1015
|
+
P,
|
|
1016
|
+
Head extends string = '',
|
|
1017
|
+
All = never,
|
|
1018
|
+
Bases = never,
|
|
1019
|
+
> = P extends `${infer B}${_ContextSeparator}${string}`
|
|
1020
|
+
? P extends `${B}${infer FromSep}`
|
|
1021
|
+
? FromSep extends `${infer C}${infer Rest}`
|
|
1022
|
+
? _ContextPrefixesOf<
|
|
1023
|
+
Rest,
|
|
1024
|
+
`${Head}${B}${C}`,
|
|
1025
|
+
All | `${Head}${B}`,
|
|
1026
|
+
FromSep extends
|
|
1027
|
+
| `${_PluralSeparator}${PluralSuffix}`
|
|
1028
|
+
| `${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`
|
|
1029
|
+
? Bases
|
|
1030
|
+
: Bases | `${Head}${B}`
|
|
1031
|
+
>
|
|
1032
|
+
: [All, Bases]
|
|
1033
|
+
: [All, Bases]
|
|
1034
|
+
: [All, Bases];
|
|
1035
|
+
|
|
1036
|
+
/**
|
|
1037
|
+
* The context prefixes of an object, built once per object (conditional type
|
|
1038
|
+
* instantiations are cached): scanning all keys for every key was quadratic
|
|
1039
|
+
* in the object size, TS2589 at about 1,000 keys with context variants.
|
|
1040
|
+
*/
|
|
1041
|
+
type _ContextPrefixes<T> = [T] extends [unknown]
|
|
1042
|
+
? _ContextPrefixesOf<_DefinedKeys<T> & string>
|
|
1043
|
+
: never;
|
|
1044
|
+
type _AllContextPrefixes<T> = [T] extends [unknown] ? _ContextPrefixes<T>[0] : never;
|
|
1045
|
+
type _ContextBases<T> = [T] extends [unknown] ? _ContextPrefixes<T>[1] : never;
|
|
1046
|
+
|
|
1047
|
+
/** Whether a string type is a single literal (not `string` or a `${string}` pattern). */
|
|
1048
|
+
type _IsLiteral<S extends PropertyKey> = {} extends Record<S, unknown> ? false : true;
|
|
1049
|
+
|
|
1050
|
+
/**
|
|
1051
|
+
* Whether T has a defined key matching Pattern: literal keys are looked up, a
|
|
1052
|
+
* pattern with a wide member scans the keys.
|
|
1053
|
+
*/
|
|
1054
|
+
type _HasDefinedKey<T, Pattern extends string> = [
|
|
1055
|
+
false extends (Pattern extends unknown ? _IsLiteral<Pattern> : never)
|
|
1056
|
+
? keyof _DefinedContextKeys<T, Pattern>
|
|
1057
|
+
: Extract<Pattern, _DefinedKeys<T>>,
|
|
760
1058
|
] extends [never]
|
|
761
1059
|
? false
|
|
762
1060
|
: true;
|
|
763
1061
|
|
|
1062
|
+
type _HasContextVariant<T, K extends string, Context> = string extends Context
|
|
1063
|
+
? _IsLiteral<K> extends true
|
|
1064
|
+
? K extends _AllContextPrefixes<T>
|
|
1065
|
+
? true
|
|
1066
|
+
: false
|
|
1067
|
+
: _HasDefinedKey<T, `${K}${_ContextSeparator}${string}`>
|
|
1068
|
+
: _HasDefinedKey<
|
|
1069
|
+
T,
|
|
1070
|
+
| `${K}${_ContextSeparator}${Context & string}`
|
|
1071
|
+
| `${K}${_ContextSeparator}${Context & string}${_PluralSeparator}${PluralSuffix}`
|
|
1072
|
+
>;
|
|
1073
|
+
|
|
764
1074
|
/** Checks whether key K has **any** context variant in T (excluding pure plural suffixes). */
|
|
765
|
-
type _IsContextualKey<T, K extends string> =
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
1075
|
+
type _IsContextualKey<T, K extends string> =
|
|
1076
|
+
_IsLiteral<K> extends true
|
|
1077
|
+
? K extends _ContextBases<T>
|
|
1078
|
+
? true
|
|
1079
|
+
: false
|
|
1080
|
+
: [
|
|
1081
|
+
Exclude<
|
|
1082
|
+
keyof _DefinedContextKeys<T, `${K}${_ContextSeparator}${string}`>,
|
|
1083
|
+
| `${K}${_PluralSeparator}${PluralSuffix}`
|
|
1084
|
+
| `${K}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`
|
|
1085
|
+
>,
|
|
1086
|
+
] extends [never]
|
|
1087
|
+
? false
|
|
1088
|
+
: true;
|
|
774
1089
|
|
|
775
1090
|
/**
|
|
776
1091
|
* Distributes {@link FilterKeysObject} over unions so heterogeneous JSON array
|