UNPKG

9.6 kBTypeScriptView Raw
1import type {
2 $OmitArrayKeys,
3 $PreservedValue,
4 $Dictionary,
5 $SpecialObject,
6 $StringKeyPathToRecord,
7} from './helpers.js';
8import type {
9 TypeOptions,
10 Namespace,
11 FlatNamespace,
12 DefaultNamespace,
13 TOptions,
14} from './options.js';
15
16/** @todo consider to replace {} with Record<string, never> */
17/* eslint @typescript-eslint/ban-types: ['error', { types: { "{}": false } }] */
18
19// Type Options
20type _ReturnObjects = TypeOptions['returnObjects'];
21type _ReturnEmptyString = TypeOptions['returnEmptyString'];
22type _ReturnNull = TypeOptions['returnNull'];
23type _KeySeparator = TypeOptions['keySeparator'];
24type _NsSeparator = TypeOptions['nsSeparator'];
25type _PluralSeparator = TypeOptions['pluralSeparator'];
26type _ContextSeparator = TypeOptions['contextSeparator'];
27type _FallbackNamespace = TypeOptions['fallbackNS'];
28type _Resources = TypeOptions['resources'];
29type _JSONFormat = TypeOptions['jsonFormat'];
30type _InterpolationPrefix = TypeOptions['interpolationPrefix'];
31type _InterpolationSuffix = TypeOptions['interpolationSuffix'];
32
33type $IsResourcesDefined = [keyof _Resources] extends [never] ? false : true;
34type $ValueIfResourcesDefined<Value, Fallback> = $IsResourcesDefined extends true
35 ? Value
36 : Fallback;
37type $FirstNamespace<Ns extends Namespace> = Ns extends readonly any[] ? Ns[0] : Ns;
38
39type Resources = $ValueIfResourcesDefined<_Resources, $Dictionary<string>>;
40
41type PluralSuffix = _JSONFormat extends 'v4'
42 ? 'zero' | 'one' | 'two' | 'few' | 'many' | 'other'
43 : number | 'plural';
44
45type WithOrWithoutPlural<Key> = _JSONFormat extends 'v4' | 'v3'
46 ? Key extends `${infer KeyWithoutOrdinalPlural}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`
47 ? KeyWithoutOrdinalPlural | Key
48 : Key extends `${infer KeyWithoutPlural}${_PluralSeparator}${PluralSuffix}`
49 ? KeyWithoutPlural | Key
50 : Key
51 : Key;
52
53type JoinKeys<K1, K2> = `${K1 & string}${_KeySeparator}${K2 & string}`;
54type AppendNamespace<Ns, Keys> = `${Ns & string}${_NsSeparator}${Keys & string}`;
55
56/** ****************************************************
57 * Build all keys and key prefixes based on Resources *
58 ***************************************************** */
59type KeysBuilderWithReturnObjects<Res, Key = keyof Res> = Key extends keyof Res
60 ? Res[Key] extends $Dictionary | readonly unknown[]
61 ?
62 | JoinKeys<Key, WithOrWithoutPlural<keyof $OmitArrayKeys<Res[Key]>>>
63 | JoinKeys<Key, KeysBuilderWithReturnObjects<Res[Key]>>
64 : never
65 : never;
66
67type KeysBuilderWithoutReturnObjects<Res, Key = keyof $OmitArrayKeys<Res>> = Key extends keyof Res
68 ? Res[Key] extends $Dictionary | readonly unknown[]
69 ? JoinKeys<Key, KeysBuilderWithoutReturnObjects<Res[Key]>>
70 : Key
71 : never;
72
73type KeysBuilder<Res, WithReturnObjects> = $IsResourcesDefined extends true
74 ? WithReturnObjects extends true
75 ? keyof Res | KeysBuilderWithReturnObjects<Res>
76 : KeysBuilderWithoutReturnObjects<Res>
77 : string;
78
79type KeysWithReturnObjects = {
80 [Ns in FlatNamespace]: WithOrWithoutPlural<KeysBuilder<Resources[Ns], true>>;
81};
82type KeysWithoutReturnObjects = {
83 [Ns in FlatNamespace]: WithOrWithoutPlural<KeysBuilder<Resources[Ns], false>>;
84};
85
86type ResourceKeys<WithReturnObjects = _ReturnObjects> = WithReturnObjects extends true
87 ? KeysWithReturnObjects
88 : KeysWithoutReturnObjects;
89
90/** **********************************************************************
91 * Parse t function keys based on the namespace, options and key prefix *
92 *********************************************************************** */
93export type KeysByTOptions<TOpt extends TOptions> = TOpt['returnObjects'] extends true
94 ? ResourceKeys<true>
95 : ResourceKeys;
96
97export type NsByTOptions<Ns extends Namespace, TOpt extends TOptions> = TOpt['ns'] extends Namespace
98 ? TOpt['ns']
99 : Ns;
100
101type ParseKeysByKeyPrefix<Keys, KPrefix> = KPrefix extends string
102 ? Keys extends `${KPrefix}${_KeySeparator}${infer Key}`
103 ? Key
104 : never
105 : Keys;
106
107type ParseKeysByNamespaces<Ns extends Namespace, Keys> = Ns extends readonly (infer UnionNsps)[]
108 ? UnionNsps extends keyof Keys
109 ? AppendNamespace<UnionNsps, Keys[UnionNsps]>
110 : never
111 : never;
112
113type ParseKeysByFallbackNs<Keys extends $Dictionary> = _FallbackNamespace extends false
114 ? never
115 : _FallbackNamespace extends (infer UnionFallbackNs extends string)[]
116 ? Keys[UnionFallbackNs]
117 : Keys[_FallbackNamespace & string];
118
119type FilterKeysByContext<Keys, TOpt extends TOptions> = TOpt['context'] extends string
120 ? Keys extends `${infer Prefix}${_ContextSeparator}${TOpt['context']}${infer Suffix}`
121 ? `${Prefix}${Suffix}`
122 : never
123 : Keys;
124
125export type ParseKeys<
126 Ns extends Namespace = DefaultNamespace,
127 TOpt extends TOptions = {},
128 KPrefix = undefined,
129 Keys extends $Dictionary = KeysByTOptions<TOpt>,
130 ActualNS extends Namespace = NsByTOptions<Ns, TOpt>,
131> = $IsResourcesDefined extends true
132 ? FilterKeysByContext<
133 | ParseKeysByKeyPrefix<Keys[$FirstNamespace<ActualNS>], KPrefix>
134 | ParseKeysByNamespaces<ActualNS, Keys>
135 | ParseKeysByFallbackNs<Keys>,
136 TOpt
137 >
138 : string;
139
140/** *******************************************************
141 * Parse t function return type and interpolation values *
142 ******************************************************** */
143type ParseInterpolationValues<Ret> =
144 Ret extends `${string}${_InterpolationPrefix}${infer Value}${_InterpolationSuffix}${infer Rest}`
145 ?
146 | (Value extends `${infer ActualValue},${string}` ? ActualValue : Value)
147 | ParseInterpolationValues<Rest>
148 : never;
149
150type InterpolationMap<Ret> = $PreservedValue<
151 $StringKeyPathToRecord<ParseInterpolationValues<Ret>, unknown>,
152 Record<string, unknown>
153>;
154
155type ParseTReturnPlural<
156 Res,
157 Key,
158 KeyWithPlural = `${Key & string}${_PluralSeparator}${PluralSuffix}`,
159> = Res[(KeyWithPlural | Key) & keyof Res];
160
161type ParseTReturnPluralOrdinal<
162 Res,
163 Key,
164 KeyWithOrdinalPlural = `${Key &
165 string}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`,
166> = Res[(KeyWithOrdinalPlural | Key) & keyof Res];
167
168type ParseTReturnWithFallback<Key, Val> = Val extends ''
169 ? _ReturnEmptyString extends true
170 ? ''
171 : Key
172 : Val extends null
173 ? _ReturnNull extends true
174 ? null
175 : Key
176 : Val;
177
178type ParseTReturn<Key, Res, TOpt extends TOptions = {}> = ParseTReturnWithFallback<
179 Key,
180 Key extends `${infer K1}${_KeySeparator}${infer RestKey}`
181 ? ParseTReturn<RestKey, Res[K1 & keyof Res], TOpt>
182 : // Process plurals only if count is provided inside options
183 TOpt['count'] extends number
184 ? TOpt['ordinal'] extends boolean
185 ? ParseTReturnPluralOrdinal<Res, Key>
186 : ParseTReturnPlural<Res, Key>
187 : // otherwise access plain key without adding plural and ordinal suffixes
188 Res extends readonly unknown[]
189 ? Key extends `${infer NKey extends number}`
190 ? Res[NKey]
191 : never
192 : Res[Key & keyof Res]
193>;
194
195type TReturnOptionalNull = _ReturnNull extends true ? null : never;
196type TReturnOptionalObjects<TOpt extends TOptions> = _ReturnObjects extends true
197 ? $SpecialObject | string
198 : TOpt['returnObjects'] extends true
199 ? $SpecialObject
200 : string;
201type DefaultTReturn<TOpt extends TOptions> = TReturnOptionalObjects<TOpt> | TReturnOptionalNull;
202
203export type KeyWithContext<Key, TOpt extends TOptions> = TOpt['context'] extends string
204 ? `${Key & string}${_ContextSeparator}${TOpt['context']}`
205 : Key;
206
207export type TFunctionReturn<
208 Ns extends Namespace,
209 Key,
210 TOpt extends TOptions,
211 ActualNS extends Namespace = NsByTOptions<Ns, TOpt>,
212 ActualKey = KeyWithContext<Key, TOpt>,
213> = $IsResourcesDefined extends true
214 ? ActualKey extends `${infer Nsp}${_NsSeparator}${infer RestKey}`
215 ? ParseTReturn<RestKey, Resources[Nsp & keyof Resources], TOpt>
216 : ParseTReturn<ActualKey, Resources[$FirstNamespace<ActualNS>], TOpt>
217 : DefaultTReturn<TOpt>;
218
219export type TFunctionDetailedResult<T = string, TOpt extends TOptions = {}> = {
220 /**
221 * The plain used key
222 */
223 usedKey: string;
224 /**
225 * The translation result.
226 */
227 res: T;
228 /**
229 * The key with context / plural
230 */
231 exactUsedKey: string;
232 /**
233 * The used language for this translation.
234 */
235 usedLng: string;
236 /**
237 * The used namespace for this translation.
238 */
239 usedNS: string;
240 /**
241 * The parameters used for interpolation.
242 */
243 usedParams: InterpolationMap<T> & { count?: TOpt['count'] };
244};
245
246type TFunctionReturnOptionalDetails<Ret, TOpt extends TOptions> = TOpt['returnDetails'] extends true
247 ? TFunctionDetailedResult<Ret, TOpt>
248 : Ret;
249
250type AppendKeyPrefix<Key, KPrefix> = KPrefix extends string
251 ? `${KPrefix}${_KeySeparator}${Key & string}`
252 : Key;
253
254/** ************************
255 * T function declaration *
256 ************************* */
257export interface TFunction<Ns extends Namespace = DefaultNamespace, KPrefix = undefined> {
258 $TFunctionBrand: $IsResourcesDefined extends true ? `${$FirstNamespace<Ns>}` : never;
259 <
260 const Key extends ParseKeys<Ns, TOpt, KPrefix> | TemplateStringsArray,
261 const TOpt extends TOptions,
262 Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, KPrefix>, TOpt>,
263 const ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>,
264 >(
265 ...args:
266 | [key: Key | Key[], options?: ActualOptions]
267 | [key: string | string[], options: TOpt & $Dictionary & { defaultValue: string }]
268 | [key: string | string[], defaultValue: string, options?: TOpt & $Dictionary]
269 ): TFunctionReturnOptionalDetails<Ret, TOpt>;
270}
271
272export type KeyPrefix<Ns extends Namespace> = ResourceKeys<true>[$FirstNamespace<Ns>] | undefined;