Browse Source

add localization to the rest methods

pull/2211/head
Cenngo 3 years ago
parent
commit
028650e38c
13 changed files with 123 additions and 23 deletions
  1. +2
    -2
      src/Discord.Net.Core/Entities/Interactions/ApplicationCommandProperties.cs
  2. +1
    -1
      src/Discord.Net.Core/Entities/Interactions/SlashCommands/SlashCommandBuilder.cs
  3. +6
    -1
      src/Discord.Net.Core/Extensions/GenericCollectionExtensions.cs
  4. +9
    -0
      src/Discord.Net.Interactions/Extensions/LocalizationExtensions.cs
  5. +3
    -0
      src/Discord.Net.Interactions/InteractionServiceConfig.cs
  6. +20
    -0
      src/Discord.Net.Interactions/LocalizationManagers/ILocalizationManager.cs
  7. +11
    -1
      src/Discord.Net.Interactions/LocalizationManagers/JsonLocalizationManager.cs
  8. +20
    -5
      src/Discord.Net.Interactions/LocalizationManagers/ResxLocalizationManager.cs
  9. +3
    -0
      src/Discord.Net.Interactions/LocalizationTarget.cs
  10. +6
    -6
      src/Discord.Net.Interactions/Utilities/ApplicationCommandRestUtil.cs
  11. +14
    -1
      src/Discord.Net.Rest/API/Rest/CreateApplicationCommandParams.cs
  12. +7
    -0
      src/Discord.Net.Rest/API/Rest/ModifyApplicationCommandParams.cs
  13. +21
    -6
      src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs

+ 2
- 2
src/Discord.Net.Core/Entities/Interactions/ApplicationCommandProperties.cs View File

@@ -20,9 +20,9 @@ namespace Discord
/// </summary>
public Optional<bool> IsDefaultPermission { get; set; }

public Optional<IDictionary<string, string>> NameLocalizations { get; set; }
public IDictionary<string, string>? NameLocalizations { get; set; }

public Optional<IDictionary<string, string>> DescriptionLocalizations { get; set; }
public IDictionary<string, string>? DescriptionLocalizations { get; set; }

internal ApplicationCommandProperties() { }
}


+ 1
- 1
src/Discord.Net.Core/Entities/Interactions/SlashCommands/SlashCommandBuilder.cs View File

@@ -266,7 +266,7 @@ namespace Discord
if (descriptionLocalizations is null)
throw new ArgumentNullException(nameof(descriptionLocalizations));

foreach (var (locale, description) in _descriptionLocalizations)
foreach (var (locale, description) in descriptionLocalizations)
{
if(!Regex.IsMatch(locale, @"^\w{2}(?:-\w{2})?$"))
throw new ArgumentException($"Invalid locale: {locale}", nameof(locale));


+ 6
- 1
src/Discord.Net.Core/Extensions/GenericCollectionExtensions.cs View File

@@ -1,10 +1,15 @@
using System.Linq;

namespace System.Collections.Generic;

public static class GenericCollectionExtensions
internal static class GenericCollectionExtensions
{
public static void Deconstruct<T1, T2>(this KeyValuePair<T1, T2> kvp, out T1 value1, out T2 value2)
{
value1 = kvp.Key;
value2 = kvp.Value;
}

public static Dictionary<T1, T2> ToDictionary<T1, T2>(this IEnumerable<KeyValuePair<T1, T2>> kvp) =>
kvp.ToDictionary(x => x.Key, x => x.Value);
}

+ 9
- 0
src/Discord.Net.Interactions/Extensions/LocalizationExtensions.cs View File

@@ -0,0 +1,9 @@
namespace Discord.Interactions.Extensions;

public static class LocalizationExtensions
{
public static void UseResxLocalization(this InteractionServiceConfig config)
{

}
}

+ 3
- 0
src/Discord.Net.Interactions/InteractionServiceConfig.cs View File

@@ -65,6 +65,9 @@ namespace Discord.Interactions
/// </summary>
public bool ExitOnMissingModalField { get; set; } = false;

/// <summary>
/// Localization provider to be used when registering application commands.
/// </summary>
public ILocalizationManager LocalizationManager { get; set; }
}



