Skip to content

Commit

Permalink
Localizing remaining string resources (dotnet#1257)
Browse files Browse the repository at this point in the history
* Renamed ValidationMessages

* WIP more strings localized

* Localizing string resources
  • Loading branch information
Keboo authored Apr 18, 2021
1 parent 3705094 commit 02c7303
Show file tree
Hide file tree
Showing 30 changed files with 1,236 additions and 85 deletions.
18 changes: 9 additions & 9 deletions src/System.CommandLine.Tests/Invocation/TypoCorrectionTests.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
using System.CommandLine.Builder;
using System.CommandLine.Invocation;
using FluentAssertions;
using System.CommandLine.Builder;
using System.CommandLine.IO;
using System.CommandLine.Parsing;
using System.Threading.Tasks;
using FluentAssertions;
using Xunit;
using static System.Environment;

namespace System.CommandLine.Tests.Invocation
{
Expand All @@ -27,7 +27,7 @@ public async Task When_option_is_mistyped_it_is_suggested()

await result.InvokeAsync(_console);

_console.Out.ToString().Should().Contain("'niof' was not matched. Did you mean 'info'?");
_console.Out.ToString().Should().Contain($"'niof' was not matched. Did you mean one of the following?{NewLine}info");
}

[Fact]
Expand Down Expand Up @@ -63,7 +63,7 @@ public async Task When_command_is_mistyped_it_is_suggested()

await result.InvokeAsync(_console);

_console.Out.ToString().Should().Contain("'sertor' was not matched. Did you mean 'restore'?");
_console.Out.ToString().Should().Contain($"'sertor' was not matched. Did you mean one of the following?{NewLine}restore");
}

[Fact]
Expand All @@ -82,7 +82,7 @@ public async Task When_there_are_multiple_matches_it_picks_the_best_matches()

await result.InvokeAsync(_console);

_console.Out.ToString().Should().Contain("'een' was not matched. Did you mean 'seen', or 'been'?");
_console.Out.ToString().Should().Contain($"'een' was not matched. Did you mean one of the following?{NewLine}seen{NewLine}been");
}

[Fact]
Expand All @@ -100,7 +100,7 @@ public async Task Hidden_commands_are_not_suggested()

await result.InvokeAsync(_console);

_console.Out.ToString().Should().Contain("'een' was not matched. Did you mean 'been'?");
_console.Out.ToString().Should().Contain($"'een' was not matched. Did you mean one of the following?{NewLine}been");
}

[Fact]
Expand Down Expand Up @@ -134,7 +134,7 @@ public async Task Hidden_options_are_not_suggested()

await result.InvokeAsync(_console);

_console.Out.ToString().Should().Contain("'een' was not matched. Did you mean 'been'?");
_console.Out.ToString().Should().Contain($"'een' was not matched. Did you mean one of the following?{NewLine}been");
}

[Fact]
Expand All @@ -150,7 +150,7 @@ public async Task Suggestions_favor_matches_with_prefix()

await result.InvokeAsync(_console);

_console.Out.ToString().Should().Contain("'-all' was not matched. Did you mean '-call'?");
_console.Out.ToString().Should().Contain($"'-all' was not matched. Did you mean one of the following?{NewLine}-call");
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@

