File: src\vstest\src\Microsoft.TestPlatform.Execution.Shared\UILanguageOverride.cs
Web Access
Project: src\src\vstest\src\Microsoft.TestPlatform.Extensions.BlameDataCollector\Microsoft.TestPlatform.Extensions.BlameDataCollector.csproj (Microsoft.TestPlatform.Extensions.BlameDataCollector)
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System;
using System.Globalization;

using Microsoft.VisualStudio.TestPlatform.CoreUtilities.Helpers;
using Microsoft.VisualStudio.TestPlatform.Utilities.Helpers.Interfaces;

namespace Microsoft.VisualStudio.TestPlatform.Execution;

// Borrowed from dotnet/sdk with some tweaks to allow testing
internal 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);
    private readonly IEnvironmentVariableHelper _environmentVariableHelper;
    private readonly Action<CultureInfo> _setDefaultThreadCurrentUICulture;

    public UiLanguageOverride()
        : this(new EnvironmentVariableHelper(), language => CultureInfo.DefaultThreadCurrentUICulture = language)
    { }

    public UiLanguageOverride(IEnvironmentVariableHelper environmentVariableHelper, Action<CultureInfo> setDefaultThreadCurrentUICulture)
    {
        _environmentVariableHelper = environmentVariableHelper;
        _setDefaultThreadCurrentUICulture = setDefaultThreadCurrentUICulture;
    }

    internal void SetCultureSpecifiedByUser()
    {
        var language = GetOverriddenUiLanguage(_environmentVariableHelper);
        if (language == null)
        {
            return;
        }

        ApplyOverrideToCurrentProcess(language, _setDefaultThreadCurrentUICulture);
        FlowOverrideToChildProcesses(language, _environmentVariableHelper);
    }

    private static void ApplyOverrideToCurrentProcess(CultureInfo language, Action<CultureInfo> setDefaultThreadCurrentUICulture)
    {
        setDefaultThreadCurrentUICulture(language);
    }

    private static CultureInfo? GetOverriddenUiLanguage(IEnvironmentVariableHelper environmentVariableHelper)
    {
        // DOTNET_CLI_UI_LANGUAGE=<culture name> is the main way for users to customize the CLI's UI language.
        string? dotnetCliLanguage = environmentVariableHelper.GetEnvironmentVariable(DOTNET_CLI_UI_LANGUAGE);
        if (dotnetCliLanguage != null)
        {
            try
            {
                return new CultureInfo(dotnetCliLanguage);
            }
            catch (CultureNotFoundException) { /* Culture not available on this system — fall back to default. */ }
        }

        // 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 = environmentVariableHelper.GetEnvironmentVariable(VSLANG);
        if (vsLang != null && int.TryParse(vsLang, out int vsLcid))
        {
            try
            {
                return new CultureInfo(vsLcid);
            }
            catch (ArgumentOutOfRangeException) { /* Invalid culture value — fall back to default. */ }
            catch (CultureNotFoundException) { /* Culture not available on this system — fall back to default. */ }
        }

        return null;
    }

    private static void FlowOverrideToChildProcesses(CultureInfo language, IEnvironmentVariableHelper environmentVariableHelper)
    {
        // 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, environmentVariableHelper);
        SetIfNotAlreadySet(VSLANG, language.LCID.ToString(CultureInfo.CurrentCulture), environmentVariableHelper); // for tools following VS guidelines to just work in CLI
        SetIfNotAlreadySet(PreferredUILang, language.Name, environmentVariableHelper); // for C#/VB targets that pass $(PreferredUILang) to compiler
    }

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