UNPKG

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