Skip to content

Commit

Permalink
feat(i18next): add hot-module-replacement to reload i18next resources (
Browse files Browse the repository at this point in the history
  • Loading branch information
sawa-ko committed Jan 12, 2022
1 parent 72eee67 commit 26a8c41
Show file tree
Hide file tree
Showing 5 changed files with 71 additions and 1 deletion.
1 change: 1 addition & 0 deletions packages/i18next/package.json
Expand Up @@ -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"
Expand Down
20 changes: 20 additions & 0 deletions packages/i18next/src/lib/InternationalizationHandler.ts
@@ -1,3 +1,4 @@
import { fromAsync, isErr } from '@sapphire/framework';
import { container, getRootData } from '@sapphire/pieces';
import { Awaitable, isFunction, NonNullObject } from '@sapphire/utilities';
import { opendir } from 'fs/promises';
Expand Down Expand Up @@ -214,4 +215,23 @@ export class InternationalizationHandler {

return { namespaces: [...new Set(namespaces)], languages };
}

public async reloadResources() {
const result = await fromAsync(async () => {
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.', result.error);
}
}
}
37 changes: 37 additions & 0 deletions packages/i18next/src/lib/types.ts
@@ -1,8 +1,38 @@
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';

/**
* 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;

/**
* 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[];

/**
* HMR options
*/
options?: WatchOptions;
}

/**
* Used to dynamically add options based on found languages in {@link InternationalizationHandler#init}.
* @since 1.1.0
Expand Down Expand Up @@ -63,6 +93,13 @@ export interface InternationalizationOptions {
*/
formatters?: I18nextFormatters[];

/**
* Reload languages and namespaces when updating the languages directory.
*
* @since 2.2.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`}.
Expand Down
13 changes: 12 additions & 1 deletion 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 {
Expand All @@ -10,6 +12,15 @@ export class I18nextPlugin extends Plugin {
public static async [preLogin](this: SapphireClient): Promise<void> {
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.options);

for (const event of ['add', 'change', 'unlink']) hmr.on(event, () => container.i18n.reloadResources());
}
}
}

SapphireClient.plugins.registerPostInitializationHook(I18nextPlugin[preGenericsInitialization], 'I18next-PreGenericsInitialization');
Expand Down
1 change: 1 addition & 0 deletions yarn.lock
Expand Up @@ -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
Expand Down

0 comments on commit 26a8c41

Please sign in to comment.