@wolfstar/plugin-i18next 2.0.1 → 2.0.2

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.
@@ -583,7 +583,7 @@ declare function createSelectMenuChoiceName<V extends NonNullObject, const TOpt
583
583
  declare namespace createSelectMenuChoiceName {
584
584
  type Result<V> = V & {
585
585
  name: string;
586
- name_localizations: import("discord-api-types/v10").LocalizationMap;
586
+ name_localizations: LocalizationMap;
587
587
  };
588
588
  }
589
589
  //#endregion
@@ -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":"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"}
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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"}
@@ -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 InterpolationMap,\n Namespace,\n ParseKeys,\n TFunction,\n TFunctionReturn,\n TFunctionReturnOptionalDetails,\n TOptions,\n} from \"i18next\";\nimport type {\n $Dictionary,\n AnyNamespace,\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 = AnyNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TFunctionReturn<\n Ns,\n AppendKeyPrefix<Key, undefined>,\n TOpt\n >,\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 = AnyNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TFunctionReturn<\n Ns,\n AppendKeyPrefix<Key, undefined>,\n TOpt\n >,\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 = AnyNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TFunctionReturn<\n Ns,\n AppendKeyPrefix<Key, undefined>,\n TOpt\n >,\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 = AnyNamespace,\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 = AnyNamespace,\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 = AnyNamespace,\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 = AnyNamespace,\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 = AnyNamespace,\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 = AnyNamespace,\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 AnyNamespace,\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 = AnyNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TFunctionReturn<\n Ns,\n AppendKeyPrefix<Key, undefined>,\n TOpt\n >,\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 = AnyNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TFunctionReturn<\n Ns,\n AppendKeyPrefix<Key, undefined>,\n TOpt\n >,\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 = AnyNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TFunctionReturn<\n Ns,\n AppendKeyPrefix<Key, undefined>,\n TOpt\n >,\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 = AnyNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TFunctionReturn<\n Ns,\n AppendKeyPrefix<Key, undefined>,\n TOpt\n >,\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 ? // `defaultMissingKey` is a runtime option, not a literal key `language` can check\n // statically against the resources.\n (language as (key: string, options: $Dictionary) => string)(\n this.options.defaultMissingKey,\n { replace: { key } },\n )\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 type LocalizationMap,\n} from \"discord-api-types/v10\";\nimport type {\n AppendKeyPrefix,\n InterpolationMap,\n Namespace,\n ParseKeys,\n TFunction,\n TFunctionReturn,\n TFunctionReturnOptionalDetails,\n TOptions,\n} from \"i18next\";\nimport type {\n $Dictionary,\n AnyNamespace,\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 = AnyNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TFunctionReturn<\n Ns,\n AppendKeyPrefix<Key, undefined>,\n TOpt\n >,\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 = AnyNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TFunctionReturn<\n Ns,\n AppendKeyPrefix<Key, undefined>,\n TOpt\n >,\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 = AnyNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TFunctionReturn<\n Ns,\n AppendKeyPrefix<Key, undefined>,\n TOpt\n >,\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 = AnyNamespace,\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 = AnyNamespace,\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 = AnyNamespace,\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 = AnyNamespace,\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 = AnyNamespace,\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 = AnyNamespace,\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: 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 AnyNamespace,\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 = AnyNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TFunctionReturn<\n Ns,\n AppendKeyPrefix<Key, undefined>,\n TOpt\n >,\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 = AnyNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TFunctionReturn<\n Ns,\n AppendKeyPrefix<Key, undefined>,\n TOpt\n >,\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 = AnyNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TFunctionReturn<\n Ns,\n AppendKeyPrefix<Key, undefined>,\n TOpt\n >,\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 = AnyNamespace,\n Ret extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TFunctionReturn<\n Ns,\n AppendKeyPrefix<Key, undefined>,\n TOpt\n >,\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 ? // `defaultMissingKey` is a runtime option, not a literal key `language` can check\n // statically against the resources.\n (language as (key: string, options: $Dictionary) => string)(\n this.options.defaultMissingKey,\n { replace: { key } },\n )\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": "2.0.1",
3
+ "version": "2.0.2",
4
4
  "description": "Plugin for @wolfstar/http-framework adding i18next-powered internationalization for HTTP interactions",
5
5
  "keywords": [
6
6
  "discord",