Ash's helpful answer is the best solution for machines running Windows 8 or above / Windows Server 2012 R2 or above.
If you still need to run remotely on a Windows 7 / Windows Server 2012 machine:
(Get-ItemProperty 'HKCU:\Control Panel\Desktop' PreferredUILanguages).PreferredUILanguages[0]
Background information:
In local execution, the simplest solution is to use $PSUICulture (returns a string with the language name, such as en-US) or Get-UICulture (returns a [cultureinfo] object), as shown in this answer.
However, this doesn't work when you use PowerShell remoting (which is why the above solution / Ash's solution is necessary):
# Does NOT return the target user's display language.
# Seemingly always returns the OS installation language.
Invoke-Command -ComputerName $someComputer { $PSUICulture }
I'm unclear on the exact reasons, but it may be related to the fact that remotely executed PowerShell code runs in an invisible window station that is distinct from the interactive desktop.
Answer from mklement0 on Stack OverflowAsh's helpful answer is the best solution for machines running Windows 8 or above / Windows Server 2012 R2 or above.
If you still need to run remotely on a Windows 7 / Windows Server 2012 machine:
(Get-ItemProperty 'HKCU:\Control Panel\Desktop' PreferredUILanguages).PreferredUILanguages[0]
Background information:
In local execution, the simplest solution is to use $PSUICulture (returns a string with the language name, such as en-US) or Get-UICulture (returns a [cultureinfo] object), as shown in this answer.
However, this doesn't work when you use PowerShell remoting (which is why the above solution / Ash's solution is necessary):
# Does NOT return the target user's display language.
# Seemingly always returns the OS installation language.
Invoke-Command -ComputerName $someComputer { $PSUICulture }
I'm unclear on the exact reasons, but it may be related to the fact that remotely executed PowerShell code runs in an invisible window station that is distinct from the interactive desktop.
The language you see there is the first one in the list from Get-WinUserLanguageList.
PS C:\> (Get-WinUserLanguageList).LocalizedName
Russian
English (United States)

When I change the order of the list, and set English first, it reverses the order in PowerShell.
PS C:\> Set-WinUserLanguageList -LanguageList en-US,ru
Confirm
Continue with this operation?
[Y] Yes [N] No [S] Suspend [?] Help (default is "Y"): Y
PS C:\> (Get-WinUserLanguageList).LocalizedName
English (United States)
Russian

