Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions locales/en-US.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"hello": "Hello World!",
"greeting": "Hello {user}!",
"commands": {
"ping": {
"desc": "Pong!"
}
}
}
50 changes: 11 additions & 39 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@
"devDependencies": {
"@swc/cli": "^0.7.10",
"@types/lodash": "^4.17.23",
"@types/node": "^25.0.8",
"@types/node": "^25.0.9",
"husky": "^9.1.7",
"libnpmpack": "^9.0.12",
"oxfmt": "^0.24.0",
Expand Down
10 changes: 10 additions & 0 deletions src/structures/Client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ import { merge } from "lodash";
import { clearClient, setClient } from "../context";
import { getPrefix } from "../utils/util";
import { Logger } from "../utils/logger/Logger";
import { I18n } from "./I18n";

const defaultOpts: Omit<FrameworkOptions, "intents"> = {
paths: {
events: "events",
commands: "commands",
locales: "locales",
},
autoRegisterCommands: true,
};
Expand All @@ -29,6 +31,7 @@ export class Client<
public commands: Collection<string, CommandBuilder>;
public aliases: Collection<string, Set<string>>;
public readonly prefix: string | false;
public readonly i18n: I18n;

declare public options: Omit<FrameworkOptions, "intents"> & {
intents: IntentsBitField;
Expand All @@ -40,6 +43,13 @@ export class Client<
this.commands = new Collection();
this.aliases = new Collection();
this.prefix = getPrefix(this.options.prefix ?? { enabled: false });
this.i18n = new I18n(this.options.i18n?.defaultLocale, this.logger);

if (this.options.paths?.locales) {
this.i18n.loadLocales(
path.join(getProjectRoot(), this.options.paths.locales)
);
}
Comment on lines +48 to +52
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Missing await: locales may not be loaded when the client is used.

loadLocales is an async function, but it's called without await. Since constructors cannot be async, any code using client.i18n.t() immediately after construction will find an empty locales Map.

🔧 Proposed fix: Add an initialization method or expose a ready promise

Option 1: Add an async init() method that must be awaited:

public async init(): Promise<void> {
  if (this.options.paths?.locales) {
    await this.i18n.loadLocales(
      path.join(getProjectRoot(), this.options.paths.locales)
    );
  }
}

Option 2: Expose a ready promise:

public readonly ready: Promise<void>;

constructor(opts: FrameworkOptions) {
  // ... existing code ...
  this.ready = this.initializeLocales();
}

private async initializeLocales(): Promise<void> {
  if (this.options.paths?.locales) {
    await this.i18n.loadLocales(
      path.join(getProjectRoot(), this.options.paths.locales)
    );
  }
}

Then callers can do: await client.ready;

🤖 Prompt for AI Agents
In `@src/structures/Client.ts` around lines 48 - 52, The constructor in Client
currently calls the async method i18n.loadLocales without awaiting it, causing
locales to possibly be unavailable; add an initialization mechanism (either an
async init() method on the Client class that callers must await, or a public
readonly ready: Promise<void> initialized from a private async
initializeLocales() called in the constructor) and move the loadLocales call
into that async path (referencing i18n.loadLocales, Client.constructor, and the
new init/ready/initializeLocales methods) so callers can await client.init() or
await client.ready before using client.i18n.t().


if (this.options.paths?.events) {
this.loadFiles(
Expand Down
Loading
Loading