@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 +76 -18
- package/dist/esm/index.d.ts +85 -72
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +21 -46
- package/dist/esm/index.js.map +1 -1
- package/package.json +1 -1
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 (`
|
|
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
|
-
###
|
|
64
|
+
### Typed keys
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
|
|
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
|
-
|
|
70
|
-
|
|
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
|
-
|
|
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 =
|
|
91
|
-
const userContent =
|
|
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
|
-
`
|
|
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,
|
|
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 =
|
|
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
|
-
`
|
|
244
|
-
`
|
|
245
|
-
`
|
|
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)`
|
|
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).
|
package/dist/esm/index.d.ts
CHANGED
|
@@ -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,
|
|
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
|
|
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
|
-
*
|
|
30
|
+
* The namespace separator i18next is configured with, `':'` unless `CustomTypeOptions` overrides it.
|
|
31
31
|
*/
|
|
32
|
-
type
|
|
33
|
-
__type__: TCustom;
|
|
34
|
-
};
|
|
32
|
+
type LocaleSeparator = TypeOptions["nsSeparator"];
|
|
35
33
|
/**
|
|
36
|
-
* A
|
|
37
|
-
*
|
|
34
|
+
* A `<namespace><separator><key>` shaped key, used as the root key overload of
|
|
35
|
+
* {@link applyLocalizedBuilder}.
|
|
38
36
|
*/
|
|
39
|
-
type
|
|
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<
|
|
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
|
|
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>
|
|
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>
|
|
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>
|
|
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
|
|
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
|
|
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:
|
|
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
|
|
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
|
package/dist/esm/index.d.ts.map
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
127
|
-
|
|
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
|
-
|
|
149
|
-
|
|
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
|
|
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)
|
|
447
|
-
|
|
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 {
|
|
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
|
package/dist/esm/index.js.map
CHANGED
|
@@ -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": "
|
|
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",
|