UNPKG

9.63 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, Context> = Context extends string
120 ? Keys extends `${infer Prefix}${_ContextSeparator}${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 Context extends TOpt['context'] = TOpt['context'],
132> = $IsResourcesDefined extends true
133 ? FilterKeysByContext<
134 | ParseKeysByKeyPrefix<Keys[$FirstNamespace<ActualNS>], KPrefix>
135 | ParseKeysByNamespaces<ActualNS, Keys>
136 | ParseKeysByFallbackNs<Keys>,
137 Context
138 >
139 : string;
140
141/** *******************************************************
142 * Parse t function return type and interpolation values *
143 ******************************************************** */
144type ParseInterpolationValues<Ret> =
145 Ret extends `${string}${_InterpolationPrefix}${infer Value}${_InterpolationSuffix}${infer Rest}`
146 ?
147 | (Value extends `${infer ActualValue},${string}` ? ActualValue : Value)
148 | ParseInterpolationValues<Rest>
149 : never;
150
151type InterpolationMap<Ret> = $PreservedValue<
152 $StringKeyPathToRecord<ParseInterpolationValues<Ret>, unknown>,
153 Record<string, unknown>
154>;
155
156type ParseTReturnPlural<
157 Res,
158 Key,
159 KeyWithPlural = `${Key & string}${_PluralSeparator}${PluralSuffix}`,
160> = Res[(KeyWithPlural | Key) & keyof Res];
161
162type ParseTReturnPluralOrdinal<
163 Res,
164 Key,
165 KeyWithOrdinalPlural = `${Key &
166 string}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`,
167> = Res[(KeyWithOrdinalPlural | Key) & keyof Res];
168
169type ParseTReturnWithFallback<Key, Val> = Val extends ''
170 ? _ReturnEmptyString extends true
171 ? ''
172 : Key
173 : Val extends null
174 ? _ReturnNull extends true
175 ? null
176 : Key
177 : Val;
178
179type ParseTReturn<Key, Res, TOpt extends TOptions = {}> = ParseTReturnWithFallback<
180 Key,
181 Key extends `${infer K1}${_KeySeparator}${infer RestKey}`
182 ? ParseTReturn<RestKey, Res[K1 & keyof Res], TOpt>
183 : // Process plurals only if count is provided inside options
184 TOpt['count'] extends number
185 ? TOpt['ordinal'] extends boolean
186 ? ParseTReturnPluralOrdinal<Res, Key>
187 : ParseTReturnPlural<Res, Key>
188 : // otherwise access plain key without adding plural and ordinal suffixes
189 Res extends readonly unknown[]
190 ? Key extends `${infer NKey extends number}`
191 ? Res[NKey]
192 : never
193 : Res[Key & keyof Res]
194>;
195
196type TReturnOptionalNull = _ReturnNull extends true ? null : never;
197type TReturnOptionalObjects<TOpt extends TOptions> = _ReturnObjects extends true
198 ? $SpecialObject | string
199 : TOpt['returnObjects'] extends true
200 ? $SpecialObject
201 : string;
202type DefaultTReturn<TOpt extends TOptions> = TReturnOptionalObjects<TOpt> | TReturnOptionalNull;
203
204export type KeyWithContext<Key, TOpt extends TOptions> = TOpt['context'] extends string
205 ? `${Key & string}${_ContextSeparator}${TOpt['context']}`
206 : Key;
207
208export type TFunctionReturn<
209 Ns extends Namespace,
210 Key,
211 TOpt extends TOptions,
212 ActualNS extends Namespace = NsByTOptions<Ns, TOpt>,
213 ActualKey = KeyWithContext<Key, TOpt>,
214> = $IsResourcesDefined extends true
215 ? ActualKey extends `${infer Nsp}${_NsSeparator}${infer RestKey}`
216 ? ParseTReturn<RestKey, Resources[Nsp & keyof Resources], TOpt>
217 : ParseTReturn<ActualKey, Resources[$FirstNamespace<ActualNS>], TOpt>
218 : DefaultTReturn<TOpt>;
219
220export type TFunctionDetailedResult<T = string, TOpt extends TOptions = {}> = {
221 /**
222 * The plain used key
223 */
224 usedKey: string;
225 /**
226 * The translation result.
227 */
228 res: T;
229 /**
230 * The key with context / plural
231 */
232 exactUsedKey: string;
233 /**
234 * The used language for this translation.
235 */
236 usedLng: string;
237 /**
238 * The used namespace for this translation.
239 */
240 usedNS: string;
241 /**
242 * The parameters used for interpolation.
243 */
244 usedParams: InterpolationMap<T> & { count?: TOpt['count'] };
245};
246
247type TFunctionReturnOptionalDetails<Ret, TOpt extends TOptions> = TOpt['returnDetails'] extends true
248 ? TFunctionDetailedResult<Ret, TOpt>
249 : Ret;
250
251type AppendKeyPrefix<Key, KPrefix> = KPrefix extends string
252 ? `${KPrefix}${_KeySeparator}${Key & string}`
253 : Key;
254
255/** ************************
256 * T function declaration *
257 ************************* */
258export interface TFunction<Ns extends Namespace = DefaultNamespace, KPrefix = undefined> {
259 $TFunctionBrand: $IsResourcesDefined extends true ? `${$FirstNamespace<Ns>}` : never;
260 <
261 const Key extends ParseKeys<Ns, TOpt, KPrefix> | TemplateStringsArray,
262 const TOpt extends TOptions,
263 Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, KPrefix>, TOpt>,
264 const ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>,
265 >(
266 ...args:
267 | [key: Key | Key[], options?: ActualOptions]
268 | [key: string | string[], options: TOpt & $Dictionary & { defaultValue: string }]
269 | [key: string | string[], defaultValue: string, options?: TOpt & $Dictionary]
270 ): TFunctionReturnOptionalDetails<Ret, TOpt>;
271}
272
273export type KeyPrefix<Ns extends Namespace> = ResourceKeys<true>[$FirstNamespace<Ns>] | undefined;