Skip to content
This repository was archived by the owner on Apr 20, 2023. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public class EnvironmentFilter
private IEnumerable<string> _environmentVariablesToKeep = new string []
{
"DOTNET_CLI_TELEMETRY_SESSIONID",
"DOTNET_CLI_UI_LANGUAGE",
"DOTNET_MULTILEVEL_LOOKUP",
"DOTNET_RUNTIME_ID",
"DOTNET_SKIP_FIRST_TIME_EXPERIENCE",
Expand Down
2 changes: 1 addition & 1 deletion src/Microsoft.DotNet.Cli.Utils/Product.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ namespace Microsoft.DotNet.Cli.Utils
{
public class Product
{
public static readonly string LongName = LocalizableStrings.DotNetCommandLineTools;
public static string LongName => LocalizableStrings.DotNetCommandLineTools;
public static readonly string Version = GetProductVersion();

private static string GetProductVersion()
Expand Down
2 changes: 2 additions & 0 deletions src/dotnet/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,8 @@ private static void InitializeProcess()
// by default, .NET Core doesn't have all code pages needed for Console apps.
// see the .NET Core Notes in https://msdn.microsoft.com/en-us/library/system.diagnostics.process(v=vs.110).aspx
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);

UILanguageOverride.Setup();

This comment was marked as spam.

This comment was marked as spam.

This comment was marked as spam.

}

internal static bool TryGetBuiltInCommand(string commandName, out BuiltInCommandMetadata builtInCommand)
Expand Down
81 changes: 81 additions & 0 deletions src/dotnet/UILanguageOverride.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Globalization;

namespace Microsoft.DotNet.Cli
{
internal static class UILanguageOverride
{
private const string DOTNET_CLI_UI_LANGUAGE = nameof(DOTNET_CLI_UI_LANGUAGE);
private const string VSLANG = nameof(VSLANG);
private const string PreferredUILang = nameof(PreferredUILang);

public static void Setup()
{
CultureInfo language = GetOverriddenUILanguage();
if (language != null)
{
ApplyOverrideToCurrentProcess(language);

This comment was marked as spam.

FlowOverrideToChildProcesses(language);
}
}

private static void ApplyOverrideToCurrentProcess(CultureInfo language)
{
CultureInfo.DefaultThreadCurrentUICulture = language;

This comment was marked as spam.

This comment was marked as spam.

}

private static void FlowOverrideToChildProcesses(CultureInfo language)
{
// Do not override any environment variables that are already set as we do not want to clobber a more granular setting with our global setting.
SetIfNotAlreadySet(DOTNET_CLI_UI_LANGUAGE, language.Name);
SetIfNotAlreadySet(VSLANG, language.LCID); // for tools following VS guidelines to just work in CLI
SetIfNotAlreadySet(PreferredUILang, language.Name); // for C#/VB targets that pass $(PreferredUILang) to compiler
}

private static CultureInfo GetOverriddenUILanguage()
{
// DOTNET_CLI_UI_LANGUAGE=<culture name> is the main way for users to customize the CLI's UI language.
string dotnetCliLanguage = Environment.GetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE);
if (dotnetCliLanguage != null)
{
try
{
return new CultureInfo(dotnetCliLanguage);
}
catch (CultureNotFoundException) { }

This comment was marked as spam.

This comment was marked as spam.

}

// VSLANG=<lcid> is set by VS and we respect that as well so that we will respect the VS
// language preference if we're invoked by VS.
string vsLang = Environment.GetEnvironmentVariable(VSLANG);
if (vsLang != null && int.TryParse(VSLANG, out int vsLcid))
{
try
{
return new CultureInfo(vsLcid);
}
catch (ArgumentOutOfRangeException) { }

This comment was marked as spam.

catch (CultureNotFoundException) { }
}

return null;
}

private static void SetIfNotAlreadySet(string environmentVariableName, string value)
{
string currentValue = Environment.GetEnvironmentVariable(environmentVariableName);
if (currentValue == null)
{
Environment.SetEnvironmentVariable(environmentVariableName, value);
}
}

private static void SetIfNotAlreadySet(string environmentVariableName, int value)
{
SetIfNotAlreadySet(environmentVariableName, value.ToString());
}
}
}
10 changes: 10 additions & 0 deletions test/Microsoft.DotNet.Tools.Tests.Utilities/TestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
Expand All @@ -23,6 +24,15 @@ public abstract class TestBase : IDisposable
private TempRoot _temp;
private static TestAssets s_testAssets;

static TestBase()
{
// set culture of test process to match CLI sub-processes when the UI language is overriden.
string overriddenUILanguage = Environment.GetEnvironmentVariable("DOTNET_CLI_UI_LANGUAGE");
if (overriddenUILanguage != null)
{
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo(overriddenUILanguage);
}
}

