UNPKG

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