So to get the current language, you merely have to call the first object in that list.
(Get-WinUserLanguageList)[0].LocalizedName
The restriction only applies to the execution of scripts. It is not a real security measure, it's only there to prevent users from accidentally run PS scripts. Here are some of many ways to bypass the resrtiction without applying changes to the system:
- Run PS from CMD and pass your command with the
-Commandparameter:
You can pass multiple lines (aka a script) by separating the lines bypowershell -Command "Get-WinUserLanguageList";:powershell -Command "Get-WinUserLanguageList; Write-Host 'Hello World!'" - Run PS from CMD and pass your code to
stdin:echo Get-WinUserLanguageList | powershell - Run PS from CMD, bypass the execution policy and execute a script file:
powershell -ExecutionPolicy Bypass -File .\my_script.ps1
If a computer is restricted from changing such things by policy, then its up to the admin of the network to run a PowerShell script at an elevated level to change such things.
Otherwise, allowing such changes means viruses could change the user experience for the worse.
The direction one should take is to create the PowerShell script, but one needs to run it at an admin level of authority.
In a comment you note that the problem was that Set-WinUserLanguageList complained about an invalid language passed as part of the array of languages to -LanguageList and, indeed, ua-UA should have been uk-UA (Ukrainian).[1]
You could have avoided the problem if you hadn't hardcoded the array passed to -LanguageList and instead retrieved the list of installed user language programmatically:
# Get the current list of installed user languages (as an array).
$userLangs = (Get-WinUserLanguageList).LanguageTag
You could then simply swap the array's first two entries for your successive Set-WinUserLanguageList calls, and use a single call to powershell.exe, the Windows PowerShell CLI.
Here's the PowerShell code in isolation, spread across multiple lines for readability, with comments (note that the Set-WinUserLanguageList calls emit warnings, which you can suppress with -WarningAction Ignore; the Start-Sleep 1 call (1-second sleep) shouldn't be necessary, but, sadly, is):
# Get the list (array) of user languages.
$userLangs = (Get-WinUserLanguageList).LanguageTag
# Create a copy that has the 1st and 2nd elements swapped.
$userLangsModified = $userLangs[1, 0] + $(if ($userLangs.Count -ge 3) { $userLangs[2..($userLangs.Count-1)] })
# Apply the workaround:
# Set the modified list.
Set-WinUserLanguageList -Force -LanguageList $userLangsModified
# Sleep a little. !! As you've found out, this is necessary
# for the 2nd call to take effect.
Start-Sleep 1
# Switch back to the original list.
Set-WinUserLanguageList -Force -LanguageList $userLangs
And here's the (of necessity) single-line form for your batch file:
@echo off
powershell -c "$userLangs = (Get-WinUserLanguageList).LanguageTag; $userLangsModified = $userLangs[1, 0] + $(if ($userLangs.Count -ge 3) { $userLangs[2..($userLangs.Count-1)] }); Set-WinUserLanguageList -Force -LanguageList $userLangsModified; Start-Sleep 1; Set-WinUserLanguageList -Force -LanguageList $userLangs"
Optional reading: How to test the validity of a culture name / language tag (whether it refers to a predefined culture / language):
It is not obvious how to test this, given that you can call a [cultureinfo] constructor with any name that is formally correct ([a-z][a-z] or [a-z][a-z]-[A-Z][A-Z], although case doesn't matter and there are variations with additional/longer tokens) without causing an error; similarly, the .GetCultureInfo() method doesn't report an error by default.
Windows PowerShell (the legacy, ships-with-Windows, Windows-only edition of PowerShell whose latest and last version is 5.1) / .NET Framework solution:
$cultureNameToTest = 'ua-UA' $isValid = try { ([cultureinfo] $cultureNameToTest).EnglishName -notmatch 'Unknown Locale' } catch { $false }- Note: Interactively, you can just do something like
[cultureinfo] 'ua-UA'and see if theDisplayNamecolumn contains substringUnknown Localeor an error is reported.
- Note: Interactively, you can just do something like
PowerShell (Core) 7 (the modern, cross-platform, install-on-demand edition) / .NET solution:
# Using the Get-Culture cmdlet $cultureNameToTest = 'ua-UA' $isValid = try { [bool] (Get-Culture $cultureNametoTest) } catch { $false } # Alternatively, using [cultureinfo]::GetCultureInfo() with the # overload with the `predefinedOnly` parameter $cultureNameToTest = 'ua-UA' $isValid = try { [cultureinfo]::GetCultureInfo($cultureNameToTest, $true) } catch { $false }- Note: Interactively, you can just do something like
Get-Culture ua-UAand see if an error is reported.
- Note: Interactively, you can just do something like
[1] While the call still succeeds for those languages that are valid (you only get a warning stating The list you attempted to set contained invalid languages which were ignored), the original uk-UA (Ukrainian) entry will in effect disappear from the list (since you mistakenly tried to reference it as ua-UA, which was ignored).
If you are executing a PowerShell command in a batch like that you might have problems in 3 places:
- The command being executed. Try including the
-NoExitparameter to thepowershell.execall so you can catch any script errors. You'll need to remove it later. - The
powershell.exeprocess. If you have syntax problems in the command, or the execution policy is restrictive PowerShell will not execute. Try adding the parameter-ExecutionPolicy Bypassto thepowershell.execall (requires administrator). Also try to execute the raw command in a PowerShell host to see if it gives the expected result. - Syntax or execution problems in the batch file. Instead of executing the batch try calling it from a command prompt, this way when it finishes executing the window will still be open and you can see any errors that it might write to
STDERR. Combine this with the-NoExitparameter and you will end up with two windows.
Let me know if it worked for you.
Don't know if this still works on Windows 7 but it does in Windows XP
reg query "hklm\system\controlset001\control\nls\language" /v Installlanguage
Then you can parse the ouput. e.g.
0409 --> English
0407 --> German
You can't use InstallLanguage under HKLM\SYSTEM\CurrentControlSet\Control\nls\language
because that's just what it says it is: Install Language
Although you can directly install localized version of Windows, this is not always done, as it wasn't on my PC. Instead a language pack is applied, which is fine, but then Display Language is not the same as Install Language.
Also if a user change his display language, InstallLanguage will not reflect the change. And there could be more users with different display languages.
- Current User Display Language
HKCU\Control Panel\Desktop
PreferredUILanguages
FOR /F "tokens=3" %%a IN ('reg query "HKCU\Control Panel\Desktop" /v PreferredUILanguages ^| find "PreferredUILanguages"') DO set UILanguage=%%a
echo User Display Language: %UILanguage%
There is difference between Local Machine language, System language and User language. There is also separate settings for BCD language, used for recovery and boot manager
- Local Machine
Install language is set upon installation and is never changed
also a Default value for "HKLM\SYSTEM\CurrentControlSet\Control\nls\language" key is set to the same value. This value is wrongly read by some InstallShield setup programs, resulting in English interface on localized Windows.
If you change Display language, new value is stored in
HKLM\SYSTEM\CurrentControlSet\Control\MUI\Settings
PreferredUiLanguages
this will override InstallLanguage value under HKLM\SYSTEM\CurrentControlSet\Control\nls\language
There is a mix-up in value types, while InstallLanguage is LCID, PreferredUiLanguages is LCID string.
This language is then reported as Local Machine Language. It isn't User Display Language
- System Language
This is the language for System user.
Before a user is logged on, this language is used.
That means it is a language for Welcome screen, and for the OOBE.
HKEY_USERS\S-1-5-18\Control Panel\Desktop\MuiCached
MachinePreferredUILanguages
There is also a WMI way to get OS language, but I didn't test which of these languages you'll get
wmic os get locale, oslanguage, codeset
FOR /F "tokens=2 delims==" %%a IN ('wmic os get OSLanguage /Value') DO set OSLanguage=%%a
echo OS Language: %OSLanguage%