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 _JSONFormat = TypeOptions['jsonFormat'];
|
31 | type _InterpolationPrefix = TypeOptions['interpolationPrefix'];
|
32 | type _InterpolationSuffix = TypeOptions['interpolationSuffix'];
|
33 | type _UnescapePrefix = TypeOptions['unescapePrefix'];
|
34 | type _UnescapeSuffix = TypeOptions['unescapeSuffix'];
|
35 |
|
36 | type $IsResourcesDefined = [keyof _Resources] extends [never] ? false : true;
|
37 | type $ValueIfResourcesDefined<Value, Fallback> = $IsResourcesDefined extends true
|
38 | ? Value
|
39 | : Fallback;
|
40 | type $FirstNamespace<Ns extends Namespace> = Ns extends readonly any[] ? Ns[0] : Ns;
|
41 |
|
42 | type Resources = $ValueIfResourcesDefined<_Resources, $Dictionary<string>>;
|
43 |
|
44 | type PluralSuffix = _JSONFormat extends 'v4'
|
45 | ? 'zero' | 'one' | 'two' | 'few' | 'many' | 'other'
|
46 | : number | 'plural';
|
47 |
|
48 | type 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 |
|
56 | type JoinKeys<K1, K2> = `${K1 & string}${_KeySeparator}${K2 & string}`;
|
57 | type AppendNamespace<Ns, Keys> = `${Ns & string}${_NsSeparator}${Keys & string}`;
|
58 |
|
59 | type 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 |
|
69 |
|
70 | type 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 |
|
78 | type 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 |
|
84 | type KeysBuilder<Res, WithReturnObjects> = $IsResourcesDefined extends true
|
85 | ? WithReturnObjects extends true
|
86 | ? keyof Res | KeysBuilderWithReturnObjects<Res>
|
87 | : KeysBuilderWithoutReturnObjects<Res>
|
88 | : string;
|
89 |
|
90 | type KeysWithReturnObjects = {
|
91 | [Ns in FlatNamespace]: WithOrWithoutPlural<KeysBuilder<Resources[Ns], true>>;
|
92 | };
|
93 | type KeysWithoutReturnObjects = {
|
94 | [Ns in FlatNamespace]: WithOrWithoutPlural<KeysBuilder<Resources[Ns], false>>;
|
95 | };
|
96 |
|
97 | type ResourceKeys<WithReturnObjects = _ReturnObjects> = WithReturnObjects extends true
|
98 | ? KeysWithReturnObjects
|
99 | : KeysWithoutReturnObjects;
|
100 |
|
101 |
|
102 |
|
103 |
|
104 | export type KeysByTOptions<TOpt extends TOptions> = TOpt['returnObjects'] extends true
|
105 | ? ResourceKeys<true>
|
106 | : ResourceKeys;
|
107 |
|
108 | export type NsByTOptions<Ns extends Namespace, TOpt extends TOptions> = TOpt['ns'] extends Namespace
|
109 | ? TOpt['ns']
|
110 | : Ns;
|
111 |
|
112 | type ParseKeysByKeyPrefix<Keys, KPrefix> = KPrefix extends string
|
113 | ? Keys extends `${KPrefix}${_KeySeparator}${infer Key}`
|
114 | ? Key
|
115 | : never
|
116 | : Keys;
|
117 |
|
118 | type 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 |
|
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 |
|
130 | export 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 |
|
138 | export 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 |
|
156 |
|
157 | type ParseActualValue<Ret> = Ret extends `${_UnescapePrefix}${infer ActualValue}${_UnescapeSuffix}`
|
158 | ? TrimSpaces<ActualValue>
|
159 | : Ret;
|
160 |
|
161 | type 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 |
|
170 | type InterpolationMap<Ret> = $PreservedValue<
|
171 | $StringKeyPathToRecord<ParseInterpolationValues<Ret>, unknown>,
|
172 | Record<string, unknown>
|
173 | >;
|
174 |
|
175 | type ParseTReturnPlural<
|
176 | Res,
|
177 | Key,
|
178 | KeyWithPlural = `${Key & string}${_PluralSeparator}${PluralSuffix}`,
|
179 | > = Res[(KeyWithPlural | Key) & keyof Res];
|
180 |
|
181 | type ParseTReturnPluralOrdinal<
|
182 | Res,
|
183 | Key,
|
184 | KeyWithOrdinalPlural = `${Key &
|
185 | string}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`,
|
186 | > = Res[(KeyWithOrdinalPlural | Key) & keyof Res];
|
187 |
|
188 | type 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 |
|
198 | type 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 | :
|
203 | TOpt['count'] extends number
|
204 | ? TOpt['ordinal'] extends boolean
|
205 | ? ParseTReturnPluralOrdinal<Res, Key>
|
206 | : ParseTReturnPlural<Res, Key>
|
207 | :
|
208 | Res extends readonly unknown[]
|
209 | ? Key extends `${infer NKey extends number}`
|
210 | ? Res[NKey]
|
211 | : never
|
212 | : Res[Key & keyof Res]
|
213 | >;
|
214 |
|
215 | type TReturnOptionalNull = _ReturnNull extends true ? null : never;
|
216 | type TReturnOptionalObjects<TOpt extends TOptions> = _ReturnObjects extends true
|
217 | ? $SpecialObject | string
|
218 | : TOpt['returnObjects'] extends true
|
219 | ? $SpecialObject
|
220 | : string;
|
221 | type DefaultTReturn<TOpt extends TOptions> = TReturnOptionalObjects<TOpt> | TReturnOptionalNull;
|
222 |
|
223 | export type KeyWithContext<Key, TOpt extends TOptions> = TOpt['context'] extends string
|
224 | ? `${Key & string}${_ContextSeparator}${TOpt['context']}`
|
225 | : Key;
|
226 |
|
227 | export 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 |
|
239 | export type TFunctionDetailedResult<T = string, TOpt extends TOptions = {}> = {
|
240 | |
241 |
|
242 |
|
243 | usedKey: string;
|
244 | |
245 |
|
246 |
|
247 | res: T;
|
248 | |
249 |
|
250 |
|
251 | exactUsedKey: string;
|
252 | |
253 |
|
254 |
|
255 | usedLng: string;
|
256 | |
257 |
|
258 |
|
259 | usedNS: string;
|
260 | |
261 |
|
262 |
|
263 | usedParams: InterpolationMap<T> & { count?: TOpt['count'] };
|
264 | };
|
265 |
|
266 | type TFunctionProcessReturnValue<Ret, DefaultValue> = Ret extends string | $SpecialObject | null
|
267 | ? Ret
|
268 | : [DefaultValue] extends [never]
|
269 | ? Ret
|
270 | : DefaultValue;
|
271 |
|
272 | type TFunctionReturnOptionalDetails<Ret, TOpt extends TOptions> = TOpt['returnDetails'] extends true
|
273 | ? TFunctionDetailedResult<Ret, TOpt>
|
274 | : Ret;
|
275 |
|
276 | type AppendKeyPrefix<Key, KPrefix> = KPrefix extends string
|
277 | ? `${KPrefix}${_KeySeparator}${Key & string}`
|
278 | : Key;
|
279 |
|
280 |
|
281 |
|
282 |
|
283 | export 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 |
|
299 | export type KeyPrefix<Ns extends Namespace> = ResourceKeys<true>[$FirstNamespace<Ns>] | undefined;
|