From a4d56d7e0352ac4639fcf38d3046f272a40d63da Mon Sep 17 00:00:00 2001 From: kaname-png Date: Tue, 11 Jan 2022 13:04:35 -0600 Subject: [PATCH 1/5] feat(i18next): hmr --- packages/i18next/package.json | 1 + .../src/lib/InternationalizationHandler.ts | 14 +++++++++ packages/i18next/src/lib/types.ts | 29 +++++++++++++++++++ packages/i18next/src/register.ts | 13 ++++++++- yarn.lock | 1 + 5 files changed, 57 insertions(+), 1 deletion(-) diff --git a/packages/i18next/package.json b/packages/i18next/package.json index 0420a5db5..461c05fde 100644 --- a/packages/i18next/package.json +++ b/packages/i18next/package.json @@ -36,6 +36,7 @@ "dependencies": { "@sapphire/utilities": "^3.1.0", "@types/i18next-fs-backend": "^1.1.2", + "chokidar": "^3.5.2", "i18next": "^21.6.5", "i18next-fs-backend": "^1.1.4", "tslib": "^2.3.1" diff --git a/packages/i18next/src/lib/InternationalizationHandler.ts b/packages/i18next/src/lib/InternationalizationHandler.ts index 2da348abe..55c259b8f 100644 --- a/packages/i18next/src/lib/InternationalizationHandler.ts +++ b/packages/i18next/src/lib/InternationalizationHandler.ts @@ -1,9 +1,11 @@ +import { fromAsync, isErr } from '@sapphire/framework'; import { container, getRootData } from '@sapphire/pieces'; import { Awaitable, isFunction, NonNullObject } from '@sapphire/utilities'; import { opendir } from 'fs/promises'; import i18next, { StringMap, TFunction, TFunctionKeys, TFunctionResult, TOptions } from 'i18next'; import Backend, { i18nextFsBackend } from 'i18next-fs-backend'; import { join } from 'path'; +import { error } from 'console'; import type { InternationalizationContext, InternationalizationOptions } from './types'; /** @@ -214,4 +216,16 @@ export class InternationalizationHandler { return { namespaces: [...new Set(namespaces)], languages }; } + + public async reloadResources() { + const result = await fromAsync(async () => { + const { namespaces, languages } = await this.walkLanguageDirectory(this.languagesDirectory); + await i18next.reloadResources(this.options.hmr?.langs ?? languages, this.options.hmr?.namespaces ?? namespaces); + container.logger.info('[i18next-Plugin] Reloaded language resources.'); + }); + + if (isErr(result)) { + container.logger.error('[i18next-Plugin]: Failed to reload language resources.', error); + } + } } diff --git a/packages/i18next/src/lib/types.ts b/packages/i18next/src/lib/types.ts index 6080a8dd7..6abed84f0 100644 --- a/packages/i18next/src/lib/types.ts +++ b/packages/i18next/src/lib/types.ts @@ -1,8 +1,30 @@ import type { Awaitable, NonNullObject } from '@sapphire/utilities'; +import type { WatchOptions } from 'chokidar'; import type { Guild, Message, MessageOptions, StageChannel, StoreChannel, User, VoiceChannel } from 'discord.js'; import type { InitOptions, StringMap, TFunctionKeys, TOptions } from 'i18next'; import type { i18nextFsBackend } from 'i18next-fs-backend'; +/** + * This option is used to set the HMR options. + * @since 2.1.6 + */ +export interface HMROptions extends WatchOptions { + /** + * HMR mode status for the i18next plugin. + */ + enabled?: boolean; + + /** + * Languages that will be reloaded when updating the languages directory. + */ + langs?: string | string[]; + + /** + * Namespaces that will be reloaded when updating the languages directory. + */ + namespaces?: string | string[]; +} + /** * Used to dynamically add options based on found languages in {@link InternationalizationHandler#init}. * @since 1.1.0 @@ -63,6 +85,13 @@ export interface InternationalizationOptions { */ formatters?: I18nextFormatters[]; + /** + * Reload languages and namespaces when updating the languages directory. + * + * @since 2.0.0 + */ + hmr?: HMROptions; + /** * A function that is to be used to retrieve the language for the current context. * Context exists of a {@link Guild `guild`}, a {@link DiscordChannel `channel`} and a {@link User `user`}. diff --git a/packages/i18next/src/register.ts b/packages/i18next/src/register.ts index c006f0fe6..15a208b5e 100644 --- a/packages/i18next/src/register.ts +++ b/packages/i18next/src/register.ts @@ -1,5 +1,7 @@ -import { container, Plugin, preGenericsInitialization, preLogin, SapphireClient } from '@sapphire/framework'; +import { container, Plugin, preGenericsInitialization, preLogin, postLogin, SapphireClient } from '@sapphire/framework'; +import { watch } from 'chokidar'; import type { ClientOptions } from 'discord.js'; + import { InternationalizationClientOptions, InternationalizationHandler } from './index'; export class I18nextPlugin extends Plugin { @@ -10,6 +12,15 @@ export class I18nextPlugin extends Plugin { public static async [preLogin](this: SapphireClient): Promise { await container.i18n.init(); } + + public static [postLogin](this: SapphireClient): void { + if (this.options.i18n?.hmr?.enabled) { + container.logger.info('[i18next-Plugin]: HMR enabled. Watching for languages changes.'); + const hmr = watch(container.i18n.languagesDirectory, this.options.i18n.hmr); + + for (const event of ['add', 'change', 'unlink']) hmr.on(event, () => container.i18n.reloadResources()); + } + } } SapphireClient.plugins.registerPostInitializationHook(I18nextPlugin[preGenericsInitialization], 'I18next-PreGenericsInitialization'); diff --git a/yarn.lock b/yarn.lock index 4dae34d67..57ad26df6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2071,6 +2071,7 @@ __metadata: dependencies: "@sapphire/utilities": ^3.1.0 "@types/i18next-fs-backend": ^1.1.2 + chokidar: ^3.5.2 i18next: ^21.6.5 i18next-fs-backend: ^1.1.4 tslib: ^2.3.1 From 375db5cfbd375313e5ce5a333333267e1d06ff22 Mon Sep 17 00:00:00 2001 From: kaname-png Date: Tue, 11 Jan 2022 15:45:00 -0600 Subject: [PATCH 2/5] fix(types): better this way --- packages/i18next/src/lib/types.ts | 9 +++++++-- packages/i18next/src/register.ts | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/i18next/src/lib/types.ts b/packages/i18next/src/lib/types.ts index 6abed84f0..4cb9a201a 100644 --- a/packages/i18next/src/lib/types.ts +++ b/packages/i18next/src/lib/types.ts @@ -8,9 +8,9 @@ import type { i18nextFsBackend } from 'i18next-fs-backend'; * This option is used to set the HMR options. * @since 2.1.6 */ -export interface HMROptions extends WatchOptions { +export interface HMROptions { /** - * HMR mode status for the i18next plugin. + * HMR status for the i18next plugin. */ enabled?: boolean; @@ -23,6 +23,11 @@ export interface HMROptions extends WatchOptions { * Namespaces that will be reloaded when updating the languages directory. */ namespaces?: string | string[]; + + /** + * HMR options + */ + options?: WatchOptions; } /** diff --git a/packages/i18next/src/register.ts b/packages/i18next/src/register.ts index 15a208b5e..4ee657fb7 100644 --- a/packages/i18next/src/register.ts +++ b/packages/i18next/src/register.ts @@ -16,7 +16,7 @@ export class I18nextPlugin extends Plugin { public static [postLogin](this: SapphireClient): void { if (this.options.i18n?.hmr?.enabled) { container.logger.info('[i18next-Plugin]: HMR enabled. Watching for languages changes.'); - const hmr = watch(container.i18n.languagesDirectory, this.options.i18n.hmr); + const hmr = watch(container.i18n.languagesDirectory, this.options.i18n.hmr.options); for (const event of ['add', 'change', 'unlink']) hmr.on(event, () => container.i18n.reloadResources()); } From e469712bc8905069ead4a419fc8cd011a765b949 Mon Sep 17 00:00:00 2001 From: kaname-png Date: Tue, 11 Jan 2022 15:45:29 -0600 Subject: [PATCH 3/5] fix(types): meh --- packages/i18next/src/lib/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/i18next/src/lib/types.ts b/packages/i18next/src/lib/types.ts index 4cb9a201a..d9fcefca1 100644 --- a/packages/i18next/src/lib/types.ts +++ b/packages/i18next/src/lib/types.ts @@ -17,7 +17,7 @@ export interface HMROptions { /** * Languages that will be reloaded when updating the languages directory. */ - langs?: string | string[]; + languages?: string | string[]; /** * Namespaces that will be reloaded when updating the languages directory. From 9f9806c3f0194001c7a8a2e80fca7303e1a92aa7 Mon Sep 17 00:00:00 2001 From: kaname-png Date: Tue, 11 Jan 2022 15:50:39 -0600 Subject: [PATCH 4/5] fix(types): uwu --- packages/i18next/src/lib/InternationalizationHandler.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/i18next/src/lib/InternationalizationHandler.ts b/packages/i18next/src/lib/InternationalizationHandler.ts index 55c259b8f..f5e038c0d 100644 --- a/packages/i18next/src/lib/InternationalizationHandler.ts +++ b/packages/i18next/src/lib/InternationalizationHandler.ts @@ -220,7 +220,7 @@ export class InternationalizationHandler { public async reloadResources() { const result = await fromAsync(async () => { const { namespaces, languages } = await this.walkLanguageDirectory(this.languagesDirectory); - await i18next.reloadResources(this.options.hmr?.langs ?? languages, this.options.hmr?.namespaces ?? namespaces); + await i18next.reloadResources(this.options.hmr?.languages ?? languages, this.options.hmr?.namespaces ?? namespaces); container.logger.info('[i18next-Plugin] Reloaded language resources.'); }); From 7cb998397d4cc2e5e43e3797c45a365ea2808a46 Mon Sep 17 00:00:00 2001 From: kaname-png Date: Tue, 11 Jan 2022 18:46:12 -0600 Subject: [PATCH 5/5] =?UTF-8?q?fix:=20favna's=20s-suggestions=20(*=20^=20?= =?UTF-8?q?=CF=89=20^)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../i18next/src/lib/InternationalizationHandler.ts | 14 ++++++++++---- packages/i18next/src/lib/types.ts | 11 +++++++---- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/packages/i18next/src/lib/InternationalizationHandler.ts b/packages/i18next/src/lib/InternationalizationHandler.ts index f5e038c0d..548dd4a11 100644 --- a/packages/i18next/src/lib/InternationalizationHandler.ts +++ b/packages/i18next/src/lib/InternationalizationHandler.ts @@ -5,7 +5,6 @@ import { opendir } from 'fs/promises'; import i18next, { StringMap, TFunction, TFunctionKeys, TFunctionResult, TOptions } from 'i18next'; import Backend, { i18nextFsBackend } from 'i18next-fs-backend'; import { join } from 'path'; -import { error } from 'console'; import type { InternationalizationContext, InternationalizationOptions } from './types'; /** @@ -219,13 +218,20 @@ export class InternationalizationHandler { public async reloadResources() { const result = await fromAsync(async () => { - const { namespaces, languages } = await this.walkLanguageDirectory(this.languagesDirectory); - await i18next.reloadResources(this.options.hmr?.languages ?? languages, this.options.hmr?.namespaces ?? namespaces); + let languages = this.options.hmr?.languages; + let namespaces = this.options.hmr?.namespaces; + if (!languages || !namespaces) { + const languageDirectoryResult = await this.walkLanguageDirectory(this.languagesDirectory); + languages ??= languageDirectoryResult.languages; + namespaces ??= languageDirectoryResult.namespaces; + } + + await i18next.reloadResources(languages, namespaces); container.logger.info('[i18next-Plugin] Reloaded language resources.'); }); if (isErr(result)) { - container.logger.error('[i18next-Plugin]: Failed to reload language resources.', error); + container.logger.error('[i18next-Plugin]: Failed to reload language resources.', result.error); } } } diff --git a/packages/i18next/src/lib/types.ts b/packages/i18next/src/lib/types.ts index d9fcefca1..4db49ba4b 100644 --- a/packages/i18next/src/lib/types.ts +++ b/packages/i18next/src/lib/types.ts @@ -5,22 +5,25 @@ import type { InitOptions, StringMap, TFunctionKeys, TOptions } from 'i18next'; import type { i18nextFsBackend } from 'i18next-fs-backend'; /** - * This option is used to set the HMR options. - * @since 2.1.6 + * 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. + * @since 2.2.0 */ export interface HMROptions { /** * HMR status for the i18next plugin. + * @default false */ - enabled?: boolean; + enabled: boolean; /** * Languages that will be reloaded when updating the languages directory. + * @default All languages that are automatically resolved from your folder setup */ languages?: string | string[]; /** * Namespaces that will be reloaded when updating the languages directory. + * @default All namespaces that are automatically resolved from your languages folder setup */ namespaces?: string | string[]; @@ -93,7 +96,7 @@ export interface InternationalizationOptions { /** * Reload languages and namespaces when updating the languages directory. * - * @since 2.0.0 + * @since 2.2.0 */ hmr?: HMROptions;