{"version":3,"sources":["../../../src/lib/InternationalizationHandler.ts"],"names":[],"mappings":"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","file":"InternationalizationHandler.mjs","sourcesContent":["import { Result } from '@sapphire/framework';\nimport { container, getRootData } from '@sapphire/pieces';\nimport { isFunction, type Awaitable } from '@sapphire/utilities';\nimport { Backend, type PathResolvable } from '@skyra/i18next-backend';\nimport i18next, {\n\ttype AppendKeyPrefix,\n\ttype DefaultNamespace,\n\ttype InterpolationMap,\n\ttype Namespace,\n\ttype ParseKeys,\n\ttype TFunction,\n\ttype TFunctionProcessReturnValue,\n\ttype TFunctionReturn,\n\ttype TFunctionReturnOptionalDetails,\n\ttype TOptions\n} from 'i18next';\nimport type { PathLike } from 'node:fs';\nimport { opendir } from 'node:fs/promises';\nimport { join } from 'node:path';\nimport { URL, fileURLToPath } from 'node:url';\nimport type { $Dictionary, $NoInfer, $SpecialObject, InternationalizationContext, InternationalizationOptions } from './types';\n\n/**\n * A generalized class for handling `i18next` JSON files and their discovery.\n * @since 1.0.0\n */\nexport class InternationalizationHandler {\n\t/**\n\t * Describes whether {@link InternationalizationHandler.init} has been run and languages are loaded in {@link InternationalizationHandler.languages}.\n\t * @since 1.0.0\n\t */\n\tpublic languagesLoaded = false;\n\n\t/**\n\t * A `Set` of initially loaded namespaces.\n\t * @since 1.2.0\n\t */\n\tpublic namespaces = new Set<string>();\n\n\t/**\n\t * A `Map` of `i18next` language functions keyed by their language code.\n\t * @since 1.0.0\n\t */\n\tpublic readonly languages = new Map<string, TFunction>();\n\n\t/**\n\t * The options InternationalizationHandler was initialized with in the client.\n\t * @since 1.0.0\n\t */\n\tpublic readonly options: InternationalizationOptions;\n\n\t/**\n\t * The director passed to `@skyra/i18next-backend`.\n\t * Also used in {@link InternationalizationHandler.walkLanguageDirectory}.\n\t * @since 1.2.0\n\t */\n\tpublic readonly languagesDirectory: string;\n\n\t/**\n\t * The backend options for `@skyra/i18next-backend` used by `i18next`.\n\t * @since 1.0.0\n\t */\n\tprotected readonly backendOptions: Backend.Options;\n\n\t/**\n\t * @param options The options that `i18next`, `@skyra/i18next-backend`, and {@link InternationalizationHandler} should use.\n\t * @since 1.0.0\n\t * @constructor\n\t */\n\tpublic constructor(options?: InternationalizationOptions) {\n\t\tthis.options = options ?? { i18next: { ignoreJSONStructure: false } };\n\n\t\tconst baseUserDirectory =\n\t\t\tcontainer.client?.options?.baseUserDirectory instanceof URL\n\t\t\t\t? fileURLToPath(container.client?.options?.baseUserDirectory)\n\t\t\t\t: container.client?.options?.baseUserDirectory;\n\n\t\tthis.languagesDirectory = this.options.defaultLanguageDirectory ?? join(baseUserDirectory ?? getRootData().root, 'languages');\n\n\t\tconst languagePaths = new Set<PathResolvable>([\n\t\t\tjoin(this.languagesDirectory, '{{lng}}', '{{ns}}.json'), //\n\t\t\t...(options?.backend?.paths ?? [])\n\t\t]);\n\n\t\tthis.backendOptions = {\n\t\t\tpaths: [...languagePaths],\n\t\t\t...this.options.backend\n\t\t};\n\n\t\tif (isFunction(this.options.fetchLanguage)) {\n\t\t\tthis.fetchLanguage = this.options.fetchLanguage;\n\t\t}\n\t}\n\n\t/**\n\t * The method to be overridden by the developer.\n\t *\n\t * @note In the event that fetchLanguage is not defined or returns null / undefined, the defaulting from {@link fetchLanguage} will be used.\n\t * @since 2.0.0\n\t * @return A string for the desired language or null for no match.\n\t * @see {@link fetchLanguage}\n\t * @example\n\t * ```typescript\n\t * // Always use the same language (no per-guild configuration):\n\t * container.i18n.fetchLanguage = () => 'en-US';\n\t * ```\n\t * @example\n\t * ```typescript\n\t * // Retrieving the language from an SQL database:\n\t * container.i18n.fetchLanguage = async (context) => {\n\t *   const guild = await driver.getOne('SELECT language FROM public.guild WHERE id = $1', [context.guild.id]);\n\t *   return guild?.language ?? 'en-US';\n\t * };\n\t * ```\n\t * @example\n\t * ```typescript\n\t * // Retrieving the language from an ORM:\n\t * container.i18n.fetchLanguage = async (context) => {\n\t *   const guild = await driver.getRepository(GuildEntity).findOne({ id: context.guild.id });\n\t *   return guild?.language ?? 'en-US';\n\t * };\n\t * ```\n\t * @example\n\t * ```typescript\n\t * // Retrieving the language on a per channel basis, e.g. per user or guild channel (ORM example but same principles apply):\n\t * container.i18n.fetchLanguage = async (context) => {\n\t *   const channel = await driver.getRepository(ChannelEntity).findOne({ id: context.channel.id });\n\t *   return channel?.language ?? 'en-US';\n\t * };\n\t * ```\n\t */\n\tpublic fetchLanguage: (context: InternationalizationContext) => Awaitable<string | null> = () => null;\n\n\t/**\n\t * Initializes the handler by loading in the namespaces, passing the data to i18next, and filling in the {@link InternationalizationHandler#languages}.\n\t * @since 1.0.0\n\t */\n\tpublic async init() {\n\t\tconst { namespaces, languages } = await this.walkRootDirectory(this.languagesDirectory);\n\t\tconst userOptions = isFunction(this.options.i18next) ? this.options.i18next(namespaces, languages) : this.options.i18next;\n\t\tconst ignoreJSONStructure = userOptions?.ignoreJSONStructure ?? false;\n\t\tconst skipOnVariables = userOptions?.interpolation?.skipOnVariables ?? false;\n\n\t\ti18next.use(Backend);\n\t\tawait i18next.init({\n\t\t\tbackend: this.backendOptions,\n\t\t\tfallbackLng: this.options.defaultName ?? 'en-US',\n\t\t\tinitImmediate: false,\n\t\t\tinterpolation: {\n\t\t\t\tescapeValue: false,\n\t\t\t\t...userOptions?.interpolation,\n\t\t\t\tskipOnVariables\n\t\t\t},\n\t\t\tload: 'all',\n\t\t\tdefaultNS: 'default',\n\t\t\tns: namespaces,\n\t\t\tpreload: languages,\n\t\t\t...userOptions,\n\t\t\tignoreJSONStructure\n\t\t});\n\n\t\tthis.namespaces = new Set(namespaces);\n\t\tfor (const item of languages) {\n\t\t\tthis.languages.set(item, i18next.getFixedT(item));\n\t\t}\n\t\tthis.languagesLoaded = true;\n\n\t\tconst formatter = i18next.services.formatter!;\n\t\tconst formatters = this.options.formatters ?? [];\n\t\t// eslint-disable-next-line @typescript-eslint/unbound-method\n\t\tfor (const { name, format, cached } of formatters) {\n\t\t\tif (cached) formatter.addCached(name, format);\n\t\t\telse formatter.add(name, format);\n\t\t}\n\t}\n\n\t/**\n\t * Retrieve a raw TFunction from the passed locale.\n\t * @param locale The language to be used.\n\t * @since 1.0.0\n\t */\n\tpublic getT(locale: string) {\n\t\tif (!this.languagesLoaded) throw new Error('Cannot call this method until InternationalizationHandler#init has been called');\n\n\t\tconst t = this.languages.get(locale);\n\t\tif (t) return t;\n\t\tthrow new ReferenceError('Invalid language provided');\n\t}\n\n\t/**\n\t * Localizes a content given one or more keys and i18next options.\n\t * @since 2.0.0\n\t * @param locale The language to be used.\n\t * @param key The key or keys to retrieve the content from.\n\t * @param options The interpolation options.\n\t * @see {@link https://www.i18next.com/overview/api#t}\n\t * @returns The localized content.\n\t */\n\tpublic format<\n\t\tconst Key extends ParseKeys<Ns, TOpt, undefined>,\n\t\tconst TOpt extends TOptions = TOptions,\n\t\tNs extends Namespace = DefaultNamespace,\n\t\tRet extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TOpt['returnObjects'] extends true ? $SpecialObject : string,\n\t\tconst ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>\n\t>(locale: string, key: Key | Key[], options?: ActualOptions): TFunctionReturnOptionalDetails<Ret, TOpt>;\n\n\t/**\n\t * Localizes a content given one or more keys and i18next options.\n\t * @since 2.0.0\n\t * @param locale The language to be used.\n\t * @param key The key or keys to retrieve the content from.\n\t * @param options The interpolation options as well as a `defaultValue` for the key and any key/value pairs.\n\t * @see {@link https://www.i18next.com/overview/api#t}\n\t * @returns The localized content.\n\t */\n\tpublic format<\n\t\tconst Key extends ParseKeys<Ns, TOpt, undefined>,\n\t\tconst TOpt extends TOptions = TOptions,\n\t\tNs extends Namespace = DefaultNamespace,\n\t\tRet extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TOpt['returnObjects'] extends true ? $SpecialObject : string,\n\t\tconst ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>\n\t>(locale: string, key: string | string[], options: TOpt & $Dictionary & { defaultValue: string }): TFunctionReturnOptionalDetails<Ret, TOpt>;\n\n\t/**\n\t * Localizes a content given one or more keys and i18next options.\n\t * @since 2.0.0\n\t * @param locale The language to be used.\n\t * @param key The key or keys to retrieve the content from.\n\t * @param defaultValue The default value to use if the key is not found.\n\t * @param options The interpolation options.\n\t * @see {@link https://www.i18next.com/overview/api#t}\n\t * @returns The localized content.\n\t */\n\tpublic format<\n\t\tconst Key extends ParseKeys<Ns, TOpt, undefined>,\n\t\tconst TOpt extends TOptions = TOptions,\n\t\tNs extends Namespace = DefaultNamespace,\n\t\tRet extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TOpt['returnObjects'] extends true ? $SpecialObject : string,\n\t\tconst ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>\n\t>(\n\t\tlocale: string,\n\t\tkey: string | string[],\n\t\tdefaultValue: string | undefined,\n\t\toptions?: TOpt & $Dictionary\n\t): TFunctionReturnOptionalDetails<Ret, TOpt>;\n\n\t/**\n\t * Localizes a content given one or more keys and i18next options.\n\t * @since 2.0.0\n\t * @param locale The language to be used.\n\t *\n\t * @remark This function also has additional parameters for `key`, `defaultValue`, and `options`, however\n\t * TSDoc does not let us document those while matching proper implementation. See the overloads for this method\n\t * for the documentation on those parameters.\n\t *\n\t * @see {@link https://www.i18next.com/overview/api#t}\n\t * @returns The localized content.\n\t */\n\tpublic format<\n\t\tconst Key extends ParseKeys<Ns, TOpt, undefined>,\n\t\tconst TOpt extends TOptions = TOptions,\n\t\tNs extends Namespace = DefaultNamespace,\n\t\tRet extends TFunctionReturn<Ns, AppendKeyPrefix<Key, undefined>, TOpt> = TOpt['returnObjects'] extends true ? $SpecialObject : string,\n\t\tconst ActualOptions extends TOpt & InterpolationMap<Ret> = TOpt & InterpolationMap<Ret>,\n\t\tDefaultValue extends string = never\n\t>(\n\t\tlocale: string,\n\t\t...[key, defaultValueOrOptions, optionsOrUndefined]:\n\t\t\t| [key: Key | Key[], options?: ActualOptions]\n\t\t\t| [key: string | string[], options: TOpt & $Dictionary & { defaultValue: string }]\n\t\t\t| [key: string | string[], defaultValue: DefaultValue | undefined, options?: TOpt & $Dictionary]\n\t): TFunctionReturnOptionalDetails<TFunctionProcessReturnValue<$NoInfer<Ret>, DefaultValue>, TOpt> {\n\t\tif (!this.languagesLoaded) throw new Error('Cannot call this method until InternationalizationHandler#init has been called');\n\n\t\tconst language = this.languages.get(locale);\n\t\tif (!language) throw new ReferenceError('Invalid language provided');\n\n\t\tconst defaultValue =\n\t\t\ttypeof defaultValueOrOptions === 'string'\n\t\t\t\t? defaultValueOrOptions\n\t\t\t\t: this.options.defaultMissingKey\n\t\t\t\t\t? language(this.options.defaultMissingKey, { replace: { key } })\n\t\t\t\t\t: '';\n\n\t\treturn language<Key, TOpt, Ret, ActualOptions, DefaultValue>(key, {\n\t\t\tdefaultValue,\n\t\t\t...((optionsOrUndefined ?? {}) as TOpt)\n\t\t} as TOpt & $Dictionary & { defaultValue: DefaultValue });\n\t}\n\n\t/**\n\t * @param directory The directory that should be walked.\n\t * @since 3.0.0\n\t */\n\tpublic async walkRootDirectory(directory: PathLike) {\n\t\tconst languages = new Set<string>();\n\t\tconst namespaces = new Set<string>();\n\n\t\tconst dir = await opendir(directory);\n\t\tfor await (const entry of dir) {\n\t\t\t// If the entry is not a directory, skip:\n\t\t\tif (!entry.isDirectory()) continue;\n\n\t\t\t// Load the directory:\n\t\t\tlanguages.add(entry.name);\n\n\t\t\tfor await (const namespace of this.walkLocaleDirectory(join(dir.path, entry.name), '')) {\n\t\t\t\tnamespaces.add(namespace);\n\t\t\t}\n\t\t}\n\n\t\treturn { namespaces: [...namespaces], languages: [...languages] };\n\t}\n\n\tpublic async reloadResources() {\n\t\tconst result = await Result.fromAsync(async () => {\n\t\t\tlet languages = this.options.hmr?.languages;\n\t\t\tlet namespaces = this.options.hmr?.namespaces;\n\t\t\tif (!languages || !namespaces) {\n\t\t\t\tconst languageDirectoryResult = await this.walkRootDirectory(this.languagesDirectory);\n\t\t\t\tlanguages ??= languageDirectoryResult.languages;\n\t\t\t\tnamespaces ??= languageDirectoryResult.namespaces;\n\t\t\t}\n\n\t\t\tawait i18next.reloadResources(languages, namespaces);\n\t\t\tcontainer.logger.info('[i18next-Plugin] Reloaded language resources.');\n\t\t});\n\n\t\tresult.inspectErr((error) => container.logger.error('[i18next-Plugin]: Failed to reload language resources.', error));\n\t}\n\n\t/**\n\t * @description Skips any files that don't end with `.json`.\n\t * @param directory The directory that should be walked.\n\t * @param ns The current namespace.\n\t * @since 3.0.0\n\t */\n\tprivate async *walkLocaleDirectory(directory: string, ns: string): AsyncGenerator<string> {\n\t\tconst dir = await opendir(directory);\n\t\tfor await (const entry of dir) {\n\t\t\tif (entry.isDirectory()) {\n\t\t\t\tyield* this.walkLocaleDirectory(join(dir.path, entry.name), `${ns}${entry.name}/`);\n\t\t\t} else if (entry.isFile() && entry.name.endsWith('.json')) {\n\t\t\t\tyield `${ns}${entry.name.slice(0, -5)}`;\n\t\t\t}\n\t\t}\n\t}\n}\n"]}