Skip to content
Merged
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
54 changes: 54 additions & 0 deletions apps/website/docs/guide/02-commands/03-context-menu-commands.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -144,3 +144,57 @@ very similar to message context menu commands.

</TabItem>
</Tabs>

You may have noticed that the context menu command names are the same as the message command names.
This is because the context menu command names are borrowed from the non-context menu command names.

In order to change the context menu command name, you can use the `nameAliases` option in the `CommandMetadata` object.

:::warning

If you are using the [i18n plugin](../05-official-plugins/05-commandkit-i18n.mdx), the plugin will add `name_localizations` to the `nameAliases` option if localization for the context menu command name is provided.

```json title="src/app/locales/en/command.json"
{
"$command": {
"name": "Report", // this will add `name_localizations` to the regular command name
"description": "Report a message or user to moderators." // this will add `description_localizations` to the regular command description
},
"$command:user-ctx": {
"name": "Report User" // this will add `name_localizations` to the `nameAliases.user` option
},
"$command:message-ctx": {
"name": "Report Message" // this will add `name_localizations` to the `nameAliases.message` option
}
}
```

:::

<Tabs>
<TabItem value="ts" label="TypeScript" default>
```ts title="src/app/commands/report-user.ts"
import type { CommandMetadata } from 'commandkit';
import { MessageFlags } from 'discord.js';

export const metadata: CommandMetadata = {
nameAliases: {
user: 'Report User',
message: 'Report Message',
}
};
```