namespace System.CommandLine.Tests
{
public class ValidationMessageLocalizationTests
public class ResourceLocalizationTests
{
[Fact]
public void Default_validation_messages_can_be_replaced_in_order_to_add_localization_support()
{
var messages = new FakeValidationMessages("the-message");
var messages = new FakeResources("the-message");

var command = new Command("the-command")
{
Expand All @@ -24,7 +24,7 @@ public void Default_validation_messages_can_be_replaced_in_order_to_add_localiza
Arity = ArgumentArity.ExactlyOne
}
};
var parser = new Parser(new CommandLineConfiguration(new[] { command }, validationMessages: messages));
var parser = new Parser(new CommandLineConfiguration(new[] { command }, resources: messages));
var result = parser.Parse("the-command");

result.Errors
Expand All @@ -36,7 +36,7 @@ public void Default_validation_messages_can_be_replaced_in_order_to_add_localiza
[Fact]
public void Default_validation_messages_can_be_replaced_using_CommandLineBuilder_in_order_to_add_localization_support()
{
var messages = new FakeValidationMessages("the-message");
var messages = new FakeResources("the-message");

var parser = new CommandLineBuilder(new Command("the-command")
{
Expand All @@ -45,7 +45,7 @@ public void Default_validation_messages_can_be_replaced_using_CommandLineBuilder
Arity = ArgumentArity.ExactlyOne
}
})
.UseValidationMessages(messages)
.UseResources(messages)
.Build();

var result = parser.Parse("the-command");
Expand All @@ -56,11 +56,11 @@ public void Default_validation_messages_can_be_replaced_using_CommandLineBuilder
.Contain("the-message");
}

public class FakeValidationMessages : Resources
public class FakeResources : Resources
{
private readonly string message;

public FakeValidationMessages(string message)
public FakeResources(string message)
{
this.message = message;
}
Expand Down
4 changes: 2 additions & 2 deletions src/System.CommandLine/ArgumentArity.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,14 +71,14 @@ public ArgumentArity(int minimumNumberOfValues, int maximumNumberOfValues)

return new MissingArgumentConversionResult(
argument,
symbolResult.ValidationMessages.RequiredArgumentMissing(symbolResult));
symbolResult.Resources.RequiredArgumentMissing(symbolResult));
}

if (tokenCount > maximumNumberOfValues)
{
return new TooManyArgumentsConversionResult(
argument,
symbolResult!.ValidationMessages.ExpectsOneArgument(symbolResult));
symbolResult!.Resources.ExpectsOneArgument(symbolResult));
}

return null;
Expand Down
16 changes: 8 additions & 8 deletions src/System.CommandLine/ArgumentExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public static Argument<FileInfo> ExistingOnly(this Argument<FileInfo> argument)
symbol.Tokens
.Select(t => t.Value)
.Where(filePath => !File.Exists(filePath))
.Select(symbol.ValidationMessages.FileDoesNotExist)
.Select(symbol.Resources.FileDoesNotExist)
.FirstOrDefault());
return argument;
}
Expand All @@ -59,7 +59,7 @@ public static Argument<DirectoryInfo> ExistingOnly(this Argument<DirectoryInfo>
symbol.Tokens
.Select(t => t.Value)
.Where(filePath => !Directory.Exists(filePath))
.Select(symbol.ValidationMessages.DirectoryDoesNotExist)
.Select(symbol.Resources.DirectoryDoesNotExist)
.FirstOrDefault());
return argument;
}
Expand All @@ -70,7 +70,7 @@ public static Argument<FileSystemInfo> ExistingOnly(this Argument<FileSystemInfo
symbol.Tokens
.Select(t => t.Value)
.Where(filePath => !Directory.Exists(filePath) && !File.Exists(filePath))
.Select(symbol.ValidationMessages.FileOrDirectoryDoesNotExist)
.Select(symbol.Resources.FileOrDirectoryDoesNotExist)
.FirstOrDefault());
return argument;
}
Expand All @@ -84,7 +84,7 @@ public static Argument<T> ExistingOnly<T>(this Argument<T> argument)
a => a.Tokens
.Select(t => t.Value)
.Where(filePath => !File.Exists(filePath))
.Select(a.ValidationMessages.FileDoesNotExist)
.Select(a.Resources.FileDoesNotExist)
.FirstOrDefault());
}
else if (typeof(IEnumerable<DirectoryInfo>).IsAssignableFrom(typeof(T)))
Expand All @@ -93,7 +93,7 @@ public static Argument<T> ExistingOnly<T>(this Argument<T> argument)
a => a.Tokens
.Select(t => t.Value)
.Where(filePath => !Directory.Exists(filePath))
.Select(a.ValidationMessages.DirectoryDoesNotExist)
.Select(a.Resources.DirectoryDoesNotExist)
.FirstOrDefault());
}
else
Expand All @@ -102,7 +102,7 @@ public static Argument<T> ExistingOnly<T>(this Argument<T> argument)
a => a.Tokens
.Select(t => t.Value)
.Where(filePath => !Directory.Exists(filePath) && !File.Exists(filePath))
.Select(a.ValidationMessages.FileOrDirectoryDoesNotExist)
.Select(a.Resources.FileOrDirectoryDoesNotExist)
.FirstOrDefault());
}

