diff --git a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
index 07c426f09..207c9485f 100644
--- a/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
+++ b/src/Discord.Net.Core/Entities/Guilds/IGuild.cs
@@ -1142,6 +1142,10 @@ namespace Discord
///
/// Gets this guilds application commands.
///
+ ///
+ /// Whether to include full localization dictionaries in the returned objects,
+ /// instead of the localized name and description fields.
+ ///
/// The options to be used when sending the request.
///
/// A task that represents the asynchronous get operation. The task result contains a read-only collection
diff --git a/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOption.cs b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOption.cs
index 98c7e8f79..556257b47 100644
--- a/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOption.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOption.cs
@@ -87,6 +87,10 @@ namespace Discord
///
public List ChannelTypes { get; set; }
+ ///
+ /// Gets or sets the localization dictionary for the name field of this option.
+ ///
+ /// Thrown when any of the dictionary keys is an invalid locale.
public IDictionary NameLocalizations
{
get => _nameLocalizations;
@@ -103,6 +107,10 @@ namespace Discord
}
}
+ ///
+ /// Gets or sets the localization dictionary for the description field of this option.
+ ///
+ /// Thrown when any of the dictionary keys is an invalid locale.
public IDictionary DescriptionLocalizations
{
get => _descriptionLocalizations;
@@ -115,7 +123,7 @@ namespace Discord
EnsureValidOptionDescription(description);
}
- _nameLocalizations = value;
+ _descriptionLocalizations = value;
}
}
diff --git a/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOptionChoice.cs b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOptionChoice.cs
index 60db06cdc..8f1ecc6d2 100644
--- a/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOptionChoice.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandOptionChoice.cs
@@ -46,6 +46,10 @@ namespace Discord
}
}
+ ///
+ /// Gets or sets the localization dictionary for the name field of this choice.
+ ///
+ /// Thrown when any of the dictionary keys is an invalid locale.
public IDictionary NameLocalizations
{
get => _nameLocalizations;
diff --git a/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandProperties.cs b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandProperties.cs
index 9fa5d67a8..3c1312504 100644
--- a/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandProperties.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/ApplicationCommandProperties.cs
@@ -1,5 +1,9 @@
+using System;
using System.Collections;
using System.Collections.Generic;
+using System.Collections.Immutable;
+using System.Linq;
+using System.Text.RegularExpressions;
namespace Discord
{
@@ -8,6 +12,9 @@ namespace Discord
///
public abstract class ApplicationCommandProperties
{
+ private IReadOnlyDictionary _nameLocalizations;
+ private IReadOnlyDictionary _descriptionLocalizations;
+
internal abstract ApplicationCommandType Type { get; }
///
@@ -20,9 +27,47 @@ namespace Discord
///
public Optional IsDefaultPermission { get; set; }
- public IDictionary? NameLocalizations { get; set; }
+ ///
+ /// Gets or sets the localization dictionary for the name field of this command.
+ ///
+ public IReadOnlyDictionary? NameLocalizations
+ {
+ get => _nameLocalizations;
+ set
+ {
+ foreach (var (locale, name) in value)
+ {
+ if(!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$"))
+ throw new ArgumentException($"Invalid locale: {locale}", nameof(locale));
+
+ Preconditions.AtLeast(name.Length, 1, nameof(name));
+ Preconditions.AtMost(name.Length, SlashCommandBuilder.MaxNameLength, nameof(name));
+ if (!Regex.IsMatch(name, @"^[\w-]{1,32}$"))
+ throw new ArgumentException("Option name cannot contain any special characters or whitespaces!", nameof(name));
+ }
+ _nameLocalizations = value;
+ }
+ }
+
+ ///
+ /// Gets or sets the localization dictionary for the description field of this command.
+ ///
+ public IReadOnlyDictionary? DescriptionLocalizations
+ {
+ get => _descriptionLocalizations;
+ set
+ {
+ foreach (var (locale, description) in value)
+ {
+ if(!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$"))
+ throw new ArgumentException($"Invalid locale: {locale}", nameof(locale));
- public IDictionary? DescriptionLocalizations { get; set; }
+ Preconditions.AtLeast(description.Length, 1, nameof(description));
+ Preconditions.AtMost(description.Length, SlashCommandBuilder.MaxDescriptionLength, nameof(description));
+ }
+ _descriptionLocalizations = value;
+ }
+ }
internal ApplicationCommandProperties() { }
}
diff --git a/src/Discord.Net.Core/Entities/Interactions/ContextMenus/MessageCommandBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/ContextMenus/MessageCommandBuilder.cs
index 831a5a54c..32bcab936 100644
--- a/src/Discord.Net.Core/Entities/Interactions/ContextMenus/MessageCommandBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/ContextMenus/MessageCommandBuilder.cs
@@ -36,6 +36,9 @@ namespace Discord
///
public bool IsDefaultPermission { get; set; } = true;
+ ///
+ /// Gets the localization dictionary for the name field of this command.
+ ///
public IReadOnlyDictionary NameLocalizations => _nameLocalizations;
private string _name;
@@ -82,6 +85,13 @@ namespace Discord
return this;
}
+ ///
+ /// Sets the collection.
+ ///
+ /// Localization dictionary for the name field of this command.
+ ///
+ /// Thrown if is null.
+ /// Thrown if any dictionary key is an invalid locale string.
public MessageCommandBuilder WithNameLocalizations(IDictionary nameLocalizations)
{
if (nameLocalizations is null)
@@ -99,6 +109,13 @@ namespace Discord
return this;
}
+ ///
+ /// Adds a new entry to the collection.
+ ///
+ /// Locale of the entry.
+ /// Localized string for the name field.
+ /// The current builder.
+ /// Thrown if is an invalid locale string.
public MessageCommandBuilder AddNameLocalization(string locale, string name)
{
if(!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$"))
@@ -112,7 +129,7 @@ namespace Discord
return this;
}
- internal static void EnsureValidCommandName(string name)
+ private static void EnsureValidCommandName(string name)
{
Preconditions.NotNullOrEmpty(name, nameof(name));
Preconditions.AtLeast(name.Length, 1, nameof(name));
diff --git a/src/Discord.Net.Core/Entities/Interactions/ContextMenus/UserCommandBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/ContextMenus/UserCommandBuilder.cs
index 4ae0c18e3..7c94dcb25 100644
--- a/src/Discord.Net.Core/Entities/Interactions/ContextMenus/UserCommandBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/ContextMenus/UserCommandBuilder.cs
@@ -36,6 +36,9 @@ namespace Discord
///
public bool IsDefaultPermission { get; set; } = true;
+ ///
+ /// Gets the localization dictionary for the name field of this command.
+ ///
public IReadOnlyDictionary NameLocalizations => _nameLocalizations;
private string _name;
@@ -80,6 +83,13 @@ namespace Discord
return this;
}
+ ///
+ /// Sets the collection.
+ ///
+ /// Localization dictionary for the name field of this command.
+ ///
+ /// Thrown if is null.
+ /// Thrown if any dictionary key is an invalid locale string.
public UserCommandBuilder WithNameLocalizations(IDictionary nameLocalizations)
{
if (nameLocalizations is null)
@@ -97,6 +107,13 @@ namespace Discord
return this;
}
+ ///
+ /// Adds a new entry to the collection.
+ ///
+ /// Locale of the entry.
+ /// Localized string for the name field.
+ /// The current builder.
+ /// Thrown if is an invalid locale string.
public UserCommandBuilder AddNameLocalization(string locale, string name)
{
if(!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$"))
@@ -110,7 +127,7 @@ namespace Discord
return this;
}
- internal static void EnsureValidCommandName(string name)
+ private static void EnsureValidCommandName(string name)
{
Preconditions.NotNullOrEmpty(name, nameof(name));
Preconditions.AtLeast(name.Length, 1, nameof(name));
diff --git a/src/Discord.Net.Core/Entities/Interactions/IApplicationCommand.cs b/src/Discord.Net.Core/Entities/Interactions/IApplicationCommand.cs
index 1b7e28159..e30d795ff 100644
--- a/src/Discord.Net.Core/Entities/Interactions/IApplicationCommand.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/IApplicationCommand.cs
@@ -39,12 +39,30 @@ namespace Discord
///
IReadOnlyCollection Options { get; }
+ ///
+ /// Gets the localization dictionary for the name field of this command.
+ ///
IReadOnlyDictionary? NameLocalizations { get; }
+ ///
+ /// Gets the localization dictionary for the description field of this command.
+ ///
IReadOnlyDictionary? DescriptionLocalizations { get; }
+ ///
+ /// Gets the localized name of this command.
+ ///
+ ///
+ /// Only returned when the `withLocalizations` query parameter is set to true when requesting the command.
+ ///
string? NameLocalized { get; }
+ ///
+ /// Gets the localized description of this command.
+ ///
+ ///
+ /// Only returned when the `withLocalizations` query parameter is set to true when requesting the command.
+ ///
string? DescriptionLocalized { get; }
///
diff --git a/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOption.cs b/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOption.cs
index 78f6487df..68bf00ebf 100644
--- a/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOption.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOption.cs
@@ -62,12 +62,30 @@ namespace Discord
///
IReadOnlyCollection ChannelTypes { get; }
+ ///
+ /// Gets the localization dictionary for the name field of this command.
+ ///
IReadOnlyDictionary? NameLocalizations { get; }
+ ///
+ /// Gets the localization dictionary for the description field of this command.
+ ///
IReadOnlyDictionary? DescriptionLocalizations { get; }
+ ///
+ /// Gets the localized name of this command.
+ ///
+ ///
+ /// Only returned when the `withLocalizations` query parameter is set to true when requesting the command.
+ ///
string? NameLocalized { get; }
+ ///
+ /// Gets the localized description of this command.
+ ///
+ ///
+ /// Only returned when the `withLocalizations` query parameter is set to true when requesting the command.
+ ///
string? DescriptionLocalized { get; }
}
}
diff --git a/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOptionChoice.cs b/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOptionChoice.cs
index 96b5c4b5d..66d0f1b9b 100644
--- a/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOptionChoice.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/IApplicationCommandOptionChoice.cs
@@ -17,8 +17,17 @@ namespace Discord
///
object Value { get; }
+ ///
+ /// Gets the localization dictionary for the name field of this command.
+ ///
IReadOnlyDictionary? NameLocalizations { get; }
+ ///
+ /// Gets the localized name of this command.
+ ///
+ ///
+ /// Only returned when the `withLocalizations` query parameter is set to true when requesting the command.
+ ///
string? NameLocalized { get; }
}
}
diff --git a/src/Discord.Net.Core/Entities/Interactions/SlashCommands/SlashCommandBuilder.cs b/src/Discord.Net.Core/Entities/Interactions/SlashCommands/SlashCommandBuilder.cs
index e553fd7fa..2a6862fd3 100644
--- a/src/Discord.Net.Core/Entities/Interactions/SlashCommands/SlashCommandBuilder.cs
+++ b/src/Discord.Net.Core/Entities/Interactions/SlashCommands/SlashCommandBuilder.cs
@@ -65,8 +65,14 @@ namespace Discord
}
}
+ ///
+ /// Gets the localization dictionary for the name field of this command.
+ ///
public IReadOnlyDictionary NameLocalizations => _nameLocalizations;
+ ///
+ /// Gets the localization dictionary for the description field of this command.
+ ///
public IReadOnlyDictionary DescriptionLocalizations => _descriptionLocalizations;
///
@@ -153,6 +159,8 @@ namespace Discord
/// If this option is set to autocomplete.
/// The options of the option to add.
/// The allowed channel types for this option.
+ /// Localization dictionary for the name field of this command.
+ /// Localization dictionary for the description field of this command.
/// The choices of this option.
/// The smallest number value the user can input.
/// The largest number value the user can input.
@@ -244,6 +252,13 @@ namespace Discord
return this;
}
+ ///
+ /// Sets the collection.
+ ///
+ /// Localization dictionary for the name field of this command.
+ ///
+ /// Thrown if is null.
+ /// Thrown if any dictionary key is an invalid locale string.
public SlashCommandBuilder WithNameLocalizations(IDictionary nameLocalizations)
{
if (nameLocalizations is null)
@@ -261,6 +276,13 @@ namespace Discord
return this;
}
+ ///
+ /// Sets the collection.
+ ///
+ /// Localization dictionary for the name field of this command.
+ ///
+ /// Thrown if is null.
+ /// Thrown if any dictionary key is an invalid locale string.
public SlashCommandBuilder WithDescriptionLocalizations(IDictionary descriptionLocalizations)
{
if (descriptionLocalizations is null)
@@ -278,6 +300,13 @@ namespace Discord
return this;
}
+ ///
+ /// Adds a new entry to the collection.
+ ///
+ /// Locale of the entry.
+ /// Localized string for the name field.
+ /// The current builder.
+ /// Thrown if is an invalid locale string.
public SlashCommandBuilder AddNameLocalization(string locale, string name)
{
if(!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$"))
@@ -291,6 +320,13 @@ namespace Discord
return this;
}
+ ///
+ /// Adds a new entry to the collection.
+ ///
+ /// Locale of the entry.
+ /// Localized string for the description field.
+ /// The current builder.
+ /// Thrown if is an invalid locale string.
public SlashCommandBuilder AddDescriptionLocalization(string locale, string description)
{
if(!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$"))
@@ -426,8 +462,14 @@ namespace Discord
///
public List ChannelTypes { get; set; }
+ ///
+ /// Gets the localization dictionary for the name field of this command.
+ ///
public IReadOnlyDictionary NameLocalizations => _nameLocalizations;
+ ///
+ /// Gets the localization dictionary for the description field of this command.
+ ///
public IReadOnlyDictionary DescriptionLocalizations => _descriptionLocalizations;
///
@@ -482,6 +524,8 @@ namespace Discord
/// If this option supports autocomplete.
/// The options of the option to add.
/// The allowed channel types for this option.
+ /// Localization dictionary for the description field of this command.
+ /// Localization dictionary for the description field of this command.
/// The choices of this option.
/// The smallest number value the user can input.
/// The largest number value the user can input.
@@ -556,6 +600,7 @@ namespace Discord
///
/// The name of the choice.
/// The value of the choice.
+ /// Localization dictionary for the description field of this command.
/// The current builder.
public SlashCommandOptionBuilder AddChoice(string name, int value, IDictionary nameLocalizations = null)
{
@@ -567,6 +612,7 @@ namespace Discord
///
/// The name of the choice.
/// The value of the choice.
+ /// Localization dictionary for the description field of this command.
/// The current builder.
public SlashCommandOptionBuilder AddChoice(string name, string value, IDictionary nameLocalizations = null)
{
@@ -578,6 +624,7 @@ namespace Discord
///
/// The name of the choice.
/// The value of the choice.
+ /// Localization dictionary for the description field of this command.
/// The current builder.
public SlashCommandOptionBuilder AddChoice(string name, double value, IDictionary nameLocalizations = null)
{
@@ -589,6 +636,7 @@ namespace Discord
///
/// The name of the choice.
/// The value of the choice.
+ /// Localization dictionary for the description field of this command.
/// The current builder.
public SlashCommandOptionBuilder AddChoice(string name, float value, IDictionary nameLocalizations = null)
{
@@ -600,6 +648,7 @@ namespace Discord
///
/// The name of the choice.
/// The value of the choice.
+ /// Localization dictionary for the description field of this command.
/// The current builder.
public SlashCommandOptionBuilder AddChoice(string name, long value, IDictionary nameLocalizations = null)
{
diff --git a/src/Discord.Net.Interactions/InteractionService.cs b/src/Discord.Net.Interactions/InteractionService.cs
index a34185407..a858d3e9d 100644
--- a/src/Discord.Net.Interactions/InteractionService.cs
+++ b/src/Discord.Net.Interactions/InteractionService.cs
@@ -60,6 +60,11 @@ namespace Discord.Interactions
public event Func ModalCommandExecuted { add { _modalCommandExecutedEvent.Add(value); } remove { _modalCommandExecutedEvent.Remove(value); } }
internal readonly AsyncEvent> _modalCommandExecutedEvent = new();
+ ///
+ /// Get the used by this Interaction Service instance to localize strings.
+ ///
+ public ILocalizationManager LocalizationManager { get; set; }
+
private readonly ConcurrentDictionary _typedModuleDefs;
private readonly CommandMap _slashCommandMap;
private readonly ConcurrentDictionary> _contextCommandMaps;
@@ -81,7 +86,6 @@ namespace Discord.Interactions
internal readonly string _wildCardExp;
internal readonly RunMode _runMode;
internal readonly RestResponseCallback _restResponseCallback;
- internal readonly ILocalizationManager _localizationManager;
///
/// Rest client to be used to register application commands.
@@ -181,7 +185,7 @@ namespace Discord.Interactions
_enableAutocompleteHandlers = config.EnableAutocompleteHandlers;
_autoServiceScopes = config.AutoServiceScopes;
_restResponseCallback = config.RestResponseCallback;
- _localizationManager = config.LocalizationManager;
+ LocalizationManager = config.LocalizationManager;
_typeConverterMap = new TypeMap(this, new ConcurrentDictionary
{
diff --git a/src/Discord.Net.Interactions/Utilities/ApplicationCommandRestUtil.cs b/src/Discord.Net.Interactions/Utilities/ApplicationCommandRestUtil.cs
index 99acf3b4d..655d4cf88 100644
--- a/src/Discord.Net.Interactions/Utilities/ApplicationCommandRestUtil.cs
+++ b/src/Discord.Net.Interactions/Utilities/ApplicationCommandRestUtil.cs
@@ -10,7 +10,7 @@ namespace Discord.Interactions
#region Parameters
public static ApplicationCommandOptionProperties ToApplicationCommandOptionProps(this SlashCommandParameterInfo parameterInfo)
{
- var localizationManager = parameterInfo.Command.Module.CommandService._localizationManager;
+ var localizationManager = parameterInfo.Command.Module.CommandService.LocalizationManager;
var parameterPath = parameterInfo.GetParameterPath();
var props = new ApplicationCommandOptionProperties
@@ -43,7 +43,7 @@ namespace Discord.Interactions
public static SlashCommandProperties ToApplicationCommandProps(this SlashCommandInfo commandInfo)
{
var commandPath = commandInfo.GetCommandPath();
- var localizationManager = commandInfo.Module.CommandService._localizationManager;
+ var localizationManager = commandInfo.Module.CommandService.LocalizationManager;
var props = new SlashCommandBuilder()
{
@@ -64,7 +64,7 @@ namespace Discord.Interactions
public static ApplicationCommandOptionProperties ToApplicationCommandOptionProps(this SlashCommandInfo commandInfo)
{
- var localizationManager = commandInfo.Module.CommandService._localizationManager;
+ var localizationManager = commandInfo.Module.CommandService.LocalizationManager;
var commandPath = commandInfo.GetCommandPath();
return new ApplicationCommandOptionProperties
@@ -82,7 +82,7 @@ namespace Discord.Interactions
public static ApplicationCommandProperties ToApplicationCommandProps(this ContextCommandInfo commandInfo)
{
- var localizationManager = commandInfo.Module.CommandService._localizationManager;
+ var localizationManager = commandInfo.Module.CommandService.LocalizationManager;
var commandPath = commandInfo.GetCommandPath();
return commandInfo.CommandType switch
@@ -136,7 +136,7 @@ namespace Discord.Interactions
options.AddRange(moduleInfo.SubModules?.SelectMany(x => x.ParseSubModule(args, ignoreDontRegister)));
- var localizationManager = moduleInfo.CommandService._localizationManager;
+ var localizationManager = moduleInfo.CommandService.LocalizationManager;
var modulePath = moduleInfo.GetModulePath();
var props = new SlashCommandBuilder
@@ -186,9 +186,9 @@ namespace Discord.Interactions
Description = moduleInfo.Description,
Type = ApplicationCommandOptionType.SubCommandGroup,
Options = options,
- NameLocalizations = moduleInfo.CommandService._localizationManager?.GetAllNames(moduleInfo.GetModulePath(), LocalizationTarget.Group)
+ NameLocalizations = moduleInfo.CommandService.LocalizationManager?.GetAllNames(moduleInfo.GetModulePath(), LocalizationTarget.Group)
?? ImmutableDictionary.Empty,
- DescriptionLocalizations = moduleInfo.CommandService._localizationManager?.GetAllDescriptions(moduleInfo.GetModulePath(), LocalizationTarget.Group)
+ DescriptionLocalizations = moduleInfo.CommandService.LocalizationManager?.GetAllDescriptions(moduleInfo.GetModulePath(), LocalizationTarget.Group)
?? ImmutableDictionary.Empty,
} };
}
diff --git a/src/Discord.Net.Rest/DiscordRestApiClient.cs b/src/Discord.Net.Rest/DiscordRestApiClient.cs
index 5fe9d922d..9b09d5157 100644
--- a/src/Discord.Net.Rest/DiscordRestApiClient.cs
+++ b/src/Discord.Net.Rest/DiscordRestApiClient.cs
@@ -8,6 +8,7 @@ using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
+using System.ComponentModel.Design;
using System.Diagnostics;
using System.Globalization;
using System.IO;