</TabItem>
<TabItem value="js" label="JavaScript">
```js title="src/app/commands/report-user.js"
/** @type {CommandMetadata} */
export const metadata = {
nameAliases: {
user: 'Report User',
message: 'Report Message',
}
};
```
</TabItem>
</Tabs>
2 changes: 2 additions & 0 deletions packages/commandkit/src/app/register/CommandRegistrar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export class CommandRegistrar {
) {
collections.push({
...json,
name: __metadata?.nameAliases?.user ?? json.name,
type: ApplicationCommandType.User,
options: undefined,
description_localizations: undefined,
Expand All @@ -80,6 +81,7 @@ export class CommandRegistrar {
) {
collections.push({
...json,
name: __metadata?.nameAliases?.message ?? json.name,
type: ApplicationCommandType.Message,
description_localizations: undefined,
// @ts-ignore
Expand Down
4 changes: 4 additions & 0 deletions packages/commandkit/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ export interface CommandMetadata {
* The bot permissions required to execute the command.
*/
botPermissions?: PermissionsString | PermissionsString[];
/**
* The name aliases for the `user` and `message` context menu commands. When i18n plugin is in use, this option will be ignored if the translation for the context menu command name is provided.
*/
nameAliases?: Record<'user' | 'message', string>;
}

/**
Expand Down
8 changes: 8 additions & 0 deletions packages/i18n/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,20 @@ If your translation file contains `$command` key with localization object, it wi
}
]
},
"$command:user-ctx": {
"name": "Ping"
},
"$command:message-ctx": {
"name": "Ping"
},
"response": "Pong! The latency is {{latency}}ms"
}
```

The `$command` key defines localization for the command name and description (or options). These properties are later merged with the actual command to build the final command object with localizations that Discord understands. Anything else in the translation file is used to localize the command response.

The `$command:user-ctx` and `$command:message-ctx` keys define localization for the user context menu and message context menu command names.

This plugin adds `locale()` function to your command context. You can use it to localize your command responses.

```ts
Expand Down
12 changes: 12 additions & 0 deletions packages/i18n/src/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,15 @@ export const DISCORD_LOCALES = new Set(
* This key is used to store metadata about commands in the i18n system.
*/
export const COMMAND_METADATA_KEY = '$command';

/**
* Metadata key for user context menu command localization.
* This key is used to store metadata about user context menu commands in the i18n system.
*/
export const USER_CTX_COMMAND_METADATA_KEY = '$command:user-ctx';

/**
* Metadata key for message context menu command localization.
* This key is used to store metadata about message context menu commands in the i18n system.
*/
export const MESSAGE_CTX_COMMAND_METADATA_KEY = '$command:message-ctx';
54 changes: 51 additions & 3 deletions packages/i18n/src/i18n.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,24 @@ import CommandKit, {
CommandKitHMREvent,
getCommandKit,
COMMANDKIT_CWD,
PreRegisterCommandsEvent,
} from 'commandkit';
import FsBackend from 'i18next-fs-backend';
import { basename, extname, join } from 'path';
import { FsBackendOptions } from 'i18next-fs-backend';
import { Locale } from 'discord.js';
import { ApplicationCommandType, Locale } from 'discord.js';
import { existsSync } from 'fs';
import { CommandTranslation, CommandTranslationMetadata } from './types';
import { COMMAND_METADATA_KEY, DISCORD_LOCALES } from './constants';
import {
BasicCommandTranslationMetadata,
CommandTranslation,
CommandTranslationMetadata,
} from './types';
import {
COMMAND_METADATA_KEY,
DISCORD_LOCALES,
MESSAGE_CTX_COMMAND_METADATA_KEY,
USER_CTX_COMMAND_METADATA_KEY,
} from './constants';
import { applyTranslations } from './utils';
import { readdir, readFile } from 'fs/promises';

Expand Down Expand Up @@ -330,10 +340,20 @@ export class I18nPlugin extends RuntimePlugin<LocalizationPluginOptions> {
if (!translation) return;

const metadata = translation[COMMAND_METADATA_KEY];
const userCtxMetadata = translation[USER_CTX_COMMAND_METADATA_KEY];
const messageCtxMetadata = translation[MESSAGE_CTX_COMMAND_METADATA_KEY];

if (metadata) {
this.metadata.set(`${locale}:${name}`, metadata);
}

if (userCtxMetadata) {
this.metadata.set(`${locale}:${name}:user-ctx`, userCtxMetadata);
}

if (messageCtxMetadata) {
this.metadata.set(`${locale}:${name}:message-ctx`, messageCtxMetadata);
}
} catch {}
}

Expand Down Expand Up @@ -383,4 +403,32 @@ export class I18nPlugin extends RuntimePlugin<LocalizationPluginOptions> {

return data;
}

public async onBeforeRegisterCommands(
ctx: CommandKitPluginRuntime,
event: PreRegisterCommandsEvent,
): Promise<void> {
const { commands } = event;
const validTypes = [
ApplicationCommandType.User,
ApplicationCommandType.Message,
];

for (const command of commands) {
if (!validTypes.includes(command.type!)) continue;

for (const locale of DISCORD_LOCALES) {
const translationBasic = (
command.type === ApplicationCommandType.User
? this.metadata.get(`${locale}:${command.name}:user-ctx`)
: this.metadata.get(`${locale}:${command.name}:message-ctx`)
) as BasicCommandTranslationMetadata;

if (translationBasic?.name) {
command.name_localizations ??= {};
command.name_localizations[locale] = translationBasic.name;
}
}
}
}
}
24 changes: 23 additions & 1 deletion packages/i18n/src/types.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
import { COMMAND_METADATA_KEY } from './constants';
import {
COMMAND_METADATA_KEY,
MESSAGE_CTX_COMMAND_METADATA_KEY,
USER_CTX_COMMAND_METADATA_KEY,
} from './constants';

/**
* Represents the metadata for command translations.
Expand All @@ -21,6 +25,16 @@ export interface CommandTranslationMetadata {
)[];
}

/**
* Represents the metadata for basic command translations.
*/
export interface BasicCommandTranslationMetadata {
/**
* The name of the command.
*/
name?: string;
}

/**
* Represents a command translation, including its metadata and translations.
*/
Expand All @@ -29,6 +43,14 @@ export interface CommandTranslation {
* The metadata for the command translation.
*/
[COMMAND_METADATA_KEY]: CommandTranslationMetadata;
/**
* The metadata for the user context menu command translation.
*/
[USER_CTX_COMMAND_METADATA_KEY]: BasicCommandTranslationMetadata;
/**
* The metadata for the message context menu command translation.
*/
[MESSAGE_CTX_COMMAND_METADATA_KEY]: BasicCommandTranslationMetadata;
/**
* The translations for the command, keyed by locale.
*/
Expand Down