Expand All @@ -127,7 +127,7 @@ public static TArgument LegalFilePathsOnly<TArgument>(
if (invalidCharactersIndex >= 0)
{
return symbol.ValidationMessages.InvalidCharactersInPath(token.Value[invalidCharactersIndex]);
return symbol.Resources.InvalidCharactersInPath(token.Value[invalidCharactersIndex]);
}
}
Expand All @@ -152,7 +152,7 @@ public static TArgument LegalFileNamesOnly<TArgument>(
if (invalidCharactersIndex >= 0)
{
return symbol.ValidationMessages.InvalidCharactersInFileName(token.Value[invalidCharactersIndex]);
return symbol.Resources.InvalidCharactersInFileName(token.Value[invalidCharactersIndex]);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,23 +23,19 @@ private static string FormatErrorMessage(
if (argument is Argument a &&
a.Parents.Count == 1)
{
// TODO: (FailedArgumentTypeConversionResult) localize

var firstParent = (IIdentifierSymbol) a.Parents[0];

var symbolType =
firstParent switch {
ICommand _ => "command",
IOption _ => "option",
_ => null
};

var alias = firstParent.Aliases.First();

return $"Cannot parse argument '{value}' for {symbolType} '{alias}' as expected type {expectedType}.";
switch(firstParent)
{
case ICommand _:
return Resources.Instance.ArgumentConversionCannotParseForCommand(value, alias, expectedType);
case IOption _:
return Resources.Instance.ArgumentConversionCannotParseForOption(value, alias, expectedType);
}
}

return $"Cannot parse argument '{value}' as expected type {expectedType}.";
return Resources.Instance.ArgumentConversionCannotParse(value, expectedType);
}
}
}
4 changes: 2 additions & 2 deletions src/System.CommandLine/Builder/CommandLineBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public CommandLineBuilder(Command? rootCommand = null)

internal HelpOption? HelpOption { get; set; }

internal Resources? ValidationMessages { get; set; }
internal Resources? Resources { get; set; }

