Note: Perhaps this is by design (although there's at least a buggy inconsistency), given that there's currently no _PowerShell_ mechanism for _in-session_ switching to another culture. If so, it should at least be documented.
[pscustomobject] @{ Test = 'initial'; Name = (Get-Culture).Name}
[cultureinfo]::currentculture = 'fr-FR'; [pscustomobject] @{ Test = '$PSCulture'; Name =$PSCulture }, [pscustomobject] @{ Test = 'GetCulture'; Name = (Get-Culture).Name }, [pscustomobject] @{ Test = '$host.CurrentCulture'; Name = $host.CurrentCulture.Name }, [pscustomobject] @{ Test = '.NET'; Name = [cultureinfo]::currentculture.name }
Note:
en-US
is the initial culture at session startup time.Get-Culture
and $host.CurrentCulture
reflect the switch, but $PSCulture
doesn't, whereas in Windows PowerShell none of the PS cmdlets/properties do.Test Name
---- ----
initial en-US
$PSCulture fr-FR
GetCulture fr-FR
$host.CurrentCulture fr-FR
.NET fr-FR
Test Name
---- ----
initial en-US
$PSCulture en-US
GetCulture fr-FR
$host.CurrentCulture fr-FR
.NET fr-FR
Test Name
---- ----
initial en-US
$PSCulture en-US
GetCulture en-US
$host.CurrentCulture en-US
.NET fr-FR
PowerShell Core v6.0.0-beta (v6.0.0-beta.1) on macOS 10.12.5
PowerShell Core v6.0.0-beta (v6.0.0-beta.1) on Ubuntu 16.04.1 LTS
PowerShell Core v6.0.0-beta (v6.0.0-beta.1) on Microsoft Windows 10 Pro (64-bit; v10.0.14393)
Windows PowerShell v5.1.14393.1198 on Microsoft Windows 10 Pro (64-bit; v10.0.14393)
PowerShell does not follow the changes in .Net. This is true for changing current directory too.
Well, as you can see, in Core it _does_ reflect .NET-level changes in Get-Culture
, but not in $PSCulture
.
Also, with respect to the current directory: there's justification for PowerShell "doing its own thing", because it has its own system of drives, the majority of which have no .NET counterpart.
The same justification doesn't apply to culture settings, which are shared by both worlds, especially given that cmdlets _do_ already respect in-session switching; e.g.:
> [cultureinfo]::currentculture = 'fr-FR'; Get-Date
samedi 20 mai 2017 13:39:08
That means we have to make sure PowerShell "not doing its own things" for Culture.
@iSazonov - yes, and that condition appears to be met (I haven't dug into the source). Do you know something to the contrary? Or are you just saying that an investigation / official feedback is needed?
@mklement0 Please see Get-Culture code.
Looking at the source code, it looks like Get-Culture
simply delegates to $host.CurrentCulture
, which is consistent with the (updated) findings above.
In the case of a console host, this seems to lead to ConsoleHost.cs
, which has the following:
public override System.Globalization.CultureInfo CurrentCulture
{
get
{
lock (hostGlobalLock)
{
#if !CORECLR
return NativeCultureResolver.Culture;
#else
return CultureInfo.CurrentCulture;
#endif
}
}
}
That explains why _Core's_ Get-Culture
/ $host.CurrentCulture
is always in sync with [cultureinfo]::CurrentCulture
.
For _Windows_ PowerShell, the value is _cached_ in NativeCultureResolver.cs
, which explains why it can get out of sync with the thread's changed-after-session-startup culture.
So we can explain the disparity, but the question is _what the design intent was_ and, therefore, _how the inconsistency should be resolved_ (my vote is to always reflect the thread's then-current culture).
I agree that a consistency should be. After quickly view codes, it seems PowerShell use Cultureinfo.CurrentCulture without caching.
@mklement0 Could you please update a status of the issue? Should we consider a fix for $PSCulture?
@iSazonov:
The status hasn't changed. Yes, I think we should fix $PSCulture
to reflect the culture in effect for the thread (session) at hand, to be consistent with Get-Culture
and $host.CurrentCulture
.
It is a departure from Windows PowerShell, however, so it needs to be documented.
(Note that backward compatibility is _already_ broken, due to Get-Culture
and $host.CurrentCulture
acting differently than in Windows PowerShell).
@SteveL-MSFT We need PowerShell Committee conclusion.
See also: the related discussion in https://github.com/PowerShell/PowerShell/issues/3833#issuecomment-431837446
@iSazonov it would help @PowerShell/powershell-committee if you or @mklement0 could summarize the question you want @PowerShell/powershell-committee to answer
@SteveL-MSFT:
In Windows PowerShell:
Get-Culture
reflects _the current user's persistently defined culture_, irrespective of (transient) in-session changes.$PSCulture
and $host.CurrentCulture
reflect the same value.In PowerShell Core:
Get-Culture
always reflects the _current thread's_ culture, which may or may not have been changed in-session.
$host.CurrentCulture
reflects the same value, whereas $PSCulture
reflects the culture that was in effect at session-startup time.
The suggestion is to correct this inconsistency and have $PSCulture
reflect the same value as Get-Culture
and $host.CurrentCulture
.
As an aside: The above shows that we've already broken backward compatibility, though it won't typically surface, as changing the culture in-session is not common.
When it comes to introducing Set-Culture
to PS Core, the difference becomes more problematic - see https://github.com/PowerShell/PowerShell/issues/3833#issuecomment-431837446.
$PSCulture
and $PSUICulture
are implemented as specific subclasses of PSVariable
. For example $PSCulture
is defined in CultureVariable.cs and looks like:
/// <summary>
/// A variable that represents $PSCulture
/// </summary>
internal class PSCultureVariable : PSVariable
{
/// <summary>
/// Constructs an instance of the variable.
/// </summary>
internal PSCultureVariable()
: base(SpecialVariables.PSCulture, true, ScopedItemOptions.ReadOnly | ScopedItemOptions.AllScope,
RunspaceInit.DollarPSCultureDescription)
{
}
/// <summary>
/// Gets or sets the value of the variable.
/// </summary>
///
public override object Value
{
get
{
DebuggerCheckVariableRead();
return System.Globalization.CultureInfo.CurrentCulture.Name;
}
}
}
As you can see, it's not a cached value and should reflect the current culture.
@BrucePay: That can't be the whole story, as the following test demonstrates:
# With en-US in effect *at session startup*:
PS> [cultureinfo]::CurrentCulture = 'de-dE'; [cultureinfo]::CurrentCulture.Name; $PSCulture
de-DE
en-US # !! session-startup value is still in effect
I suspect there's a bug where the special binding for $PSCulture
is being replaced by a regular PSVariable
. I'm still investigating to what's going on.
And yes there is a bug right in the initialization code. In SessionStateScope.cs:SetVariable(), the code takes the carefully constructed custom-type object, extracts the basic information, creates a new vanilla PSVariable and inserts that into session state instead of using the custom variable. I would guess this has been broken for forever. Fixing it to use the original variable is technically a breaking change but it's probably worth it.
@PowerShell/powershell-committee reviewed this. We agree the intent is that Get-Culture
and $PSCulture
have the same behavior and reflects the current thread culture. This looks like a long standing bug.
:tada:This issue was addressed in #10138, which has now been successfully released as v7.0.0-preview.5
.:tada:
Handy links:
Most helpful comment
And yes there is a bug right in the initialization code. In SessionStateScope.cs:SetVariable(), the code takes the carefully constructed custom-type object, extracts the basic information, creates a new vanilla PSVariable and inserts that into session state instead of using the custom variable. I would guess this has been broken for forever. Fixing it to use the original variable is technically a breaking change but it's probably worth it.