UNPKG

10.1 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'];
32type _UnescapePrefix = TypeOptions['unescapePrefix'];
33type _UnescapeSuffix = TypeOptions['unescapeSuffix'];
34
35type $IsResourcesDefined = [keyof _Resources] extends [never] ? false : true;
36type $ValueIfResourcesDefined<Value, Fallback> = $IsResourcesDefined extends true
37 ? Value
38 : Fallback;
39type $FirstNamespace<Ns extends Namespace> = Ns extends readonly any[] ? Ns[0] : Ns;
40
41type Resources = $ValueIfResourcesDefined<_Resources, $Dictionary<string>>;
42
43type PluralSuffix = _JSONFormat extends 'v4'
44 ? 'zero' | 'one' | 'two' | 'few' | 'many' | 'other'
45 : number | 'plural';
46
47type WithOrWithoutPlural<Key> = _JSONFormat extends 'v4' | 'v3'
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
123// this seems not to work for ts < 4.7.2
124// type ParseKeysByFallbackNs<Keys extends $Dictionary> = _FallbackNamespace extends false
125// ? never
126// : _FallbackNamespace extends (infer UnionFallbackNs extends string)[]
127// ? Keys[UnionFallbackNs]
128// : Keys[_FallbackNamespace & string];
129// so let's try this:
130type First<T> = T extends [infer U, ...any[]] ? U : any;
131type ParseKeysByFallbackNs<Keys extends $Dictionary> = _FallbackNamespace extends false
132 ? never
133 : _FallbackNamespace extends string
134 ? Keys[_FallbackNamespace & string]
135 : Keys[First<_FallbackNamespace>];
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> = $IsResourcesDefined extends true
144 ?
145 | ParseKeysByKeyPrefix<Keys[$FirstNamespace<ActualNS>], KPrefix>
146 | ParseKeysByNamespaces<ActualNS, Keys>
147 | ParseKeysByFallbackNs<Keys>
148 : string;
149
150/** *******************************************************
151 * Parse t function return type and interpolation values *
152 ******************************************************** */
153type ParseActualValue<Ret> = Ret extends `${_UnescapePrefix}${infer ActualValue}${_UnescapeSuffix}`
154 ? TrimSpaces<ActualValue>
155 : Ret;
156
157type ParseInterpolationValues<Ret> =
158 Ret extends `${string}${_InterpolationPrefix}${infer Value}${_InterpolationSuffix}${infer Rest}`
159 ?
160 | (Value extends `${infer ActualValue},${string}`
161 ? ParseActualValue<ActualValue>
162 : ParseActualValue<Value>)
163 | ParseInterpolationValues<Rest>
164 : never;
165
166type InterpolationMap<Ret> = $PreservedValue<
167 $StringKeyPathToRecord<ParseInterpolationValues<Ret>, unknown>,
168 Record<string, unknown>
169>;
170
171type ParseTReturnPlural<
172 Res,
173 Key,
174 KeyWithPlural = `${Key & string}${_PluralSeparator}${PluralSuffix}`,
175> = Res[(KeyWithPlural | Key) & keyof Res];
176
177type ParseTReturnPluralOrdinal<
178 Res,
179 Key,
180 KeyWithOrdinalPlural = `${Key &
181 string}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`,
182> = Res[(KeyWithOrdinalPlural | Key) & keyof Res];
183
184type ParseTReturnWithFallback<Key, Val> = Val extends ''
185 ? _ReturnEmptyString extends true
186 ? ''
187 : Key
188 : Val extends null
189 ? _ReturnNull extends true
190 ? null
191 : Key
192 : Val;
193
194type ParseTReturn<Key, Res, TOpt extends TOptions = {}> = ParseTReturnWithFallback<
195 Key,
196 Key extends `${infer K1}${_KeySeparator}${infer RestKey}`
197 ? ParseTReturn<RestKey, Res[K1 & keyof Res], TOpt>
198 : // Process plurals only if count is provided inside options
199 TOpt['count'] extends number
200 ? TOpt['ordinal'] extends boolean
201 ? ParseTReturnPluralOrdinal<Res, Key>
202 : ParseTReturnPlural<Res, Key>
203 : // otherwise access plain key without adding plural and ordinal suffixes
204 Res extends readonly unknown[]
205 ? Key extends `${infer NKey extends number}`
206 ? Res[NKey]
207 : never
208 : Res[Key & keyof Res]
209>;
210
211type TReturnOptionalNull = _ReturnNull extends true ? null : never;
212type TReturnOptionalObjects<TOpt extends TOptions> = _ReturnObjects extends true
213 ? $SpecialObject | string
214 : TOpt['returnObjects'] extends true
215 ? $SpecialObject
216 : string;
217type DefaultTReturn<TOpt extends TOptions> = TReturnOptionalObjects<TOpt> | TReturnOptionalNull;
218
219export type KeyWithContext<Key, TOpt extends TOptions> = TOpt['context'] extends string
220 ? `${Key & string}${_ContextSeparator}${TOpt['context']}`
221 : Key;
222
223export type TFunctionReturn<
224 Ns extends Namespace,
225 Key,
226 TOpt extends TOptions,
227 ActualNS extends Namespace = NsByTOptions<Ns, TOpt>,
228 ActualKey = KeyWithContext<Key, TOpt>,
229> = $IsResourcesDefined extends true
230 ? ActualKey extends `${infer Nsp}${_NsSeparator}${infer RestKey}`
231 ? ParseTReturn<RestKey, Resources[Nsp & keyof Resources], TOpt>
232 : ParseTReturn<ActualKey, Resources[$FirstNamespace<ActualNS>], TOpt>
233 : DefaultTReturn<TOpt>;
234
235export type TFunctionDetailedResult<T = string, TOpt extends TOptions = {}> = {
236 /**
237 * The plain used key
238 */
239 usedKey: string;
240 /**
241 * The translation result.
242 */
243 res: T;
244 /**
245 * The key with context / plural
246 */
247 exactUsedKey: string;
248 /**
249 * The used language for this translation.
250 */
251 usedLng: string;
252 /**
253 * The used namespace for this translation.
254 */
255 usedNS: string;
256 /**
257 * The parameters used for interpolation.
258 */
259 usedParams: InterpolationMap<T> & { count?: TOpt['count'] };
260};
261
262type TFunctionReturnOptionalDetails<Ret, TOpt extends TOptions> = TOpt['returnDetails'] extends true
263 ? TFunctionDetailedResult<Ret, TOpt>
264 : Ret;
265
266type AppendKeyPrefix<Key, KPrefix> = KPrefix extends string
267 ? `${KPrefix}${_KeySeparator}${Key & string}`
268 : Key;
269
270/** ************************
271 * T function declaration *
272 ************************* */
273export interface TFunction<Ns extends Namespace = DefaultNamespace, KPrefix = undefined> {
274 $TFunctionBrand: $IsResourcesDefined extends true ? `${$FirstNamespace<Ns>}` : never;
275 <
276 Key extends ParseKeys<Ns, TOpt, KPrefix> | TemplateStringsArray,
277 TOpt extends TOptions,
278 Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, KPrefix>, TOpt>,
279 >(
280 ...args:
281 | [key: Key | Key[], options?: TOpt & InterpolationMap<Ret>]
282 | [key: string | string[], options: TOpt & $Dictionary & { defaultValue: string }]
283 | [key: string | string[], defaultValue: string, options?: TOpt & $Dictionary]
284 ): TFunctionReturnOptionalDetails<Ret, TOpt>;
285}
286
287export type KeyPrefix<Ns extends Namespace> = ResourceKeys<true>[$FirstNamespace<Ns>] | undefined;