public Parser Build()
{
Expand All @@ -41,7 +41,7 @@ public Parser Build()
new[] { rootCommand },
enablePosixBundling: EnablePosixBundling,
enableDirectives: EnableDirectives,
validationMessages: ValidationMessages,
resources: Resources,
responseFileHandling: ResponseFileHandling,
middlewarePipeline: _middlewareList.OrderBy(m => m.order)
.Select(m => m.middleware)
Expand Down
24 changes: 10 additions & 14 deletions src/System.CommandLine/Builder/CommandLineBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,11 +189,11 @@ await feature.EnsureRegistered(async () =>
await dotnetSuggestProcess.CompleteAsync();
return $"{dotnetSuggestProcess.StartInfo.FileName} exited with code {dotnetSuggestProcess.ExitCode}{NewLine}OUT:{NewLine}{stdOut}{NewLine}ERR:{NewLine}{stdErr}";
return Resources.Instance.DotnetSuggestExitMessage(dotnetSuggestProcess.StartInfo.FileName, dotnetSuggestProcess.ExitCode, stdOut.ToString(), stdErr.ToString());
}
catch (Exception exception)
{
return $"Exception during registration:{NewLine}{exception}";
return Resources.Instance.DotnetSuggestExceptionOccurred(exception);
}
finally
{
Expand Down Expand Up @@ -222,9 +222,7 @@ public static CommandLineBuilder UseDebugDirective(
string debuggableProcessNames = GetEnvironmentVariable(environmentVariableName);
if (string.IsNullOrWhiteSpace(debuggableProcessNames))
{
context.Console.Error.WriteLine("Debug directive specified, but no process names are listed as allowed for debug.");
context.Console.Error.WriteLine($"Add your process name to the '{environmentVariableName}' environment variable.");
context.Console.Error.WriteLine($"The value of the variable should be the name of the processes, separated by a semi-colon ';', for example '{environmentVariableName}={process.ProcessName}'");
context.Console.Error.WriteLine(Resources.Instance.DebugDirectiveExecutableNotSpecified(environmentVariableName, process.ProcessName));
context.ExitCode = errorExitCode ?? 1;
return;
}
Expand All @@ -234,17 +232,15 @@ public static CommandLineBuilder UseDebugDirective(
if (processNames.Contains(process.ProcessName, StringComparer.Ordinal))
{
var processId = process.Id;
context.Console.Out.WriteLine($"Attach your debugger to process {processId} ({process.ProcessName}).");
var startTime = DateTime.Now;
context.Console.Out.WriteLine(Resources.Instance.DebugDirectiveAttachToProcess(processId, process.ProcessName));
while (!Debugger.IsAttached)
{
await Task.Delay(500);
}
}
else
{
context.Console.Error.WriteLine($"Process name '{process.ProcessName}' is not included in the list of debuggable process names in the {environmentVariableName} environment variable ('{debuggableProcessNames}')");
context.Console.Error.WriteLine(Resources.Instance.DebugDirectiveProcessNotIncludedInEnvironmentVariable(process.ProcessName, environmentVariableName, debuggableProcessNames));
context.ExitCode = errorExitCode ?? 1;
return;
}
Expand Down Expand Up @@ -325,7 +321,7 @@ void Default(Exception exception, InvocationContext context)
context.Console.ResetTerminalForegroundColor();
context.Console.SetTerminalForegroundRed();

context.Console.Error.Write("Unhandled exception: ");
context.Console.Error.Write(Resources.Instance.ExceptionHandlerHeader());
context.Console.Error.WriteLine(exception.ToString());

context.Console.ResetTerminalForegroundColor();
Expand Down Expand Up @@ -506,11 +502,11 @@ public static CommandLineBuilder UseTypoCorrections(
return builder;
}

public static CommandLineBuilder UseValidationMessages(
public static CommandLineBuilder UseResources(
this CommandLineBuilder builder,
Resources validationMessages)
{
builder.ValidationMessages = validationMessages;
builder.Resources = validationMessages;
return builder;
}

Expand All @@ -527,12 +523,12 @@ public static CommandLineBuilder UseVersionOption(

var versionOption = new Option<bool>(
"--version",
description: "Show version information",
description: Resources.Instance.VersionOptionDescription(),
parseArgument: result =>
{
if (result.FindResultFor(command)?.Children.Count > 1)
{
result.ErrorMessage = "--version option cannot be combined with other arguments.";
result.ErrorMessage = Resources.Instance.VersionOptionCannotBeCombinedWithOtherArguments("--version");
return false;
}
Expand Down
10 changes: 5 additions & 5 deletions src/System.CommandLine/CommandLineConfiguration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ public class CommandLineConfiguration
/// <param name="symbols">The symbols to parse.</param>
/// <param name="enablePosixBundling"><c>true</c> to enable POSIX bundling; otherwise, <c>false</c>.</param>
/// <param name="enableDirectives"><c>true</c> to enable directive parsing; otherwise, <c>false</c>.</param>
/// <param name="validationMessages">Provide custom validation messages.</param>
/// <param name="resources">Provide custom validation messages.</param>
/// <param name="responseFileHandling">One of the enumeration values that specifies how response files (.rsp) are handled.</param>
/// <param name="middlewarePipeline">Provide a custom middleware pipeline.</param>
/// <param name="helpBuilderFactory">Provide a custom help builder.</param>
Expand All @@ -35,7 +35,7 @@ public CommandLineConfiguration(
IReadOnlyList<Symbol> symbols,
bool enablePosixBundling = true,
bool enableDirectives = true,
Resources? validationMessages = null,
Resources? resources = null,
ResponseFileHandling responseFileHandling = ResponseFileHandling.ParseArgsAsLineSeparated,
IReadOnlyCollection<InvocationMiddleware>? middlewarePipeline = null,
Func<BindingContext, IHelpBuilder>? helpBuilderFactory = null,
Expand Down Expand Up @@ -83,7 +83,7 @@ public CommandLineConfiguration(

EnablePosixBundling = enablePosixBundling;
EnableDirectives = enableDirectives;
ValidationMessages = validationMessages ?? Resources.Instance;
Resources = resources ?? Resources.Instance;
ResponseFileHandling = responseFileHandling;
Middleware = middlewarePipeline ?? new List<InvocationMiddleware>();
HelpBuilderFactory = helpBuilderFactory ?? (context =>
Expand Down Expand Up @@ -146,9 +146,9 @@ private void AddGlobalOptionsToChildren(Command parentCommand)
public bool EnablePosixBundling { get; }

/// <summary>
/// Gets the validation messages.
/// Gets the localizable resources.
/// </summary>
public Resources ValidationMessages { get; }
public Resources Resources { get; }

internal Func<BindingContext, IHelpBuilder> HelpBuilderFactory { get; }

Expand Down
Loading

0 comments on commit 02c7303

Please sign in to comment.