@sapphire/plugin-i18next 6.1.1-next.eccc557.0 → 7.0.0-next.1f2cccc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (53) hide show
  1. package/dist/cjs/index.cjs +39 -0
  2. package/dist/cjs/index.cjs.map +1 -0
  3. package/dist/cjs/index.d.ts +428 -0
  4. package/dist/cjs/lib/Augmentations.d.cjs +4 -0
  5. package/dist/cjs/lib/Augmentations.d.cjs.map +1 -0
  6. package/dist/cjs/lib/InternationalizationHandler.cjs +236 -0
  7. package/dist/cjs/lib/InternationalizationHandler.cjs.map +1 -0
  8. package/dist/cjs/lib/functions.cjs +125 -0
  9. package/dist/cjs/lib/functions.cjs.map +1 -0
  10. package/dist/cjs/lib/types.cjs +4 -0
  11. package/dist/cjs/lib/types.cjs.map +1 -0
  12. package/dist/cjs/register.cjs +33 -0
  13. package/dist/cjs/register.cjs.map +1 -0
  14. package/dist/cjs/register.d.ts +10 -0
  15. package/dist/esm/chunk-6QB3UK4Q.mjs +11 -0
  16. package/dist/esm/chunk-6QB3UK4Q.mjs.map +1 -0
  17. package/dist/esm/index.d.mts +428 -0
  18. package/dist/esm/index.mjs +11 -0
  19. package/dist/esm/index.mjs.map +1 -0
  20. package/dist/esm/lib/Augmentations.d.mjs +3 -0
  21. package/dist/esm/lib/Augmentations.d.mjs.map +1 -0
  22. package/dist/esm/lib/InternationalizationHandler.mjs +224 -0
  23. package/dist/esm/lib/InternationalizationHandler.mjs.map +1 -0
  24. package/dist/esm/lib/functions.mjs +115 -0
  25. package/dist/esm/lib/functions.mjs.map +1 -0
  26. package/dist/esm/lib/types.mjs +3 -0
  27. package/dist/esm/lib/types.mjs.map +1 -0
  28. package/dist/esm/register.d.mts +10 -0
  29. package/dist/esm/register.mjs +30 -0
  30. package/dist/esm/register.mjs.map +1 -0
  31. package/package.json +37 -22
  32. package/dist/index.d.ts +0 -16
  33. package/dist/index.d.ts.map +0 -1
  34. package/dist/index.js +0 -26
  35. package/dist/index.js.map +0 -1
  36. package/dist/index.mjs +0 -13
  37. package/dist/lib/InternationalizationHandler.d.ts +0 -127
  38. package/dist/lib/InternationalizationHandler.d.ts.map +0 -1
  39. package/dist/lib/InternationalizationHandler.js +0 -271
  40. package/dist/lib/InternationalizationHandler.js.map +0 -1
  41. package/dist/lib/functions.d.ts +0 -137
  42. package/dist/lib/functions.d.ts.map +0 -1
  43. package/dist/lib/functions.js +0 -241
  44. package/dist/lib/functions.js.map +0 -1
  45. package/dist/lib/types.d.ts +0 -151
  46. package/dist/lib/types.d.ts.map +0 -1
  47. package/dist/lib/types.js +0 -3
  48. package/dist/lib/types.js.map +0 -1
  49. package/dist/register.d.ts +0 -9
  50. package/dist/register.d.ts.map +0 -1
  51. package/dist/register.js +0 -28
  52. package/dist/register.js.map +0 -1
  53. package/dist/register.mjs +0 -4