protected static string RepoRoot
{
Expand Down
12 changes: 9 additions & 3 deletions test/dotnet-new.Tests/NewCommandTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

namespace Microsoft.DotNet.New.Tests
{
public class NewCommandTests
public class NewCommandTests : TestBase
{
[Fact]
public void WhenSwitchIsSkippedThenItPrintsError()
Expand All @@ -16,7 +16,10 @@ public void WhenSwitchIsSkippedThenItPrintsError()

cmd.ExitCode.Should().NotBe(0);

cmd.StdErr.Should().StartWith("No templates matched the input template name: Web1.1.");
if (!DotnetUnderTest.IsLocalized())
{
cmd.StdErr.Should().StartWith("No templates matched the input template name: Web1.1.");
}
}

[Fact]
Expand All @@ -26,7 +29,10 @@ public void WhenTemplateNameIsNotUniquelyMatchedThenItIndicatesProblemToUser()

cmd.ExitCode.Should().NotBe(0);

cmd.StdErr.Should().StartWith("Unable to determine the desired template from the input template name: c.");
if (!DotnetUnderTest.IsLocalized())
{
cmd.StdErr.Should().StartWith("Unable to determine the desired template from the input template name: c.");
}
}
}
}
12 changes: 7 additions & 5 deletions test/dotnet-run.Tests/GivenDotnetRunRunsCsProj.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
using Microsoft.DotNet.Tools.Test.Utilities;
using Xunit;

using LocalizableStrings = Microsoft.DotNet.Tools.Run.LocalizableStrings;

namespace Microsoft.DotNet.Cli.Run.Tests
{
public class GivenDotnetRunBuildsCsproj : TestBase
Expand Down Expand Up @@ -280,7 +282,7 @@ public void ItGivesAnErrorWhenAttemptingToUseALaunchProfileThatDoesNotExistWhenT
.ExecuteWithCapturedOutput("--launch-profile test")
.Should().Pass()
.And.HaveStdOutContaining("Hello World!")
.And.HaveStdErrContaining("The specified launch profile could not be located.");
.And.HaveStdErrContaining(LocalizableStrings.RunCommandExceptionCouldNotLocateALaunchSettingsFile);
}

[Fact]
Expand Down Expand Up @@ -368,7 +370,7 @@ public void ItGivesAnErrorWhenTheLaunchProfileNotFound()
.ExecuteWithCapturedOutput("--launch-profile Third")
.Should().Pass()
.And.HaveStdOutContaining("(NO MESSAGE)")
.And.HaveStdErrContaining("The launch profile \"Third\" could not be applied.");
.And.HaveStdErrContaining(string.Format(LocalizableStrings.RunCommandExceptionCouldNotApplyLaunchSettings, "Third", "").Trim());
}

[Fact]
Expand Down Expand Up @@ -396,7 +398,7 @@ public void ItGivesAnErrorWhenTheLaunchProfileCanNotBeHandled()
.ExecuteWithCapturedOutput("--launch-profile \"IIS Express\"")
.Should().Pass()
.And.HaveStdOutContaining("(NO MESSAGE)")
.And.HaveStdErrContaining("The launch profile \"IIS Express\" could not be applied.");
.And.HaveStdErrContaining(string.Format(LocalizableStrings.RunCommandExceptionCouldNotApplyLaunchSettings, "IIS Express", "").Trim());
}

[Fact]
Expand Down Expand Up @@ -485,7 +487,7 @@ public void ItSkipsLaunchProfilesWhenThereIsNoUsableDefault()

cmd.Should().Pass()
.And.HaveStdOutContaining("(NO MESSAGE)")
.And.HaveStdErrContaining("The launch profile \"(Default)\" could not be applied.");
.And.HaveStdErrContaining(string.Format(LocalizableStrings.RunCommandExceptionCouldNotApplyLaunchSettings, LocalizableStrings.DefaultLaunchProfileDisplayName, "").Trim());
}

[Fact]
Expand Down Expand Up @@ -514,7 +516,7 @@ public void ItPrintsAnErrorWhenLaunchSettingsAreCorrupted()

cmd.Should().Pass()
.And.HaveStdOutContaining("(NO MESSAGE)")
.And.HaveStdErrContaining("The launch profile \"(Default)\" could not be applied.");
.And.HaveStdErrContaining(string.Format(LocalizableStrings.RunCommandExceptionCouldNotApplyLaunchSettings, LocalizableStrings.DefaultLaunchProfileDisplayName, "").Trim());
}
}
}
2 changes: 0 additions & 2 deletions test/dotnet.Tests/ParserTests/ValdidationMessageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ namespace Microsoft.DotNet.Tests.ParserTests
{
public class ValidationMessageTests
{
private readonly ITestOutputHelper output;

[Fact]
public void ValidationMessagesFormatCorrectly()
{
Expand Down