Skip to content

Commit 022e138

Browse files
authoredApr 14, 2022
feat: add support for localized slash commands (v13 backport) (#7766)
1 parent 9e4a900 commit 022e138

6 files changed

+171
-24
lines changed
 

‎package-lock.json

+7-7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
"@sapphire/async-queue": "^1.1.9",
5656
"@types/node-fetch": "^2.5.12",
5757
"@types/ws": "^8.2.2",
58-
"discord-api-types": "^0.27.1",
58+
"discord-api-types": "^0.30.0",
5959
"form-data": "^4.0.0",
6060
"node-fetch": "^2.6.1",
6161
"ws": "^8.4.0"

‎src/managers/ApplicationCommandManager.js

+15-3
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,8 @@ class ApplicationCommandManager extends CachedManager {
6464
* Options used to fetch Application Commands from Discord
6565
* @typedef {BaseFetchOptions} FetchApplicationCommandOptions
6666
* @property {Snowflake} [guildId] The guild's id to fetch commands for, for when the guild is not cached
67+
* @property {LocaleString} [locale] The locale to use when fetching this command
68+
* @property {boolean} [withLocalizations] Whether to fetch all localization data
6769
*/
6870

6971
/**
@@ -82,9 +84,9 @@ class ApplicationCommandManager extends CachedManager {
8284
* .then(commands => console.log(`Fetched ${commands.size} commands`))
8385
* .catch(console.error);
8486
*/
85-
async fetch(id, { guildId, cache = true, force = false } = {}) {
87+
async fetch(id, { guildId, cache = true, force = false, locale, withLocalizations } = {}) {
8688
if (typeof id === 'object') {
87-
({ guildId, cache = true } = id);
89+
({ guildId, cache = true, locale, withLocalizations } = id);
8890
} else if (id) {
8991
if (!force) {
9092
const existing = this.cache.get(id);
@@ -94,7 +96,15 @@ class ApplicationCommandManager extends CachedManager {
9496
return this._add(command, cache);
9597
}
9698

97-
const data = await this.commandPath({ guildId }).get();
99+
const data = await this.commandPath({ guildId }).get({
100+
headers: {
101+
'X-Discord-Locale': locale,
102+
},
103+
query:
104+
typeof withLocalizations === 'boolean'
105+
? new URLSearchParams({ with_localizations: withLocalizations })
106+
: undefined,
107+
});
98108
return data.reduce((coll, command) => coll.set(command.id, this._add(command, cache, guildId)), new Collection());
99109
}
100110

@@ -206,7 +216,9 @@ class ApplicationCommandManager extends CachedManager {
206216
static transformCommand(command) {
207217
return {
208218
name: command.name,
219+
name_localizations: command.nameLocalizations ?? command.name_localizations,
209220
description: command.description,
221+
description_localizations: command.descriptionLocalizations ?? command.description_localizations,
210222
type: typeof command.type === 'number' ? command.type : ApplicationCommandTypes[command.type],
211223
options: command.options?.map(o => ApplicationCommand.transformOption(o)),
212224
default_permission: command.defaultPermission ?? command.default_permission,

‎src/structures/ApplicationCommand.js

+112-3
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,26 @@ class ApplicationCommand extends Base {
6262
this.name = data.name;
6363
}
6464

65+
if ('name_localizations' in data) {
66+
/**
67+
* The name localizations for this command
68+
* @type {?Object<string, string>}
69+
*/
70+
this.nameLocalizations = data.name_localizations;
71+
} else {
72+
this.nameLocalizations ??= null;
73+
}
74+
75+
if ('name_localized' in data) {
76+
/**
77+
* The localized name for this command
78+
* @type {?Object<string, string>}
79+
*/
80+
this.nameLocalized = data.name_localized;
81+
} else {
82+
this.nameLocalized ??= null;
83+
}
84+
6585
if ('description' in data) {
6686
/**
6787
* The description of this command
@@ -70,6 +90,26 @@ class ApplicationCommand extends Base {
7090
this.description = data.description;
7191
}
7292

93+
if ('description_localizations' in data) {
94+
/**
95+
* The description localizations for this command
96+
* @type {?string}
97+
*/
98+
this.descriptionLocalizations = data.description_localizations;
99+
} else {
100+
this.descriptionLocalizations ??= null;
101+
}
102+
103+
if ('description_localized' in data) {
104+
/**
105+
* The localized description for this command
106+
* @type {?string}
107+
*/
108+
this.descriptionLocalized = data.description_localized;
109+
} else {
110+
this.descriptionLocalized ??= null;
111+
}
112+
73113
if ('options' in data) {
74114
/**
75115
* The options of this command
@@ -128,7 +168,9 @@ class ApplicationCommand extends Base {
128168
* Data for creating or editing an application command.
129169
* @typedef {Object} ApplicationCommandData
130170
* @property {string} name The name of the command
171+
* @property {Object<string, string>} [nameLocalizations] The localizations for the command name
131172
* @property {string} description The description of the command
173+
* @property {Object<string, string>} [descriptionLocalizations] The localizations for the command description
132174
* @property {ApplicationCommandType} [type] The type of the command
133175
* @property {ApplicationCommandOptionData[]} [options] Options for the command
134176
* @property {boolean} [defaultPermission] Whether the command is enabled by default when the app is added to a guild
@@ -143,17 +185,31 @@ class ApplicationCommand extends Base {
143185
* @typedef {Object} ApplicationCommandOptionData
144186
* @property {ApplicationCommandOptionType|number} type The type of the option
145187
* @property {string} name The name of the option
188+
* @property {Object<string, string>} [nameLocalizations] The name localizations for the option
146189
* @property {string} description The description of the option
190+
* @property {Object<string, string>} [descriptionLocalizations] The description localizations for the option
147191
* @property {boolean} [autocomplete] Whether the option is an autocomplete option
148192
* @property {boolean} [required] Whether the option is required
149-
* @property {ApplicationCommandOptionChoice[]} [choices] The choices of the option for the user to pick from
193+
* @property {ApplicationCommandOptionChoiceData[]} [choices] The choices of the option for the user to pick from
150194
* @property {ApplicationCommandOptionData[]} [options] Additional options if this option is a subcommand (group)
151195
* @property {ChannelType[]|number[]} [channelTypes] When the option type is channel,
152196
* the allowed types of channels that can be selected
153197
* @property {number} [minValue] The minimum value for an `INTEGER` or `NUMBER` option
154198
* @property {number} [maxValue] The maximum value for an `INTEGER` or `NUMBER` option
155199
*/
156200

201+
/**
202+
* @typedef {Object} ApplicationCommandOptionChoiceData
203+
* @property {string} name The name of the choice
204+
* @property {Object<string, string>} [nameLocalizations] The localized names for this choice
205+
* @property {string|number} value The value of the choice
206+
*/
207+
208+
/**
209+
* @param {ApplicationCommandOptionChoiceData} ApplicationCommandOptionChoice
210+
* @property {string} [nameLocalized] The localized name for this choice
211+
*/
212+
157213
/**
158214
* Edits this application command.
159215
* @param {ApplicationCommandData} data The data to update the command with
@@ -179,6 +235,23 @@ class ApplicationCommand extends Base {
179235
return this.edit({ name });
180236
}
181237

238+
/**
239+
* Edits the localized names of this ApplicationCommand
240+
* @param {Object<string, string>} nameLocalizations The new localized names for the command
241+
* @returns {Promise<ApplicationCommand>}
242+
* @example
243+
* // Edit the name localizations of this command
244+
* command.setLocalizedNames({
245+
* 'en-GB': 'test',
246+
* 'pt-BR': 'teste',
247+
* })
248+
* .then(console.log)
249+
* .catch(console.error)
250+
*/
251+
setNameLocalizations(nameLocalizations) {
252+
return this.edit({ nameLocalizations });
253+
}
254+
182255
/**
183256
* Edits the description of this ApplicationCommand
184257
* @param {string} description The new description of the command
@@ -188,6 +261,23 @@ class ApplicationCommand extends Base {
188261
return this.edit({ description });
189262
}
190263

264+
/**
265+
* Edits the localized descriptions of this ApplicationCommand
266+
* @param {Object<string, string>} descriptionLocalizations The new localized descriptions for the command
267+
* @returns {Promise<ApplicationCommand>}
268+
* @example
269+
* // Edit the description localizations of this command
270+
* command.setLocalizedDescriptions({
271+
* 'en-GB': 'A test command',
272+
* 'pt-BR': 'Um comando de teste',
273+
* })
274+
* .then(console.log)
275+
* .catch(console.error)
276+
*/
277+
setDescriptionLocalizations(descriptionLocalizations) {
278+
return this.edit({ descriptionLocalizations });
279+
}
280+
191281
/**
192282
* Edits the default permission of this ApplicationCommand
193283
* @param {boolean} [defaultPermission=true] The default permission for this command
@@ -344,7 +434,11 @@ class ApplicationCommand extends Base {
344434
* @typedef {Object} ApplicationCommandOption
345435
* @property {ApplicationCommandOptionType} type The type of the option
346436
* @property {string} name The name of the option
437+
* @property {Object<string, string>} [nameLocalizations] The localizations for the option name
438+
* @property {string} [nameLocalized] The localized name for this option
347439
* @property {string} description The description of the option
440+
* @property {Object<string, string>} [descriptionLocalizations] The localizations for the option description
441+
* @property {string} [descriptionLocalized] The localized description for this option
348442
* @property {boolean} [required] Whether the option is required
349443
* @property {boolean} [autocomplete] Whether the option is an autocomplete option
350444
* @property {ApplicationCommandOptionChoice[]} [choices] The choices of the option for the user to pick from
@@ -359,12 +453,14 @@ class ApplicationCommand extends Base {
359453
* A choice for an application command option.
360454
* @typedef {Object} ApplicationCommandOptionChoice
361455
* @property {string} name The name of the choice
456+
* @property {string} [nameLocalized] The localized name for this choice
457+
* @property {Object<string, string>} [nameLocalizations] The localized names for this choice
362458
* @property {string|number} value The value of the choice
363459
*/
364460

365461
/**
366462
* Transforms an {@link ApplicationCommandOptionData} object into something that can be used with the API.
367-
* @param {ApplicationCommandOptionData} option The option to transform
463+
* @param {ApplicationCommandOptionData|ApplicationCommandOption} option The option to transform
368464
* @param {boolean} [received] Whether this option has been received from Discord
369465
* @returns {APIApplicationCommandOption}
370466
* @private
@@ -374,14 +470,27 @@ class ApplicationCommand extends Base {
374470
const channelTypesKey = received ? 'channelTypes' : 'channel_types';
375471
const minValueKey = received ? 'minValue' : 'min_value';
376472
const maxValueKey = received ? 'maxValue' : 'max_value';
473+
const nameLocalizationsKey = received ? 'nameLocalizations' : 'name_localizations';
474+
const nameLocalizedKey = received ? 'nameLocalized' : 'name_localized';
475+
const descriptionLocalizationsKey = received ? 'descriptionLocalizations' : 'description_localizations';
476+
const descriptionLocalizedKey = received ? 'descriptionLocalized' : 'description_localized';
377477
return {
378478
type: typeof option.type === 'number' && !received ? option.type : ApplicationCommandOptionTypes[option.type],
379479
name: option.name,
480+
[nameLocalizationsKey]: option.nameLocalizations ?? option.name_localizations,
481+
[nameLocalizedKey]: option.nameLocalized ?? option.name_localized,
380482
description: option.description,
483+
[descriptionLocalizationsKey]: option.descriptionLocalizations ?? option.description_localizations,
484+
[descriptionLocalizedKey]: option.descriptionLocalized ?? option.description_localized,
381485
required:
382486
option.required ?? (stringType === 'SUB_COMMAND' || stringType === 'SUB_COMMAND_GROUP' ? undefined : false),
383487
autocomplete: option.autocomplete,
384-
choices: option.choices,
488+
choices: option.choices?.map(choice => ({
489+
name: choice.name,
490+
[nameLocalizedKey]: choice.nameLocalized ?? choice.name_localized,
491+
[nameLocalizationsKey]: choice.nameLocalizations ?? choice.name_localizations,
492+
value: choice.value,
493+
})),
385494
options: option.options?.map(o => this.transformOption(o, received)),
386495
[channelTypesKey]: received
387496
? option.channel_types?.map(type => ChannelTypes[type])

‎src/structures/AutocompleteInteraction.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ class AutocompleteInteraction extends Interaction {
7676

7777
/**
7878
* Sends results for the autocomplete of this interaction.
79-
* @param {ApplicationCommandOptionChoice[]} options The options for the autocomplete
79+
* @param {ApplicationCommandOptionChoiceData[]} options The options for the autocomplete
8080
* @returns {Promise<void>}
8181
* @example
8282
* // respond to autocomplete interaction

0 commit comments

Comments
 (0)
Please sign in to comment.