@@ -0,0 +1,428 @@
1
+ import { Awaitable } from '@sapphire/utilities';
2
+ import { Backend } from '@skyra/i18next-backend';
3
+ import * as i18next from 'i18next';
4
+ import { InitOptions, TFunction, ParseKeys, TOptions, Namespace, TFunctionReturn, AppendKeyPrefix, InterpolationMap, TFunctionReturnOptionalDetails, DefaultNamespace } from 'i18next';
5
+ export { TFunction, TOptions, default as i18next } from 'i18next';
6
+ import { PathLike } from 'node:fs';
7
+ import { WatchOptions } from 'chokidar';
8
+ import { Message, StageChannel, VoiceChannel, Guild, User, Interaction, LocalizationMap, BaseInteraction, APIApplicationCommandOptionChoice } from 'discord.js';
9
+
10
+ /**
11
+ * This is a re-exported type from i18next.
12
+ * It is essentially an object of key-value pairs, where the key is a string and the value is any.
13
+ */
14
+ interface $Dictionary {
15
+ [key: string]: any;
16
+ }
17
+ /**
18
+ * This is a re-exported type from i18next.
19
+ * It is the returned type from `resolveKey` when `returnObjects` is `true` in the options.
20
+ */
21
+ type $SpecialObject = $Dictionary | Array<string | $Dictionary>;
22
+ /**
23
+ * Configure whether to use Hot-Module-Replacement (HMR) for your i18next resources using these options. The minimum config to enable HMR is to set `enabled` to true. Any other properties are optional.
24
+ * @since 2.2.0
25
+ */
26
+ interface HMROptions {
27
+ /**
28
+ * HMR status for the i18next plugin.
29
+ * @default false
30
+ */
31
+ enabled: boolean;
32
+ /**
33
+ * Languages that will be reloaded when updating the languages directory.
34
+ * @default All languages that are automatically resolved from your folder setup
35
+ */
36
+ languages?: string | string[];
37
+ /**
38
+ * Namespaces that will be reloaded when updating the languages directory.
39
+ * @default All namespaces that are automatically resolved from your languages folder setup
40
+ */
41
+ namespaces?: string | string[];
42
+ /**
43
+ * HMR options
44
+ */
45
+ options?: WatchOptions;
46
+ }
47
+ /**
48
+ * Used to dynamically add options based on found languages in {@link InternationalizationHandler#init}.
49
+ * @since 1.1.0
50
+ * @private
51
+ */
52
+ type DynamicOptions<T extends InitOptions> = (namespaces: string[], languages: string[]) => T;
53
+ /**
54
+ * The options used in {@link InternationalizationHandler}.
55
+ * @since 1.0.0
56
+ */
57
+ interface InternationalizationOptions {
58
+ /**
59
+ * Used as the default 2nd to last fallback locale if no other is found.
60
+ * It's only followed by "en-US".
61
+ * @since 1.0.0
62
+ */
63
+ defaultName?: string;
64
+ /**
65
+ * The options passed to `backend` in `i18next.init`.
66
+ * @since 1.0.0
67
+ */
68
+ backend?: Backend.Options;
69
+ /**
70
+ * The options passed to `i18next.init`.
71
+ * @since 1.0.0
72
+ */
73
+ i18next?: InitOptions | DynamicOptions<InitOptions>;
74
+ /**
75
+ * The directory in which "i18next-fs-backend" should search for files.
76
+ * @default `rootDirectory/language`
77
+ * @since 1.0.0
78
+ */
79
+ defaultLanguageDirectory?: string;
80
+ /**
81
+ * The default value to be used if a specific language key isn't found.
82
+ * Defaults to "default:default".
83
+ * @since 1.0.0
84
+ */
85
+ defaultMissingKey?: string;
86
+ /**
87
+ * The default NS that is prefixed to all keys that don't specify it.
88
+ * Defaults to "default".
89
+ * @since 1.0.0
90
+ */
91
+ defaultNS?: string;
92
+ /**
93
+ * Array of formatters to add to i18n.
94
+ *
95
+ * @since 2.0.0
96
+ * @default []
97
+ */
98
+ formatters?: I18nextFormatters[];
99
+ /**
100
+ * Reload languages and namespaces when updating the languages directory.
101
+ *
102
+ * @since 2.2.0
103
+ */
104
+ hmr?: HMROptions;
105
+ /**
106
+ * A function that is to be used to retrieve the language for the current context.
107
+ * Context exists of a {@link Guild `guild`}, a {@link DiscordChannel `channel`} and a {@link User `user`}.
108
+ *
109
+ * If this is not set, then the language will always be the default language.
110
+ *
111
+ * This will be inserted for {@link InternationalizationHandler.fetchLanguage}.
112
+ * @since 2.0.0
113
+ * @default () => InternationalizationOptions.defaultName
114
+ */
115
+ fetchLanguage?: (context: InternationalizationContext) => Awaitable<string | null>;
116
+ }
117
+ type TextBasedDiscordChannel = Message['channel'];
118
+ type DiscordChannel = TextBasedDiscordChannel | StageChannel | VoiceChannel;
119
+ /**
120
+ * Context for {@link InternationalizationHandler.fetchLanguage} functions.
121
+ * This context enables implementation of per-guild, per-channel, and per-user localization.
122
+ */
123
+ interface InternationalizationContext {
124
+ /** The {@link Guild} object to fetch the preferred language for, or `null` if the language is to be fetched in a DM. */
125
+ guild: Guild | null;
126
+ /** The {@link DiscordChannel} object to fetch the preferred language for. */
127
+ channel: DiscordChannel | null;
128
+ /** The user to fetch the preferred language for. */
129
+ user: User | null;
130
+ interactionGuildLocale?: Interaction['guildLocale'];
131
+ interactionLocale?: Interaction['locale'];
132
+ }
133
+ interface InternationalizationClientOptions {
134
+ i18n?: InternationalizationOptions;
135
+ }
136
+ interface I18nextFormatters {
137
+ name: string;
138
+ format(value: any, lng: string | undefined, options: any): string;
139
+ }
140
+ interface LocalizedData {
141
+ value: string;
142
+ localizations: LocalizationMap;
143
+ }
144
+ interface BuilderWithName {
145
+ setName(name: string): this;
146
+ setNameLocalizations(localizedNames: LocalizationMap | null): this;
147
+ }
148
+ interface BuilderWithDescription {
149
+ setDescription(description: string): this;
150
+ setDescriptionLocalizations(localizedDescriptions: LocalizationMap | null): this;
151
+ }
152
+ type BuilderWithNameAndDescription = BuilderWithName & BuilderWithDescription;
153
+ type ChannelTarget = Message | DiscordChannel;
154
+ type Target = BaseInteraction | ChannelTarget | Guild;
155
+
156
+ /**
157
+ * A generalized class for handling `i18next` JSON files and their discovery.
158
+ * @since 1.0.0
159
+ */
160
+ declare class InternationalizationHandler {
161
+ /**
162
+ * Describes whether {@link InternationalizationHandler.init} has been run and languages are loaded in {@link InternationalizationHandler.languages}.
163
+ * @since 1.0.0
164
+ */
165
+ languagesLoaded: boolean;
166
+ /**
167
+ * A `Set` of initially loaded namespaces.
168
+ * @since 1.2.0
169
+ */
170
+ namespaces: Set<string>;
171
+ /**
172
+ * A `Map` of `i18next` language functions keyed by their language code.
173
+ * @since 1.0.0
174
+ */
175
+ readonly languages: Map<string, TFunction<"translation", undefined>>;
176
+ /**
177
+ * The options InternationalizationHandler was initialized with in the client.
178
+ * @since 1.0.0
179
+ */
180
+ readonly options: InternationalizationOptions;
181
+ /**
182
+ * The director passed to `@skyra/i18next-backend`.
183
+ * Also used in {@link InternationalizationHandler.walkLanguageDirectory}.
184
+ * @since 1.2.0
185
+ */
186
+ readonly languagesDirectory: string;
187
+ /**
188
+ * The backend options for `@skyra/i18next-backend` used by `i18next`.
189
+ * @since 1.0.0
190
+ */
191
+ protected readonly backendOptions: Backend.Options;
192
+ /**
193
+ * @param options The options that `i18next`, `@skyra/i18next-backend`, and {@link InternationalizationHandler} should use.
194
+ * @since 1.0.0
195
+ * @constructor
196
+ */
197
+ constructor(options?: InternationalizationOptions);
198
+ /**
199
+ * The method to be overridden by the developer.
200
+ *
201
+ * @note In the event that fetchLanguage is not defined or returns null / undefined, the defaulting from {@link fetchLanguage} will be used.
202
+ * @since 2.0.0
203
+ * @return A string for the desired language or null for no match.
204
+ * @see {@link fetchLanguage}
205
+ * @example
206
+ * ```typescript
207
+ * // Always use the same language (no per-guild configuration):
208
+ * container.i18n.fetchLanguage = () => 'en-US';
209
+ * ```
210
+ * @example
211
+ * ```typescript
212
+ * // Retrieving the language from an SQL database:
213
+ * container.i18n.fetchLanguage = async (context) => {
214
+ * const guild = await driver.getOne('SELECT language FROM public.guild WHERE id = $1', [context.guild.id]);
215
+ * return guild?.language ?? 'en-US';
216
+ * };
217
+ * ```
218
+ * @example
219
+ * ```typescript
220
+ * // Retrieving the language from an ORM:
221
+ * container.i18n.fetchLanguage = async (context) => {
222
+ * const guild = await driver.getRepository(GuildEntity).findOne({ id: context.guild.id });
223
+ * return guild?.language ?? 'en-US';
224
+ * };
225
+ * ```
226
+ * @example
227
+ * ```typescript
228
+ * // Retrieving the language on a per channel basis, e.g. per user or guild channel (ORM example but same principles apply):
229
+ * container.i18n.fetchLanguage = async (context) => {
230
+ * const channel = await driver.getRepository(ChannelEntity).findOne({ id: context.channel.id });
231
+ * return channel?.language ?? 'en-US';
232
+ * };
233
+ * ```
234
+ */
235
+ fetchLanguage: (context: InternationalizationContext) => Awaitable<string | null>;
236
+ /**
237
+ * Initializes the handler by loading in the namespaces, passing the data to i18next, and filling in the {@link InternationalizationHandler#languages}.
238
+ * @since 1.0.0
239
+ */
240
+ init(): Promise<void>;
241
+ /**
242
+ * Retrieve a raw TFunction from the passed locale.
243
+ * @param locale The language to be used.
244
+ * @since 1.0.0
245
+ */
246
+ getT(locale: string): TFunction<"translation", undefined>;
247
+ /**
248
+ * Localizes a content given one or more keys and i18next options.
249
+ * @since 2.0.0
250
+ * @param locale The language to be used.
251
+ * @param key The key or keys to retrieve the content from.
252
+ * @param options The interpolation options.
253
+ * @see {@link https://www.i18next.com/overview/api#t}
254
+ * @returns The localized content.
255
+ */
256
+ format<const Key extends ParseKeys<Ns, TOpt, undefined>, const TOpt extends TOptions, Ns extends Namespace, Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt>, const ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>>(locale: string, ...[key, defaultValueOrOptions, optionsOrUndefined]: [key: Key | Key[], options?: ActualOptions] | [key: string | string[], options: TOpt & $Dictionary & {
257
+ defaultValue: string;
258
+ }] | [key: string | string[], defaultValue: string | undefined, options?: TOpt & $Dictionary]): TFunctionReturnOptionalDetails<Ret, TOpt>;
259
+ /**
260
+ * @param directory The directory that should be walked.
261
+ * @since 3.0.0
262
+ */
263
+ walkRootDirectory(directory: PathLike): Promise<{
264
+ namespaces: string[];
265
+ languages: string[];
266
+ }>;
267
+ reloadResources(): Promise<void>;
268
+ /**
269
+ * @description Skips any files that don't end with `.json`.
270
+ * @param directory The directory that should be walked.
271
+ * @param ns The current namespace.
272
+ * @since 3.0.0
273
+ */
274
+ private walkLocaleDirectory;
275
+ }
276
+
277
+ /**
278
+ * Retrieves the language name for a specific target, using {@link InternationalizationHandler.fetchLanguage}.
279
+ * If {@link InternationalizationHandler.fetchLanguage} is not defined or this function returns a nullish value,
280
+ * then there will be a series of fallback attempts in the following descending order:
281
+ * 1. Returns {@link Guild.preferredLocale}.
282
+ * 2. Returns {@link InternationalizationOptions.defaultName} if no guild was provided.
283
+ * 3. Returns `'en-US'` if nothing else was found.
284
+ * @since 2.0.0
285
+ * @param target The target to fetch the language from.
286
+ * @see {@link resolveLanguage}
287
+ * @returns The name of the language key.
288
+ */
289
+ declare function fetchLanguage(target: Target): Promise<string>;
290
+ /**
291
+ * Retrieves the language-assigned function from i18next designated to a target's preferred language code.
292
+ * @since 2.0.0
293
+ * @param target The target to fetch the language from.
294
+ * @returns The language function from i18next.
295
+ */
296
+ declare function fetchT(target: Target): Promise<i18next.TFunction<"translation", undefined>>;
297
+ /**
298
+ * Resolves a key and its parameters.
299
+ * @since 2.0.0
300
+ * @param target The target to fetch the language key from.
301
+ * @param key The i18next key.
302
+ * @param options The options to be passed to TFunction.
303
+ * @returns The data that `key` held, processed by i18next.
304
+ */
305
+ declare function resolveKey<const Key extends ParseKeys<Ns, TOpt, undefined>, const TOpt extends TOptions = TOptions, Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TOpt['returnObjects'] extends true ? $SpecialObject : string, Ns extends Namespace = DefaultNamespace, 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 & {
306
+ defaultValue: string;
307
+ }] | [key: string | string[], defaultValue: string, options?: TOpt & $Dictionary]): Promise<TFunctionReturnOptionalDetails<Ret, TOpt>>;
308
+ /**
309
+ * Gets the value and the localizations from a language key.
310
+ * @param key The key to get the localizations from.
311
+ * @returns The retrieved data.
312
+ * @remarks This should be called **strictly** after loading the locales.
313
+ */
314
+ declare function getLocalizedData<const TOpt extends TOptions = TOptions, Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(key: ParseKeys<Ns, TOpt, KPrefix>): LocalizedData;
315
+ /**
316
+ * Applies the localized names on the builder, calling `setName` and `setNameLocalizations`.
317
+ * @param builder The builder to apply the localizations to.
318
+ * @param key The key to get the localizations from.
319
+ * @returns The updated builder.
320
+ */
321
+ declare function applyNameLocalizedBuilder<T extends BuilderWithName, const TOpt extends TOptions = TOptions, Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(builder: T, key: ParseKeys<Ns, TOpt, KPrefix>): T;
322
+ /**
323
+ * Applies the localized descriptions on the builder, calling `setDescription` and `setDescriptionLocalizations`.
324
+ * @param builder The builder to apply the localizations to.
325
+ * @param key The key to get the localizations from.
326
+ * @returns The updated builder.
327
+ */
328
+ declare function applyDescriptionLocalizedBuilder<T extends BuilderWithDescription, const TOpt extends TOptions = TOptions, Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(builder: T, key: ParseKeys<Ns, TOpt, KPrefix>): T;
329
+ /**
330
+ * Applies the localized names and descriptions on the builder, calling {@link applyNameLocalizedBuilder} and
331
+ * {@link applyDescriptionLocalizedBuilder}.
332
+ *
333
+ * @param builder The builder to apply the localizations to.
334
+ *
335
+ * @param params The root key or the key for the name and description keys.
336
+ * This needs to be either 1 or 2 parameters.
337
+ * See examples below for more information.
338
+ *
339
+ * @returns The updated builder. You can chain subsequent builder methods on this.
340
+ *
341
+ * @remarks If only 2 parameters were passed, then this function will automatically append `Name` and `Description`
342
+ * to the root-key (wherein `root-key` is second parameter in the function, after `builder`)
343
+ * passed through the second parameter.
344
+ *
345
+ * For example given `applyLocalizedBuilder(builder, 'userinfo')` the localized options will use the i18next keys
346
+ * `userinfoName` and `userinfoDescription`.
347
+ *
348
+ * In the following example we provide all parameters and add a User Option
349
+ * `applyLocalizedBuilder` needs either
350
+ * @example
351
+ * ```typescript
352
+ * class UserInfoCommand extends Command {
353
+ * public registerApplicationCommands(registry: ChatInputCommand.Registry) {
354
+ * registry.registerChatInputCommand(
355
+ * (builder) =>
356
+ * applyLocalizedBuilder(builder, 'commands/names:userinfo', 'commands/descriptions:userinfo')
357
+ * .addUserOption(
358
+ * (input) => applyLocalizedBuilder(input, 'commands/options:userinfo-name', 'commands/options:userinfo-description').setRequired(true)
359
+ * )
360
+ * );
361
+ * }
362
+ * }
363
+ * ```
364
+ *
365
+ * In the following example we provide single root keys which means `Name` and `Description` get appended as mentioned above.
366
+ * @example
367
+ * ```typescript
368
+ * class UserInfoCommand extends Command {
369
+ * public registerApplicationCommands(registry: ChatInputCommand.Registry) {
370
+ * registry.registerChatInputCommand(
371
+ * (builder) =>
372
+ * applyLocalizedBuilder(builder, 'commands:userinfo')
373
+ * .addUserOption(
374
+ * (input) => applyLocalizedBuilder(input, 'options:userinfo').setRequired(true)
375
+ * )
376
+ * );
377
+ * }
378
+ * }
379
+ * ```
380
+ */
381
+ declare function applyLocalizedBuilder<T extends BuilderWithNameAndDescription, const TOpt extends TOptions = TOptions, Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(builder: T, ...params: [root: string] | [name: ParseKeys<Ns, TOpt, KPrefix>, description: ParseKeys<Ns, TOpt, KPrefix>]): T;
382
+ /**
383
+ * Constructs an object that can be passed into `setChoices` for String or Number option with localized names.
384
+ *
385
+ * @param key The i18next key for the name of the select option name.
386
+ * @param options The additional Select Menu options. This should _at least_ include the `value` key.
387
+ * @returns An object with anything provided through {@link createLocalizedChoice.options} with `name` and `name_localizations` added.
388
+ *
389
+ * @example
390
+ * ```typescript
391
+ * export class TypeCommand extends Command {
392
+ * public override registerApplicationCommands(registry: ChatInputCommand.Registry) {
393
+ * registry.registerChatInputCommand((builder) =>
394
+ * applyLocalizedBuilder(builder, 'commands/names:type').addStringOption((option) =>
395
+ * applyLocalizedBuilder(option, 'commands/options:type')
396
+ * .setRequired(true)
397
+ * .setChoices(
398
+ * createLocalizedChoice('selects/pokemon:type-grass', { value: 'grass' }),
399
+ * createLocalizedChoice('selects/pokemon:type-water', { value: 'water' }),
400
+ * createLocalizedChoice('selects/pokemon:type-fire', { value: 'fire' }),
401
+ * createLocalizedChoice('selects/pokemon:type-electric', { value: 'electric' })
402
+ * )
403
+ * )
404
+ * );
405
+ * }
406
+ * }
407
+ * ```
408
+ */
409
+ declare function createLocalizedChoice<ValueType = string | number, const TOpt extends TOptions = TOptions, Ns extends Namespace = DefaultNamespace, KPrefix = undefined>(key: ParseKeys<Ns, TOpt, KPrefix>, options: Omit<APIApplicationCommandOptionChoice<ValueType>, 'name' | 'name_localizations'>): APIApplicationCommandOptionChoice<ValueType>;
410
+
411
+ declare module '@sapphire/pieces' {
412
+ interface Container {
413
+ i18n: InternationalizationHandler;
414
+ }
415
+ }
416
+ declare module 'discord.js' {
417
+ interface ClientOptions extends InternationalizationClientOptions {
418
+ }
419
+ }
420
+ /**
421
+ * The [@sapphire/plugin-i18next](https://github.com/sapphiredev/plugins/blob/main/packages/i18next) version that you are currently using.
422
+ * An example use of this is showing it of in a bot information command.
423
+ *
424
+ * Note to Sapphire developers: This needs to explicitly be `string` so it is not typed as the string that gets replaced by esbuild
425
+ */
426
+ declare const version: string;
427
+
428
+ export { type $Dictionary, type $SpecialObject, type BuilderWithDescription, type BuilderWithName, type BuilderWithNameAndDescription, type ChannelTarget, type DiscordChannel, type DynamicOptions, type HMROptions, type I18nextFormatters, type InternationalizationClientOptions, type InternationalizationContext, InternationalizationHandler, type InternationalizationOptions, type LocalizedData, type Target, type TextBasedDiscordChannel, applyDescriptionLocalizedBuilder, applyLocalizedBuilder, applyNameLocalizedBuilder, createLocalizedChoice, fetchLanguage, fetchT, getLocalizedData, resolveKey, version };
@@ -0,0 +1,11 @@
1
+ import './chunk-6QB3UK4Q.mjs';
2
+ export { default as i18next } from 'i18next';
3
+ export * from './lib/InternationalizationHandler.mjs';
4
+ export * from './lib/functions.mjs';
5
+ export * from './lib/types.mjs';
6
+
7
+ var version = "7.0.0-next.1f2cccc.0";
8
+
9
+ export { version };
10
+ //# sourceMappingURL=out.js.map
11
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/index.ts"],"names":["default"],"mappings":";;;AAGA,SAAoB,WAAXA,gBAAyD;AAClE,cAAc;AACd,cAAc;AACd,cAAc;AAmBP,IAAM,UAAkB","sourcesContent":["import type { InternationalizationHandler } from './lib/InternationalizationHandler';\nimport type { InternationalizationClientOptions } from './lib/types';\n\nexport { default as i18next, type TFunction, type TOptions } from 'i18next';\nexport * from './lib/InternationalizationHandler';\nexport * from './lib/functions';\nexport * from './lib/types';\n\ndeclare module '@sapphire/pieces' {\n\tinterface Container {\n\t\ti18n: InternationalizationHandler;\n\t}\n}\n\ndeclare module 'discord.js' {\n\texport interface ClientOptions extends InternationalizationClientOptions {}\n}\n\n/**\n * The [@sapphire/plugin-i18next](https://github.com/sapphiredev/plugins/blob/main/packages/i18next) version that you are currently using.\n * An example use of this is showing it of in a bot information command.\n *\n * Note to Sapphire developers: This needs to explicitly be `string` so it is not typed as the string that gets replaced by esbuild\n */\n// eslint-disable-next-line @typescript-eslint/no-inferrable-types\nexport const version: string = '7.0.0-next.1f2cccc.0';\n"]}
@@ -0,0 +1,3 @@
1
+
2
+ //# sourceMappingURL=out.js.map
3
+ //# sourceMappingURL=Augmentations.d.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"names":[],"mappings":""}
@@ -0,0 +1,224 @@
1
+ import { __name, __publicField } from '../chunk-6QB3UK4Q.mjs';
2
+ import { Result } from '@sapphire/framework';
3
+ import { container, getRootData } from '@sapphire/pieces';
4
+ import { isFunction } from '@sapphire/utilities';
5
+ import { Backend } from '@skyra/i18next-backend';
6
+ import i18next from 'i18next';
7
+ import { opendir } from 'node:fs/promises';
8
+ import { join } from 'node:path';
9
+ import { URL, fileURLToPath } from 'node:url';
10
+
11
+ var _InternationalizationHandler = class _InternationalizationHandler {
12
+ /**
13
+ * @param options The options that `i18next`, `@skyra/i18next-backend`, and {@link InternationalizationHandler} should use.
14
+ * @since 1.0.0
15
+ * @constructor
16
+ */
17
+ constructor(options) {
18
+ /**
19
+ * Describes whether {@link InternationalizationHandler.init} has been run and languages are loaded in {@link InternationalizationHandler.languages}.
20
+ * @since 1.0.0
21
+ */
22
+ __publicField(this, "languagesLoaded", false);
23
+ /**
24
+ * A `Set` of initially loaded namespaces.
25
+ * @since 1.2.0
26
+ */
27
+ __publicField(this, "namespaces", /* @__PURE__ */ new Set());
28
+ /**
29
+ * A `Map` of `i18next` language functions keyed by their language code.
30
+ * @since 1.0.0
31
+ */
32
+ __publicField(this, "languages", /* @__PURE__ */ new Map());
33
+ /**
34
+ * The options InternationalizationHandler was initialized with in the client.
35
+ * @since 1.0.0
36
+ */
37
+ __publicField(this, "options");
38
+ /**
39
+ * The director passed to `@skyra/i18next-backend`.
40
+ * Also used in {@link InternationalizationHandler.walkLanguageDirectory}.
41
+ * @since 1.2.0
42
+ */
43
+ __publicField(this, "languagesDirectory");
44
+ /**
45
+ * The backend options for `@skyra/i18next-backend` used by `i18next`.
46
+ * @since 1.0.0
47
+ */
48
+ __publicField(this, "backendOptions");
49
+ /**
50
+ * The method to be overridden by the developer.
51
+ *
52
+ * @note In the event that fetchLanguage is not defined or returns null / undefined, the defaulting from {@link fetchLanguage} will be used.
53
+ * @since 2.0.0
54
+ * @return A string for the desired language or null for no match.
55
+ * @see {@link fetchLanguage}
56
+ * @example
57
+ * ```typescript
58
+ * // Always use the same language (no per-guild configuration):
59
+ * container.i18n.fetchLanguage = () => 'en-US';
60
+ * ```
61
+ * @example
62
+ * ```typescript
63
+ * // Retrieving the language from an SQL database:
64
+ * container.i18n.fetchLanguage = async (context) => {
65
+ * const guild = await driver.getOne('SELECT language FROM public.guild WHERE id = $1', [context.guild.id]);
66
+ * return guild?.language ?? 'en-US';
67
+ * };
68
+ * ```
69
+ * @example
70
+ * ```typescript
71
+ * // Retrieving the language from an ORM:
72
+ * container.i18n.fetchLanguage = async (context) => {
73
+ * const guild = await driver.getRepository(GuildEntity).findOne({ id: context.guild.id });
74
+ * return guild?.language ?? 'en-US';
75
+ * };
76
+ * ```
77
+ * @example
78
+ * ```typescript
79
+ * // Retrieving the language on a per channel basis, e.g. per user or guild channel (ORM example but same principles apply):
80
+ * container.i18n.fetchLanguage = async (context) => {
81
+ * const channel = await driver.getRepository(ChannelEntity).findOne({ id: context.channel.id });
82
+ * return channel?.language ?? 'en-US';
83
+ * };
84
+ * ```
85
+ */
86
+ __publicField(this, "fetchLanguage", /* @__PURE__ */ __name(() => null, "fetchLanguage"));
87
+ this.options = options ?? { i18next: { ignoreJSONStructure: false } };
88
+ const baseUserDirectory = container.client?.options?.baseUserDirectory instanceof URL ? fileURLToPath(container.client?.options?.baseUserDirectory) : container.client?.options?.baseUserDirectory;
89
+ this.languagesDirectory = this.options.defaultLanguageDirectory ?? join(baseUserDirectory ?? getRootData().root, "languages");
90
+ const languagePaths = /* @__PURE__ */ new Set([
91
+ join(this.languagesDirectory, "{{lng}}", "{{ns}}.json"),
92
+ //
93
+ ...options?.backend?.paths ?? []
94
+ ]);
95
+ this.backendOptions = {
96
+ paths: [...languagePaths],
97
+ ...this.options.backend
98
+ };
99
+ if (isFunction(this.options.fetchLanguage)) {
100
+ this.fetchLanguage = this.options.fetchLanguage;
101
+ }
102
+ }
103
+ /**
104
+ * Initializes the handler by loading in the namespaces, passing the data to i18next, and filling in the {@link InternationalizationHandler#languages}.
105
+ * @since 1.0.0
106
+ */
107
+ async init() {
108
+ const { namespaces, languages } = await this.walkRootDirectory(this.languagesDirectory);
109
+ const userOptions = isFunction(this.options.i18next) ? this.options.i18next(namespaces, languages) : this.options.i18next;
110
+ const ignoreJSONStructure = userOptions?.ignoreJSONStructure ?? false;
111
+ const skipOnVariables = userOptions?.interpolation?.skipOnVariables ?? false;
112
+ i18next.use(Backend);
113
+ await i18next.init({
114
+ backend: this.backendOptions,
115
+ fallbackLng: this.options.defaultName ?? "en-US",
116
+ initImmediate: false,
117
+ interpolation: {
118
+ escapeValue: false,
119
+ ...userOptions?.interpolation,
120
+ skipOnVariables
121
+ },
122
+ load: "all",
123
+ defaultNS: "default",
124
+ ns: namespaces,
125
+ preload: languages,
126
+ ...userOptions,
127
+ ignoreJSONStructure
128
+ });
129
+ this.namespaces = new Set(namespaces);
130
+ for (const item of languages) {
131
+ this.languages.set(item, i18next.getFixedT(item));
132
+ }
133
+ this.languagesLoaded = true;
134
+ const formatters = this.options.formatters ?? [];
135
+ for (const { name, format } of formatters) {
136
+ i18next.services.formatter.add(name, format);
137
+ }
138
+ }
139
+ /**
140
+ * Retrieve a raw TFunction from the passed locale.
141
+ * @param locale The language to be used.
142
+ * @since 1.0.0
143
+ */
144
+ getT(locale) {
145
+ if (!this.languagesLoaded)
146
+ throw new Error("Cannot call this method until InternationalizationHandler#init has been called");
147
+ const t = this.languages.get(locale);
148
+ if (t)
149
+ return t;
150
+ throw new ReferenceError("Invalid language provided");
151
+ }
152
+ /**
153
+ * Localizes a content given one or more keys and i18next options.
154
+ * @since 2.0.0
155
+ * @param locale The language to be used.
156
+ * @param key The key or keys to retrieve the content from.
157
+ * @param options The interpolation options.
158
+ * @see {@link https://www.i18next.com/overview/api#t}
159
+ * @returns The localized content.
160
+ */
161
+ format(locale, ...[key, defaultValueOrOptions, optionsOrUndefined]) {
162
+ if (!this.languagesLoaded)
163
+ throw new Error("Cannot call this method until InternationalizationHandler#init has been called");
164
+ const language = this.languages.get(locale);
165
+ if (!language)
166
+ throw new ReferenceError("Invalid language provided");
167
+ const defaultValue = typeof defaultValueOrOptions === "string" ? defaultValueOrOptions : this.options.defaultMissingKey ? language(this.options.defaultMissingKey, { replace: { key } }) : "";
168
+ return language(key, defaultValue, optionsOrUndefined);
169
+ }
170
+ /**
171
+ * @param directory The directory that should be walked.
172
+ * @since 3.0.0
173
+ */
174
+ async walkRootDirectory(directory) {
175
+ const languages = /* @__PURE__ */ new Set();
176
+ const namespaces = /* @__PURE__ */ new Set();
177
+ const dir = await opendir(directory);
178
+ for await (const entry of dir) {
179
+ if (!entry.isDirectory())
180
+ continue;
181
+ languages.add(entry.name);
182
+ for await (const namespace of this.walkLocaleDirectory(join(dir.path, entry.name), "")) {
183
+ namespaces.add(namespace);
184
+ }
185
+ }
186
+ return { namespaces: [...namespaces], languages: [...languages] };
187
+ }
188
+ async reloadResources() {
189
+ const result = await Result.fromAsync(async () => {
190
+ let languages = this.options.hmr?.languages;
191
+ let namespaces = this.options.hmr?.namespaces;
192
+ if (!languages || !namespaces) {
193
+ const languageDirectoryResult = await this.walkRootDirectory(this.languagesDirectory);
194
+ languages ??= languageDirectoryResult.languages;
195
+ namespaces ??= languageDirectoryResult.namespaces;
196
+ }
197
+ await i18next.reloadResources(languages, namespaces);
198
+ container.logger.info("[i18next-Plugin] Reloaded language resources.");
199
+ });
200
+ result.inspectErr((error) => container.logger.error("[i18next-Plugin]: Failed to reload language resources.", error));
201
+ }
202
+ /**
203
+ * @description Skips any files that don't end with `.json`.
204
+ * @param directory The directory that should be walked.
205
+ * @param ns The current namespace.
206
+ * @since 3.0.0
207
+ */
208
+ async *walkLocaleDirectory(directory, ns) {
209
+ const dir = await opendir(directory);
210
+ for await (const entry of dir) {
211
+ if (entry.isDirectory()) {
212
+ yield* this.walkLocaleDirectory(join(dir.path, entry.name), `${ns}${entry.name}/`);
213
+ } else if (entry.isFile() && entry.name.endsWith(".json")) {
214
+ yield `${ns}${entry.name.slice(0, -5)}`;
215
+ }
216
+ }
217
+ }
218
+ };
219
+ __name(_InternationalizationHandler, "InternationalizationHandler");
220
+ var InternationalizationHandler = _InternationalizationHandler;
221
+
222
+ export { InternationalizationHandler };
223
+ //# sourceMappingURL=out.js.map
224
+ //# sourceMappingURL=InternationalizationHandler.mjs.map