Powershell: Set-Culture should support setting the current session's culture also / optionally only

Created on 20 May 2017  路  22Comments  路  Source: PowerShell/PowerShell

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 only
  • Session ... (transiently) set culture for the current session only
  • CurrentUserAndSession ... (persistently) current user account + current session.

For backward compatibility, CurrentUser would have to be the default.

Environment data

PowerShell Core v6.0.0-beta (v6.0.0-beta.1)
Area-Cmdlets-Utility Committee-Reviewed Issue-Discussion Up-for-Grabs

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 new Get-PSCulture cmdlets that are symmetric to Set-PSCulture. Current Get-Culture would be deprecated. Set-PSCulture would be expected to update $PSCulture

All 22 comments

@SteveL-MSFT Have we plan to implement Set-Culture?

It seems we can change a culture for current thread only.

@iSazonov no immediate plans

@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.

    • This makes it the PowerShell-idiomatic equivalent of [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.

    • Only if someone changes the culture in-session using the .NET framework directly (with [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

    • However, the behavior is consistent with Windows PowerShell _as long as no in-session changes are made._
  • 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.

    • Add instructions to the 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).

    • As discussed above, a persistent _shell-specific_ setting is ill-advised.

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:

    • System default: There is no way that I'm aware of.
    • Current-user default: See this article; requires reboot / OS-session logoff to take effect.
  • Linux: Various Linux distros may have different mechanisms.

    • Ubuntu:
    • System default: maintained in plain-text file /etc/default/locale (can be updated directly or via update-locale; both methods require sudo).
    • Current-user default: maintained in optional plain-text file ~/.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.

    • For the reasons stated, such an approach is not for everyone and compromises the robustness of cross-shell scripting / calling the PowerShell CLI from - at least as long as PowerShell still sources $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).

Was this page helpful?
0 / 5 - 0 ratings