locl-js 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Sztrida Sámuel
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,380 @@
1
+ /**
2
+ * The type of the default formatters object.
3
+ */
4
+ type DefaultFormatters = typeof defaultFormatters;
5
+ /**
6
+ * A collection of default formatting functions that can be used in translations.
7
+ * @example
8
+ * ```ts
9
+ * const translator = initLangPack({
10
+ * resources: {
11
+ * en: { greeting: "Hello, {name | upper}!" }
12
+ * },
13
+ * fallbackLanguage: "en"
14
+ * });
15
+ *
16
+ * translator.t("greeting", { name: "world" }); // "Hello, WORLD!"
17
+ * ```
18
+ */
19
+ declare const defaultFormatters: {
20
+ /**
21
+ * Converts a string to uppercase.
22
+ * @param val - The value to format.
23
+ * @returns The uppercased string.
24
+ */
25
+ upper: (val: any) => string;
26
+ /**
27
+ * Converts a string to lowercase.
28
+ * @param val - The value to format.
29
+ * @returns The lowercased string.
30
+ */
31
+ lower: (val: any) => string;
32
+ /**
33
+ * Capitalizes the first letter of a string.
34
+ * @param val - The value to format.
35
+ * @returns The capitalized string.
36
+ */
37
+ capitalize: (val: any) => string;
38
+ /**
39
+ * Trims whitespace from the beginning and end of a string.
40
+ * @param val - The value to format.
41
+ * @returns The trimmed string.
42
+ */
43
+ trim: (val: any) => string;
44
+ /**
45
+ * Truncates a string to a specified length.
46
+ * @param val - The value to format.
47
+ * @param args - An array of arguments.
48
+ * @param {number} [args[0]=10] - The maximum length of the string.
49
+ * @param {string} [args[1]="..."] - The suffix to append if the string is truncated.
50
+ * @returns The truncated string.
51
+ */
52
+ truncate: (val: any, args: string[]) => string;
53
+ /**
54
+ * Formats a number using `Intl.NumberFormat`.
55
+ * @param val - The value to format.
56
+ * @param args - An array of arguments.
57
+ * @param {string} [args[0]] - The locale to use.
58
+ * @param {string} [args[1]] - The style of formatting to use (e.g., "decimal", "percent").
59
+ * @returns The formatted number.
60
+ */
61
+ number: (val: any, args: string[]) => string;
62
+ /**
63
+ * Formats a number as a currency string using `Intl.NumberFormat`.
64
+ * @param val - The value to format.
65
+ * @param args - An array of arguments.
66
+ * @param {string} [args[0]="USD"] - The currency code.
67
+ * @param {string} [args[1]] - The locale to use.
68
+ * @returns The formatted currency string.
69
+ */
70
+ currency: (val: any, args: string[]) => string;
71
+ /**
72
+ * Formats a date using `Intl.DateTimeFormat`.
73
+ * @param val - The value to format.
74
+ * @param args - An array of arguments.
75
+ * @param {string} [args[0]] - The locale to use.
76
+ * @param {string} [args[1]] - The date style to use (e.g., "short", "long").
77
+ * @returns The formatted date string.
78
+ */
79
+ date: (val: any, args: string[]) => string;
80
+ /**
81
+ * Formats a date as a relative time string (e.g., "2 hours ago").
82
+ * @param val - The value to format.
83
+ * @param args - An array of arguments.
84
+ * @param {string} [args[0]] - The locale to use.
85
+ * @returns The relative time string.
86
+ */
87
+ relativeDate: (val: any, args: string[]) => string;
88
+ /**
89
+ * Converts a value to a JSON string.
90
+ * @param val - The value to format.
91
+ * @returns The JSON string.
92
+ */
93
+ json: (val: any) => string;
94
+ /**
95
+ * Converts a boolean value to a "Yes" or "No" string.
96
+ * @param val - The value to format.
97
+ * @param args - An array of arguments.
98
+ * @param {string} [args[0]="Yes"] - The string to return for a truthy value.
99
+ * @param {string} [args[1]="No"] - The string to return for a falsy value.
100
+ * @returns "Yes" or "No".
101
+ */
102
+ yesNo: (val: any, args?: [string, string]) => string;
103
+ /**
104
+ * Converts a boolean value to a "true" or "false" string.
105
+ * @param val - The value to format.
106
+ * @param args - An array of arguments.
107
+ * @param {string} [args[0]="true"] - The string to return for a truthy value.
108
+ * @param {string} [args[1]="false"] - The string to return for a falsy value.
109
+ * @returns "true" or "false".
110
+ */
111
+ boolean: (val: any, args?: [string, string]) => string;
112
+ /**
113
+ * Pads the start of a string with another string.
114
+ * @param val - The value to format.
115
+ * @param args - An array of arguments.
116
+ * @param {number} [args[0]=0] - The target length of the string.
117
+ * @param {string} [args[1]=" "] - The string to pad with.
118
+ * @returns The padded string.
119
+ */
120
+ padStart: (val: any, args: string[]) => string;
121
+ /**
122
+ * Pads the end of a string with another string.
123
+ * @param val - The value to format.
124
+ * @param args - An array of arguments.
125
+ * @param {number} [args[0]=0] - The target length of the string.
126
+ * @param {string} [args[1]=" "] - The string to pad with.
127
+ * @returns The padded string.
128
+ */
129
+ padEnd: (val: any, args: string[]) => string;
130
+ };
131
+
132
+ type NestedKeyOf<ObjectType extends object> = {
133
+ [Key in keyof ObjectType & string]: ObjectType[Key] extends any[] ? `${Key}` : ObjectType[Key] extends object ? `${Key}` | `${Key}.${NestedKeyOf<ObjectType[Key]>}` : `${Key}`;
134
+ }[keyof ObjectType & string];
135
+ type NestedKeyOfObj<ObjectType extends object, SkipArrays extends boolean = false> = {
136
+ [Key in keyof ObjectType & string]: ObjectType[Key] extends Array<any> ? SkipArrays extends true ? never : `${Key}` | `${Key}.${number}` : ObjectType[Key] extends object ? `${Key}` | `${Key}.${NestedKeyOfObj<ObjectType[Key], SkipArrays>}` : never;
137
+ }[keyof ObjectType & string];
138
+ type PathValue<T, P extends string> = P extends `${infer Key}.${infer Rest}` ? Key extends keyof T ? Rest extends string ? PathValue<T[Key], Rest> : never : never : P extends keyof T ? T[P] : never;
139
+ type UnionToIntersection<U> = (U extends any ? (k: U) => void : never) extends ((k: infer I) => void) ? I : never;
140
+ type DeepPick<T, P extends string> = P extends `${infer K}.${infer Rest}` ? K extends keyof T ? {
141
+ [Key in K]: DeepPick<T[K], Rest>;
142
+ } : never : P extends keyof T ? {
143
+ [Key in P]: T[P];
144
+ } : never;
145
+ type Language<T extends Record<string, any>> = keyof T & string;
146
+ type Scope<T extends Record<string, any>, Fallback extends keyof T> = NestedKeyOfObj<T[Fallback], true>;
147
+ type ScopeType<T extends Record<string, any>, F extends keyof T> = Scope<T, F> | Scope<T, F>[] | undefined;
148
+ type EnsureObject<T> = T extends object ? T : {};
149
+ type TranslationObjectFor<S extends Scope<T, Fallback> | Scope<T, Fallback>[] | undefined, T extends Record<string, any>, Fallback extends keyof T> = S extends undefined ? EnsureObject<T[Fallback]> : S extends Scope<T, Fallback> ? EnsureObject<PathValue<T[Fallback], S>> : S extends Scope<T, Fallback>[] ? EnsureObject<UnionToIntersection<DeepPick<T[Fallback], S[number]>>> : never;
150
+ type Translations<S extends ScopeType<T, F>, T extends Record<string, any>, F extends keyof T & string> = TranslationObjectFor<S, T, F>;
151
+ type PluralForms = "zero" | "one" | "two" | "few" | "many" | "other";
152
+ type PluralKeys<S extends ScopeType<T, F>, T extends Record<string, any>, F extends keyof T & string> = ExtractPluralKeys<Translations<S, T, F>>;
153
+ type ExtractPluralKeys<T, Path extends string = "", Depth extends any[] = []> = Depth['length'] extends 32 ? never : T extends object ? {
154
+ [K in keyof T & string]: T[K] extends Partial<Record<PluralForms, string>> ? Path extends "" ? K : `${Path}.${K}` : K extends `${infer Base}_one` ? `${Base}_other` extends keyof T ? Path extends "" ? Base : `${Path}.${Base}` : never : T[K] extends object ? ExtractPluralKeys<T[K], Path extends "" ? K : `${Path}.${K}`, [...Depth, 1]> : never;
155
+ }[keyof T & string] : never;
156
+ interface InterpolationOptions {
157
+ [key: string]: string | number | Date | undefined;
158
+ count?: number;
159
+ }
160
+ interface PluralInterpolationOptions extends InterpolationOptions {
161
+ count: number;
162
+ }
163
+ interface FormatOptions<F extends Record<string, Formatter>, UseDefaultFormatter extends boolean> {
164
+ formatter: keyof EffectiveFormatters<F, UseDefaultFormatter>;
165
+ args?: string[];
166
+ }
167
+ type Formatter<TArgs extends unknown[] = string[]> = (value: any, args?: TArgs) => string;
168
+
169
+ type EffectiveFormatters<F extends Record<string, Formatter>, UseDefault extends boolean> = UseDefault extends true ? F & DefaultFormatters : F;
170
+ type RequiredPlural = "one" | "other";
171
+ type OptionalPlural = Exclude<PluralForms, RequiredPlural>;
172
+ type ObjectPlural = {
173
+ [K in RequiredPlural]: string;
174
+ } & Partial<Record<OptionalPlural, string>>;
175
+ type Primitive = string | number | boolean | null | undefined;
176
+ type StrKey<T> = Extract<keyof T, string>;
177
+ type PluralObjectKeys<T> = {
178
+ [K in StrKey<T>]: T[K] extends object ? Extract<keyof T[K], PluralForms> extends never ? never : K : never;
179
+ }[StrKey<T>];
180
+ type SuffixKey<T> = Extract<StrKey<T>, `${string}_${PluralForms}`>;
181
+ type SuffixBaseUnion<T> = SuffixKey<T> extends `${infer B}_${PluralForms}` ? B : never;
182
+ type NamespaceKeys<T> = {
183
+ [K in StrKey<T>]: T[K] extends object ? Extract<keyof T[K], PluralForms> extends never ? K : never : never;
184
+ }[StrKey<T>];
185
+ type PlainStringKeys<T> = {
186
+ [K in StrKey<T>]: K extends SuffixKey<T> ? never : T[K] extends string ? K : never;
187
+ }[StrKey<T>];
188
+ type LangWithPlurals<T> = T extends Primitive ? T : T extends (infer U)[] ? LangWithPlurals<U>[] : {
189
+ [K in NamespaceKeys<T>]: LangWithPlurals<T[K]>;
190
+ } & {
191
+ [K in PluralObjectKeys<T>]: ObjectPlural;
192
+ } & {
193
+ [B in SuffixBaseUnion<T> as `${B}_${RequiredPlural}`]: string;
194
+ } & {
195
+ [B in SuffixBaseUnion<T> as `${B}_${OptionalPlural}`]?: string;
196
+ } & {
197
+ [K in PlainStringKeys<T>]: string;
198
+ } & {
199
+ [K in Exclude<StrKey<T>, NamespaceKeys<T> | PluralObjectKeys<T> | PlainStringKeys<T> | SuffixKey<T>>]: T[K];
200
+ };
201
+
202
+ /**
203
+ * Configuration for the LocL instance.
204
+ * @template T - The type of the resources object.
205
+ * @template Fallback - The fallback language.
206
+ * @template F - The type of the custom formatters.
207
+ */
208
+ interface LocLConfig<T extends Record<string, any>, Fallback extends keyof T & string, F extends Record<string, Formatter> = {}> {
209
+ /** An object containing all language translations. */
210
+ resources: T;
211
+ /** The initial language to use. */
212
+ language?: (keyof T & string) | string;
213
+ /** The default language to use if a translation is missing. */
214
+ fallbackLanguage: Fallback;
215
+ /** Narrows the translation object to a specific scope(s) (e.g., "common"). */
216
+ scope?: ScopeType<T, Fallback>;
217
+ /** A map of custom formatting functions. */
218
+ formatters?: F;
219
+ /** Whether to include the built-in formatters. Defaults to `true`. */
220
+ useDefaultFormatters?: boolean;
221
+ /** Enables warnings for missing keys and scopes. Defaults to `false`. */
222
+ devMode?: boolean;
223
+ /** Enables caching of scopes and proxies. Defaults to `true`. */
224
+ useCache?: boolean;
225
+ }
226
+ /**
227
+ * The main class for handling translations.
228
+ * It provides methods for translating keys, handling plurals, and formatting values.
229
+ * @template T - The type of the resources object.
230
+ * @template Fallback - The fallback language.
231
+ * @template S - The type of the scope.
232
+ * @template F - The type of the custom formatters.
233
+ * @template UseDefaultFormatter - Whether to use the default formatters.
234
+ */
235
+ declare class LocL<T extends Record<string, any>, Fallback extends keyof T & string, S extends ScopeType<T, Fallback> = undefined, F extends Record<string, Formatter> = {}, UseDefaultFormatter extends boolean = true> {
236
+ private config;
237
+ private language;
238
+ private fallbackLanguage;
239
+ private scope?;
240
+ private pluralRules;
241
+ private formatters;
242
+ private cache;
243
+ private proxyCache;
244
+ /**
245
+ * Creates a new LocL instance.
246
+ * @param config - The configuration object.
247
+ */
248
+ constructor(config: LocLConfig<T, Fallback> & {
249
+ fallbackLanguage: Fallback;
250
+ formatters?: F;
251
+ useDefaultFormatters?: UseDefaultFormatter;
252
+ });
253
+ /**
254
+ * Creates a new proxy translator instance with a different configuration.
255
+ * This is a lightweight way to create a translator with a different scope or language
256
+ * without creating a completely new instance.
257
+ * @param config - The configuration object.
258
+ * @param {N} [config.scope] - The scope to load translations from.
259
+ * @param {Language<T>} [config.language] - The language to use.
260
+ * @returns A new proxy `LocL` instance.
261
+ */
262
+ withConfig<N extends ScopeType<T, Fallback>>(config: {
263
+ scope?: N;
264
+ language?: Language<T>;
265
+ }): LocL<T, Fallback, N>;
266
+ /**
267
+ * Creates a new `LocL` instance with a different language or scope.
268
+ * @param language - The language to use for the new instance. Defaults to the current language.
269
+ * @param scope - The scope to use for the new instance.
270
+ * @returns A new `LocL` instance.
271
+ */
272
+ clone<N extends ScopeType<T, Fallback> = undefined>(language?: Language<T>, scope?: N): LocL<T, Fallback, N>;
273
+ /**
274
+ * Changes the current language of the translator.
275
+ * @param lang - The new language to set.
276
+ */
277
+ changeLanguage(lang: Language<T>): void;
278
+ /**
279
+ * Translates a key.
280
+ * If no key is provided, it returns the entire translation object for the current scope.
281
+ * @param key - The key to translate.
282
+ * @param values - An object with values to interpolate into the translation.
283
+ * @param format - An object with formatting options.
284
+ * @returns The translated and formatted string, or the translation object.
285
+ */
286
+ t(): TranslationObjectFor<S, T, Fallback>;
287
+ t<K extends NestedKeyOf<TranslationObjectFor<S, T, Fallback>>>(key: K, values?: InterpolationOptions, format?: FormatOptions<F, UseDefaultFormatter>): PathValue<TranslationObjectFor<S, T, Fallback>, K> extends string ? string : PathValue<TranslationObjectFor<S, T, Fallback>, K>;
288
+ /**
289
+ * Translates a key without strict type checking.
290
+ * This is useful for compatibility with other i18n libraries or dynamic keys.
291
+ * @param key - The key to translate.
292
+ * @param values - An object with values to interpolate into the translation.
293
+ * @param format - An object with formatting options.
294
+ * @returns The translated and formatted string.
295
+ */
296
+ tt(key?: any, values?: InterpolationOptions, format?: FormatOptions<F, UseDefaultFormatter>): any;
297
+ /**
298
+ * Formats a value using a specific formatter.
299
+ * @param value - The value to format.
300
+ * @param formatter - The name of the formatter to use.
301
+ * @param args - An array of arguments to pass to the formatter.
302
+ * @returns The formatted string.
303
+ */
304
+ format(value: string, formatter: keyof EffectiveFormatters<F, UseDefaultFormatter>, args?: string[]): string;
305
+ /**
306
+ * Translates a key with pluralization.
307
+ * It automatically selects the correct plural form based on the `count` value.
308
+ * @param key - The base key for the pluralization.
309
+ * @param values - An object with a `count` property and other values to interpolate.
310
+ * @param format - An object with formatting options.
311
+ * @returns The translated, pluralized, and formatted string.
312
+ */
313
+ plural<K extends PluralKeys<S, T, Fallback>>(key: K, values: PluralInterpolationOptions, format?: FormatOptions<F, UseDefaultFormatter>): string;
314
+ /**
315
+ * Gets a translation object or a specific translation value.
316
+ * If no key is provided, it returns the entire translation object for the current language and scope.
317
+ * @param key - The key of the translation to get.
318
+ * @returns The translation object or value, or `undefined` if not found.
319
+ */
320
+ get(): TranslationObjectFor<S, T, Fallback>;
321
+ get<K extends NestedKeyOf<TranslationObjectFor<S, T, Fallback>>>(key: K): PathValue<TranslationObjectFor<S, T, Fallback>, K> | undefined;
322
+ /**
323
+ * Gets a nested object from the translations.
324
+ * This is a type-safe way to get a nested object.
325
+ * @param key - The key of the object to get.
326
+ * @returns The nested translation object, or `undefined` if not found.
327
+ */
328
+ getObj<K extends NestedKeyOfObj<TranslationObjectFor<S, T, Fallback>, false>>(key: K): PathValue<TranslationObjectFor<S, T, Fallback>, K> | undefined;
329
+ private isLanguage;
330
+ private getCacheKey;
331
+ private lookupWithFallback;
332
+ private buildTranslationObject;
333
+ private findTranslation;
334
+ private checkPlural;
335
+ private interpolate;
336
+ private applyFormat;
337
+ private makeReadOnly;
338
+ }
339
+
340
+ /**
341
+ * Initializes a new LocL instance.
342
+ *
343
+ * @example
344
+ * ```ts
345
+ * const translator = initLocL({
346
+ * resources: {
347
+ * en: { greeting: "Hello, {name}!" },
348
+ * de: { greeting: "Hallo, {name}!" }
349
+ * },
350
+ * fallbackLanguage: "en"
351
+ * });
352
+ *
353
+ * const greeting = translator.t("greeting", { name: "World" });
354
+ * ```
355
+ *
356
+ * @template T - The type of the resources object, mapping language codes to translation objects.
357
+ * @template Fallback - The fallback language, which must be a key in the resources object.
358
+ * @template S - The specific scope or scopes to load from the translations.
359
+ * @template F - The type for custom formatters.
360
+ * @template UseDefaultFormatter - A boolean indicating whether to include default formatters.
361
+ *
362
+ * @param config - The configuration object for the translator.
363
+ * @param {T} config.resources - An object containing all language translations.
364
+ * @param {Fallback} config.fallbackLanguage - The default language to use if a translation is missing.
365
+ * @param {(keyof T & string)} [config.language] - The initial language to use.
366
+ * @param {S} [config.scope] - Narrows the translation object to a specific scope (e.g., "common").
367
+ * @param {F} [config.formatters] - A map of custom formatting functions.
368
+ * @param {boolean} [config.useDefaultFormatters=true] - Whether to include the built-in formatters.
369
+ * @param {boolean} [config.devMode=false] - Enables warnings for missing keys and scopes.
370
+ * @param {boolean} [config.useCache=true] - Enables caching of scopes and proxies.
371
+ *
372
+ * @returns A new `LocL` instance configured with the provided options.
373
+ */
374
+ declare function initLocL<T extends Record<string, any>, Fallback extends keyof T & string, S extends ScopeType<T, Fallback> | undefined = undefined, F extends Record<string, Formatter> = {}, UseDefaultFormatter extends boolean = true>(config: LocLConfig<T, Fallback> & {
375
+ scope?: S;
376
+ formatters?: F;
377
+ useDefaultFormatters?: UseDefaultFormatter;
378
+ }): LocL<T, Fallback, S, F, UseDefaultFormatter>;
379
+
380
+ export { type LangWithPlurals, initLocL };