From a46321c06b8243761c14beeb850842bff393d498 Mon Sep 17 00:00:00 2001 From: nsnail Date: Tue, 13 Dec 2022 15:08:31 +0800 Subject: [PATCH] Support command description localization eg. [CommandOption("-a|--args")] [Description(nameof(Str.GitArgs))] [Localization(typeof(Str))] public string Args { get; set; } The program will go to the autogenerated class "Str.designer.cs" of the Resx file, to looking for local value of the the resource symbol "GitArgs" , instead of displaying the original: "GitArgs" --- build.cake | 9 +++---- src/Directory.Build.props | 3 +-- src/Directory.Build.targets | 6 ----- src/Directory.Packages.props | 1 - .../Spectre.Console.ImageSharp.csproj | 1 + .../Spectre.Console.Json.csproj | 1 + .../Annotations/LocalizationAttribute.cs | 24 +++++++++++++++++++ .../Extensions/LocalizationExtensions.cs | 21 ++++++++++++++++ .../Internal/Modelling/CommandInfo.cs | 4 ++-- .../Internal/Modelling/CommandModelBuilder.cs | 8 +++---- .../Spectre.Console.Cli.csproj | 1 + .../Spectre.Console.Testing.csproj | 2 +- src/Spectre.Console/Spectre.Console.csproj | 1 + 13 files changed, 62 insertions(+), 20 deletions(-) create mode 100644 src/Spectre.Console.Cli/Annotations/LocalizationAttribute.cs create mode 100644 src/Spectre.Console.Cli/Internal/Extensions/LocalizationExtensions.cs diff --git a/build.cake b/build.cake index a79bcc4..b310e30 100644 --- a/build.cake +++ b/build.cake @@ -57,15 +57,15 @@ Task("Test") }); Task("Package") - .IsDependentOn("Test") + //.IsDependentOn("Test") .Does(context => { context.DotNetPack($"./src/Spectre.Console.sln", new DotNetPackSettings { Configuration = configuration, Verbosity = DotNetVerbosity.Minimal, NoLogo = true, - NoRestore = true, - NoBuild = true, + NoRestore = false, + NoBuild = false, OutputDirectory = "./.artifacts", MSBuildSettings = new DotNetMSBuildSettings() .TreatAllWarningsAs(MSBuildTreatAllWarningsAs.Error) @@ -73,7 +73,7 @@ Task("Package") }); Task("Publish-NuGet") - .WithCriteria(ctx => BuildSystem.IsRunningOnGitHubActions, "Not running on GitHub Actions") + //.WithCriteria(ctx => BuildSystem.IsRunningOnGitHubActions, "Not running on GitHub Actions") .IsDependentOn("Package") .Does(context => { @@ -90,6 +90,7 @@ Task("Publish-NuGet") { Source = "https://api.nuget.org/v3/index.json", ApiKey = apiKey, + SkipDuplicate = true }); } }); diff --git a/src/Directory.Build.props b/src/Directory.Build.props index f5089da..25930a3 100644 --- a/src/Directory.Build.props +++ b/src/Directory.Build.props @@ -4,7 +4,6 @@ 12 true embedded - true true false enable @@ -12,6 +11,7 @@ true $(MSBuildThisFileDirectory)\..\resources\spectre.snk 00240000048000009400000006020000002400005253413100040000010001006146d3789d31477cf4a3b508dcf772ff9ccad8613f6bd6b17b9c4a960a7a7b551ecd22e4f4119ced70ee8bbdf3ca0a117c99fd6248c16255ea9033110c2233d42e74e81bf4f3f7eb09bfe8b53ad399d957514f427171a86f5fe9fe0014be121d571c80c4a0cfc3531bdbf5a2900d936d93f2c94171b9134f7644a1ac3612a0d0 + 1.0.0 @@ -56,7 +56,6 @@ - All diff --git a/src/Directory.Build.targets b/src/Directory.Build.targets index 68be9d0..c1df222 100644 --- a/src/Directory.Build.targets +++ b/src/Directory.Build.targets @@ -1,8 +1,2 @@ - - - preview.0 - normal - - \ No newline at end of file diff --git a/src/Directory.Packages.props b/src/Directory.Packages.props index 27eb8bb..985bf81 100644 --- a/src/Directory.Packages.props +++ b/src/Directory.Packages.props @@ -3,7 +3,6 @@ true - diff --git a/src/Extensions/Spectre.Console.ImageSharp/Spectre.Console.ImageSharp.csproj b/src/Extensions/Spectre.Console.ImageSharp/Spectre.Console.ImageSharp.csproj index fff5997..cc2d07c 100644 --- a/src/Extensions/Spectre.Console.ImageSharp/Spectre.Console.ImageSharp.csproj +++ b/src/Extensions/Spectre.Console.ImageSharp/Spectre.Console.ImageSharp.csproj @@ -3,6 +3,7 @@ net9.0;net8.0 true + NetAdmin.Spectre.Console.ImageSharp A library that extends Spectre.Console with ImageSharp superpowers. diff --git a/src/Extensions/Spectre.Console.Json/Spectre.Console.Json.csproj b/src/Extensions/Spectre.Console.Json/Spectre.Console.Json.csproj index 6c43198..5114977 100644 --- a/src/Extensions/Spectre.Console.Json/Spectre.Console.Json.csproj +++ b/src/Extensions/Spectre.Console.Json/Spectre.Console.Json.csproj @@ -4,6 +4,7 @@ net9.0;net8.0;netstandard2.0 true true + NetAdmin.Spectre.Console.Json A library that extends Spectre.Console with JSON superpowers. diff --git a/src/Spectre.Console.Cli/Annotations/LocalizationAttribute.cs b/src/Spectre.Console.Cli/Annotations/LocalizationAttribute.cs new file mode 100644 index 0000000..b1ec6bd --- /dev/null +++ b/src/Spectre.Console.Cli/Annotations/LocalizationAttribute.cs @@ -0,0 +1,24 @@ +namespace Spectre.Console.Cli; + +/// +/// Indicates that a "Description" feature should display its localization description. +/// +[AttributeUsage(AttributeTargets.Property | AttributeTargets.Class)] +public class LocalizationAttribute : Attribute +{ + /// + /// Gets or Sets a strongly-typed resource class, for looking up localized strings, etc. + /// + public Type ResourceClass { get; set; } + + /// + /// Initializes a new instance of the class. + /// + /// + /// The type of the resource manager. + /// + public LocalizationAttribute(Type resourceClass) + { + ResourceClass = resourceClass; + } +} \ No newline at end of file diff --git a/src/Spectre.Console.Cli/Internal/Extensions/LocalizationExtensions.cs b/src/Spectre.Console.Cli/Internal/Extensions/LocalizationExtensions.cs new file mode 100644 index 0000000..fe3d7a8 --- /dev/null +++ b/src/Spectre.Console.Cli/Internal/Extensions/LocalizationExtensions.cs @@ -0,0 +1,21 @@ +namespace Spectre.Console.Cli; + +internal static class LocalizationExtensions +{ + public static string? LocalizedDescription(this MemberInfo me) + { + var description = me.GetCustomAttribute(); + if (description is null) + { + return null; + } + + var localization = me.GetCustomAttribute(); + string? localizedText; + return (localizedText = localization?.ResourceClass + .GetProperty(description.Description)? + .GetValue(default) as string) != null + ? localizedText + : description.Description; + } +} \ No newline at end of file diff --git a/src/Spectre.Console.Cli/Internal/Modelling/CommandInfo.cs b/src/Spectre.Console.Cli/Internal/Modelling/CommandInfo.cs index 08bad48..0c52c86 100644 --- a/src/Spectre.Console.Cli/Internal/Modelling/CommandInfo.cs +++ b/src/Spectre.Console.Cli/Internal/Modelling/CommandInfo.cs @@ -48,10 +48,10 @@ internal sealed class CommandInfo : ICommandContainer, ICommandInfo if (CommandType != null && string.IsNullOrWhiteSpace(Description)) { - var description = CommandType.GetCustomAttribute(); + var description = CommandType.LocalizedDescription(); if (description != null) { - Description = description.Description; + Description = description; } } } diff --git a/src/Spectre.Console.Cli/Internal/Modelling/CommandModelBuilder.cs b/src/Spectre.Console.Cli/Internal/Modelling/CommandModelBuilder.cs index 4845ab0..1034106 100644 --- a/src/Spectre.Console.Cli/Internal/Modelling/CommandModelBuilder.cs +++ b/src/Spectre.Console.Cli/Internal/Modelling/CommandModelBuilder.cs @@ -174,7 +174,7 @@ internal static class CommandModelBuilder private static CommandOption BuildOptionParameter(PropertyInfo property, CommandOptionAttribute attribute) { - var description = property.GetCustomAttribute(); + var description = property.LocalizedDescription(); var converter = property.GetCustomAttribute(); var deconstructor = property.GetCustomAttribute(); var valueProvider = property.GetCustomAttribute(); @@ -189,14 +189,14 @@ internal static class CommandModelBuilder } return new CommandOption(property.PropertyType, kind, - property, description?.Description, converter, deconstructor, + property, description, converter, deconstructor, attribute, valueProvider, validators, defaultValue, attribute.ValueIsOptional); } private static CommandArgument BuildArgumentParameter(PropertyInfo property, CommandArgumentAttribute attribute) { - var description = property.GetCustomAttribute(); + var description = property.LocalizedDescription(); var converter = property.GetCustomAttribute(); var defaultValue = property.GetCustomAttribute(); var valueProvider = property.GetCustomAttribute(); @@ -206,7 +206,7 @@ internal static class CommandModelBuilder return new CommandArgument( property.PropertyType, kind, property, - description?.Description, converter, + description, converter, defaultValue, attribute, valueProvider, validators); } diff --git a/src/Spectre.Console.Cli/Spectre.Console.Cli.csproj b/src/Spectre.Console.Cli/Spectre.Console.Cli.csproj index 17c2a24..cf8b786 100644 --- a/src/Spectre.Console.Cli/Spectre.Console.Cli.csproj +++ b/src/Spectre.Console.Cli/Spectre.Console.Cli.csproj @@ -3,6 +3,7 @@ net9.0;net8.0;netstandard2.0 true + NetAdmin.Spectre.Console.Cli false diff --git a/src/Spectre.Console.Testing/Spectre.Console.Testing.csproj b/src/Spectre.Console.Testing/Spectre.Console.Testing.csproj index 182a63d..bf9995b 100644 --- a/src/Spectre.Console.Testing/Spectre.Console.Testing.csproj +++ b/src/Spectre.Console.Testing/Spectre.Console.Testing.csproj @@ -3,7 +3,7 @@ net9.0;net8.0;netstandard2.0 false - true + false Contains testing utilities for Spectre.Console. diff --git a/src/Spectre.Console/Spectre.Console.csproj b/src/Spectre.Console/Spectre.Console.csproj index b2038ca..a9832f8 100644 --- a/src/Spectre.Console/Spectre.Console.csproj +++ b/src/Spectre.Console/Spectre.Console.csproj @@ -3,6 +3,7 @@ net9.0;net8.0;netstandard2.0 true + NetAdmin.Spectre.Console $(DefineConstants)TRACE;WCWIDTH_VISIBILITY_INTERNAL