Note: As of v6-beta.1, Set-Culture
isn't part of PowerShell Core yet, but I'm assuming it will be at some point (if technically feasible).
Set-Culture
currently _only_ changes the current user account's culture _persistently_, _for future sessions_.
Especially given that Get-Culture
already reflects _in-session changes_ of the culture (though it's not entirely clear at this point whether that's the intent - see #3831 ), it would be helpful to additionally support _also_ / _only_ setting the _current session's_ culture.
Note that even without changes, the current help topic deserves updating to make it clear that the _current_ session is not affected by the call.
Perhaps an optional -Scope
parameter that supports the following values could be introduced:
CurrentUser
... (persistently) set culture for the current user account for future sessions onlySession
... (transiently) set culture for the current session onlyCurrentUserAndSession
... (persistently) current user account + current session.For backward compatibility, CurrentUser
would have to be the default.
PowerShell Core v6.0.0-beta (v6.0.0-beta.1)
@SteveL-MSFT Have we plan to implement Set-Culture?
It seems we can change a culture for current thread only.
@iSazonov no immediate plans
We could use DefaultThreadCurrentCulture.
The problem is that we have some appdomains:
@SteveL-MSFT @daxian-dbw @lzybkr Could you please comment? Is it possible to enumerate appdomains and set a culture?
@iSazonov: CultureInfo.DefaultThreadCurrentCulture
is a _per-application_ default, which I don't think is appropriate for our needs.
Looking at this again months later:
Presumably, we don't want to get into supporting _persistent_ locale (culture) changes on _Unix-like platforms_, because there are no easy, standardized solutions.[1]
As far as I'm aware, there's no support in .NET Core for this, just as there is no support for persisting environment variables in general ([environment]::SetEnvironmentVariable()
doesn't support the User
and Machine
targets on Unix, and, similarly, our own RFC for enhanced environment-variable support only targets persistence support on Windows).
This means that Set-Culture
would only support -Scope Session
on Unix and would therefore have to be the default there - which deviates from the default behavior (implied -Scope CurrentUser
) in Windows PowerShell.
That said, the same deviation already exists in the extant Get-Culture
cmdlet, which reports the _current-thread_ culture (only and invariably) in PowerShell Core on _all_ platforms vs. the _persisted user culture_ (only and invariably) in Windows PowerShell (irrespective of the thread's current culture).
In short: I think what is called for is:
Make Set-Culture
default to -Scope Session
on _all_ platforms, and support only Session
on Unix-like platforms.
[cultureinfo]::CurrentCulture = ...
On _Windows_, also support -Scope CurrentUser
(current Windows PowerShell behavior) and -Scope CurrentUserAndSession
for updating the user's default culture for future sessions and optionally also the current one.
[1] Some pointers: On macOS, defaults write -g AppleLocale <local-id>
updates the _current user's_ locale persistently. On Debian-like Linux distros such as Ubuntu it is file /etc/default/locale
that stores the default locale, but _system-wide_. I'm unclear on how it works in other Linux distros. However, _user-level_ defaults on Linux would generally require modifying user-level initialization files, which are _shell-specific_.
Taking a step back:
In Windows PowerShell:
Get-Culture
is built on the assumption that the _culture won't change in-session_, as it only ever reflects the persisted user-level setting.
[cultureinfo]::CurrentCulture = ...
) do they see a discrepancy between the (thread's) _current_ culture and the value reported by Get-Culture
(and $PSCulture
and $host.CurrentCulture
).Similarly, Set-Culture
only updates the _persisted_ setting without changing the in-session culture.
In PowerShell Core:
Get-Culture
($host.CurrentCulture
and $PSCulture
presumably soon (#3831) ) always reflects the thread's _current_ culture, whether it was changed after session startup or not.
Set-Culture
doesn't exist yet.
The Windows PowerShell *-Culture
cmdlets were seemingly built with _just_ reflecting / updating the _persisted_ culture setting in mind, irrespective of in-session changes.
In PowerShell Core we don't have support for that:
Get-Culture
already always reflects the _current_ culture - whether it is the persisted setting or was changed in-session
If we were to implement Set-Culture
to default to the _session only_, it would result in _fundamentally different_ behavior than in Windows PowerShell, which is maybe too problematic.
So, if consistency with Windows PowerShell's Set-Culture
is paramount, the choices are:
Continue to hold off on implementing Set-Culture
in PS Core.
Get-Culture
and Set-Culture
help topics to show how to change the current culture in-session using the .NET framework.Implement it (as suggested in the initial post), but with the same default behavior as in Windows PowerShell, which means:
On Unix-like platforms, the only command form that will work is Set-Culture -Scope Session ...
, and neglecting to specify -Scope Session
will have to result in a not-implemented (statement-terminating) error.
On Windows, the default behavior is as in Windows PowerShell (-Scope CurrentUser
implied), with the (not-extant in WinPS) options to _also_ change the in-session culture (-Scope CurrentUserAndSession
) or _just_ the in-session culture (-Scope Session
).
Note that the price for this nod to consistency with Windows PowerShell would be an asymmetry between Get-Culture
and Set-Culture
in PS Core:
In terms of the proposed Set-Culture
implementation, Get-Culture
's default behavior is conceptually -Scope Session
(and again we'd lack the ability to implement -Scope CurrentUser
on Unix), whereas Set-Culture
's would be -Scope CurrentUser
.
@mklement0 Thanks! We could start with -Scope Session
.
Yes, @iSazonov, but we need to decide on whether:
-Scope Session
should be the _default_ - which would make it consistent with PS Core Get-Culture
, but incompatible with Windows PowerShell's Set-Culture
.
-Scope CurrentUser
should be the default, to align (conceptually) with Windows PowerShell, which, however, makes it asymmetrical with PS Core Get-Culture
and requires explicit use of -Scope Session
for in-session changes (which would be the only scope supported on Unix, at least for now; in other words: something like Set-Culture de-DE
would _fail_ without -Scope Session
).
@SteveL-MSFT Could PowerShell Committee consider the cmdlet and _defaults_ options (keep backward compatibility with Windows PowerShell or consistency with Get-Culture)?
@PowerShell/powershell-committee discussed this and would accept setting culture for PowerShell at system, user scopes, and process scope. The cmdlets would modify powershell.config.json and pwsh should read this on startup and set culture. Because these are PowerShell specific, recommendation is to call it Set-PSCulture
. If there is a need to set system culture, that's an ask for Windows to create such a cmdlet. Also have new Get-PSCulture
cmdlets that are symmetric to Set-PSCulture
. Current Get-Culture
would be deprecated. Set-PSCulture
would be expected to update $PSCulture
If there is a need to set system culture, that's an ask for Windows to create such a cmdlet.
What about Unix-es? Maybe ask .Net Core team for the API for all platforms?
@mklement0 Could you please summarize actual specifications for Get-PSCulture and Set-PSCulture cmdlets? Then I'd start with Get-PSCulture step-by-step.
@iSazonov:
I think it is ill-advised to make PowerShell have its own culture settings, divorced from the host platform's:
Calling _into_ PowerShell (e.g., from bash
) means that PowerShell may operate with a different culture than the caller.
Calling _out_ of PowerShell means that external programs may operate on a different culture (i.e., the host platform's) - this could be remedied with setting the relevant environment variables to match culture configured for PowerShell, but _only on Unix_.
Both scenarios invite confusion.
Therefore, my advice is not to build an "enclave" and instead comply with / operate on the host platform's (potentially user-specific) culture settings - even if that means that on Unix-like platforms we can only offer setting the _current process_' culture - which would mirror the situation with respect to _environment variables_ (even at the level of CoreFx), whose _persistent_ modification is only supported on Windows.
In short: I don't want to help with fleshing out Get-PSCulture
and Set-PSCulture
, because I think they're a bad idea.
@mklement0 If bash allows to put
LC_ALL=C
LANG=C
export LC_ALL LANG
in its profile (~/.bashrc or ~/.bash_profile) why PowerShell can not?
@iSazonov:
Yes, you can do that in Bash - but it too is a bad idea, because it's treacherous to configure a _single shell_ in a way that deviates from the _OS session's_ culture.
If you place these settings in ~/bashrc
, at least they're restricted to _interactive sessions_, but that wouldn't be the case with powershell.config.json
(and - sadly - also not if placed in $PROFILE
, due to #992).
Thus, calls _from the outside_ via pwsh -c
that sensibly assume that the OS session's culture is in effect would be in for a rude awakening.
I see two primary uses cases for Set-Culture
:
(a) Ad hoc: Change the _current PowerShell session_'s culture for _tests and experimentation_.
(b) Persistently: Change the _OS session's_ startup culture for the current users (possibly also the system default).
We can easily do (a) on Unix, even more thoroughly than in Windows, because if we also set environment var. LANG
, all _external programs_ launched thereafter will see that culture _process-wide_.
We can do (b) on Windows with little effort (the way it already works in WinPS), but on macOS and Linux it's much trickier, so I think it's perfectly fine to get started with simply _not_ supporting persistent culture updates on Unix-like platforms, as is already the case with (non-)support for persistently updating environment variables in CoreFx ([Environment]::SetEnvironmentVariable()
).
We can do more research later - find some food for thought below.
Persistently changing the current user's / the system's default _OS session's_ culture on Unix-like platforms
macOS:
Linux: Various Linux distros may have different mechanisms.
/etc/default/locale
(can be updated directly or via update-locale
; both methods require sudo
).~/.pam_environment
Both cases require reboot / OS-session logoff to take effect.
Other distros?
@mklement0 We haven't standard API to set culture in Unixes at OS level. And we'll do not resolve this in near future. So you is very strong in limiting user opportunities. If my servers is ru-RU culture I'd want to switch PowerShell to en-US and forget about datetime format parsing and similar problems.
This is very convenient when .Net application automatically sets the interface language based on the OS culture. But if you only need to switch the language in one application and you do not have this option, then this is annoying problem.
We haven't standard API to set culture in Unixes at OS level.
Indeed; the bottom section of my previous post was meant to show that rolling our own solution would be far from trivial.
And we'll do not resolve this in near future.
Understood; as I suggested: just support it at the _session (process) level_ on Unix.
If my servers is ru-RU culture I'd want to switch PowerShell to en-US and forget about datetime format parsing and similar problems.
Switching _just PowerShell_ to a different culture is problematic for the reasons discussed.
Which ones do you disagree with?
Someone who really wants to do that can _manually_ update their $PROFILE
with the relevant .NET call and, on Unix, environment-variable definitions OR, if we implement Set-Culture
as (defaulting to) process / session scope, as I'm proposing, by simply calling Set-Culture
.
This is very convenient when .Net application automatically sets the interface language based on the OS culture. But if you only need to switch the language in one application and you do not have this option, then this is annoying problem.
I don't understand.
I don't understand.
If you run a management tool with native language and read documentation on English - it is problem and you'd want to switch interface language of the tool on the fly.
Which ones do you disagree with?
Root problem is that there is no standard api for setting a culture on OS level. Therefore, everything else looks like a personal preference - in every possible scenario, one sees a problem and the other benefits.
you'd want to switch interface language of the tool on the fly.
That's exactly what Set-Culture
would enable you to do (at least if we also make it set [System.Threading.Thread]::CurrentThread.CurrentUICulture
, which we should):
_Temporarily_ change the culture, for the duration of the session (or until changed again later in the session).
On a related note, it would be handy to have a command for running one or more commands with a given culture and then revert to the previous one; here's what I personally use, gratefully adapted from an ancient blog post by @rkeithhill
PS> Use-Culture ru-RU { Get-Date }
锌褟褌薪懈褑邪, 1 屑邪褉褌邪 2019 谐. 13:53:21
Root problem is that there is no standard api for setting a culture on OS level.
It is a problem and it is unfortunate, but not one that must necessarily be solved by us.
Only supporting -Scope Session
(process) for Set-Culture
on Unix-like platforms _and defaulting to that_ is the best we can do under the circumstances:
It allows in-session changes, supporting the scenario you mentioned, for instance.
It allows users to place a Set-Culture
call in their $PROFILE
, _if_ they really want to do that.
$PROFILE
even in non-interactive scenarios (you can work around that with disciplined use of -noprofile
, but note that if the culture were baked into
powershell.config.json
, you wouldn't even have that option).Should CoreFx APIs someday allow changing the culture _persistently_ on Unix, we can enable it in Set-Culture
then.
The analyzer warning you link to recommends settings (and getting) [CultureInfo]::CurrentCulture]
instead of [System.Threading.Thread]::CurrentThread.CurrentCulture]
, which in .NET Core is a _must_ for _cross-thread_ use.
While that makes sense, we wouldn't be setting the culture for a different _thread_ anyway, however (and assigning to [CultureInfo]::CurrentCulture]
still only changes the targeted _thread_'s culture).
So how does this fit into our conversation?
On a related note, however, with respect to _in-session_ changes (the only kind we can currently provide on Unix, as discussed):
As a _shell_ that provides an environment for _external programs_ as well, we want to change the culture for them too - which assigning to [CultureInfo]::CurrentCulture]
by itself doesn't do:
On Unix-like platforms, as stated, we change the culture for external programs launched thereafter by also setting the relevant locale environment variable, LANG
.
On Windows, regrettably, we seemingly cannot do this, which is a limitation we'd have to document (setting [cultureinfo]::DefaultThreadCurrentCulture
only sets the default for (all threads in) the current application domain, and has no effect on external processes, whether they run managed code or not; the Windows API also seems to offer no process-wide setting).
Most helpful comment
@PowerShell/powershell-committee discussed this and would accept setting culture for PowerShell at system, user scopes, and process scope. The cmdlets would modify powershell.config.json and pwsh should read this on startup and set culture. Because these are PowerShell specific, recommendation is to call it
Set-PSCulture
. If there is a need to set system culture, that's an ask for Windows to create such a cmdlet. Also have newGet-PSCulture
cmdlets that are symmetric toSet-PSCulture
. CurrentGet-Culture
would be deprecated.Set-PSCulture
would be expected to update$PSCulture