+ 20
- 0
src/Discord.Net.Interactions/LocalizationManagers/ILocalizationManager.cs View File

@@ -4,9 +4,29 @@ using System.Threading.Tasks;

namespace Discord.Interactions
{
/// <summary>
/// Respresents localization provider for Discord Application Commands.
/// </summary>
public interface ILocalizationManager
{
/// <summary>
/// Get every the resource name for every available locale.
/// </summary>
/// <param name="key">Location of the resource.</param>
/// <param name="destinationType">Type of the resource.</param>
/// <returns>
/// A dictionary containing every available locale and the resource name.
/// </returns>
IDictionary<string, string> GetAllNames(IList<string> key, LocalizationTarget destinationType);

/// <summary>
/// Get every the resource description for every available locale.
/// </summary>
/// <param name="key">Location of the resource.</param>
/// <param name="destinationType">Type of the resource.</param>
/// <returns>
/// A dictionary containing every available locale and the resource name.
/// </returns>
IDictionary<string, string> GetAllDescriptions(IList<string> key, LocalizationTarget destinationType);
}
}

+ 11
- 1
src/Discord.Net.Interactions/LocalizationManagers/JsonLocalizationManager.cs View File

@@ -8,7 +8,10 @@ using System.Threading.Tasks;

namespace Discord.Interactions
{
internal class JsonLocalizationManager : ILocalizationManager
/// <summary>
/// The default localization provider for Json resource files.
/// </summary>
public sealed class JsonLocalizationManager : ILocalizationManager
{
private const string NameIdentifier = "name";
private const string DescriptionIdentifier = "description";
@@ -17,15 +20,22 @@ namespace Discord.Interactions
private readonly string _fileName;
private readonly Regex _localeParserRegex = new Regex(@"\w+.(?<locale>\w{2}(?:-\w{2})?).json", RegexOptions.Compiled | RegexOptions.Singleline);

/// <summary>
/// Initializes a new instance of the <see cref="JsonLocalizationManager"/> class.
/// </summary>
/// <param name="basePath">Base path of the Json file.</param>
/// <param name="fileName">Name of the Json file.</param>
public JsonLocalizationManager(string basePath, string fileName)
{
_basePath = basePath;
_fileName = fileName;
}

/// <inheritdoc />
public IDictionary<string, string> GetAllDescriptions(IList<string> key, LocalizationTarget destinationType) =>
GetValues(key, DescriptionIdentifier);

/// <inheritdoc />
public IDictionary<string, string> GetAllNames(IList<string> key, LocalizationTarget destinationType) =>
GetValues(key, NameIdentifier);



+ 20
- 5
src/Discord.Net.Interactions/LocalizationManagers/ResxLocalizationManager.cs View File

@@ -8,16 +8,25 @@ using System.Threading.Tasks;

namespace Discord.Interactions
{
internal sealed class ResxLocalizationManager : ILocalizationManager
/// <summary>
/// The default localization provider for Resx files.
/// </summary>
public sealed class ResxLocalizationManager : ILocalizationManager
{
private const string NameIdentifier = "name";
private const string DescriptionIdentifier = "description";

private readonly string _baseResource;
private readonly Assembly _assembly;
private readonly ConcurrentDictionary<string, ResourceManager> _localizerCache = new();
private static readonly ConcurrentDictionary<string, ResourceManager> _localizerCache = new();
private readonly IEnumerable<CultureInfo> _supportedLocales;

/// <summary>
/// Initializes a new instance of the <see cref="ResxLocalizationManager"/> class.
/// </summary>
/// <param name="baseResource">Name of the base resource.</param>
/// <param name="assembly">The main assembly for the resources.</param>
/// <param name="supportedLocales">Cultures the <see cref="ResxLocalizationManager"/> should search for.</param>
public ResxLocalizationManager(string baseResource, Assembly assembly, params CultureInfo[] supportedLocales)
{
_baseResource = baseResource;
@@ -25,9 +34,11 @@ namespace Discord.Interactions
_supportedLocales = supportedLocales;
}

/// <inheritdoc />
public IDictionary<string, string> GetAllDescriptions(IList<string> key, LocalizationTarget destinationType) =>
GetValues(key, DescriptionIdentifier);

/// <inheritdoc />
public IDictionary<string, string> GetAllNames(IList<string> key, LocalizationTarget destinationType) =>
GetValues(key, NameIdentifier);

@@ -40,9 +51,13 @@ namespace Discord.Interactions

foreach (var locale in _supportedLocales)
{
var value = resourceManager.GetString(identifier, locale);
if (value is not null)
result[locale.Name] = value;
try
{
var value = resourceManager.GetString(identifier, locale);
if (value is not null)
result[locale.Name] = value;
}
catch (MissingManifestResourceException){}
}

return result;


+ 3
- 0
src/Discord.Net.Interactions/LocalizationTarget.cs View File

@@ -1,5 +1,8 @@
namespace Discord.Interactions
{
/// <summary>
/// Resource targets for localization.
/// </summary>
public enum LocalizationTarget
{
Group,


+ 6
- 6
src/Discord.Net.Interactions/Utilities/ApplicationCommandRestUtil.cs View File

@@ -205,22 +205,22 @@ namespace Discord.Interactions
Description = command.Description,
IsDefaultPermission = command.IsDefaultPermission,
Options = command.Options?.Select(x => x.ToApplicationCommandOptionProps())?.ToList() ?? Optional<List<ApplicationCommandOptionProperties>>.Unspecified,
NameLocalizations = command.NameLocalizations?.ToImmutableDictionary(),
DescriptionLocalizations = command.DescriptionLocalizations?.ToImmutableDictionary(),
NameLocalizations = command.NameLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty,
DescriptionLocalizations = command.DescriptionLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty,
},
ApplicationCommandType.User => new UserCommandProperties
{
Name = command.Name,
IsDefaultPermission = command.IsDefaultPermission,
NameLocalizations = command.NameLocalizations?.ToImmutableDictionary(),
DescriptionLocalizations = command.DescriptionLocalizations?.ToImmutableDictionary()
NameLocalizations = command.NameLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty,
DescriptionLocalizations = command.DescriptionLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty
},
ApplicationCommandType.Message => new MessageCommandProperties
{
Name = command.Name,
IsDefaultPermission = command.IsDefaultPermission,
NameLocalizations = command.NameLocalizations?.ToImmutableDictionary(),
DescriptionLocalizations = command.DescriptionLocalizations?.ToImmutableDictionary()
NameLocalizations = command.NameLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty,
DescriptionLocalizations = command.DescriptionLocalizations?.ToImmutableDictionary() ?? ImmutableDictionary<string, string>.Empty
},
_ => throw new InvalidOperationException($"Cannot create command properties for command type {command.Type}"),
};


+ 14
- 1
src/Discord.Net.Rest/API/Rest/CreateApplicationCommandParams.cs View File

@@ -1,4 +1,8 @@
using Newtonsoft.Json;
using System.Collections;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;

namespace Discord.API.Rest
{
@@ -19,13 +23,22 @@ namespace Discord.API.Rest
[JsonProperty("default_permission")]
public Optional<bool> DefaultPermission { get; set; }

[JsonProperty("name_localizations")]
public Optional<Dictionary<string, string>?> NameLocalizations { get; set; }

[JsonProperty("description_localizations")]
public Optional<Dictionary<string, string>?> DescriptionLocalizations { get; set; }

public CreateApplicationCommandParams() { }
public CreateApplicationCommandParams(string name, string description, ApplicationCommandType type, ApplicationCommandOption[] options = null)
public CreateApplicationCommandParams(string name, string description, ApplicationCommandType type, ApplicationCommandOption[] options = null,
IDictionary<string, string> nameLocalizations = null, IDictionary<string, string> descriptionLocalizations = null)
{
Name = name;
Description = description;
Options = Optional.Create(options);
Type = type;
NameLocalizations = nameLocalizations?.ToDictionary(x => x.Key, x => x.Value) ?? Optional<Dictionary<string, string>?>.Unspecified;
DescriptionLocalizations = descriptionLocalizations?.ToDictionary(x => x.Key, x => x.Value) ?? Optional<Dictionary<string, string>?>.Unspecified;
}
}
}

+ 7
- 0
src/Discord.Net.Rest/API/Rest/ModifyApplicationCommandParams.cs View File

@@ -1,4 +1,5 @@
using Newtonsoft.Json;
using System.Collections.Generic;

namespace Discord.API.Rest
{
@@ -15,5 +16,11 @@ namespace Discord.API.Rest

[JsonProperty("default_permission")]
public Optional<bool> DefaultPermission { get; set; }

[JsonProperty("name_localizations")]
public Optional<Dictionary<string, string>?> NameLocalizations { get; set; }

[JsonProperty("description_localizations")]
public Optional<Dictionary<string, string>?> DescriptionLocalizations { get; set; }
}
}

+ 21
- 6
src/Discord.Net.Rest/Entities/Interactions/InteractionHelper.cs View File

@@ -3,6 +3,7 @@ using Discord.API.Rest;
using Discord.Net;
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
@@ -100,7 +101,9 @@ namespace Discord.Rest
Type = arg.Type,
DefaultPermission = arg.IsDefaultPermission.IsSpecified
? arg.IsDefaultPermission.Value
: Optional<bool>.Unspecified
: Optional<bool>.Unspecified,
NameLocalizations = arg.NameLocalizations?.ToDictionary(),
DescriptionLocalizations = arg.DescriptionLocalizations?.ToDictionary()
};

if (arg is SlashCommandProperties slashProps)
@@ -134,9 +137,13 @@ namespace Discord.Rest
Type = arg.Type,
DefaultPermission = arg.IsDefaultPermission.IsSpecified
? arg.IsDefaultPermission.Value
: Optional<bool>.Unspecified
: Optional<bool>.Unspecified,
NameLocalizations = arg.NameLocalizations?.ToDictionary(),
DescriptionLocalizations = arg.DescriptionLocalizations?.ToDictionary()
};

