Skip to content

Instantly share code, notes, and snippets.

@sba923
Last active March 4, 2024 18:13
Show Gist options
  • Save sba923/a71dd721e78ae08842616db244f19437 to your computer and use it in GitHub Desktop.
Save sba923/a71dd721e78ae08842616db244f19437 to your computer and use it in GitHub Desktop.
Remove the unwanted keyboard layouts that Windows keeps adding based on input languages
# this is one of Stéphane BARIZIEN's public domain scripts
# the most recent version can be found at:
# https://gist.github.com/sba923/a71dd721e78ae08842616db244f19437#file-remove-uselesskeyboardlayouts-ps1
[CmdletBinding()]
param([string] $PhysicalKeyboardLayout)
$iswinps = ($null, 'Desktop') -contains $PSVersionTable.PSEdition
if (!$iswinps)
{
Write-Host("This script requires Windows PowerShell, it doesn't run (yet) under PowerShell Core")
Exit(1)
}
$languagelist = Get-WinUserLanguageList
# remember what the first entry is
$firstlanguage = $languagelist[0].LanguageTag
# guess the physical keyboard layout: it should be the layout with the highest
# number of occurrences for languages other than English
$keyboardlayoutused = @{}
for ($languageindex = 0; $languageindex -lt $languagelist.Count; $languageindex++)
{
if ($languagelist[$languageindex].LanguageTag -ne 'en-US')
{
for ($keyboardlayoutindex = 0; $keyboardlayoutindex -lt $languagelist[$languageindex].InputMethodTips.Count; $keyboardlayoutindex++)
{
$keyboardlayout = $languagelist[$languageindex].InputMethodTips[$keyboardlayoutindex] -replace '.*:', ''
if ($null -ne $keyboardlayoutused[$keyboardlayout])
{
$keyboardlayoutused[$keyboardlayout]++
}
else
{
$keyboardlayoutused[$keyboardlayout] = 1
}
}
}
}
# if at least one keyboard layout is configured, assume the actual physical keyboard layout
# is the one configured for the highest number of input methods,
# otherwise use the value of the -PhysicalKeyboardLayout parameter
if ($keyboardlayoutused.Keys.Count -ne 0)
{
$PhysicalKeyboardLayout = (@(
foreach ($k in $keyboardlayoutused.Keys)
{
[PSCustomObject] @{
KeyboardLayout = $k
Count = $keyboardlayoutused[$k]
}
}) | Sort-Object -desc count | Select-Object -first 1).KeyboardLayout
Write-Verbose ("Assuming physical keyboard layout is {0}" -f $PhysicalKeyboardLayout)
}
elseif ($PSBoundParameters.ContainsKey('PhysicalKeyboardLayout'))
{
Write-Verbose ("Using specified physicaly keyboard layout {0}" -f $PhysicalKeyboardLayout)
}
else
{
throw "Cannot guess the physical keyboard layout, must specify it with -PhysicalKeyboardLayout as 4-hexdigit value."
}
# make sure there's an entry for the physical keyboard layout for all the configured languages
$updateneeded = $false
for ($languageindex = 0; $languageindex -lt $languagelist.Count; $languageindex++)
{
$physicalkeyboardlayoutconfigured = $false
for ($keyboardlayoutindex = 0; $keyboardlayoutindex -lt $languagelist[$languageindex].InputMethodTips.Count; $keyboardlayoutindex++)
{
if ($languagelist[$languageindex].InputMethodTips[$keyboardlayoutindex] -match (':' + $PhysicalKeyboardLayout))
{
$physicalkeyboardlayoutconfigured = $true
}
}
if (!$physicalkeyboardlayoutconfigured)
{
Write-Verbose ("Adding keyboard layout {0} for language '{1}'" -f $PhysicalKeyboardLayout, $languagelist[$languageindex].LanguageTag)
if ($languagelist[$languageindex].InputMethodTips.Count -ge 1)
{
$languagelist[$languageindex].InputMethodTips.Add((($languagelist[$languageindex].InputMethodTips[0] -replace ':.*', '') + ':' + $PhysicalKeyboardLayout))
}
else
{
$languagelist[$languageindex].InputMethodTips.Add(("{0:x4}" -f [System.Globalization.CultureInfo]::GetCultureInfo($languagelist[$languageindex].LanguageTag).LCID).ToUpper() + ':' + $PhysicalKeyboardLayout)
}
$updateneeded = $true
}
}
if ($updateneeded)
{
# make sure the first entry in the list hasn't changed
# as it seems Set-WinUserLanguageList sets the Windows UI display language to the first entry in the list
if ($languagelist[0].LanguageTag -ne $firstlanguage)
{
$newlist = @()
$languagelist | Where-Object { $_.LanguageTag -eq $firstlanguage } | ForEach-Object { $newlist += $_ }
$languagelist | Where-Object { $_.LanguageTag -ne $firstlanguage } | ForEach-Object { $newlist += $_ }
$languagelist = $newlist
}
Set-WinUserLanguageList -LanguageList $languagelist -Force
}
# first add the "language = XXX, keyboard layout = default layout for language XXX" if it's not on the list, to make sure removal does remove it 😜
$updateneeded = $false
$languagelist.LanguageTag | Foreach-Object {
$languagetag = $_
$languagecode = ($languagelist | Where-Object { $_.LanguageTag -eq $languagetag }).InputMethodTips[0].SubString(0, 4)
$defaultinputmethod = $languagecode + ':0000' + $languagecode
$defaultkeyboardlayout = $defaultinputmethod -replace '^....', ''
for ($languageindex = 0; $languageindex -lt $languagelist.Count; $languageindex++)
{
if ($languagelist[$languageindex].LanguageTag -eq $languagetag)
{
$defaultlayoutpresent = $false
for ($keyboardlayoutindex = 0; $keyboardlayoutindex -lt $languagelist[$languageindex].InputMethodTips.Count; $keyboardlayoutindex++)
{
if ($languagelist[$languageindex].InputMethodTips[$keyboardlayoutindex] -match $defaultkeyboardlayout)
{
$defaultlayoutpresent = $true
}
}
if (!$defaultlayoutpresent)
{
Write-Verbose ("Adding input method {0} to ensure proper removal of keyboard layout {1} for language '{2}'" -f $defaultinputmethod, ($defaultinputmethod -replace '.*:', ''), $languagelist[$languageindex].LanguageTag)
$languagelist[$languageindex].InputMethodTips.Add($defaultinputmethod)
[Diagnostics.CodeAnalysis.SuppressMessageAttribute('PSUseDeclaredVarsMoreThanAssignments', 'updateneeded', Justification = 'variable is used in various scopes throughout the script')]
$updateneeded = $true
}
}
}
}
if ($updateneeded)
{
# make sure the first entry in the list hasn't changed
# as it seems Set-WinUserLanguageList sets the Windows UI display language to the first entry in the list
if ($languagelist[0].LanguageTag -ne $firstlanguage)
{
$newlist = @()
$languagelist | Where-Object { $_.LanguageTag -eq $firstlanguage } | ForEach-Object { $newlist += $_ }
$languagelist | Where-Object { $_.LanguageTag -ne $firstlanguage } | ForEach-Object { $newlist += $_ }
$languagelist = $newlist
}
Set-WinUserLanguageList -LanguageList $languagelist -Force
}
# now remove the unwanted keyboard layouts
$updateneeded = $false
for ($languageindex = 0; $languageindex -lt $languagelist.Count; $languageindex++)
{
for ($keyboardlayoutindex = 0; $keyboardlayoutindex -lt $languagelist[$languageindex].InputMethodTips.Count; $keyboardlayoutindex++)
{
if ($languagelist[$languageindex].InputMethodTips[$keyboardlayoutindex] -notmatch (':' + $PhysicalKeyboardLayout))
{
Write-Verbose ("Removing keyboard layout {0} for language '{1}'" -f ($languagelist[$languageindex].InputMethodTips[$keyboardlayoutindex] -replace '.*:', ''), $languagelist[$languageindex].LanguageTag)
$languagelist[$languageindex].InputMethodTips.RemoveAt($keyboardlayoutindex)
$updateneeded = $true
}
}
}
if ($updateneeded)
{
# make sure the first entry in the list hasn't changed
# as it seems Set-WinUserLanguageList sets the Windows UI display language to the first entry in the list
if ($languagelist[0].LanguageTag -ne $firstlanguage)
{
$newlist = @()
$languagelist | Where-Object { $_.LanguageTag -eq $firstlanguage } | ForEach-Object { $newlist += $_ }
$languagelist | Where-Object { $_.LanguageTag -ne $firstlanguage } | ForEach-Object { $newlist += $_ }
$languagelist = $newlist
}
Set-WinUserLanguageList -LanguageList $languagelist -Force
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment