@wolfstar/plugin-i18next 1.0.1-next-20260830111821 → 2.0.0-next-20260830115752

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/README.md CHANGED
@@ -4,7 +4,7 @@ Plugin for [`@wolfstar/http-framework`](https://www.npmjs.com/package/@wolfstar/
4
4
 
5
5
  It merges two upstream implementations:
6
6
 
7
- - [`@wolfstar/http-framework-i18n`](https://github.com/wolfstar-project/stars-components/tree/main/packages/http-framework-i18n) — the HTTP-interaction helpers (`resolveKey`, `applyLocalizedBuilder`, typed `T` / `FT` keys).
7
+ - [`@wolfstar/http-framework-i18n`](https://github.com/wolfstar-project/stars-components/tree/main/packages/http-framework-i18n) — the HTTP-interaction helpers (`getSupportedLanguageT`, `applyLocalizedBuilder`, localized builders).
8
8
  - [`@sapphire/plugin-i18next`](https://github.com/sapphiredev/plugins/tree/main/packages/i18next) — the plugin architecture (`container.i18n`, `InternationalizationHandler`, custom `fetchLanguage`, formatters, HMR).
9
9
 
10
10
  Interactions arrive over HTTP as raw payloads, so every helper operates on `discord-api-types` structures instead of `discord.js` class instances.
@@ -61,23 +61,27 @@ languages/
61
61
 
62
62
  Every top-level directory is a language, every nested `.json` file is a namespace (`commands/ping`).
63
63
 
64
- ### Definition
64
+ ### Typed keys
65
65
 
66
- ```typescript
67
- import { FT, T } from "@wolfstar/plugin-i18next";
66
+ Keys are typed through i18next's own `CustomTypeOptions` augmentation. Generate it from the locale
67
+ files with
68
+ [`@wolfstar/i18next-type-generator`](https://www.npmjs.com/package/@wolfstar/i18next-type-generator):
68
69
 
69
- export const Success = T("commands/ping:success");
70
- export const SuccessWithLatency = FT<{ latency: number }>("commands/ping:successWithLatency");
70
+ ```bash
71
+ i18next-type-generator ./languages/en-US/ ./src/@types/i18next.d.ts
71
72
  ```
72
73
 
74
+ See [i18next's TypeScript guide](https://www.i18next.com/overview/typescript) for the options the
75
+ augmentation accepts. Without it every helper still accepts plain strings, only untyped.
76
+
73
77
  ### Consumption
74
78
 
75
79
  ```typescript
76
80
  import {
77
81
  getSupportedLanguageName,
82
+ getSupportedLanguageT,
78
83
  getSupportedUserLanguageName,
79
- resolveKey,
80
- resolveUserKey,
84
+ getSupportedUserLanguageT,
81
85
  } from "@wolfstar/plugin-i18next";
82
86
 
83
87
  // The guild's language, falling back to the user's one in DMs:
@@ -87,12 +91,21 @@ const guildLanguage = getSupportedLanguageName(interaction);
87
91
  const userLanguage = getSupportedUserLanguageName(interaction);
88
92
 
89
93
  // Synchronous, resolved straight from the interaction payload:
90
- const content = resolveKey(interaction, Success);
91
- const userContent = resolveUserKey(interaction, SuccessWithLatency, { latency: 42 });
94
+ const content = getSupportedLanguageT(interaction, "commands/ping:success");
95
+ const userContent = getSupportedUserLanguageT(interaction, "commands/ping:successWithLatency", {
96
+ latency: 42,
97
+ });
98
+
99
+ // Omitting the key returns the bound `TFunction` instead, for when several keys share a language:
100
+ const t = getSupportedUserLanguageT(interaction);
101
+ const name = t("commands/ping:name");
102
+
103
+ // Bound functions carry the language they were fixed to, as they did in `@wolfstar/http-framework-i18n`:
104
+ t.lng; // 'es-ES
92
105
  ```
93
106
 
94
- `resolve*` helpers are synchronous and only read the locales carried by the interaction. Use the
95
- asynchronous `fetch*` helpers when the language comes from somewhere else — they go through
107
+ `getSupported*T` helpers are synchronous and only read the locales carried by the interaction. Use
108
+ the asynchronous `fetch*` helpers when the language comes from somewhere else — they go through
96
109
  `container.i18n.fetchLanguage`:
97
110
 
98
111
  ```typescript
@@ -126,12 +139,12 @@ Every helper accepts any of four raw payloads, told apart structurally — the f
126
139
  payloads can be passed straight through:
127
140
 
128
141
  ```typescript
129
- import { getSupportedLanguageName, resolveKey } from "@wolfstar/plugin-i18next";
142
+ import { getSupportedLanguageName, getSupportedLanguageT } from "@wolfstar/plugin-i18next";
130
143
 
131
144
  // A guild resolves through its `preferred_locale`:
132
145
  const guild = await container.rest.get(Routes.guild(guildId));
133
146
  const language = getSupportedLanguageName(guild);
134
- const content = resolveKey(guild, Success);
147
+ const content = getSupportedLanguageT(guild, "commands/ping:success");
135
148
  ```
136
149
 
137
150
  Channels and messages carry no locale of their own, so the synchronous helpers fall back to
@@ -240,9 +253,10 @@ pnpm add @wolfstar/plugin-i18next
240
253
  +});
241
254
  ```
242
255
 
243
- `T`, `FT`, `resolveKey`, `resolveUserKey`, `getSupportedLanguageName`, `getSupportedUserLanguageName`,
244
- `getSupportedLanguageT`, `getSupportedUserLanguageT`, `supportedLanguages`, `isSupportedDiscordLocale`,
245
- `getLocalizedData`, `applyNameLocalizedBuilder`, `applyDescriptionLocalizedBuilder`, `applyLocalizedBuilder` and
256
+ `getSupportedLanguageName`, `getSupportedUserLanguageName`, `getSupportedLanguageT`,
257
+ `getSupportedUserLanguageT` (which now also take the key and its options directly, like `resolveKey`
258
+ did), `supportedLanguages`, `isSupportedDiscordLocale`, `getLocalizedData`,
259
+ `applyNameLocalizedBuilder`, `applyDescriptionLocalizedBuilder`, `applyLocalizedBuilder` and
246
260
  `createSelectMenuChoiceName` keep the same names and signatures — only the module specifier changes.
247
261
 
248
262
  | Removed upstream | Replacement here |
@@ -250,17 +264,61 @@ pnpm add @wolfstar/plugin-i18next
250
264
  | `load(directory)` | `defaultLanguageDirectory` option |
251
265
  | `init(options)` | The `preLoad` hook; raw options go to the `i18next` option |
252
266
  | `addFormatters(...formatters)` | `formatters` option |
253
- | `getT(locale)` | `container.i18n.getT(locale)` |
267
+ | `getT(locale, namespace?)` | `container.i18n.getT(locale, namespace?)` |
254
268
  | `loadedLocales` | `container.i18n.languages` |
255
269
  | `loadedNamespaces` | `container.i18n.namespaces` |
256
270
  | `loadedPaths` | Derived from `defaultLanguageDirectory`; extra paths via the `backend` option |
257
271
  | `loadedFormatters` | `container.i18n.options.formatters` |
258
272
  | `Formatter` | `I18nextFormatter` |
259
273
 
274
+ `T`, `FT`, `resolveKey` and `resolveUserKey` were removed upstream in
275
+ [wolfstar-project/stars-components#30](https://github.com/wolfstar-project/stars-components/pull/30) and are gone here
276
+ too — see [Migrating off `T` / `FT` / `resolve*`](#migrating-off-t--ft--resolve) below.
277
+
260
278
  Other differences: `@wolfstar/http-framework@^3.1.0` is now a peer dependency, `i18next` moves from `^22` to `^25`, and
261
279
  the locales directory defaults to `<root>/languages` instead of an explicit path passed to `load()` — the layout itself
262
280
  is unchanged.
263
281
 
282
+ ## Migrating off `T` / `FT` / `resolve*`
283
+
284
+ The branded-key helpers were replaced by i18next's native TypeScript support, so keys are plain
285
+ strings typed by the `CustomTypeOptions` augmentation
286
+ [`@wolfstar/i18next-type-generator`](https://www.npmjs.com/package/@wolfstar/i18next-type-generator) emits.
287
+
288
+ ```diff
289
+ -import { FT, T } from "@wolfstar/plugin-i18next";
290
+ -
291
+ -export const Success = T("commands/ping:success");
292
+ -export const SuccessWithLatency = FT<{ latency: number }>("commands/ping:successWithLatency");
293
+ ```
294
+
295
+ | Removed | Replacement |
296
+ | -------------------------------------- | -------------------------------------------------------- |
297
+ | `T(key)` / `FT<Args>(key)` | The key itself, typed by the generated augmentation |
298
+ | `resolveKey(target, key, options)` | `getSupportedLanguageT(target, key, options)` |
299
+ | `resolveUserKey(target, key, options)` | `getSupportedUserLanguageT(target, key, options)` |
300
+ | `TypedT` / `TypedFT` | `ParseKeys` from `i18next` |
301
+ | `Value` / `Values` / `Difference` | Interpolation options are inferred from the locale files |
302
+
303
+ `TFunction#lng` (along with `lngs`, `ns` and `keyPrefix`) is still declared, so code reading the
304
+ language off a bound function keeps working — i18next assigns them on every function `getFixedT`
305
+ returns but does not type them.
306
+
307
+ `LocalePrefixKey` is no longer hardcoded to `commands/<file>:<key>`: it is now
308
+ `` `${string}${LocaleSeparator}${string}` ``, with `LocaleSeparator` read from i18next's
309
+ `TypeOptions["nsSeparator"]`.
310
+
311
+ ```diff
312
+ -const content = resolveKey(interaction, Success);
313
+ -const userContent = resolveUserKey(interaction, SuccessWithLatency, { latency: 42 });
314
+ +const content = getSupportedLanguageT(interaction, "commands/ping:success");
315
+ +const userContent = getSupportedUserLanguageT(
316
+ + interaction,
317
+ + "commands/ping:successWithLatency",
318
+ + { latency: 42 },
319
+ +);
320
+ ```
321
+
264
322
  ## Credits
265
323
 
266
324
  Adapted from [`@wolfstar/http-framework-i18n`](https://github.com/wolfstar-project/stars-components/tree/main/packages/http-framework-i18n) and [`@sapphire/plugin-i18next`](https://github.com/sapphiredev/plugins/tree/main/packages/i18next) (MIT).
@@ -1,4 +1,4 @@
1
- import i18next, { AppendKeyPrefix, DefaultNamespace, InitOptions, InitOptions as InitOptions$1, InterpolationMap, Namespace, ParseKeys, TFunction, TFunction as TFunction$1, TFunctionReturn, TFunctionReturnOptionalDetails, TOptions, TOptions as TOptions$1, TOptionsBase, TOptionsBase as TOptionsBase$1 } from "i18next";
1
+ import i18next, { AppendKeyPrefix, DefaultNamespace, InitOptions, InitOptions as InitOptions$1, InterpolationMap, Namespace, ParseKeys, TFunction, TFunction as TFunction$1, TFunctionReturn, TFunctionReturnOptionalDetails, TOptions, TOptions as TOptions$1, TOptionsBase, TypeOptions } from "i18next";
2
2
  import { APIApplicationCommandOptionChoice, APIInteraction, APIPingInteraction, APIUser, ChannelType, LocaleString, LocalizationMap } from "discord-api-types/v10";
3
3
  import { Awaitable, NonNullObject } from "@sapphire/utilities";
4
4
  import { Backend } from "@wolfstar/i18next-backend";
@@ -22,39 +22,19 @@ interface $Dictionary {
22
22
  [key: string]: any;
23
23
  }
24
24
  /**
25
- * This is a re-exported type from i18next. It is the returned type from `resolveKey` when
26
- * `returnObjects` is `true` in the options.
25
+ * This is a re-exported type from i18next. It is the returned type from the translation helpers
26
+ * when `returnObjects` is `true` in the options.
27
27
  */
28
28
  type $SpecialObject = $Dictionary | Array<string | $Dictionary>;
29
29
  /**
30
- * A translation key typed with the value it resolves to, created through {@link T}.
30
+ * The namespace separator i18next is configured with, `':'` unless `CustomTypeOptions` overrides it.
31
31
  */
32
- type TypedT<TCustom = string> = string & {
33
- __type__: TCustom;
34
- };
32
+ type LocaleSeparator = TypeOptions["nsSeparator"];
35
33
  /**
36
- * A translation key typed with both the interpolation arguments it takes and the value it resolves
37
- * to, created through {@link FT}.
34
+ * A `<namespace><separator><key>` shaped key, used as the root key overload of
35
+ * {@link applyLocalizedBuilder}.
38
36
  */
39
- type TypedFT<TArgs extends NonNullObject = NonNullObject, TReturn = string> = string & {
40
- __args__: TArgs;
41
- __return__: TReturn;
42
- };
43
- interface Value<T = string> {
44
- value: T;
45
- }
46
- interface Values<T = string> {
47
- values: readonly T[];
48
- count: number;
49
- }
50
- interface Difference<T = string> {
51
- previous: T;
52
- next: T;
53
- }
54
- /**
55
- * A `commands/<file>:<key>` shaped key, used as the root key overload of {@link applyLocalizedBuilder}.
56
- */
57
- type LocalePrefixKey = `commands/${string}:${string}`;
37
+ type LocalePrefixKey = `${string}${LocaleSeparator}${string}`;
58
38
  /**
59
39
  * The subset of an interaction payload the localization helpers need in order to resolve a language.
60
40
  *
@@ -346,9 +326,15 @@ declare class InternationalizationHandler {
346
326
  init(): Promise<void>;
347
327
  /**
348
328
  * Retrieve a raw `TFunction` from the passed locale.
329
+ *
330
+ * @remarks
331
+ * Passing a namespace returns a function bound to it, so its keys can be written without the
332
+ * `<namespace>:` prefix and are typed against the `CustomTypeOptions` augmentation generated by
333
+ * [`@wolfstar/i18next-type-generator`](https://www.npmjs.com/package/@wolfstar/i18next-type-generator).
349
334
  * @param locale The language to be used.
335
+ * @param namespace The namespace to bind the returned function to, defaulting to i18next's one.
350
336
  */
351
- getT(locale: string): TFunction$1<"translation", undefined>;
337
+ getT<const Ns extends Namespace = DefaultNamespace>(locale: string, namespace?: Ns): TFunction$1<Ns>;
352
338
  /**
353
339
  * Localizes a content given one or more keys and i18next options.
354
340
  * @param locale The language to be used.
@@ -415,24 +401,6 @@ declare class InternationalizationHandler {
415
401
  }
416
402
  //#endregion
417
403
  //#region src/lib/functions.d.ts
418
- /**
419
- * Brands a translation key with the type it resolves to.
420
- * @param k The i18next key.
421
- * @example
422
- * ```typescript
423
- * export const InvalidInput = T('path/to/file:invalidInput');
424
- * ```
425
- */
426
- declare function T<TCustom = string>(k: string): TypedT<TCustom>;
427
- /**
428
- * Brands a translation key with both its interpolation arguments and the type it resolves to.
429
- * @param k The i18next key.
430
- * @example
431
- * ```typescript
432
- * export const AddResult = FT<{ left: number; right: number; result: number }>('path/to/file:addResult');
433
- * ```
434
- */
435
- declare function FT<TArgs extends NonNullObject = NonNullObject, TReturn = string>(k: string): TypedFT<TArgs, TReturn>;
436
404
  /**
437
405
  * Every locale Discord supports.
438
406
  */
@@ -455,9 +423,23 @@ declare function isSupportedDiscordLocale(language: string): language is LocaleS
455
423
  declare function getSupportedUserLanguageName(target: Target): LocaleString;
456
424
  /**
457
425
  * Resolves the `TFunction` for {@link getSupportedUserLanguageName}.
426
+ *
427
+ * @remarks
428
+ * Pass the key and its options straight after the target to resolve it in one call; the bound
429
+ * function is only returned when no key is given. Keys of a namespace other than the default one
430
+ * carry their `<namespace>:` prefix, or the namespace is passed through the `ns` option.
458
431
  * @param target The target to read the locales from.
459
432
  */
460
433
  declare function getSupportedUserLanguageT(target: Target): TFunction$1;
434
+ /**
435
+ * Resolves a key with the user's language, as resolved by {@link getSupportedUserLanguageName}.
436
+ * @param target The target to read the locales from.
437
+ * @param key The key or keys to retrieve the content from.
438
+ * @param options The interpolation options.
439
+ */
440
+ declare function getSupportedUserLanguageT<const Key extends ParseKeys<Ns, TOpt, undefined>, const TOpt extends TOptions$1 = TOptions$1, Ns extends Namespace = DefaultNamespace, Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TOpt["returnObjects"] extends true ? $SpecialObject : string, const ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>>(target: Target, ...[key, defaultValueOrOptions, optionsOrUndefined]: [key: Key | Key[], options?: ActualOptions] | [key: string | string[], options: TOpt & $Dictionary & {
441
+ defaultValue: string;
442
+ }] | [key: string | string[], defaultValue: string, options?: TOpt & $Dictionary]): TFunctionReturnOptionalDetails<Ret, TOpt>;
461
443
  /**
462
444
  * Resolves the loaded language that best matches the guild's locale, falling back to the user's one
463
445
  * when the target does not belong to a guild, then to
@@ -473,9 +455,23 @@ declare function getSupportedUserLanguageT(target: Target): TFunction$1;
473
455
  declare function getSupportedLanguageName(target: Target): LocaleString;
474
456
  /**
475
457
  * Resolves the `TFunction` for {@link getSupportedLanguageName}.
458
+ *
459
+ * @remarks
460
+ * Pass the key and its options straight after the target to resolve it in one call; the bound
461
+ * function is only returned when no key is given. Keys of a namespace other than the default one
462
+ * carry their `<namespace>:` prefix, or the namespace is passed through the `ns` option.
476
463
  * @param target The target to read the locales from.
477
464
  */
478
465
  declare function getSupportedLanguageT(target: Target): TFunction$1;
466
+ /**
467
+ * Resolves a key with the guild's language, as resolved by {@link getSupportedLanguageName}.
468
+ * @param target The target to read the locales from.
469
+ * @param key The key or keys to retrieve the content from.
470
+ * @param options The interpolation options.
471
+ */
472
+ declare function getSupportedLanguageT<const Key extends ParseKeys<Ns, TOpt, undefined>, const TOpt extends TOptions$1 = TOptions$1, Ns extends Namespace = DefaultNamespace, Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TOpt["returnObjects"] extends true ? $SpecialObject : string, const ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>>(target: Target, ...[key, defaultValueOrOptions, optionsOrUndefined]: [key: Key | Key[], options?: ActualOptions] | [key: string | string[], options: TOpt & $Dictionary & {
473
+ defaultValue: string;
474
+ }] | [key: string | string[], defaultValue: string, options?: TOpt & $Dictionary]): TFunctionReturnOptionalDetails<Ret, TOpt>;
479
475
  /**
480
476
  * Retrieves the language name for a target, using {@link InternationalizationHandler.fetchLanguage}.
481
477
  *
@@ -491,6 +487,9 @@ declare function getSupportedLanguageT(target: Target): TFunction$1;
491
487
  declare function fetchLanguage(target: Target): Promise<string>;
492
488
  /**
493
489
  * Retrieves the language-assigned function from i18next designated to a target's preferred language.
490
+ *
491
+ * @remarks
492
+ * Use {@link fetchKey} to resolve a key in a single call.
494
493
  * @param target The target to fetch the language from.
495
494
  */
496
495
  declare function fetchT(target: Target): Promise<TFunction$1>;
@@ -500,43 +499,27 @@ declare function fetchT(target: Target): Promise<TFunction$1>;
500
499
  * is honoured.
501
500
  *
502
501
  * @remarks
503
- * Use {@link resolveKey} when the language can be resolved from the target payload alone, it is
504
- * synchronous and does not hit the hook.
502
+ * Use {@link getSupportedLanguageT} when the language can be resolved from the target payload
503
+ * alone, it is synchronous and does not hit the hook.
505
504
  * @param target The target to fetch the language key from.
506
505
  */
507
506
  declare function fetchKey<const Key extends ParseKeys<Ns, TOpt, undefined>, const TOpt extends TOptions$1 = TOptions$1, Ns extends Namespace = DefaultNamespace, Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TOpt["returnObjects"] extends true ? $SpecialObject : string, const ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>>(target: Target, ...[key, defaultValueOrOptions, optionsOrUndefined]: [key: Key | Key[], options?: ActualOptions] | [key: string | string[], options: TOpt & $Dictionary & {
508
507
  defaultValue: string;
509
508
  }] | [key: string | string[], defaultValue: string, options?: TOpt & $Dictionary]): Promise<TFunctionReturnOptionalDetails<Ret, TOpt>>;
510
- /**
511
- * Resolves a key with the user's language, as resolved by {@link getSupportedUserLanguageName}.
512
- */
513
- declare function resolveUserKey<TReturn>(target: Target, key: TypedT<TReturn>, options?: TOptionsBase$1 | string): TReturn;
514
- declare function resolveUserKey<TReturn>(target: Target, key: TypedT<TReturn>, defaultValue: TReturn, options?: TOptionsBase$1 | string): TReturn;
515
- declare function resolveUserKey<TArgs extends NonNullObject, TReturn>(target: Target, key: TypedFT<TArgs, TReturn>, options?: TOptions$1<TArgs>): TReturn;
516
- declare function resolveUserKey<TArgs extends NonNullObject, TReturn>(target: Target, key: TypedFT<TArgs, TReturn>, defaultValue: TReturn, options?: TOptions$1<TArgs>): TReturn;
517
- declare function resolveUserKey(target: Target, key: string | string[], ...args: [any?, any?]): string;
518
- /**
519
- * Resolves a key with the guild's language, as resolved by {@link getSupportedLanguageName}.
520
- */
521
- declare function resolveKey<TReturn>(target: Target, key: TypedT<TReturn>, options?: TOptionsBase$1 | string): TReturn;
522
- declare function resolveKey<TReturn>(target: Target, key: TypedT<TReturn>, defaultValue: TReturn, options?: TOptionsBase$1 | string): TReturn;
523
- declare function resolveKey<TArgs extends NonNullObject, TReturn>(target: Target, key: TypedFT<TArgs, TReturn>, options?: TOptions$1<TArgs>): TReturn;
524
- declare function resolveKey<TArgs extends NonNullObject, TReturn>(target: Target, key: TypedFT<TArgs, TReturn>, defaultValue: TReturn, options?: TOptions$1<TArgs>): TReturn;
525
- declare function resolveKey(target: Target, key: string | string[], ...args: [any?, any?]): string;
526
509
  /**
527
510
  * Gets the value and the localizations from a language key.
528
511
  * @param key The key to get the localizations from.
529
512
  * @returns The retrieved data.
530
513
  * @remarks This should be called **strictly** after loading the locales.
531
514
  */
532
- declare function getLocalizedData<const TOpt extends TOptions$1 = TOptions$1, Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(key: ParseKeys<Ns, TOpt, KPrefix> | TypedT): LocalizedData;
515
+ declare function getLocalizedData<const TOpt extends TOptions$1 = TOptions$1, Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(key: ParseKeys<Ns, TOpt, KPrefix>): LocalizedData;
533
516
  /**
534
517
  * Applies the localized names on the builder, calling `setName` and `setNameLocalizations`.
535
518
  * @param builder The builder to apply the localizations to.
536
519
  * @param key The key to get the localizations from.
537
520
  * @returns The updated builder.
538
521
  */
539
- declare function applyNameLocalizedBuilder<T extends BuilderWithName, const TOpt extends TOptions$1 = TOptions$1, Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(builder: T, key: ParseKeys<Ns, TOpt, KPrefix> | TypedT): T;
522
+ declare function applyNameLocalizedBuilder<T extends BuilderWithName, const TOpt extends TOptions$1 = TOptions$1, Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(builder: T, key: ParseKeys<Ns, TOpt, KPrefix>): T;
540
523
  /**
541
524
  * Applies the localized descriptions on the builder, calling `setDescription` and
542
525
  * `setDescriptionLocalizations`.
@@ -544,7 +527,7 @@ declare function applyNameLocalizedBuilder<T extends BuilderWithName, const TOpt
544
527
  * @param key The key to get the localizations from.
545
528
  * @returns The updated builder.
546
529
  */
547
- declare function applyDescriptionLocalizedBuilder<T extends BuilderWithDescription, const TOpt extends TOptions$1 = TOptions$1, Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(builder: T, key: ParseKeys<Ns, TOpt, KPrefix> | TypedT): T;
530
+ declare function applyDescriptionLocalizedBuilder<T extends BuilderWithDescription, const TOpt extends TOptions$1 = TOptions$1, Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(builder: T, key: ParseKeys<Ns, TOpt, KPrefix>): T;
548
531
  /**
549
532
  * Applies the localized names and descriptions on the builder, calling
550
533
  * {@link applyNameLocalizedBuilder} and {@link applyDescriptionLocalizedBuilder}.
@@ -569,7 +552,7 @@ declare function applyDescriptionLocalizedBuilder<T extends BuilderWithDescripti
569
552
  * applyLocalizedBuilder(builder, 'commands/userinfo:name');
570
553
  * ```
571
554
  */
572
- declare function applyLocalizedBuilder<T extends BuilderWithNameAndDescription, const TOpt extends TOptions$1 = TOptions$1, Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(builder: T, ...params: [root: LocalePrefixKey] | [name: ParseKeys<Ns, TOpt, KPrefix> | TypedT, description: ParseKeys<Ns, TOpt, KPrefix> | TypedT]): T;
555
+ declare function applyLocalizedBuilder<T extends BuilderWithNameAndDescription, const TOpt extends TOptions$1 = TOptions$1, Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(builder: T, ...params: [root: LocalePrefixKey] | [name: ParseKeys<Ns, TOpt, KPrefix>, description: ParseKeys<Ns, TOpt, KPrefix>]): T;
573
556
  /**
574
557
  * Constructs an object that can be passed into `setChoices` for a String or Number option with
575
558
  * localized names.
@@ -579,13 +562,13 @@ declare function applyLocalizedBuilder<T extends BuilderWithNameAndDescription,
579
562
  * @returns An object with anything provided through `options`, with `name` and `name_localizations`
580
563
  * added.
581
564
  */
582
- declare function createLocalizedChoice<ValueType = string | number, const TOpt extends TOptions$1 = TOptions$1, Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(key: ParseKeys<Ns, TOpt, KPrefix> | TypedT, options: Omit<APIApplicationCommandOptionChoice<ValueType>, "name" | "name_localizations">): APIApplicationCommandOptionChoice<ValueType>;
565
+ declare function createLocalizedChoice<ValueType = string | number, const TOpt extends TOptions$1 = TOptions$1, Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(key: ParseKeys<Ns, TOpt, KPrefix>, options: Omit<APIApplicationCommandOptionChoice<ValueType>, "name" | "name_localizations">): APIApplicationCommandOptionChoice<ValueType>;
583
566
  /**
584
567
  * Constructs a select menu option with a localized `name`, spreading any extra value on top.
585
568
  * @param key The i18next key for the name of the select option.
586
569
  * @param value The additional select option properties.
587
570
  */
588
- declare function createSelectMenuChoiceName<V extends NonNullObject>(key: TypedT, value?: V): createSelectMenuChoiceName.Result<V>;
571
+ declare function createSelectMenuChoiceName<V extends NonNullObject, const TOpt extends TOptions$1 = TOptions$1, Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(key: ParseKeys<Ns, TOpt, KPrefix>, value?: V): createSelectMenuChoiceName.Result<V>;
589
572
  declare namespace createSelectMenuChoiceName {
590
573
  type Result<V> = V & {
591
574
  name: string;
@@ -594,6 +577,36 @@ declare namespace createSelectMenuChoiceName {
594
577
  }
595
578
  //#endregion
596
579
  //#region src/index.d.ts
580
+ declare module "i18next" {
581
+ /**
582
+ * The metadata `i18next.getFixedT` assigns to the functions it returns, which
583
+ * `@wolfstar/http-framework-i18n` used to declare and i18next itself does not type.
584
+ *
585
+ * @remarks
586
+ * Only a fixed function carries it, so it is populated on anything returned by
587
+ * {@link InternationalizationHandler.getT} and the helpers built on top of it, but not on the
588
+ * bare `i18next.t`.
589
+ */
590
+ interface TFunction<Ns extends Namespace = DefaultNamespace, KPrefix = undefined> {
591
+ /**
592
+ * The language the function is fixed to.
593
+ */
594
+ lng: string;
595
+ /**
596
+ * The languages the function is fixed to, set instead of {@link TFunction.lng} when several
597
+ * were passed to `getFixedT`.
598
+ */
599
+ lngs?: readonly string[];
600
+ /**
601
+ * The namespace the function is fixed to, if any.
602
+ */
603
+ ns?: Ns;
604
+ /**
605
+ * The key prefix the function is fixed to, if any.
606
+ */
607
+ keyPrefix?: KPrefix;
608
+ }
609
+ }
597
610
  declare module "@sapphire/pieces" {
598
611
  interface Container {
599
612
  /**
@@ -612,5 +625,5 @@ declare module "@wolfstar/http-framework" {
612
625
  }
613
626
  }
614
627
  //#endregion
615
- export { type $Dictionary, type $NoInfer, type $SpecialObject, type BuilderWithDescription, type BuilderWithName, type BuilderWithNameAndDescription, type ChannelTarget, type Difference, type DynamicOptions, FT, type GuildTarget, type HMROptions, type I18nextFormatter, type I18nextNamedCachedFormatter, type I18nextNamedFormatter, type InitOptions, type Interaction, type InternationalizationClientOptions, type InternationalizationContext, InternationalizationHandler, type InternationalizationOptions, type LocalePrefixKey, type LocalizedData, type MessageTarget, T, type TFunction, type TOptions, type TOptionsBase, type Target, type TypedFT, type TypedT, type Value, type Values, applyDescriptionLocalizedBuilder, applyLocalizedBuilder, applyNameLocalizedBuilder, createLocalizedChoice, createSelectMenuChoiceName, fetchKey, fetchLanguage, fetchT, getLocalizedData, getSupportedLanguageName, getSupportedLanguageT, getSupportedUserLanguageName, getSupportedUserLanguageT, i18next, isSupportedDiscordLocale, resolveKey, resolveUserKey, supportedLanguages };
628
+ export { type $Dictionary, type $NoInfer, type $SpecialObject, type BuilderWithDescription, type BuilderWithName, type BuilderWithNameAndDescription, type ChannelTarget, type DynamicOptions, type GuildTarget, type HMROptions, type I18nextFormatter, type I18nextNamedCachedFormatter, type I18nextNamedFormatter, type InitOptions, type Interaction, type InternationalizationClientOptions, type InternationalizationContext, InternationalizationHandler, type InternationalizationOptions, type LocalePrefixKey, type LocaleSeparator, type LocalizedData, type MessageTarget, type TFunction, type TOptions, type TOptionsBase, type Target, applyDescriptionLocalizedBuilder, applyLocalizedBuilder, applyNameLocalizedBuilder, createLocalizedChoice, createSelectMenuChoiceName, fetchKey, fetchLanguage, fetchT, getLocalizedData, getSupportedLanguageName, getSupportedLanguageT, getSupportedUserLanguageName, getSupportedUserLanguageT, i18next, isSupportedDiscordLocale, supportedLanguages };
616
629
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/lib/types.ts","../../src/lib/InternationalizationHandler.ts","../../src/lib/functions.ts","../../src/index.ts"],"mappings":";;;;;;;;;;;;;;;KAqBY,SAAS,MAAM,GAAG;;;;;UAMb;GACd;;;;;;KAOS,iBAAiB,cAAc,eAAe;;;;KAK9C,OAAO;EAA+B,UAAU;;;;;;KAMhD,QAAQ,cAAc,gBAAgB,eAAe;EAC/D,UAAU;EACV,YAAY;;UAGG,MAAM;EACrB,OAAO;;UAGQ,OAAO;EACtB,iBAAiB;EACjB;;UAGe,WAAW;EAC1B,UAAU;EACV,MAAM;;;;;KAMI;;;;;;;;KASA,cAAc,KACxB,QAAQ,gBAAgB,+DAGxB,QAAQ,KAAK,QAAQ,gBAAgB;;;;;;;;;UAUtB;EACf;;;;;EAKA;;;;;;;;;;UAWe;EACf;EACA,MAAM;EACN;;;;;;;;;UAUe;EACf;EACA;EACA;EACA,SAAS,KAAK;;;;;;;;;;KAWJ,SAAS,cAAc,gBAAgB,gBAAgB;;;;;;UAOlD;;;;;EAKf;;;;;;;;;EAUA;;;;;;;;;EAUA;;;;;;;;EASA,UAAU;;;;;KAMA,eAAe,UAAU,kBACnC,sBACA,wBACG;;;;UAKY;;;;;EAKf;;;;EAKA,UAAU,QAAQ;;;;EAKlB,UAAU,gBAAc,eAAe;;;;;EAMvC;;;;;EAMA;;;;;EAMA;;;;;EAMA,aAAa;;;;EAKb,MAAM;;;;;;;;;EAUN,iBAAiB,SAAS,gCAAgC;;;;;;UAO3C;;EAEf;;EAEA;;EAEA;;EAEA,yBAAyB;;EAEzB,oBAAoB;;;;;;;;;EASpB;;UAGe;EACf,OAAO;;;;;;;;KASG,mBAAmB,wBAAwB;;;;;;UAOtC;EACf;EACA;EACA,OAAO,YAAY,yBAAyB;;;;;;;UAQ7B;EACf;EACA;EACA,OAAO,yBAAyB,gBAAgB;;UAGjC;EACf;EACA,eAAe;;UAGA;EACf,QAAQ;EACR,qBAAqB,gBAAgB;;UAGtB;EACf,eAAe;EACf,4BAA4B,uBAAuB;;KAGzC,gCAAgC,kBAAkB;;;;;;cCrSjD;;;;;EAKJ;;;;EAKA,YAAU;;;;WAKD,WAAS,YAAA;;;;WAKT,SAAS;;;;;WAMT;;;;qBAKG,gBAAgB,QAAQ;;;;UAKnC;;;;;EAMW,YAAA,UAAU;;;;;;;;;;;;;;;;;;;;;;;EA0CtB,gBAAgB,SAAS,gCAAgC;;;;;EAOnD,QAAI;;;;;EA2CV,KAAK,iBAAc;;;;;;;;;EAoBnB,aACC,YAAY,UAAU,IAAI,wBAC1B,aAAa,aAAW,YAC9B,WAAW,YAAY,kBACvB,YAAY,gBAAgB,IAAI,gBAAgB,iBAAiB,QAC/D,qCAAqC,+BACjC,sBAAsB,OAAO,iBAAiB,OAAO,OAAO,iBAAiB,MAEnF,gBACA,KAAK,MAAM,OACX,UAAU,gBACT,+BAA+B,KAAK;;;;;;;;;;EAWhC,aACC,YAAY,UAAU,IAAI,wBAC1B,aAAa,aAAW,YAC9B,WAAW,YAAY,kBACvB,YAAY,gBAAgB,IAAI,gBAAgB,iBAAiB,QAC/D,qCAAqC,yBAEvC,gBACA,wBACA,SAAS,OAAO;IAAgB;MAC/B,+BAA+B,KAAK;;;;;;;;;;EAWhC,aACC,YAAY,UAAU,IAAI,wBAC1B,aAAa,aAAW,YAC9B,WAAW,YAAY,kBACvB,YAAY,gBAAgB,IAAI,gBAAgB,iBAAiB,QAC/D,qCAAqC,yBAEvC,gBACA,wBACA,kCACA,UAAU,OAAO,cAChB,+BAA+B,KAAK;;;;;EAgE1B,kBAAkB,WAAW,WAAQ;;;;;;;;EAwB3C,mBAAmB;;;;;;UAYZ;;;;UAuBA;;;;;;;;;UA+BC;;;;;;;;;;;;iBC9VD,EAAE,kBAAkB,YAAY,OAAO;;;;;;;;;iBAYvC,GAAG,cAAc,gBAAgB,eAAe,kBAC9D,YACC,QAAQ,OAAO;;;;cAOL,oBAAuD,YAAY;;;;;iBAMhE,yBAAyB,mBAAmB,YAAY;;;;;;;;;;;iBA8GxD,6BAA6B,QAAQ,SAAS;;;;;iBAQ9C,0BAA0B,QAAQ,SAAS;;;;;;;;;;;;;iBAgB3C,yBAAyB,QAAQ,SAAS;;;;;iBAQ1C,sBAAsB,QAAQ,SAAS;;;;;;;;;;;;;iBAiBjC,cAAc,QAAQ,SAAS;;;;;iBAU/B,OAAO,QAAQ,SAAS,QAAQ;;;;;;;;;;;iBAchC,eACd,YAAY,UAAU,IAAI,wBAC1B,aAAa,aAAW,YAC9B,WAAW,YAAY,kBACvB,YAAY,gBAAgB,IAAI,gBAAgB,iBAAiB,QAC/D,qCAAqC,+BACjC,sBAAsB,OAAO,iBAAiB,OAAO,OAAO,iBAAiB,MAEnF,QAAQ,YACJ,KAAK,uBAAuB,sBAC3B,KAAK,MAAM,OAAO,UAAU,kBAC5B,wBAAwB,SAAS,OAAO;EAAgB;MACxD,wBAAwB,sBAAsB,UAAU,OAAO,eACnE,QAAQ,+BAA+B,KAAK;;;;iBAqB/B,eAAe,SAC7B,QAAQ,QACR,KAAK,OAAO,UACZ,UAAU,0BACT;iBACa,eAAe,SAC7B,QAAQ,QACR,KAAK,OAAO,UACZ,cAAc,SACd,UAAU,0BACT;iBACa,eAAe,cAAc,eAAe,SAC1D,QAAQ,QACR,KAAK,QAAQ,OAAO,UACpB,UAAU,WAAS,SAClB;iBACa,eAAe,cAAc,eAAe,SAC1D,QAAQ,QACR,KAAK,QAAQ,OAAO,UACpB,cAAc,SACd,UAAU,WAAS,SAClB;iBACa,eACd,QAAQ,QACR,2BACG;;;;iBASW,WAAW,SACzB,QAAQ,QACR,KAAK,OAAO,UACZ,UAAU,0BACT;iBACa,WAAW,SACzB,QAAQ,QACR,KAAK,OAAO,UACZ,cAAc,SACd,UAAU,0BACT;iBACa,WAAW,cAAc,eAAe,SACtD,QAAQ,QACR,KAAK,QAAQ,OAAO,UACpB,UAAU,WAAS,SAClB;iBACa,WAAW,cAAc,eAAe,SACtD,QAAQ,QACR,KAAK,QAAQ,OAAO,UACpB,cAAc,SACd,UAAU,WAAS,SAClB;iBACa,WAAW,QAAQ,QAAQ,2BAA2B;;;;;;;iBA8DtD,uBACR,aAAa,aAAW,YAC9B,WAAW,YAAY,kBACvB,qBACA,KAAK,UAAU,IAAI,MAAM,WAAW,SAAS;;;;;;;iBAgB/B,0BACd,UAAU,uBACJ,aAAa,aAAW,YAC9B,WAAW,YAAY,kBACvB,qBACA,SAAS,GAAG,KAAK,UAAU,IAAI,MAAM,WAAW,SAAM;;;;;;;;iBAYxC,iCACd,UAAU,8BACJ,aAAa,aAAW,YAC9B,WAAW,YAAY,kBACvB,qBACA,SAAS,GAAG,KAAK,UAAU,IAAI,MAAM,WAAW,SAAM;;;;;;;;;;;;;;;;;;;;;;;;;iBA6BxC,sBACd,UAAU,qCACJ,aAAa,aAAW,YAC9B,WAAW,YAAY,kBACvB,qBAEA,SAAS,MACN,SACE,MAAM,oBAEL,MAAM,UAAU,IAAI,MAAM,WAAW,QACrC,aAAa,UAAU,IAAI,MAAM,WAAW,UAEjD;;;;;;;;;;iBAuBa,sBACd,mCACM,aAAa,aAAW,YAC9B,WAAW,YAAY,kBACvB,qBAEA,KAAK,UAAU,IAAI,MAAM,WAAW,QACpC,SAAS,KAAK,kCAAkC,6CAC/C,kCAAkC;;;;;;iBAerB,2BAA2B,UAAU,eACnD,KAAK,QACL,QAAQ,IACP,2BAA2B,OAAO;kBASpB;OACH,OAAO,KAAK;IACtB;IACA,oDAAoD;;;;;;YCthB5C;;;;IAIR,MAAM;;;;YAKE;;;;;IAKR,OAAO"}
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../../src/lib/types.ts","../../src/lib/InternationalizationHandler.ts","../../src/lib/functions.ts","../../src/index.ts"],"mappings":"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"}
package/dist/esm/index.js CHANGED
@@ -10,28 +10,6 @@ import { join } from "node:path";
10
10
 
11
11
  //#region src/lib/functions.ts
12
12
  /**
13
- * Brands a translation key with the type it resolves to.
14
- * @param k The i18next key.
15
- * @example
16
- * ```typescript
17
- * export const InvalidInput = T('path/to/file:invalidInput');
18
- * ```
19
- */
20
- function T(k) {
21
- return k;
22
- }
23
- /**
24
- * Brands a translation key with both its interpolation arguments and the type it resolves to.
25
- * @param k The i18next key.
26
- * @example
27
- * ```typescript
28
- * export const AddResult = FT<{ left: number; right: number; result: number }>('path/to/file:addResult');
29
- * ```
30
- */
31
- function FT(k) {
32
- return k;
33
- }
34
- /**
35
13
  * Every locale Discord supports.
36
14
  */
37
15
  const supportedLanguages = new Set(Object.values(Locale));
@@ -122,12 +100,9 @@ function getSupportedUserLanguageNameFromContext(context) {
122
100
  function getSupportedUserLanguageName(target) {
123
101
  return getSupportedUserLanguageNameFromContext(resolveContext(target));
124
102
  }
125
- /**
126
- * Resolves the `TFunction` for {@link getSupportedUserLanguageName}.
127
- * @param target The target to read the locales from.
128
- */
129
- function getSupportedUserLanguageT(target) {
130
- return container.i18n.getT(getSupportedUserLanguageName(target));
103
+ function getSupportedUserLanguageT(target, ...args) {
104
+ const t = container.i18n.getT(getSupportedUserLanguageName(target));
105
+ return args.length === 0 ? t : t(...args);
131
106
  }
132
107
  /**
133
108
  * Resolves the loaded language that best matches the guild's locale, falling back to the user's one
@@ -144,12 +119,9 @@ function getSupportedUserLanguageT(target) {
144
119
  function getSupportedLanguageName(target) {
145
120
  return getSupportedLanguageNameFromContext(resolveContext(target));
146
121
  }
147
- /**
148
- * Resolves the `TFunction` for {@link getSupportedLanguageName}.
149
- * @param target The target to read the locales from.
150
- */
151
- function getSupportedLanguageT(target) {
152
- return container.i18n.getT(getSupportedLanguageName(target));
122
+ function getSupportedLanguageT(target, ...args) {
123
+ const t = container.i18n.getT(getSupportedLanguageName(target));
124
+ return args.length === 0 ? t : t(...args);
153
125
  }
154
126
  /**
155
127
  * Retrieves the language name for a target, using {@link InternationalizationHandler.fetchLanguage}.
@@ -170,6 +142,9 @@ async function fetchLanguage(target) {
170
142
  }
171
143
  /**
172
144
  * Retrieves the language-assigned function from i18next designated to a target's preferred language.
145
+ *
146
+ * @remarks
147
+ * Use {@link fetchKey} to resolve a key in a single call.
173
148
  * @param target The target to fetch the language from.
174
149
  */
175
150
  async function fetchT(target) {
@@ -181,8 +156,8 @@ async function fetchT(target) {
181
156
  * is honoured.
182
157
  *
183
158
  * @remarks
184
- * Use {@link resolveKey} when the language can be resolved from the target payload alone, it is
185
- * synchronous and does not hit the hook.
159
+ * Use {@link getSupportedLanguageT} when the language can be resolved from the target payload
160
+ * alone, it is synchronous and does not hit the hook.
186
161
  * @param target The target to fetch the language key from.
187
162
  */
188
163
  async function fetchKey(target, ...[key, defaultValueOrOptions, optionsOrUndefined]) {
@@ -191,12 +166,6 @@ async function fetchKey(target, ...[key, defaultValueOrOptions, optionsOrUndefin
191
166
  if (typeof defaultValueOrOptions === "string") return container.i18n.format(language, key, defaultValueOrOptions, optionsOrUndefined);
192
167
  return container.i18n.format(language, key, void 0, defaultValueOrOptions);
193
168
  }
194
- function resolveUserKey(target, ...args) {
195
- return getSupportedUserLanguageT(target)(...args);
196
- }
197
- function resolveKey(target, ...args) {
198
- return getSupportedLanguageT(target)(...args);
199
- }
200
169
  let cachedLocales = null;
201
170
  let cachedLocalesSize = -1;
202
171
  /**
@@ -438,13 +407,19 @@ var InternationalizationHandler = class {
438
407
  }
439
408
  /**
440
409
  * Retrieve a raw `TFunction` from the passed locale.
410
+ *
411
+ * @remarks
412
+ * Passing a namespace returns a function bound to it, so its keys can be written without the
413
+ * `<namespace>:` prefix and are typed against the `CustomTypeOptions` augmentation generated by
414
+ * [`@wolfstar/i18next-type-generator`](https://www.npmjs.com/package/@wolfstar/i18next-type-generator).
441
415
  * @param locale The language to be used.
416
+ * @param namespace The namespace to bind the returned function to, defaulting to i18next's one.
442
417
  */
443
- getT(locale) {
418
+ getT(locale, namespace) {
444
419
  if (!this.languagesLoaded) throw new Error("Cannot call this method until InternationalizationHandler#init has been called");
445
420
  const t = this.languages.get(locale);
446
- if (t) return t;
447
- throw new ReferenceError(`Invalid language (${locale})`);
421
+ if (!t) throw new ReferenceError(`Invalid language (${locale})`);
422
+ return namespace === void 0 ? t : i18next$1.getFixedT(locale, namespace);
448
423
  }
449
424
  /**
450
425
  * Localizes a content given one or more keys and i18next options.
@@ -543,5 +518,5 @@ var InternationalizationHandler = class {
543
518
  };
544
519
 
545
520
  //#endregion
546
- export { FT, InternationalizationHandler, T, applyDescriptionLocalizedBuilder, applyLocalizedBuilder, applyNameLocalizedBuilder, createLocalizedChoice, createSelectMenuChoiceName, fetchKey, fetchLanguage, fetchT, getLocalizedData, getSupportedLanguageName, getSupportedLanguageT, getSupportedUserLanguageName, getSupportedUserLanguageT, i18next, isSupportedDiscordLocale, resolveKey, resolveUserKey, supportedLanguages };
521
+ export { InternationalizationHandler, applyDescriptionLocalizedBuilder, applyLocalizedBuilder, applyNameLocalizedBuilder, createLocalizedChoice, createSelectMenuChoiceName, fetchKey, fetchLanguage, fetchT, getLocalizedData, getSupportedLanguageName, getSupportedLanguageT, getSupportedUserLanguageName, getSupportedUserLanguageT, i18next, isSupportedDiscordLocale, supportedLanguages };
547
522
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["i18next"],"sources":["../../src/lib/functions.ts","../../src/lib/InternationalizationHandler.ts"],"sourcesContent":["import { Collection } from \"@discordjs/collection\";\nimport { container } from \"@sapphire/pieces\";\nimport type { NonNullObject } from \"@sapphire/utilities\";\nimport {\n Locale,\n type APIApplicationCommandOptionChoice,\n type LocaleString,\n} from \"discord-api-types/v10\";\nimport type {\n AppendKeyPrefix,\n DefaultNamespace,\n InterpolationMap,\n Namespace,\n ParseKeys,\n TFunction,\n TFunctionReturn,\n TFunctionReturnOptionalDetails,\n TOptions,\n TOptionsBase,\n} from \"i18next\";\nimport type {\n $Dictionary,\n $SpecialObject,\n BuilderWithDescription,\n BuilderWithName,\n BuilderWithNameAndDescription,\n InternationalizationContext,\n LocalePrefixKey,\n LocalizedData,\n Target,\n TypedFT,\n TypedT,\n} from \"./types\";\n\n/**\n * Brands a translation key with the type it resolves to.\n * @param k The i18next key.\n * @example\n * ```typescript\n * export const InvalidInput = T('path/to/file:invalidInput');\n * ```\n */\nexport function T<TCustom = string>(k: string): TypedT<TCustom> {\n return k as TypedT<TCustom>;\n}\n\n/**\n * Brands a translation key with both its interpolation arguments and the type it resolves to.\n * @param k The i18next key.\n * @example\n * ```typescript\n * export const AddResult = FT<{ left: number; right: number; result: number }>('path/to/file:addResult');\n * ```\n */\nexport function FT<TArgs extends NonNullObject = NonNullObject, TReturn = string>(\n k: string,\n): TypedFT<TArgs, TReturn> {\n return k as TypedFT<TArgs, TReturn>;\n}\n\n/**\n * Every locale Discord supports.\n */\nexport const supportedLanguages = new Set(Object.values(Locale)) as ReadonlySet<LocaleString>;\n\n/**\n * Checks whether the given language is a locale Discord supports.\n * @param language The language to check.\n */\nexport function isSupportedDiscordLocale(language: string): language is LocaleString {\n return supportedLanguages.has(language as LocaleString);\n}\n\n/**\n * Resolves the fallback language to use when none of a target's locales is loaded.\n *\n * {@link InternationalizationOptions.defaultName} wins as long as it is both a Discord locale and a\n * loaded language; otherwise `'en-US'` is used.\n * @internal\n */\nfunction getFallbackLanguageName(): LocaleString {\n const { languages, options } = container.i18n;\n const { defaultName } = options;\n\n return defaultName && isSupportedDiscordLocale(defaultName) && languages.has(defaultName)\n ? defaultName\n : \"en-US\";\n}\n\n/**\n * Narrows a locale to a loaded Discord locale, or `null` when it is neither.\n * @internal\n */\nfunction getLoadedLocale(locale: string | undefined): LocaleString | null {\n if (!locale) return null;\n return isSupportedDiscordLocale(locale) && container.i18n.languages.has(locale) ? locale : null;\n}\n\n/**\n * Builds the {@link InternationalizationContext} for any supported target.\n *\n * @remarks\n * The members of {@link Target} are told apart structurally, since the framework receives raw\n * payloads rather than class instances: an interaction has `locale`, a message has `channel_id`, a\n * channel has `type`, and anything left is a guild.\n * @internal\n */\nfunction resolveContext(target: Target): InternationalizationContext {\n if (\"locale\" in target) {\n return {\n guildId: target.guild_id ?? null,\n channelId: target.channel_id ?? null,\n userId: target.user?.id ?? target.member?.user.id ?? null,\n interactionGuildLocale: target.guild_locale,\n interactionLocale: target.locale,\n preferredLocale: target.guild_locale,\n };\n }\n\n if (\"channel_id\" in target) {\n return {\n guildId: target.guild_id ?? null,\n channelId: target.channel_id,\n userId: target.author?.id ?? null,\n };\n }\n\n if (\"type\" in target) {\n return {\n guildId: target.guild_id ?? null,\n channelId: target.id,\n userId: null,\n };\n }\n\n return {\n guildId: target.id,\n channelId: null,\n userId: null,\n preferredLocale: target.preferred_locale,\n };\n}\n\n/**\n * @internal\n */\nfunction getSupportedLanguageNameFromContext(context: InternationalizationContext): LocaleString {\n // Guild-scoped: the guild's locale is what matters, and the user's is deliberately ignored.\n // Outside a guild there is no guild locale to prefer, so the user's one is used instead.\n const preferred = context.guildId\n ? getLoadedLocale(context.preferredLocale)\n : getLoadedLocale(context.interactionLocale);\n\n return preferred ?? getFallbackLanguageName();\n}\n\n/**\n * @internal\n */\nfunction getSupportedUserLanguageNameFromContext(\n context: InternationalizationContext,\n): LocaleString {\n return (\n getLoadedLocale(context.interactionLocale) ??\n getLoadedLocale(context.preferredLocale) ??\n getFallbackLanguageName()\n );\n}\n\n/**\n * Resolves the loaded language that best matches the user's locale, falling back to the guild's one,\n * then to {@link InternationalizationOptions.defaultName}, and finally to `'en-US'`.\n *\n * @remarks\n * Only an {@link Interaction} carries a user locale. For a {@link GuildTarget} this is equivalent to\n * {@link getSupportedLanguageName}, and a {@link ChannelTarget} or {@link MessageTarget} always\n * resolves to the fallback.\n * @param target The target to read the locales from.\n */\nexport function getSupportedUserLanguageName(target: Target): LocaleString {\n return getSupportedUserLanguageNameFromContext(resolveContext(target));\n}\n\n/**\n * Resolves the `TFunction` for {@link getSupportedUserLanguageName}.\n * @param target The target to read the locales from.\n */\nexport function getSupportedUserLanguageT(target: Target): TFunction {\n return container.i18n.getT(getSupportedUserLanguageName(target));\n}\n\n/**\n * Resolves the loaded language that best matches the guild's locale, falling back to the user's one\n * when the target does not belong to a guild, then to\n * {@link InternationalizationOptions.defaultName}, and finally to `'en-US'`.\n *\n * @remarks\n * The guild locale comes from `guild_locale` on an {@link Interaction} and from `preferred_locale`\n * on a {@link GuildTarget}. A {@link ChannelTarget} and a {@link MessageTarget} carry no locale, so\n * they resolve to the fallback unless a custom\n * {@link InternationalizationHandler.fetchLanguage} hook is used through {@link fetchLanguage}.\n * @param target The target to read the locales from.\n */\nexport function getSupportedLanguageName(target: Target): LocaleString {\n return getSupportedLanguageNameFromContext(resolveContext(target));\n}\n\n/**\n * Resolves the `TFunction` for {@link getSupportedLanguageName}.\n * @param target The target to read the locales from.\n */\nexport function getSupportedLanguageT(target: Target): TFunction {\n return container.i18n.getT(getSupportedLanguageName(target));\n}\n\n/**\n * Retrieves the language name for a target, using {@link InternationalizationHandler.fetchLanguage}.\n *\n * If that hook is not defined or returns a nullish value, the language is resolved from the\n * locales the target carries through {@link getSupportedLanguageName}, which itself falls back to\n * {@link InternationalizationOptions.defaultName} and then to `'en-US'`.\n\n * @remarks\n * This is the only helper that can resolve a language for a {@link ChannelTarget} or a\n * {@link MessageTarget}, since those payloads carry no locale and the hook receives their\n * `guildId` and `channelId`.\n * @param target The target to fetch the language from.\n */\nexport async function fetchLanguage(target: Target): Promise<string> {\n const context = resolveContext(target);\n const language = await container.i18n.fetchLanguage(context);\n return language ?? getSupportedLanguageNameFromContext(context);\n}\n\n/**\n * Retrieves the language-assigned function from i18next designated to a target's preferred language.\n * @param target The target to fetch the language from.\n */\nexport async function fetchT(target: Target): Promise<TFunction> {\n return container.i18n.getT(await fetchLanguage(target));\n}\n\n/**\n * Resolves a key and its parameters using {@link fetchLanguage}, meaning a custom\n * {@link InternationalizationHandler.fetchLanguage} hook (for example, a per-guild database lookup)\n * is honoured.\n *\n * @remarks\n * Use {@link resolveKey} when the language can be resolved from the target payload alone, it is\n * synchronous and does not hit the hook.\n * @param target The target to fetch the language key from.\n */\nexport async function fetchKey<\n const Key extends ParseKeys<Ns, TOpt, undefined>,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> =\n TOpt[\"returnObjects\"] extends true ? $SpecialObject : string,\n const ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>,\n>(\n target: Target,\n ...[key, defaultValueOrOptions, optionsOrUndefined]:\n | [key: Key | Key[], options?: ActualOptions]\n | [key: string | string[], options: TOpt & $Dictionary & { defaultValue: string }]\n | [key: string | string[], defaultValue: string, options?: TOpt & $Dictionary]\n): Promise<TFunctionReturnOptionalDetails<Ret, TOpt>> {\n const parsedOptions =\n typeof defaultValueOrOptions === \"string\" ? optionsOrUndefined : defaultValueOrOptions;\n const language =\n typeof parsedOptions?.lng === \"string\" ? parsedOptions.lng : await fetchLanguage(target);\n\n if (typeof defaultValueOrOptions === \"string\") {\n return container.i18n.format<Key, TOpt, Ns, Ret>(\n language,\n key,\n defaultValueOrOptions,\n optionsOrUndefined,\n );\n }\n\n return container.i18n.format<Key, TOpt, Ns, Ret>(language, key, undefined, defaultValueOrOptions);\n}\n\n/**\n * Resolves a key with the user's language, as resolved by {@link getSupportedUserLanguageName}.\n */\nexport function resolveUserKey<TReturn>(\n target: Target,\n key: TypedT<TReturn>,\n options?: TOptionsBase | string,\n): TReturn;\nexport function resolveUserKey<TReturn>(\n target: Target,\n key: TypedT<TReturn>,\n defaultValue: TReturn,\n options?: TOptionsBase | string,\n): TReturn;\nexport function resolveUserKey<TArgs extends NonNullObject, TReturn>(\n target: Target,\n key: TypedFT<TArgs, TReturn>,\n options?: TOptions<TArgs>,\n): TReturn;\nexport function resolveUserKey<TArgs extends NonNullObject, TReturn>(\n target: Target,\n key: TypedFT<TArgs, TReturn>,\n defaultValue: TReturn,\n options?: TOptions<TArgs>,\n): TReturn;\nexport function resolveUserKey(\n target: Target,\n key: string | string[],\n ...args: [any?, any?]\n): string;\nexport function resolveUserKey(target: Target, ...args: [any, any?, any?]) {\n return (getSupportedUserLanguageT(target) as (...args: any[]) => unknown)(...args);\n}\n\n/**\n * Resolves a key with the guild's language, as resolved by {@link getSupportedLanguageName}.\n */\nexport function resolveKey<TReturn>(\n target: Target,\n key: TypedT<TReturn>,\n options?: TOptionsBase | string,\n): TReturn;\nexport function resolveKey<TReturn>(\n target: Target,\n key: TypedT<TReturn>,\n defaultValue: TReturn,\n options?: TOptionsBase | string,\n): TReturn;\nexport function resolveKey<TArgs extends NonNullObject, TReturn>(\n target: Target,\n key: TypedFT<TArgs, TReturn>,\n options?: TOptions<TArgs>,\n): TReturn;\nexport function resolveKey<TArgs extends NonNullObject, TReturn>(\n target: Target,\n key: TypedFT<TArgs, TReturn>,\n defaultValue: TReturn,\n options?: TOptions<TArgs>,\n): TReturn;\nexport function resolveKey(target: Target, key: string | string[], ...args: [any?, any?]): string;\nexport function resolveKey(target: Target, ...args: [any, any?, any?]) {\n return (getSupportedLanguageT(target) as (...args: any[]) => unknown)(...args);\n}\n\nlet cachedLocales: Collection<LocaleString, TFunction> | null = null;\nlet cachedLocalesSize = -1;\n\n/**\n * The loaded languages Discord supports, keyed by locale.\n *\n * @remarks\n * Memoized on the size of `container.i18n.languages` so that locales discovered by a hot reload\n * (see {@link InternationalizationHandler.reloadResources}) are picked up without a restart.\n * @internal\n */\nfunction getLocales(): Collection<LocaleString, TFunction> {\n const { languages } = container.i18n;\n if (cachedLocales && cachedLocalesSize === languages.size) return cachedLocales;\n\n const locales = new Collection<LocaleString, TFunction>();\n\n for (const [locale, t] of languages) {\n if (!isSupportedDiscordLocale(locale)) {\n process.emitWarning(\"Unsupported Discord locale\", {\n code: \"UNSUPPORTED_LOCALE\",\n detail: `'${locale}' is not assignable to type LocaleString`,\n });\n continue;\n }\n\n locales.set(locale, t);\n }\n\n cachedLocales = locales;\n cachedLocalesSize = languages.size;\n return locales;\n}\n\n/**\n * @internal\n */\nfunction getDefaultT(): TFunction {\n const defaultLocale = container.i18n.options.defaultName ?? \"en-US\";\n\n if (!isSupportedDiscordLocale(defaultLocale)) {\n throw new TypeError(\n `Unsupported Discord locale found:\\n'${defaultLocale}' is not within the list of ${[...supportedLanguages]}`,\n );\n }\n\n const defaultT = getLocales().get(defaultLocale);\n if (defaultT) return defaultT;\n throw new TypeError(`Could not find ${defaultLocale}`);\n}\n\n/**\n * Gets the value and the localizations from a language key.\n * @param key The key to get the localizations from.\n * @returns The retrieved data.\n * @remarks This should be called **strictly** after loading the locales.\n */\nexport function getLocalizedData<\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n KPrefix = undefined,\n>(key: ParseKeys<Ns, TOpt, KPrefix> | TypedT): LocalizedData {\n const locales = getLocales();\n const defaultT = getDefaultT();\n\n return {\n value: defaultT(key as never),\n localizations: Object.fromEntries(locales.map((t, locale) => [locale, t(key as never)])),\n };\n}\n\n/**\n * Applies the localized names on the builder, calling `setName` and `setNameLocalizations`.\n * @param builder The builder to apply the localizations to.\n * @param key The key to get the localizations from.\n * @returns The updated builder.\n */\nexport function applyNameLocalizedBuilder<\n T extends BuilderWithName,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n KPrefix = undefined,\n>(builder: T, key: ParseKeys<Ns, TOpt, KPrefix> | TypedT) {\n const result = getLocalizedData(key);\n return builder.setName(result.value).setNameLocalizations(result.localizations);\n}\n\n/**\n * Applies the localized descriptions on the builder, calling `setDescription` and\n * `setDescriptionLocalizations`.\n * @param builder The builder to apply the localizations to.\n * @param key The key to get the localizations from.\n * @returns The updated builder.\n */\nexport function applyDescriptionLocalizedBuilder<\n T extends BuilderWithDescription,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n KPrefix = undefined,\n>(builder: T, key: ParseKeys<Ns, TOpt, KPrefix> | TypedT) {\n const result = getLocalizedData(key);\n return builder.setDescription(result.value).setDescriptionLocalizations(result.localizations);\n}\n\n/**\n * Applies the localized names and descriptions on the builder, calling\n * {@link applyNameLocalizedBuilder} and {@link applyDescriptionLocalizedBuilder}.\n *\n * @param builder The builder to apply the localizations to.\n * @param params The root key, or the key for the name and the key for the description.\n * @returns The updated builder. You can chain subsequent builder methods on this.\n *\n * @remarks\n * If only 2 parameters were passed, `name` will be defined as `${root}Name` and `description` as\n * `${root}Description`, being `root` the second parameter in the function, after `builder`.\n *\n * @example\n * ```typescript\n * // Both keys given explicitly:\n * applyLocalizedBuilder(builder, 'commands/names:userinfo', 'commands/descriptions:userinfo');\n * ```\n *\n * @example\n * ```typescript\n * // Root key only, resolves `commands/userinfo:nameName` and `commands/userinfo:nameDescription`:\n * applyLocalizedBuilder(builder, 'commands/userinfo:name');\n * ```\n */\nexport function applyLocalizedBuilder<\n T extends BuilderWithNameAndDescription,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n KPrefix = undefined,\n>(\n builder: T,\n ...params:\n | [root: LocalePrefixKey]\n | [\n name: ParseKeys<Ns, TOpt, KPrefix> | TypedT,\n description: ParseKeys<Ns, TOpt, KPrefix> | TypedT,\n ]\n): T {\n type LocalKeysType = ParseKeys<Ns, TOpt, KPrefix> | TypedT;\n\n const [localeName, localeDescription] =\n params.length === 1\n ? [`${params[0]}Name` as LocalKeysType, `${params[0]}Description` as LocalKeysType]\n : params;\n\n applyNameLocalizedBuilder(builder, localeName);\n applyDescriptionLocalizedBuilder(builder, localeDescription);\n\n return builder;\n}\n\n/**\n * Constructs an object that can be passed into `setChoices` for a String or Number option with\n * localized names.\n *\n * @param key The i18next key for the name of the choice.\n * @param options The remaining choice options. This should _at least_ include the `value` key.\n * @returns An object with anything provided through `options`, with `name` and `name_localizations`\n * added.\n */\nexport function createLocalizedChoice<\n ValueType = string | number,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n KPrefix = undefined,\n>(\n key: ParseKeys<Ns, TOpt, KPrefix> | TypedT,\n options: Omit<APIApplicationCommandOptionChoice<ValueType>, \"name\" | \"name_localizations\">,\n): APIApplicationCommandOptionChoice<ValueType> {\n const result = getLocalizedData(key);\n\n return {\n ...options,\n name: result.value,\n name_localizations: result.localizations,\n };\n}\n\n/**\n * Constructs a select menu option with a localized `name`, spreading any extra value on top.\n * @param key The i18next key for the name of the select option.\n * @param value The additional select option properties.\n */\nexport function createSelectMenuChoiceName<V extends NonNullObject>(\n key: TypedT,\n value?: V,\n): createSelectMenuChoiceName.Result<V> {\n const result = getLocalizedData(key);\n return {\n ...value,\n name: result.value,\n name_localizations: result.localizations,\n } as createSelectMenuChoiceName.Result<V>;\n}\n\nexport namespace createSelectMenuChoiceName {\n export type Result<V> = V & {\n name: string;\n name_localizations: import(\"discord-api-types/v10\").LocalizationMap;\n };\n}\n","import { getRootData } from \"@sapphire/pieces\";\nimport { Result } from \"@sapphire/result\";\nimport { isFunction, type Awaitable } from \"@sapphire/utilities\";\nimport { Backend, type PathResolvable } from \"@wolfstar/i18next-backend\";\nimport i18next, {\n type AppendKeyPrefix,\n type DefaultNamespace,\n type InterpolationMap,\n type Namespace,\n type ParseKeys,\n type TFunction,\n type TFunctionProcessReturnValue,\n type TFunctionReturn,\n type TFunctionReturnOptionalDetails,\n type TOptions,\n} from \"i18next\";\nimport type { PathLike } from \"node:fs\";\nimport { opendir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type {\n $Dictionary,\n $NoInfer,\n $SpecialObject,\n InternationalizationContext,\n InternationalizationOptions,\n} from \"./types\";\n\n/**\n * A generalized class for handling `i18next` JSON files and their discovery.\n */\nexport class InternationalizationHandler {\n /**\n * Describes whether {@link InternationalizationHandler.init} has been run and languages are\n * loaded in {@link InternationalizationHandler.languages}.\n */\n public languagesLoaded = false;\n\n /**\n * A `Set` of initially loaded namespaces.\n */\n public namespaces = new Set<string>();\n\n /**\n * A `Map` of `i18next` language functions keyed by their language code.\n */\n public readonly languages = new Map<string, TFunction>();\n\n /**\n * The options {@link InternationalizationHandler} was initialized with.\n */\n public readonly options: InternationalizationOptions;\n\n /**\n * The directory passed to `@wolfstar/i18next-backend`. Also used in\n * {@link InternationalizationHandler.walkRootDirectory}.\n */\n public readonly languagesDirectory: string;\n\n /**\n * The backend options for `@wolfstar/i18next-backend` used by `i18next`.\n */\n protected readonly backendOptions: Backend.Options;\n\n /**\n * The tail of the reload queue, used by {@link reloadResources} to run reloads one at a time.\n */\n private reloadTask: Promise<void> = Promise.resolve();\n\n /**\n * @param options The options that `i18next`, `@wolfstar/i18next-backend`, and\n * {@link InternationalizationHandler} should use.\n */\n public constructor(options?: InternationalizationOptions) {\n this.options = options ?? { i18next: { ignoreJSONStructure: false } };\n this.languagesDirectory =\n this.options.defaultLanguageDirectory ?? join(getRootData().root, \"languages\");\n\n const languagePaths = new Set<PathResolvable>([\n join(this.languagesDirectory, \"{{lng}}\", \"{{ns}}.json\"),\n ...(options?.backend?.paths ?? []),\n ]);\n\n this.backendOptions = {\n paths: [...languagePaths],\n ...this.options.backend,\n };\n\n if (isFunction(this.options.fetchLanguage)) {\n this.fetchLanguage = this.options.fetchLanguage;\n }\n }\n\n /**\n * The method to be overridden by the developer.\n *\n * @remarks\n * In the event that `fetchLanguage` is not defined or returns null / undefined, the interaction's\n * locales are used instead.\n * @returns A string for the desired language or null for no match.\n * @example\n * ```typescript\n * // Always use the same language (no per-guild configuration):\n * container.i18n.fetchLanguage = () => 'en-US';\n * ```\n * @example\n * ```typescript\n * // Retrieving the language from an ORM:\n * container.i18n.fetchLanguage = async (context) => {\n * if (!context.guildId) return null;\n * const guild = await driver.getRepository(GuildEntity).findOne({ id: context.guildId });\n * return guild?.language ?? 'en-US';\n * };\n * ```\n */\n public fetchLanguage: (context: InternationalizationContext) => Awaitable<string | null> = () =>\n null;\n\n /**\n * Initializes the handler by loading in the namespaces, passing the data to i18next, and filling\n * in {@link InternationalizationHandler.languages}.\n */\n public async init() {\n const { namespaces, languages } = await this.walkRootDirectory(this.languagesDirectory);\n const userOptions = isFunction(this.options.i18next)\n ? this.options.i18next(namespaces, languages)\n : this.options.i18next;\n const ignoreJSONStructure = userOptions?.ignoreJSONStructure ?? false;\n const skipOnVariables = userOptions?.interpolation?.skipOnVariables ?? false;\n\n i18next.use(Backend);\n await i18next.init({\n backend: this.backendOptions,\n fallbackLng: this.options.defaultName ?? \"en-US\",\n initImmediate: false,\n interpolation: {\n escapeValue: false,\n ...userOptions?.interpolation,\n skipOnVariables,\n },\n load: \"all\",\n defaultNS: this.options.defaultNS ?? \"default\",\n ns: namespaces,\n preload: languages,\n ...userOptions,\n ignoreJSONStructure,\n });\n\n this.namespaces = new Set(namespaces);\n for (const item of languages) {\n this.languages.set(item, i18next.getFixedT(item));\n }\n this.languagesLoaded = true;\n\n const formatter = i18next.services.formatter!;\n for (const { name, format, cached } of this.options.formatters ?? []) {\n if (cached) formatter.addCached(name, format);\n else formatter.add(name, format);\n }\n }\n\n /**\n * Retrieve a raw `TFunction` from the passed locale.\n * @param locale The language to be used.\n */\n public getT(locale: string) {\n if (!this.languagesLoaded) {\n throw new Error(\n \"Cannot call this method until InternationalizationHandler#init has been called\",\n );\n }\n\n const t = this.languages.get(locale);\n if (t) return t;\n throw new ReferenceError(`Invalid language (${locale})`);\n }\n\n /**\n * Localizes a content given one or more keys and i18next options.\n * @param locale The language to be used.\n * @param key The key or keys to retrieve the content from.\n * @param options The interpolation options.\n * @see {@link https://www.i18next.com/overview/api#t}\n * @returns The localized content.\n */\n public format<\n const Key extends ParseKeys<Ns, TOpt, undefined>,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> =\n TOpt[\"returnObjects\"] extends true ? $SpecialObject : string,\n const ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>,\n >(\n locale: string,\n key: Key | Key[],\n options?: ActualOptions,\n ): TFunctionReturnOptionalDetails<Ret, TOpt>;\n\n /**\n * Localizes a content given one or more keys and i18next options.\n * @param locale The language to be used.\n * @param key The key or keys to retrieve the content from.\n * @param options The interpolation options as well as a `defaultValue` for the key and any\n * key/value pairs.\n * @see {@link https://www.i18next.com/overview/api#t}\n * @returns The localized content.\n */\n public format<\n const Key extends ParseKeys<Ns, TOpt, undefined>,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> =\n TOpt[\"returnObjects\"] extends true ? $SpecialObject : string,\n >(\n locale: string,\n key: string | string[],\n options: TOpt & $Dictionary & { defaultValue: string },\n ): TFunctionReturnOptionalDetails<Ret, TOpt>;\n\n /**\n * Localizes a content given one or more keys and i18next options.\n * @param locale The language to be used.\n * @param key The key or keys to retrieve the content from.\n * @param defaultValue The default value to use if the key is not found.\n * @param options The interpolation options.\n * @see {@link https://www.i18next.com/overview/api#t}\n * @returns The localized content.\n */\n public format<\n const Key extends ParseKeys<Ns, TOpt, undefined>,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> =\n TOpt[\"returnObjects\"] extends true ? $SpecialObject : string,\n >(\n locale: string,\n key: string | string[],\n defaultValue: string | undefined,\n options?: TOpt & $Dictionary,\n ): TFunctionReturnOptionalDetails<Ret, TOpt>;\n\n /**\n * Localizes a content given one or more keys and i18next options.\n * @param locale The language to be used.\n *\n * @remarks\n * This function also has additional parameters for `key`, `defaultValue`, and `options`, however\n * TSDoc does not let us document those while matching the implementation signature. See the\n * overloads for this method for the documentation on those parameters.\n *\n * @see {@link https://www.i18next.com/overview/api#t}\n * @returns The localized content.\n */\n public format<\n const Key extends ParseKeys<Ns, TOpt, undefined>,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> =\n TOpt[\"returnObjects\"] extends true ? $SpecialObject : string,\n const ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>,\n DefaultValue extends string = never,\n >(\n locale: string,\n ...[key, defaultValueOrOptions, optionsOrUndefined]:\n | [key: Key | Key[], options?: ActualOptions]\n | [key: string | string[], options: TOpt & $Dictionary & { defaultValue: string }]\n | [\n key: string | string[],\n defaultValue: DefaultValue | undefined,\n options?: TOpt & $Dictionary,\n ]\n ): TFunctionReturnOptionalDetails<\n TFunctionProcessReturnValue<$NoInfer<Ret>, DefaultValue>,\n TOpt\n > {\n const language = this.getT(locale);\n\n // `defaultValueOrOptions` holds the default value only when it is a string; otherwise it holds\n // the options object and `optionsOrUndefined` is not provided.\n const hasDefaultValue = typeof defaultValueOrOptions === \"string\";\n const options = (hasDefaultValue ? optionsOrUndefined : defaultValueOrOptions) ?? {};\n const defaultValue = hasDefaultValue\n ? defaultValueOrOptions\n : this.options.defaultMissingKey\n ? language(this.options.defaultMissingKey, { replace: { key } })\n : \"\";\n\n return language(\n key as never,\n {\n defaultValue,\n ...(options as TOpt),\n } as never,\n ) as TFunctionReturnOptionalDetails<\n TFunctionProcessReturnValue<$NoInfer<Ret>, DefaultValue>,\n TOpt\n >;\n }\n\n /**\n * Walks the root languages directory, collecting every language and namespace found in it.\n * @param directory The directory that should be walked.\n */\n public async walkRootDirectory(directory: PathLike) {\n const languages = new Set<string>();\n const namespaces = new Set<string>();\n\n const dir = await opendir(directory);\n for await (const entry of dir) {\n // If the entry is not a directory, skip:\n if (!entry.isDirectory()) continue;\n\n // Load the directory:\n languages.add(entry.name);\n\n for await (const namespace of this.walkLocaleDirectory(join(dir.path, entry.name), \"\")) {\n namespaces.add(namespace);\n }\n }\n\n return { namespaces: [...namespaces], languages: [...languages] };\n }\n\n /**\n * Reloads the languages and namespaces registered in i18next, used by the HMR watcher registered\n * in `@wolfstar/plugin-i18next/register`.\n */\n public reloadResources(): Promise<void> {\n // Serialize reloads: the watcher can fire several events for a single edit (`addDir` followed by\n // `add`, for instance), and overlapping `i18next.loadLanguages` calls would race each other.\n this.reloadTask = this.reloadTask.then(() => this.performReload());\n return this.reloadTask;\n }\n\n /**\n * Registers the languages and namespaces that were discovered after {@link init} ran, so locales\n * and namespaces added while the process is running become usable without a restart.\n * @param discovered The result of {@link walkRootDirectory}.\n */\n private async registerDiscoveredResources(discovered: {\n languages: string[];\n namespaces: string[];\n }) {\n const newNamespaces = discovered.namespaces.filter(\n (namespace) => !this.namespaces.has(namespace),\n );\n if (newNamespaces.length > 0) {\n await i18next.loadNamespaces(newNamespaces);\n for (const namespace of newNamespaces) this.namespaces.add(namespace);\n }\n\n const newLanguages = discovered.languages.filter((language) => !this.languages.has(language));\n if (newLanguages.length > 0) {\n await i18next.loadLanguages(newLanguages);\n for (const language of newLanguages)\n this.languages.set(language, i18next.getFixedT(language));\n }\n }\n\n /**\n * @internal\n */\n private async performReload() {\n const result = await Result.fromAsync(async () => {\n let languages = this.options.hmr?.languages;\n let namespaces = this.options.hmr?.namespaces;\n\n // Only walk the directory when at least one of the two was not pinned through the options,\n // matching what `init` discovers, and register anything that appeared since.\n if (!languages || !namespaces) {\n const languageDirectoryResult = await this.walkRootDirectory(this.languagesDirectory);\n await this.registerDiscoveredResources(languageDirectoryResult);\n languages ??= languageDirectoryResult.languages;\n namespaces ??= languageDirectoryResult.namespaces;\n }\n\n await i18next.reloadResources(languages, namespaces);\n console.info(\"[plugin-i18next] Reloaded language resources.\");\n });\n\n result.inspectErr((error: unknown) =>\n console.error(\"[plugin-i18next] Failed to reload language resources.\", error),\n );\n }\n\n /**\n * Walks a single locale directory, yielding every namespace found in it.\n *\n * @remarks\n * Skips any file that does not end with `.json`.\n * @param directory The directory that should be walked.\n * @param ns The current namespace.\n */\n private async *walkLocaleDirectory(directory: string, ns: string): AsyncGenerator<string> {\n const dir = await opendir(directory);\n for await (const entry of dir) {\n if (entry.isDirectory()) {\n yield* this.walkLocaleDirectory(join(dir.path, entry.name), `${ns}${entry.name}/`);\n } else if (entry.isFile() && entry.name.endsWith(\".json\")) {\n yield `${ns}${entry.name.slice(0, -5)}`;\n }\n }\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["i18next"],"sources":["../../src/lib/functions.ts","../../src/lib/InternationalizationHandler.ts"],"sourcesContent":["import { Collection } from \"@discordjs/collection\";\nimport { container } from \"@sapphire/pieces\";\nimport type { NonNullObject } from \"@sapphire/utilities\";\nimport {\n Locale,\n type APIApplicationCommandOptionChoice,\n type LocaleString,\n} from \"discord-api-types/v10\";\nimport type {\n AppendKeyPrefix,\n DefaultNamespace,\n InterpolationMap,\n Namespace,\n ParseKeys,\n TFunction,\n TFunctionReturn,\n TFunctionReturnOptionalDetails,\n TOptions,\n} from \"i18next\";\nimport type {\n $Dictionary,\n $SpecialObject,\n BuilderWithDescription,\n BuilderWithName,\n BuilderWithNameAndDescription,\n InternationalizationContext,\n LocalePrefixKey,\n LocalizedData,\n Target,\n} from \"./types\";\n\n/**\n * Every locale Discord supports.\n */\nexport const supportedLanguages = new Set(Object.values(Locale)) as ReadonlySet<LocaleString>;\n\n/**\n * Checks whether the given language is a locale Discord supports.\n * @param language The language to check.\n */\nexport function isSupportedDiscordLocale(language: string): language is LocaleString {\n return supportedLanguages.has(language as LocaleString);\n}\n\n/**\n * Resolves the fallback language to use when none of a target's locales is loaded.\n *\n * {@link InternationalizationOptions.defaultName} wins as long as it is both a Discord locale and a\n * loaded language; otherwise `'en-US'` is used.\n * @internal\n */\nfunction getFallbackLanguageName(): LocaleString {\n const { languages, options } = container.i18n;\n const { defaultName } = options;\n\n return defaultName && isSupportedDiscordLocale(defaultName) && languages.has(defaultName)\n ? defaultName\n : \"en-US\";\n}\n\n/**\n * Narrows a locale to a loaded Discord locale, or `null` when it is neither.\n * @internal\n */\nfunction getLoadedLocale(locale: string | undefined): LocaleString | null {\n if (!locale) return null;\n return isSupportedDiscordLocale(locale) && container.i18n.languages.has(locale) ? locale : null;\n}\n\n/**\n * Builds the {@link InternationalizationContext} for any supported target.\n *\n * @remarks\n * The members of {@link Target} are told apart structurally, since the framework receives raw\n * payloads rather than class instances: an interaction has `locale`, a message has `channel_id`, a\n * channel has `type`, and anything left is a guild.\n * @internal\n */\nfunction resolveContext(target: Target): InternationalizationContext {\n if (\"locale\" in target) {\n return {\n guildId: target.guild_id ?? null,\n channelId: target.channel_id ?? null,\n userId: target.user?.id ?? target.member?.user.id ?? null,\n interactionGuildLocale: target.guild_locale,\n interactionLocale: target.locale,\n preferredLocale: target.guild_locale,\n };\n }\n\n if (\"channel_id\" in target) {\n return {\n guildId: target.guild_id ?? null,\n channelId: target.channel_id,\n userId: target.author?.id ?? null,\n };\n }\n\n if (\"type\" in target) {\n return {\n guildId: target.guild_id ?? null,\n channelId: target.id,\n userId: null,\n };\n }\n\n return {\n guildId: target.id,\n channelId: null,\n userId: null,\n preferredLocale: target.preferred_locale,\n };\n}\n\n/**\n * @internal\n */\nfunction getSupportedLanguageNameFromContext(context: InternationalizationContext): LocaleString {\n // Guild-scoped: the guild's locale is what matters, and the user's is deliberately ignored.\n // Outside a guild there is no guild locale to prefer, so the user's one is used instead.\n const preferred = context.guildId\n ? getLoadedLocale(context.preferredLocale)\n : getLoadedLocale(context.interactionLocale);\n\n return preferred ?? getFallbackLanguageName();\n}\n\n/**\n * @internal\n */\nfunction getSupportedUserLanguageNameFromContext(\n context: InternationalizationContext,\n): LocaleString {\n return (\n getLoadedLocale(context.interactionLocale) ??\n getLoadedLocale(context.preferredLocale) ??\n getFallbackLanguageName()\n );\n}\n\n/**\n * Resolves the loaded language that best matches the user's locale, falling back to the guild's one,\n * then to {@link InternationalizationOptions.defaultName}, and finally to `'en-US'`.\n *\n * @remarks\n * Only an {@link Interaction} carries a user locale. For a {@link GuildTarget} this is equivalent to\n * {@link getSupportedLanguageName}, and a {@link ChannelTarget} or {@link MessageTarget} always\n * resolves to the fallback.\n * @param target The target to read the locales from.\n */\nexport function getSupportedUserLanguageName(target: Target): LocaleString {\n return getSupportedUserLanguageNameFromContext(resolveContext(target));\n}\n\n/**\n * Resolves the `TFunction` for {@link getSupportedUserLanguageName}.\n *\n * @remarks\n * Pass the key and its options straight after the target to resolve it in one call; the bound\n * function is only returned when no key is given. Keys of a namespace other than the default one\n * carry their `<namespace>:` prefix, or the namespace is passed through the `ns` option.\n * @param target The target to read the locales from.\n */\nexport function getSupportedUserLanguageT(target: Target): TFunction;\n/**\n * Resolves a key with the user's language, as resolved by {@link getSupportedUserLanguageName}.\n * @param target The target to read the locales from.\n * @param key The key or keys to retrieve the content from.\n * @param options The interpolation options.\n */\nexport function getSupportedUserLanguageT<\n const Key extends ParseKeys<Ns, TOpt, undefined>,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> =\n TOpt[\"returnObjects\"] extends true ? $SpecialObject : string,\n const ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>,\n>(\n target: Target,\n ...[key, defaultValueOrOptions, optionsOrUndefined]:\n | [key: Key | Key[], options?: ActualOptions]\n | [key: string | string[], options: TOpt & $Dictionary & { defaultValue: string }]\n | [key: string | string[], defaultValue: string, options?: TOpt & $Dictionary]\n): TFunctionReturnOptionalDetails<Ret, TOpt>;\nexport function getSupportedUserLanguageT(target: Target, ...args: [any?, any?, any?]) {\n const t = container.i18n.getT(getSupportedUserLanguageName(target));\n return args.length === 0 ? t : (t as (...rest: any[]) => unknown)(...args);\n}\n\n/**\n * Resolves the loaded language that best matches the guild's locale, falling back to the user's one\n * when the target does not belong to a guild, then to\n * {@link InternationalizationOptions.defaultName}, and finally to `'en-US'`.\n *\n * @remarks\n * The guild locale comes from `guild_locale` on an {@link Interaction} and from `preferred_locale`\n * on a {@link GuildTarget}. A {@link ChannelTarget} and a {@link MessageTarget} carry no locale, so\n * they resolve to the fallback unless a custom\n * {@link InternationalizationHandler.fetchLanguage} hook is used through {@link fetchLanguage}.\n * @param target The target to read the locales from.\n */\nexport function getSupportedLanguageName(target: Target): LocaleString {\n return getSupportedLanguageNameFromContext(resolveContext(target));\n}\n\n/**\n * Resolves the `TFunction` for {@link getSupportedLanguageName}.\n *\n * @remarks\n * Pass the key and its options straight after the target to resolve it in one call; the bound\n * function is only returned when no key is given. Keys of a namespace other than the default one\n * carry their `<namespace>:` prefix, or the namespace is passed through the `ns` option.\n * @param target The target to read the locales from.\n */\nexport function getSupportedLanguageT(target: Target): TFunction;\n/**\n * Resolves a key with the guild's language, as resolved by {@link getSupportedLanguageName}.\n * @param target The target to read the locales from.\n * @param key The key or keys to retrieve the content from.\n * @param options The interpolation options.\n */\nexport function getSupportedLanguageT<\n const Key extends ParseKeys<Ns, TOpt, undefined>,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> =\n TOpt[\"returnObjects\"] extends true ? $SpecialObject : string,\n const ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>,\n>(\n target: Target,\n ...[key, defaultValueOrOptions, optionsOrUndefined]:\n | [key: Key | Key[], options?: ActualOptions]\n | [key: string | string[], options: TOpt & $Dictionary & { defaultValue: string }]\n | [key: string | string[], defaultValue: string, options?: TOpt & $Dictionary]\n): TFunctionReturnOptionalDetails<Ret, TOpt>;\nexport function getSupportedLanguageT(target: Target, ...args: [any?, any?, any?]) {\n const t = container.i18n.getT(getSupportedLanguageName(target));\n return args.length === 0 ? t : (t as (...rest: any[]) => unknown)(...args);\n}\n\n/**\n * Retrieves the language name for a target, using {@link InternationalizationHandler.fetchLanguage}.\n *\n * If that hook is not defined or returns a nullish value, the language is resolved from the\n * locales the target carries through {@link getSupportedLanguageName}, which itself falls back to\n * {@link InternationalizationOptions.defaultName} and then to `'en-US'`.\n\n * @remarks\n * This is the only helper that can resolve a language for a {@link ChannelTarget} or a\n * {@link MessageTarget}, since those payloads carry no locale and the hook receives their\n * `guildId` and `channelId`.\n * @param target The target to fetch the language from.\n */\nexport async function fetchLanguage(target: Target): Promise<string> {\n const context = resolveContext(target);\n const language = await container.i18n.fetchLanguage(context);\n return language ?? getSupportedLanguageNameFromContext(context);\n}\n\n/**\n * Retrieves the language-assigned function from i18next designated to a target's preferred language.\n *\n * @remarks\n * Use {@link fetchKey} to resolve a key in a single call.\n * @param target The target to fetch the language from.\n */\nexport async function fetchT(target: Target): Promise<TFunction> {\n return container.i18n.getT(await fetchLanguage(target));\n}\n\n/**\n * Resolves a key and its parameters using {@link fetchLanguage}, meaning a custom\n * {@link InternationalizationHandler.fetchLanguage} hook (for example, a per-guild database lookup)\n * is honoured.\n *\n * @remarks\n * Use {@link getSupportedLanguageT} when the language can be resolved from the target payload\n * alone, it is synchronous and does not hit the hook.\n * @param target The target to fetch the language key from.\n */\nexport async function fetchKey<\n const Key extends ParseKeys<Ns, TOpt, undefined>,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> =\n TOpt[\"returnObjects\"] extends true ? $SpecialObject : string,\n const ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>,\n>(\n target: Target,\n ...[key, defaultValueOrOptions, optionsOrUndefined]:\n | [key: Key | Key[], options?: ActualOptions]\n | [key: string | string[], options: TOpt & $Dictionary & { defaultValue: string }]\n | [key: string | string[], defaultValue: string, options?: TOpt & $Dictionary]\n): Promise<TFunctionReturnOptionalDetails<Ret, TOpt>> {\n const parsedOptions =\n typeof defaultValueOrOptions === \"string\" ? optionsOrUndefined : defaultValueOrOptions;\n const language =\n typeof parsedOptions?.lng === \"string\" ? parsedOptions.lng : await fetchLanguage(target);\n\n if (typeof defaultValueOrOptions === \"string\") {\n return container.i18n.format<Key, TOpt, Ns, Ret>(\n language,\n key,\n defaultValueOrOptions,\n optionsOrUndefined,\n );\n }\n\n return container.i18n.format<Key, TOpt, Ns, Ret>(language, key, undefined, defaultValueOrOptions);\n}\n\nlet cachedLocales: Collection<LocaleString, TFunction> | null = null;\nlet cachedLocalesSize = -1;\n\n/**\n * The loaded languages Discord supports, keyed by locale.\n *\n * @remarks\n * Memoized on the size of `container.i18n.languages` so that locales discovered by a hot reload\n * (see {@link InternationalizationHandler.reloadResources}) are picked up without a restart.\n * @internal\n */\nfunction getLocales(): Collection<LocaleString, TFunction> {\n const { languages } = container.i18n;\n if (cachedLocales && cachedLocalesSize === languages.size) return cachedLocales;\n\n const locales = new Collection<LocaleString, TFunction>();\n\n for (const [locale, t] of languages) {\n if (!isSupportedDiscordLocale(locale)) {\n process.emitWarning(\"Unsupported Discord locale\", {\n code: \"UNSUPPORTED_LOCALE\",\n detail: `'${locale}' is not assignable to type LocaleString`,\n });\n continue;\n }\n\n locales.set(locale, t);\n }\n\n cachedLocales = locales;\n cachedLocalesSize = languages.size;\n return locales;\n}\n\n/**\n * @internal\n */\nfunction getDefaultT(): TFunction {\n const defaultLocale = container.i18n.options.defaultName ?? \"en-US\";\n\n if (!isSupportedDiscordLocale(defaultLocale)) {\n throw new TypeError(\n `Unsupported Discord locale found:\\n'${defaultLocale}' is not within the list of ${[...supportedLanguages]}`,\n );\n }\n\n const defaultT = getLocales().get(defaultLocale);\n if (defaultT) return defaultT;\n throw new TypeError(`Could not find ${defaultLocale}`);\n}\n\n/**\n * Gets the value and the localizations from a language key.\n * @param key The key to get the localizations from.\n * @returns The retrieved data.\n * @remarks This should be called **strictly** after loading the locales.\n */\nexport function getLocalizedData<\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n KPrefix = undefined,\n>(key: ParseKeys<Ns, TOpt, KPrefix>): LocalizedData {\n const locales = getLocales();\n const defaultT = getDefaultT();\n\n return {\n value: defaultT(key as never),\n localizations: Object.fromEntries(locales.map((t, locale) => [locale, t(key as never)])),\n };\n}\n\n/**\n * Applies the localized names on the builder, calling `setName` and `setNameLocalizations`.\n * @param builder The builder to apply the localizations to.\n * @param key The key to get the localizations from.\n * @returns The updated builder.\n */\nexport function applyNameLocalizedBuilder<\n T extends BuilderWithName,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n KPrefix = undefined,\n>(builder: T, key: ParseKeys<Ns, TOpt, KPrefix>) {\n const result = getLocalizedData(key);\n return builder.setName(result.value).setNameLocalizations(result.localizations);\n}\n\n/**\n * Applies the localized descriptions on the builder, calling `setDescription` and\n * `setDescriptionLocalizations`.\n * @param builder The builder to apply the localizations to.\n * @param key The key to get the localizations from.\n * @returns The updated builder.\n */\nexport function applyDescriptionLocalizedBuilder<\n T extends BuilderWithDescription,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n KPrefix = undefined,\n>(builder: T, key: ParseKeys<Ns, TOpt, KPrefix>) {\n const result = getLocalizedData(key);\n return builder.setDescription(result.value).setDescriptionLocalizations(result.localizations);\n}\n\n/**\n * Applies the localized names and descriptions on the builder, calling\n * {@link applyNameLocalizedBuilder} and {@link applyDescriptionLocalizedBuilder}.\n *\n * @param builder The builder to apply the localizations to.\n * @param params The root key, or the key for the name and the key for the description.\n * @returns The updated builder. You can chain subsequent builder methods on this.\n *\n * @remarks\n * If only 2 parameters were passed, `name` will be defined as `${root}Name` and `description` as\n * `${root}Description`, being `root` the second parameter in the function, after `builder`.\n *\n * @example\n * ```typescript\n * // Both keys given explicitly:\n * applyLocalizedBuilder(builder, 'commands/names:userinfo', 'commands/descriptions:userinfo');\n * ```\n *\n * @example\n * ```typescript\n * // Root key only, resolves `commands/userinfo:nameName` and `commands/userinfo:nameDescription`:\n * applyLocalizedBuilder(builder, 'commands/userinfo:name');\n * ```\n */\nexport function applyLocalizedBuilder<\n T extends BuilderWithNameAndDescription,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n KPrefix = undefined,\n>(\n builder: T,\n ...params:\n | [root: LocalePrefixKey]\n | [name: ParseKeys<Ns, TOpt, KPrefix>, description: ParseKeys<Ns, TOpt, KPrefix>]\n): T {\n type LocalKeysType = ParseKeys<Ns, TOpt, KPrefix>;\n\n const [localeName, localeDescription] =\n params.length === 1\n ? [`${params[0]}Name` as LocalKeysType, `${params[0]}Description` as LocalKeysType]\n : params;\n\n applyNameLocalizedBuilder(builder, localeName);\n applyDescriptionLocalizedBuilder(builder, localeDescription);\n\n return builder;\n}\n\n/**\n * Constructs an object that can be passed into `setChoices` for a String or Number option with\n * localized names.\n *\n * @param key The i18next key for the name of the choice.\n * @param options The remaining choice options. This should _at least_ include the `value` key.\n * @returns An object with anything provided through `options`, with `name` and `name_localizations`\n * added.\n */\nexport function createLocalizedChoice<\n ValueType = string | number,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n KPrefix = undefined,\n>(\n key: ParseKeys<Ns, TOpt, KPrefix>,\n options: Omit<APIApplicationCommandOptionChoice<ValueType>, \"name\" | \"name_localizations\">,\n): APIApplicationCommandOptionChoice<ValueType> {\n const result = getLocalizedData(key);\n\n return {\n ...options,\n name: result.value,\n name_localizations: result.localizations,\n };\n}\n\n/**\n * Constructs a select menu option with a localized `name`, spreading any extra value on top.\n * @param key The i18next key for the name of the select option.\n * @param value The additional select option properties.\n */\nexport function createSelectMenuChoiceName<\n V extends NonNullObject,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n KPrefix = undefined,\n>(key: ParseKeys<Ns, TOpt, KPrefix>, value?: V): createSelectMenuChoiceName.Result<V> {\n const result = getLocalizedData(key);\n return {\n ...value,\n name: result.value,\n name_localizations: result.localizations,\n } as createSelectMenuChoiceName.Result<V>;\n}\n\nexport namespace createSelectMenuChoiceName {\n export type Result<V> = V & {\n name: string;\n name_localizations: import(\"discord-api-types/v10\").LocalizationMap;\n };\n}\n","import { getRootData } from \"@sapphire/pieces\";\nimport { Result } from \"@sapphire/result\";\nimport { isFunction, type Awaitable } from \"@sapphire/utilities\";\nimport { Backend, type PathResolvable } from \"@wolfstar/i18next-backend\";\nimport i18next, {\n type AppendKeyPrefix,\n type DefaultNamespace,\n type InterpolationMap,\n type Namespace,\n type ParseKeys,\n type TFunction,\n type TFunctionProcessReturnValue,\n type TFunctionReturn,\n type TFunctionReturnOptionalDetails,\n type TOptions,\n} from \"i18next\";\nimport type { PathLike } from \"node:fs\";\nimport { opendir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport type {\n $Dictionary,\n $NoInfer,\n $SpecialObject,\n InternationalizationContext,\n InternationalizationOptions,\n} from \"./types\";\n\n/**\n * A generalized class for handling `i18next` JSON files and their discovery.\n */\nexport class InternationalizationHandler {\n /**\n * Describes whether {@link InternationalizationHandler.init} has been run and languages are\n * loaded in {@link InternationalizationHandler.languages}.\n */\n public languagesLoaded = false;\n\n /**\n * A `Set` of initially loaded namespaces.\n */\n public namespaces = new Set<string>();\n\n /**\n * A `Map` of `i18next` language functions keyed by their language code.\n */\n public readonly languages = new Map<string, TFunction>();\n\n /**\n * The options {@link InternationalizationHandler} was initialized with.\n */\n public readonly options: InternationalizationOptions;\n\n /**\n * The directory passed to `@wolfstar/i18next-backend`. Also used in\n * {@link InternationalizationHandler.walkRootDirectory}.\n */\n public readonly languagesDirectory: string;\n\n /**\n * The backend options for `@wolfstar/i18next-backend` used by `i18next`.\n */\n protected readonly backendOptions: Backend.Options;\n\n /**\n * The tail of the reload queue, used by {@link reloadResources} to run reloads one at a time.\n */\n private reloadTask: Promise<void> = Promise.resolve();\n\n /**\n * @param options The options that `i18next`, `@wolfstar/i18next-backend`, and\n * {@link InternationalizationHandler} should use.\n */\n public constructor(options?: InternationalizationOptions) {\n this.options = options ?? { i18next: { ignoreJSONStructure: false } };\n this.languagesDirectory =\n this.options.defaultLanguageDirectory ?? join(getRootData().root, \"languages\");\n\n const languagePaths = new Set<PathResolvable>([\n join(this.languagesDirectory, \"{{lng}}\", \"{{ns}}.json\"),\n ...(options?.backend?.paths ?? []),\n ]);\n\n this.backendOptions = {\n paths: [...languagePaths],\n ...this.options.backend,\n };\n\n if (isFunction(this.options.fetchLanguage)) {\n this.fetchLanguage = this.options.fetchLanguage;\n }\n }\n\n /**\n * The method to be overridden by the developer.\n *\n * @remarks\n * In the event that `fetchLanguage` is not defined or returns null / undefined, the interaction's\n * locales are used instead.\n * @returns A string for the desired language or null for no match.\n * @example\n * ```typescript\n * // Always use the same language (no per-guild configuration):\n * container.i18n.fetchLanguage = () => 'en-US';\n * ```\n * @example\n * ```typescript\n * // Retrieving the language from an ORM:\n * container.i18n.fetchLanguage = async (context) => {\n * if (!context.guildId) return null;\n * const guild = await driver.getRepository(GuildEntity).findOne({ id: context.guildId });\n * return guild?.language ?? 'en-US';\n * };\n * ```\n */\n public fetchLanguage: (context: InternationalizationContext) => Awaitable<string | null> = () =>\n null;\n\n /**\n * Initializes the handler by loading in the namespaces, passing the data to i18next, and filling\n * in {@link InternationalizationHandler.languages}.\n */\n public async init() {\n const { namespaces, languages } = await this.walkRootDirectory(this.languagesDirectory);\n const userOptions = isFunction(this.options.i18next)\n ? this.options.i18next(namespaces, languages)\n : this.options.i18next;\n const ignoreJSONStructure = userOptions?.ignoreJSONStructure ?? false;\n const skipOnVariables = userOptions?.interpolation?.skipOnVariables ?? false;\n\n i18next.use(Backend);\n await i18next.init({\n backend: this.backendOptions,\n fallbackLng: this.options.defaultName ?? \"en-US\",\n initImmediate: false,\n interpolation: {\n escapeValue: false,\n ...userOptions?.interpolation,\n skipOnVariables,\n },\n load: \"all\",\n defaultNS: this.options.defaultNS ?? \"default\",\n ns: namespaces,\n preload: languages,\n ...userOptions,\n ignoreJSONStructure,\n });\n\n this.namespaces = new Set(namespaces);\n for (const item of languages) {\n this.languages.set(item, i18next.getFixedT(item));\n }\n this.languagesLoaded = true;\n\n const formatter = i18next.services.formatter!;\n for (const { name, format, cached } of this.options.formatters ?? []) {\n if (cached) formatter.addCached(name, format);\n else formatter.add(name, format);\n }\n }\n\n /**\n * Retrieve a raw `TFunction` from the passed locale.\n *\n * @remarks\n * Passing a namespace returns a function bound to it, so its keys can be written without the\n * `<namespace>:` prefix and are typed against the `CustomTypeOptions` augmentation generated by\n * [`@wolfstar/i18next-type-generator`](https://www.npmjs.com/package/@wolfstar/i18next-type-generator).\n * @param locale The language to be used.\n * @param namespace The namespace to bind the returned function to, defaulting to i18next's one.\n */\n public getT<const Ns extends Namespace = DefaultNamespace>(\n locale: string,\n namespace?: Ns,\n ): TFunction<Ns> {\n if (!this.languagesLoaded) {\n throw new Error(\n \"Cannot call this method until InternationalizationHandler#init has been called\",\n );\n }\n\n const t = this.languages.get(locale);\n if (!t) throw new ReferenceError(`Invalid language (${locale})`);\n\n // The cached functions in `languages` are bound to the default namespace, so a fixed one is\n // only created when a namespace is explicitly requested.\n return (\n namespace === undefined ? t : i18next.getFixedT<Ns>(locale, namespace)\n ) as TFunction<Ns>;\n }\n\n /**\n * Localizes a content given one or more keys and i18next options.\n * @param locale The language to be used.\n * @param key The key or keys to retrieve the content from.\n * @param options The interpolation options.\n * @see {@link https://www.i18next.com/overview/api#t}\n * @returns The localized content.\n */\n public format<\n const Key extends ParseKeys<Ns, TOpt, undefined>,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> =\n TOpt[\"returnObjects\"] extends true ? $SpecialObject : string,\n const ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>,\n >(\n locale: string,\n key: Key | Key[],\n options?: ActualOptions,\n ): TFunctionReturnOptionalDetails<Ret, TOpt>;\n\n /**\n * Localizes a content given one or more keys and i18next options.\n * @param locale The language to be used.\n * @param key The key or keys to retrieve the content from.\n * @param options The interpolation options as well as a `defaultValue` for the key and any\n * key/value pairs.\n * @see {@link https://www.i18next.com/overview/api#t}\n * @returns The localized content.\n */\n public format<\n const Key extends ParseKeys<Ns, TOpt, undefined>,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> =\n TOpt[\"returnObjects\"] extends true ? $SpecialObject : string,\n >(\n locale: string,\n key: string | string[],\n options: TOpt & $Dictionary & { defaultValue: string },\n ): TFunctionReturnOptionalDetails<Ret, TOpt>;\n\n /**\n * Localizes a content given one or more keys and i18next options.\n * @param locale The language to be used.\n * @param key The key or keys to retrieve the content from.\n * @param defaultValue The default value to use if the key is not found.\n * @param options The interpolation options.\n * @see {@link https://www.i18next.com/overview/api#t}\n * @returns The localized content.\n */\n public format<\n const Key extends ParseKeys<Ns, TOpt, undefined>,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> =\n TOpt[\"returnObjects\"] extends true ? $SpecialObject : string,\n >(\n locale: string,\n key: string | string[],\n defaultValue: string | undefined,\n options?: TOpt & $Dictionary,\n ): TFunctionReturnOptionalDetails<Ret, TOpt>;\n\n /**\n * Localizes a content given one or more keys and i18next options.\n * @param locale The language to be used.\n *\n * @remarks\n * This function also has additional parameters for `key`, `defaultValue`, and `options`, however\n * TSDoc does not let us document those while matching the implementation signature. See the\n * overloads for this method for the documentation on those parameters.\n *\n * @see {@link https://www.i18next.com/overview/api#t}\n * @returns The localized content.\n */\n public format<\n const Key extends ParseKeys<Ns, TOpt, undefined>,\n const TOpt extends TOptions = TOptions,\n Ns extends Namespace = DefaultNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> =\n TOpt[\"returnObjects\"] extends true ? $SpecialObject : string,\n const ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>,\n DefaultValue extends string = never,\n >(\n locale: string,\n ...[key, defaultValueOrOptions, optionsOrUndefined]:\n | [key: Key | Key[], options?: ActualOptions]\n | [key: string | string[], options: TOpt & $Dictionary & { defaultValue: string }]\n | [\n key: string | string[],\n defaultValue: DefaultValue | undefined,\n options?: TOpt & $Dictionary,\n ]\n ): TFunctionReturnOptionalDetails<\n TFunctionProcessReturnValue<$NoInfer<Ret>, DefaultValue>,\n TOpt\n > {\n const language = this.getT(locale);\n\n // `defaultValueOrOptions` holds the default value only when it is a string; otherwise it holds\n // the options object and `optionsOrUndefined` is not provided.\n const hasDefaultValue = typeof defaultValueOrOptions === \"string\";\n const options = (hasDefaultValue ? optionsOrUndefined : defaultValueOrOptions) ?? {};\n const defaultValue = hasDefaultValue\n ? defaultValueOrOptions\n : this.options.defaultMissingKey\n ? language(this.options.defaultMissingKey, { replace: { key } })\n : \"\";\n\n return language(\n key as never,\n {\n defaultValue,\n ...(options as TOpt),\n } as never,\n ) as TFunctionReturnOptionalDetails<\n TFunctionProcessReturnValue<$NoInfer<Ret>, DefaultValue>,\n TOpt\n >;\n }\n\n /**\n * Walks the root languages directory, collecting every language and namespace found in it.\n * @param directory The directory that should be walked.\n */\n public async walkRootDirectory(directory: PathLike) {\n const languages = new Set<string>();\n const namespaces = new Set<string>();\n\n const dir = await opendir(directory);\n for await (const entry of dir) {\n // If the entry is not a directory, skip:\n if (!entry.isDirectory()) continue;\n\n // Load the directory:\n languages.add(entry.name);\n\n for await (const namespace of this.walkLocaleDirectory(join(dir.path, entry.name), \"\")) {\n namespaces.add(namespace);\n }\n }\n\n return { namespaces: [...namespaces], languages: [...languages] };\n }\n\n /**\n * Reloads the languages and namespaces registered in i18next, used by the HMR watcher registered\n * in `@wolfstar/plugin-i18next/register`.\n */\n public reloadResources(): Promise<void> {\n // Serialize reloads: the watcher can fire several events for a single edit (`addDir` followed by\n // `add`, for instance), and overlapping `i18next.loadLanguages` calls would race each other.\n this.reloadTask = this.reloadTask.then(() => this.performReload());\n return this.reloadTask;\n }\n\n /**\n * Registers the languages and namespaces that were discovered after {@link init} ran, so locales\n * and namespaces added while the process is running become usable without a restart.\n * @param discovered The result of {@link walkRootDirectory}.\n */\n private async registerDiscoveredResources(discovered: {\n languages: string[];\n namespaces: string[];\n }) {\n const newNamespaces = discovered.namespaces.filter(\n (namespace) => !this.namespaces.has(namespace),\n );\n if (newNamespaces.length > 0) {\n await i18next.loadNamespaces(newNamespaces);\n for (const namespace of newNamespaces) this.namespaces.add(namespace);\n }\n\n const newLanguages = discovered.languages.filter((language) => !this.languages.has(language));\n if (newLanguages.length > 0) {\n await i18next.loadLanguages(newLanguages);\n for (const language of newLanguages)\n this.languages.set(language, i18next.getFixedT(language));\n }\n }\n\n /**\n * @internal\n */\n private async performReload() {\n const result = await Result.fromAsync(async () => {\n let languages = this.options.hmr?.languages;\n let namespaces = this.options.hmr?.namespaces;\n\n // Only walk the directory when at least one of the two was not pinned through the options,\n // matching what `init` discovers, and register anything that appeared since.\n if (!languages || !namespaces) {\n const languageDirectoryResult = await this.walkRootDirectory(this.languagesDirectory);\n await this.registerDiscoveredResources(languageDirectoryResult);\n languages ??= languageDirectoryResult.languages;\n namespaces ??= languageDirectoryResult.namespaces;\n }\n\n await i18next.reloadResources(languages, namespaces);\n console.info(\"[plugin-i18next] Reloaded language resources.\");\n });\n\n result.inspectErr((error: unknown) =>\n console.error(\"[plugin-i18next] Failed to reload language resources.\", error),\n );\n }\n\n /**\n * Walks a single locale directory, yielding every namespace found in it.\n *\n * @remarks\n * Skips any file that does not end with `.json`.\n * @param directory The directory that should be walked.\n * @param ns The current namespace.\n */\n private async *walkLocaleDirectory(directory: string, ns: string): AsyncGenerator<string> {\n const dir = await opendir(directory);\n for await (const entry of dir) {\n if (entry.isDirectory()) {\n yield* this.walkLocaleDirectory(join(dir.path, entry.name), `${ns}${entry.name}/`);\n } else if (entry.isFile() && entry.name.endsWith(\".json\")) {\n yield `${ns}${entry.name.slice(0, -5)}`;\n }\n }\n }\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wolfstar/plugin-i18next",
3
- "version": "1.0.1-next-20260830111821",
3
+ "version": "2.0.0-next-20260830115752",
4
4
  "description": "Plugin for @wolfstar/http-framework adding i18next-powered internationalization for HTTP interactions",
5
5
  "keywords": [
6
6
  "discord",