Console.WriteLine("Locales:" + string.Join(",", arg.NameLocalizations.Keys));

if (arg is SlashCommandProperties slashProps)
{
Preconditions.NotNullOrEmpty(slashProps.Description, nameof(slashProps.Description));
@@ -171,7 +178,9 @@ namespace Discord.Rest
Type = arg.Type,
DefaultPermission = arg.IsDefaultPermission.IsSpecified
? arg.IsDefaultPermission.Value
: Optional<bool>.Unspecified
: Optional<bool>.Unspecified,
NameLocalizations = arg.NameLocalizations?.ToDictionary(),
DescriptionLocalizations = arg.DescriptionLocalizations?.ToDictionary()
};

if (arg is SlashCommandProperties slashProps)
@@ -231,7 +240,9 @@ namespace Discord.Rest
Name = args.Name,
DefaultPermission = args.IsDefaultPermission.IsSpecified
? args.IsDefaultPermission.Value
: Optional<bool>.Unspecified
: Optional<bool>.Unspecified,
NameLocalizations = args.NameLocalizations?.ToDictionary(),
DescriptionLocalizations = args.DescriptionLocalizations?.ToDictionary()
};

if (args is SlashCommandProperties slashProps)
@@ -285,7 +296,9 @@ namespace Discord.Rest
Type = arg.Type,
DefaultPermission = arg.IsDefaultPermission.IsSpecified
? arg.IsDefaultPermission.Value
: Optional<bool>.Unspecified
: Optional<bool>.Unspecified,
NameLocalizations = arg.NameLocalizations?.ToDictionary(),
DescriptionLocalizations = arg.DescriptionLocalizations?.ToDictionary()
};

if (arg is SlashCommandProperties slashProps)
@@ -318,7 +331,9 @@ namespace Discord.Rest
Name = arg.Name,
DefaultPermission = arg.IsDefaultPermission.IsSpecified
? arg.IsDefaultPermission.Value
: Optional<bool>.Unspecified
: Optional<bool>.Unspecified,
NameLocalizations = arg.NameLocalizations?.ToDictionary(),
DescriptionLocalizations = arg.DescriptionLocalizations?.ToDictionary()
};

if (arg is SlashCommandProperties slashProps)


Loading…
Cancel
Save