import type { $OmitArrayKeys, $PreservedValue, $Dictionary, $SpecialObject, $StringKeyPathToRecord, $NoInfer, $Prune, $Turtles, } from './helpers.js'; import type { TypeOptions, Namespace, FlatNamespace, DefaultNamespace, TOptions, TOptionsBase, } from './options.js'; /** @todo consider to replace {} with Record */ /* eslint @typescript-eslint/ban-types: ['error', { types: { "{}": false } }] */ // Type Options type _ReturnObjects = TypeOptions['returnObjects']; type _ReturnEmptyString = TypeOptions['returnEmptyString']; type _ReturnNull = TypeOptions['returnNull']; type _KeySeparator = TypeOptions['keySeparator']; type _NsSeparator = TypeOptions['nsSeparator']; type _PluralSeparator = TypeOptions['pluralSeparator']; type _ContextSeparator = TypeOptions['contextSeparator']; type _FallbackNamespace = TypeOptions['fallbackNS']; type _Resources = TypeOptions['resources']; type _CompatibilityJSON = TypeOptions['compatibilityJSON']; type _InterpolationPrefix = TypeOptions['interpolationPrefix']; type _InterpolationSuffix = TypeOptions['interpolationSuffix']; type _UnescapePrefix = TypeOptions['unescapePrefix']; type _UnescapeSuffix = TypeOptions['unescapeSuffix']; type _StrictKeyChecks = TypeOptions['strictKeyChecks']; type _EnableSelector = TypeOptions['enableSelector']; type $IsResourcesDefined = [keyof _Resources] extends [never] ? false : true; type $ValueIfResourcesDefined = $IsResourcesDefined extends true ? Value : Fallback; type $FirstNamespace = Ns extends readonly any[] ? Ns[0] : Ns; type Resources = $ValueIfResourcesDefined<_Resources, $Dictionary>; type PluralSuffix = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other'; type WithOrWithoutPlural = _CompatibilityJSON extends 'v4' ? Key extends `${infer KeyWithoutOrdinalPlural}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}` ? KeyWithoutOrdinalPlural | Key : Key extends `${infer KeyWithoutPlural}${_PluralSeparator}${PluralSuffix}` ? KeyWithoutPlural | Key : Key : Key; type JoinKeys = `${K1 & string}${_KeySeparator}${K2 & string}`; type AppendNamespace = `${Ns & string}${_NsSeparator}${Keys & string}`; type TrimSpaces = T extends `${infer Char}${infer Rest}` ? Char extends ' ' ? TrimSpaces : TrimSpaces : T extends '' ? Acc : never; interface Branded { $TFunctionBrand: $IsResourcesDefined extends true ? `${Ns extends readonly any[] ? Ns[0] : Ns}` : never; } /** **************************************************** * Build all keys and key prefixes based on Resources * ***************************************************** */ type KeysBuilderWithReturnObjects = Key extends keyof Res ? Res[Key] extends $Dictionary | readonly unknown[] ? | JoinKeys>> | JoinKeys> : never : never; type KeysBuilderWithoutReturnObjects> = Key extends keyof Res ? Res[Key] extends $Dictionary | readonly unknown[] ? JoinKeys> : Key : never; type KeysBuilder = $IsResourcesDefined extends true ? WithReturnObjects extends true ? keyof Res | KeysBuilderWithReturnObjects : KeysBuilderWithoutReturnObjects : string; type KeysWithReturnObjects = { [Ns in FlatNamespace]: WithOrWithoutPlural>; }; type KeysWithoutReturnObjects = { [Ns in FlatNamespace]: WithOrWithoutPlural>; }; type ResourceKeys = WithReturnObjects extends true ? KeysWithReturnObjects : KeysWithoutReturnObjects; /** ********************************************************************** * Parse t function keys based on the namespace, options and key prefix * *********************************************************************** */ export type KeysByTOptions = TOpt['returnObjects'] extends true ? ResourceKeys : ResourceKeys; export type NsByTOptions = TOpt['ns'] extends Namespace ? TOpt['ns'] : Ns; type ParseKeysByKeyPrefix = KPrefix extends string ? Keys extends `${KPrefix}${_KeySeparator}${infer Key}` ? Key : never : Keys; type ParseKeysByNamespaces = Ns extends readonly (infer UnionNsps)[] ? UnionNsps extends keyof Keys ? AppendNamespace : never : never; type ParseKeysByFallbackNs = _FallbackNamespace extends false ? never : _FallbackNamespace extends (infer UnionFallbackNs extends string)[] ? Keys[UnionFallbackNs] : Keys[_FallbackNamespace & string]; export type FilterKeysByContext = Context extends string ? Keys extends | `${infer Prefix}${_ContextSeparator}${Context}${_PluralSeparator}${PluralSuffix}` | `${infer Prefix}${_ContextSeparator}${Context}` ? Prefix : never : Keys; export type ParseKeys< Ns extends Namespace = DefaultNamespace, TOpt extends TOptions = {}, KPrefix = undefined, Keys extends $Dictionary = KeysByTOptions, ActualNS extends Namespace = NsByTOptions, Context extends TOpt['context'] = TOpt['context'], > = $IsResourcesDefined extends true ? FilterKeysByContext< | ParseKeysByKeyPrefix], KPrefix> | ParseKeysByNamespaces | ParseKeysByFallbackNs, Context > : string; /** ******************************************************* * Parse t function return type and interpolation values * ******************************************************** */ type ParseActualValue = Ret extends `${_UnescapePrefix}${infer ActualValue}${_UnescapeSuffix}` ? TrimSpaces : Ret; type ParseInterpolationValues = Ret extends `${string}${_InterpolationPrefix}${infer Value}${_InterpolationSuffix}${infer Rest}` ? | (Value extends `${infer ActualValue},${string}` ? ParseActualValue : ParseActualValue) | ParseInterpolationValues : never; export type InterpolationMap = $PreservedValue< $StringKeyPathToRecord, unknown>, Record >; type ParseTReturnPlural< Res, Key, KeyWithPlural = `${Key & string}${_PluralSeparator}${PluralSuffix}`, > = Res[(KeyWithPlural | Key) & keyof Res]; type ParseTReturnPluralOrdinal< Res, Key, KeyWithOrdinalPlural = `${Key & string}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`, > = Res[(KeyWithOrdinalPlural | Key) & keyof Res]; type ParseTReturnWithFallback = Val extends '' ? _ReturnEmptyString extends true ? '' : Key : Val extends null ? _ReturnNull extends true ? null : Key : Val; type ParseTReturn = ParseTReturnWithFallback< Key, Key extends `${infer K1}${_KeySeparator}${infer RestKey}` ? ParseTReturn : // Process plurals only if count is provided inside options TOpt['count'] extends number ? TOpt['ordinal'] extends boolean ? ParseTReturnPluralOrdinal : ParseTReturnPlural : // otherwise access plain key without adding plural and ordinal suffixes Res extends readonly unknown[] ? Key extends `${infer NKey extends number}` ? Res[NKey] : never : Res[Key & keyof Res] >; type TReturnOptionalNull = _ReturnNull extends true ? null : never; type TReturnOptionalObjects = _ReturnObjects extends true ? $SpecialObject | string : TOpt['returnObjects'] extends true ? $SpecialObject : string; type DefaultTReturn = | TReturnOptionalObjects | TReturnOptionalNull; export type KeyWithContext = TOpt['context'] extends string ? `${Key & string}${_ContextSeparator}${TOpt['context']}` : Key; // helper that maps the configured fallbackNS value to the matching resources slice type FallbackResourcesOf = FallbackNS extends readonly (infer FN)[] ? R[FN & keyof R] : [FallbackNS] extends [false] ? never : R[Extract & keyof R]; /* reuse the parse helpers as top-level aliases (no nested type declarations) */ type _PrimaryParse< ActualKey, PrimaryNS extends keyof Resources, TOpt extends TOptions, > = ParseTReturn; type _FallbackParse = [ FallbackResourcesOf, ] extends [never] ? never : ParseTReturn, TOpt>; export type TFunctionReturn< Ns extends Namespace, Key, TOpt extends TOptions, ActualNS extends Namespace = NsByTOptions, ActualKey = KeyWithContext, > = $IsResourcesDefined extends true ? ActualKey extends `${infer Nsp}${_NsSeparator}${infer RestKey}` ? ParseTReturn : $FirstNamespace extends infer PrimaryNS ? [PrimaryNS] extends [keyof Resources] ? [_PrimaryParse] extends [never] ? [_FallbackParse] extends [never] ? DefaultTReturn : _FallbackParse : _PrimaryParse : never : never : DefaultTReturn; export type TFunctionDetailedResult = { /** * The plain used key */ usedKey: string; /** * The translation result. */ res: T; /** * The key with context / plural */ exactUsedKey: string; /** * The used language for this translation. */ usedLng: string; /** * The used namespace for this translation. */ usedNS: string; /** * The parameters used for interpolation. */ usedParams: InterpolationMap & { count?: TOpt['count'] }; }; type TFunctionProcessReturnValue = Ret extends string | $SpecialObject | null ? Ret : [DefaultValue] extends [never] ? Ret : DefaultValue; type TFunctionReturnOptionalDetails = TOpt['returnDetails'] extends true ? TFunctionDetailedResult : Ret; type AppendKeyPrefix = KPrefix extends string ? `${KPrefix}${_KeySeparator}${Key & string}` : Key; /** * Resolves the effective key prefix by preferring a per-call `keyPrefix` from * options over the interface-level `KPrefix` (set via getFixedT's 3rd argument). */ type EffectiveKPrefix = TOpt extends { keyPrefix: infer OptKP extends string } ? OptKP : KPrefix; /** ************************ * T function declaration * ************************* */ interface TFunctionStrict extends Branded { < const Key extends ParseKeys> | TemplateStringsArray, const TOpt extends TOptions, Ret extends TFunctionReturn>, TOpt>, >( key: Key | Key[], options?: TOpt & InterpolationMap, ): TFunctionReturnOptionalDetails, never>, TOpt>; < const Key extends ParseKeys> | TemplateStringsArray, const TOpt extends TOptions, Ret extends TFunctionReturn>, TOpt>, >( key: Key | Key[], defaultValue: string, options?: TOpt & InterpolationMap, ): TFunctionReturnOptionalDetails, never>, TOpt>; } interface TFunctionNonStrict extends Branded { < const Key extends ParseKeys> | TemplateStringsArray, const TOpt extends TOptions, Ret extends TFunctionReturn>, TOpt>, const ActualOptions extends TOpt & InterpolationMap = TOpt & InterpolationMap, DefaultValue extends string = never, >( ...args: | [key: Key | Key[], options?: ActualOptions] | [key: string | string[], options: TOpt & $Dictionary & { defaultValue: DefaultValue }] | [key: string | string[], defaultValue: DefaultValue, options?: TOpt & $Dictionary] ): TFunctionReturnOptionalDetails, DefaultValue>, TOpt>; } type TFunctionSignature< Ns extends Namespace = DefaultNamespace, KPrefix = undefined, > = _EnableSelector extends true | 'optimize' ? TFunctionSelector> : _StrictKeyChecks extends true ? TFunctionStrict : TFunctionNonStrict; export interface TFunction extends TFunctionSignature {} export type KeyPrefix = ResourceKeys[$FirstNamespace] | undefined; /// ////////////// /// /// ↆ selector ↆ /// /// ////////////// /// type NsArg = Ns[number] | readonly Ns[number][]; interface TFunctionSelector extends Branded { < Target extends ConstrainTarget, const NewNs extends NsArg & Namespace, const Opts extends SelectorOptions, NewSrc extends GetSource, >( selector: SelectorFn, Opts>, options: Opts & InterpolationMap & { ns: NewNs }, ): SelectorReturn; < Target extends ConstrainTarget, const NewNs extends NsArg = Ns[number], const Opts extends SelectorOptions = SelectorOptions, >( selector: SelectorFn, Opts>, options?: Opts & InterpolationMap, ): SelectorReturn; } interface SelectorOptions extends Omit, $Dictionary { ns?: Ns; } type SelectorReturn< Target, Opts extends { defaultValue?: unknown; returnObjects?: boolean }, > = $IsResourcesDefined extends true ? TFunctionReturnOptionalDetails, Opts> : DefaultTReturn; interface SelectorFn> { (translations: Select): Target; } type ApplyKeyPrefix< T extends [any], KPrefix, > = KPrefix extends `${infer Head}${_KeySeparator}${infer Tail}` ? ApplyKeyPrefix<[T[0][Head]], Tail> : T[0][KPrefix & string]; type ApplyTarget< Target, Opts extends { returnObjects?: unknown }, > = Opts['returnObjects'] extends true ? unknown : Target; type ConstrainTarget> = _ReturnObjects extends true ? unknown : Opts['returnObjects'] extends true ? unknown : $IsResourcesDefined extends false ? unknown : string; type ProcessReturnValue = $Turtles extends Target ? string : [DefaultValue] extends [never] ? Target : unknown extends DefaultValue ? Target : Target | DefaultValue; type PickNamespaces = { [P in K as P extends keyof T ? P : never]: T[P & keyof T]; }; type GetSource< Ns extends Namespace, KPrefix, Res = Ns extends readonly [keyof Resources, any, ...any] ? Resources[Ns[0]] & PickNamespaces : Resources[$FirstNamespace], > = KPrefix extends keyof Res ? Res[KPrefix] : undefined extends KPrefix ? Res : ApplyKeyPrefix<[Res], KPrefix>; type Select = $IsResourcesDefined extends false ? $Turtles : [_EnableSelector] extends ['optimize'] ? T : FilterKeys; type FilterKeys = never | T extends readonly any[] ? { [I in keyof T]: FilterKeys } : $Prune< { [K in keyof T as T[K] extends object ? K : Context extends string ? never : K extends `${string}${_PluralSeparator}${PluralSuffix}` ? never : K]: T[K] extends object ? FilterKeys : T[K]; } & { [K in keyof T as T[K] extends object ? never : Context extends string ? never : K extends | `${infer Prefix}${_PluralSeparator}${PluralSuffix}` | `${infer Prefix}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}` ? Prefix : never]: T[K] extends object ? FilterKeys : T[K]; } & { [K in keyof T as T[K] extends object ? never : Context extends string ? K extends | `${infer Prefix}${_ContextSeparator}${Context}` | `${infer Prefix}${_ContextSeparator}${Context}${_PluralSeparator}${PluralSuffix}` ? Prefix : never : never]: T[K] extends object ? FilterKeys : T[K]; } >;