1 | import type {
|
2 | $OmitArrayKeys,
|
3 | $PreservedValue,
|
4 | $Dictionary,
|
5 | $SpecialObject,
|
6 | $StringKeyPathToRecord,
|
7 | $NoInfer,
|
8 | } from './helpers.js';
|
9 | import type {
|
10 | TypeOptions,
|
11 | Namespace,
|
12 | FlatNamespace,
|
13 | DefaultNamespace,
|
14 | TOptions,
|
15 | } from './options.js';
|
16 |
|
17 |
|
18 |
|
19 |
|
20 |
|
21 | type _ReturnObjects = TypeOptions['returnObjects'];
|
22 | type _ReturnEmptyString = TypeOptions['returnEmptyString'];
|
23 | type _ReturnNull = TypeOptions['returnNull'];
|
24 | type _KeySeparator = TypeOptions['keySeparator'];
|
25 | type _NsSeparator = TypeOptions['nsSeparator'];
|
26 | type _PluralSeparator = TypeOptions['pluralSeparator'];
|
27 | type _ContextSeparator = TypeOptions['contextSeparator'];
|
28 | type _FallbackNamespace = TypeOptions['fallbackNS'];
|
29 | type _Resources = TypeOptions['resources'];
|
30 | type _CompatibilityJSON = TypeOptions['compatibilityJSON'];
|
31 | type _InterpolationPrefix = TypeOptions['interpolationPrefix'];
|
32 | type _InterpolationSuffix = TypeOptions['interpolationSuffix'];
|
33 | type _UnescapePrefix = TypeOptions['unescapePrefix'];
|
34 | type _UnescapeSuffix = TypeOptions['unescapeSuffix'];
|
35 | type _StrictKeyChecks = TypeOptions['strictKeyChecks'];
|
36 |
|
37 | type $IsResourcesDefined = [keyof _Resources] extends [never] ? false : true;
|
38 | type $ValueIfResourcesDefined<Value, Fallback> = $IsResourcesDefined extends true
|
39 | ? Value
|
40 | : Fallback;
|
41 | type $FirstNamespace<Ns extends Namespace> = Ns extends readonly any[] ? Ns[0] : Ns;
|
42 |
|
43 | type Resources = $ValueIfResourcesDefined<_Resources, $Dictionary<string>>;
|
44 |
|
45 | type PluralSuffix = 'zero' | 'one' | 'two' | 'few' | 'many' | 'other';
|
46 |
|
47 | type 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 |
|
55 | type JoinKeys<K1, K2> = `${K1 & string}${_KeySeparator}${K2 & string}`;
|
56 | type AppendNamespace<Ns, Keys> = `${Ns & string}${_NsSeparator}${Keys & string}`;
|
57 |
|
58 | type 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 |
|
68 |
|
69 | type 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 |
|
77 | type 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 |
|
83 | type KeysBuilder<Res, WithReturnObjects> = $IsResourcesDefined extends true
|
84 | ? WithReturnObjects extends true
|
85 | ? keyof Res | KeysBuilderWithReturnObjects<Res>
|
86 | : KeysBuilderWithoutReturnObjects<Res>
|
87 | : string;
|
88 |
|
89 | type KeysWithReturnObjects = {
|
90 | [Ns in FlatNamespace]: WithOrWithoutPlural<KeysBuilder<Resources[Ns], true>>;
|
91 | };
|
92 | type KeysWithoutReturnObjects = {
|
93 | [Ns in FlatNamespace]: WithOrWithoutPlural<KeysBuilder<Resources[Ns], false>>;
|
94 | };
|
95 |
|
96 | type ResourceKeys<WithReturnObjects = _ReturnObjects> = WithReturnObjects extends true
|
97 | ? KeysWithReturnObjects
|
98 | : KeysWithoutReturnObjects;
|
99 |
|
100 |
|
101 |
|
102 |
|
103 | export type KeysByTOptions<TOpt extends TOptions> = TOpt['returnObjects'] extends true
|
104 | ? ResourceKeys<true>
|
105 | : ResourceKeys;
|
106 |
|
107 | export type NsByTOptions<Ns extends Namespace, TOpt extends TOptions> = TOpt['ns'] extends Namespace
|
108 | ? TOpt['ns']
|
109 | : Ns;
|
110 |
|
111 | type ParseKeysByKeyPrefix<Keys, KPrefix> = KPrefix extends string
|
112 | ? Keys extends `${KPrefix}${_KeySeparator}${infer Key}`
|
113 | ? Key
|
114 | : never
|
115 | : Keys;
|
116 |
|
117 | type 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 | type ParseKeysByFallbackNs<Keys extends $Dictionary> = _FallbackNamespace extends false
|
124 | ? never
|
125 | : _FallbackNamespace extends (infer UnionFallbackNs extends string)[]
|
126 | ? Keys[UnionFallbackNs]
|
127 | : Keys[_FallbackNamespace & string];
|
128 |
|
129 | export 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 |
|
137 | export 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 |
|
155 |
|
156 | type ParseActualValue<Ret> = Ret extends `${_UnescapePrefix}${infer ActualValue}${_UnescapeSuffix}`
|
157 | ? TrimSpaces<ActualValue>
|
158 | : Ret;
|
159 |
|
160 | type 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 |
|
169 | type InterpolationMap<Ret> = $PreservedValue<
|
170 | $StringKeyPathToRecord<ParseInterpolationValues<Ret>, unknown>,
|
171 | Record<string, unknown>
|
172 | >;
|
173 |
|
174 | type ParseTReturnPlural<
|
175 | Res,
|
176 | Key,
|
177 | KeyWithPlural = `${Key & string}${_PluralSeparator}${PluralSuffix}`,
|
178 | > = Res[(KeyWithPlural | Key) & keyof Res];
|
179 |
|
180 | type ParseTReturnPluralOrdinal<
|
181 | Res,
|
182 | Key,
|
183 | KeyWithOrdinalPlural = `${Key &
|
184 | string}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`,
|
185 | > = Res[(KeyWithOrdinalPlural | Key) & keyof Res];
|
186 |
|
187 | type 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 |
|
197 | type 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 | :
|
202 | TOpt['count'] extends number
|
203 | ? TOpt['ordinal'] extends boolean
|
204 | ? ParseTReturnPluralOrdinal<Res, Key>
|
205 | : ParseTReturnPlural<Res, Key>
|
206 | :
|
207 | Res extends readonly unknown[]
|
208 | ? Key extends `${infer NKey extends number}`
|
209 | ? Res[NKey]
|
210 | : never
|
211 | : Res[Key & keyof Res]
|
212 | >;
|
213 |
|
214 | type TReturnOptionalNull = _ReturnNull extends true ? null : never;
|
215 | type TReturnOptionalObjects<TOpt extends TOptions> = _ReturnObjects extends true
|
216 | ? $SpecialObject | string
|
217 | : TOpt['returnObjects'] extends true
|
218 | ? $SpecialObject
|
219 | : string;
|
220 | type DefaultTReturn<TOpt extends TOptions> = TReturnOptionalObjects<TOpt> | TReturnOptionalNull;
|
221 |
|
222 | export type KeyWithContext<Key, TOpt extends TOptions> = TOpt['context'] extends string
|
223 | ? `${Key & string}${_ContextSeparator}${TOpt['context']}`
|
224 | : Key;
|
225 |
|
226 | export 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 |
|
238 | export type TFunctionDetailedResult<T = string, TOpt extends TOptions = {}> = {
|
239 | |
240 |
|
241 |
|
242 | usedKey: string;
|
243 | |
244 |
|
245 |
|
246 | res: T;
|
247 | |
248 |
|
249 |
|
250 | exactUsedKey: string;
|
251 | |
252 |
|
253 |
|
254 | usedLng: string;
|
255 | |
256 |
|
257 |
|
258 | usedNS: string;
|
259 | |
260 |
|
261 |
|
262 | usedParams: InterpolationMap<T> & { count?: TOpt['count'] };
|
263 | };
|
264 |
|
265 | type TFunctionProcessReturnValue<Ret, DefaultValue> = Ret extends string | $SpecialObject | null
|
266 | ? Ret
|
267 | : [DefaultValue] extends [never]
|
268 | ? Ret
|
269 | : DefaultValue;
|
270 |
|
271 | type TFunctionReturnOptionalDetails<Ret, TOpt extends TOptions> = TOpt['returnDetails'] extends true
|
272 | ? TFunctionDetailedResult<Ret, TOpt>
|
273 | : Ret;
|
274 |
|
275 | type AppendKeyPrefix<Key, KPrefix> = KPrefix extends string
|
276 | ? `${KPrefix}${_KeySeparator}${Key & string}`
|
277 | : Key;
|
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 | interface 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 |
|
304 | interface 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 |
|
320 | export type TFunction<
|
321 | Ns extends Namespace = DefaultNamespace,
|
322 | KPrefix = undefined,
|
323 | > = _StrictKeyChecks extends true ? TFunctionStrict<Ns, KPrefix> : TFunctionNonStrict<Ns, KPrefix>;
|
324 |
|
325 | export type KeyPrefix<Ns extends Namespace> = ResourceKeys<true>[$FirstNamespace<Ns>] | undefined;
|