1 | import type {
|
2 | $OmitArrayKeys,
|
3 | $PreservedValue,
|
4 | $Dictionary,
|
5 | $SpecialObject,
|
6 | $StringKeyPathToRecord,
|
7 | } from './helpers.js';
|
8 | import type {
|
9 | TypeOptions,
|
10 | Namespace,
|
11 | FlatNamespace,
|
12 | DefaultNamespace,
|
13 | TOptions,
|
14 | } from './options.js';
|
15 |
|
16 |
|
17 |
|
18 |
|
19 |
|
20 | type _ReturnObjects = TypeOptions['returnObjects'];
|
21 | type _ReturnEmptyString = TypeOptions['returnEmptyString'];
|
22 | type _ReturnNull = TypeOptions['returnNull'];
|
23 | type _KeySeparator = TypeOptions['keySeparator'];
|
24 | type _NsSeparator = TypeOptions['nsSeparator'];
|
25 | type _PluralSeparator = TypeOptions['pluralSeparator'];
|
26 | type _ContextSeparator = TypeOptions['contextSeparator'];
|
27 | type _FallbackNamespace = TypeOptions['fallbackNS'];
|
28 | type _Resources = TypeOptions['resources'];
|
29 | type _JSONFormat = TypeOptions['jsonFormat'];
|
30 | type _InterpolationPrefix = TypeOptions['interpolationPrefix'];
|
31 | type _InterpolationSuffix = TypeOptions['interpolationSuffix'];
|
32 | type _UnescapePrefix = TypeOptions['unescapePrefix'];
|
33 | type _UnescapeSuffix = TypeOptions['unescapeSuffix'];
|
34 |
|
35 | type $IsResourcesDefined = [keyof _Resources] extends [never] ? false : true;
|
36 | type $ValueIfResourcesDefined<Value, Fallback> = $IsResourcesDefined extends true
|
37 | ? Value
|
38 | : Fallback;
|
39 | type $FirstNamespace<Ns extends Namespace> = Ns extends readonly any[] ? Ns[0] : Ns;
|
40 |
|
41 | type Resources = $ValueIfResourcesDefined<_Resources, $Dictionary<string>>;
|
42 |
|
43 | type PluralSuffix = _JSONFormat extends 'v4'
|
44 | ? 'zero' | 'one' | 'two' | 'few' | 'many' | 'other'
|
45 | : number | 'plural';
|
46 |
|
47 | type 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 |
|
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 |
|
124 |
|
125 |
|
126 |
|
127 |
|
128 |
|
129 |
|
130 | type First<T> = T extends [infer U, ...any[]] ? U : any;
|
131 | type ParseKeysByFallbackNs<Keys extends $Dictionary> = _FallbackNamespace extends false
|
132 | ? never
|
133 | : _FallbackNamespace extends string
|
134 | ? Keys[_FallbackNamespace & string]
|
135 | : Keys[First<_FallbackNamespace>];
|
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 | > = $IsResourcesDefined extends true
|
144 | ?
|
145 | | ParseKeysByKeyPrefix<Keys[$FirstNamespace<ActualNS>], KPrefix>
|
146 | | ParseKeysByNamespaces<ActualNS, Keys>
|
147 | | ParseKeysByFallbackNs<Keys>
|
148 | : string;
|
149 |
|
150 |
|
151 |
|
152 |
|
153 | type ParseActualValue<Ret> = Ret extends `${_UnescapePrefix}${infer ActualValue}${_UnescapeSuffix}`
|
154 | ? TrimSpaces<ActualValue>
|
155 | : Ret;
|
156 |
|
157 | type 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 |
|
166 | type InterpolationMap<Ret> = $PreservedValue<
|
167 | $StringKeyPathToRecord<ParseInterpolationValues<Ret>, unknown>,
|
168 | Record<string, unknown>
|
169 | >;
|
170 |
|
171 | type ParseTReturnPlural<
|
172 | Res,
|
173 | Key,
|
174 | KeyWithPlural = `${Key & string}${_PluralSeparator}${PluralSuffix}`,
|
175 | > = Res[(KeyWithPlural | Key) & keyof Res];
|
176 |
|
177 | type ParseTReturnPluralOrdinal<
|
178 | Res,
|
179 | Key,
|
180 | KeyWithOrdinalPlural = `${Key &
|
181 | string}${_PluralSeparator}ordinal${_PluralSeparator}${PluralSuffix}`,
|
182 | > = Res[(KeyWithOrdinalPlural | Key) & keyof Res];
|
183 |
|
184 | type 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 |
|
194 | type 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 | :
|
199 | TOpt['count'] extends number
|
200 | ? TOpt['ordinal'] extends boolean
|
201 | ? ParseTReturnPluralOrdinal<Res, Key>
|
202 | : ParseTReturnPlural<Res, Key>
|
203 | :
|
204 | Res extends readonly unknown[]
|
205 | ? Key extends `${infer NKey extends number}`
|
206 | ? Res[NKey]
|
207 | : never
|
208 | : Res[Key & keyof Res]
|
209 | >;
|
210 |
|
211 | type TReturnOptionalNull = _ReturnNull extends true ? null : never;
|
212 | type TReturnOptionalObjects<TOpt extends TOptions> = _ReturnObjects extends true
|
213 | ? $SpecialObject | string
|
214 | : TOpt['returnObjects'] extends true
|
215 | ? $SpecialObject
|
216 | : string;
|
217 | type DefaultTReturn<TOpt extends TOptions> = TReturnOptionalObjects<TOpt> | TReturnOptionalNull;
|
218 |
|
219 | export type KeyWithContext<Key, TOpt extends TOptions> = TOpt['context'] extends string
|
220 | ? `${Key & string}${_ContextSeparator}${TOpt['context']}`
|
221 | : Key;
|
222 |
|
223 | export 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 |
|
235 | export type TFunctionDetailedResult<T = string, TOpt extends TOptions = {}> = {
|
236 | |
237 |
|
238 |
|
239 | usedKey: string;
|
240 | |
241 |
|
242 |
|
243 | res: T;
|
244 | |
245 |
|
246 |
|
247 | exactUsedKey: string;
|
248 | |
249 |
|
250 |
|
251 | usedLng: string;
|
252 | |
253 |
|
254 |
|
255 | usedNS: string;
|
256 | |
257 |
|
258 |
|
259 | usedParams: InterpolationMap<T> & { count?: TOpt['count'] };
|
260 | };
|
261 |
|
262 | type TFunctionReturnOptionalDetails<Ret, TOpt extends TOptions> = TOpt['returnDetails'] extends true
|
263 | ? TFunctionDetailedResult<Ret, TOpt>
|
264 | : Ret;
|
265 |
|
266 | type AppendKeyPrefix<Key, KPrefix> = KPrefix extends string
|
267 | ? `${KPrefix}${_KeySeparator}${Key & string}`
|
268 | : Key;
|
269 |
|
270 |
|
271 |
|
272 |
|
273 | export 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 |
|
287 | export type KeyPrefix<Ns extends Namespace> = ResourceKeys<true>[$FirstNamespace<Ns>] | undefined;
|