Files
win-remote-toolkit/scripts/powershell/configure-ime.ps1
2026-04-24 17:44:02 +08:00

113 lines
5.9 KiB
PowerShell

param(
[string]$TargetUser = 'User'
)
[Console]::OutputEncoding = [Text.Encoding]::UTF8
$ErrorActionPreference = 'Stop'
# Resolve target user SID
$sid = (New-Object System.Security.Principal.NTAccount($TargetUser)).Translate([System.Security.Principal.SecurityIdentifier]).Value
$hivePath = "HKEY_USERS\$sid"
$regBase = "Registry::$hivePath"
$ntuserDat = "C:\Users\$TargetUser\NTUSER.DAT"
$weLoaded = $false
if (-not (Test-Path $regBase)) {
if (-not (Test-Path $ntuserDat)) {
# Try alternate casing (NTFS is case-insensitive but PS sees the literal case)
$alt = Get-Item "C:\Users\*" | Where-Object { $_.Name -ieq $TargetUser } | Select-Object -First 1
if ($alt) { $ntuserDat = Join-Path $alt.FullName 'NTUSER.DAT' }
}
if (-not (Test-Path $ntuserDat)) { throw "NTUSER.DAT not found for $TargetUser" }
Write-Host "[*] Loading $ntuserDat"
& reg.exe load $hivePath $ntuserDat | Out-Null
if ($LASTEXITCODE -ne 0) { throw "reg load failed" }
$weLoaded = $true
}
try {
# ---------- 1. Add en-US to language list as FIRST language (default) ----------
# This targets the CURRENT user running this script. For the target user, we
# write the preload keys directly into their hive.
Write-Host "[*] Setting target user's keyboard preload (en-US first, zh-TW second)"
$preloadPath = "$regBase\Keyboard Layout\Preload"
if (-not (Test-Path $preloadPath)) { New-Item -Path $preloadPath -Force | Out-Null }
# 00000409 = English (US); 00000404 = Chinese (Traditional, Taiwan)
New-ItemProperty -Path $preloadPath -Name '1' -Value '00000409' -PropertyType String -Force | Out-Null
New-ItemProperty -Path $preloadPath -Name '2' -Value '00000404' -PropertyType String -Force | Out-Null
$substitutesPath = "$regBase\Keyboard Layout\Substitutes"
if (Test-Path $substitutesPath) {
Remove-Item $substitutesPath -Recurse -Force
}
# ---------- 2. Set Chinese IME to default to English (alphanumeric) mode ----------
Write-Host "[*] Setting Microsoft Bopomofo to default English mode"
$chtPath = "$regBase\Software\Microsoft\InputMethod\Settings\CHT"
if (-not (Test-Path $chtPath)) { New-Item -Path $chtPath -Force | Out-Null }
# 0 = default English mode, 1 = default Chinese mode
New-ItemProperty -Path $chtPath -Name 'Enable Default Input Mode' -Value 0 -PropertyType DWord -Force | Out-Null
# ---------- 3. Set Bopomofo's Chinese/English switch hotkey to Ctrl+Space ----------
# Microsoft Bopomofo exposes this option in its Settings UI:
# Shift (default) | Ctrl + Space | Caps Lock
# The registry value is 'Enable Ctrl Space Switch Chinese English' (boolean-ish).
Write-Host "[*] Setting Ctrl+Space as Chinese/English switch for MS Bopomofo"
New-ItemProperty -Path $chtPath -Name 'Enable Ctrl Space Switch Chinese English' -Value 1 -PropertyType DWord -Force | Out-Null
# Also disable Shift as the switch (optional, prevents confusion)
New-ItemProperty -Path $chtPath -Name 'Enable Shift Chinese English' -Value 0 -PropertyType DWord -Force | Out-Null
# ---------- 4. Disable OS-level Ctrl+Shift / Alt+Shift language toggle ----------
# So Ctrl+Space is the only thing that triggers language switching.
Write-Host "[*] Disabling OS-level Language/Layout hotkeys"
$togglePath = "$regBase\Keyboard Layout\Toggle"
if (-not (Test-Path $togglePath)) { New-Item -Path $togglePath -Force | Out-Null }
# Values: 1 = Ctrl+Shift, 2 = Left Alt+Shift, 3 = Grave Accent, 4 = Not assigned
New-ItemProperty -Path $togglePath -Name 'Hotkey' -Value '3' -PropertyType String -Force | Out-Null
New-ItemProperty -Path $togglePath -Name 'Language Hotkey' -Value '3' -PropertyType String -Force | Out-Null
New-ItemProperty -Path $togglePath -Name 'Layout Hotkey' -Value '3' -PropertyType String -Force | Out-Null
# ---------- 5. Tell the language subsystem en-US is the default ----------
# Remove user-profile language forcing so that Preload order is respected
$userProfileLangPath = "$regBase\Control Panel\International\User Profile"
if (Test-Path $userProfileLangPath) {
$langs = (Get-ItemProperty $userProfileLangPath).Languages
if ($langs) {
# Prepend en-US to the language list
$newLangs = @('en-US') + ($langs | Where-Object { $_ -ne 'en-US' })
New-ItemProperty -Path $userProfileLangPath -Name 'Languages' -Value $newLangs -PropertyType MultiString -Force | Out-Null
Write-Host "[*] Updated user-profile language list: $($newLangs -join ', ')"
}
}
# Ensure per-language subkeys exist
$enUSPath = "$userProfileLangPath\en-US"
if (-not (Test-Path $enUSPath)) { New-Item -Path $enUSPath -Force | Out-Null }
# CachedLanguageName is cosmetic but prevents re-detection flakiness
New-ItemProperty -Path $enUSPath -Name '0409:00000409' -Value 1 -PropertyType DWord -Force | Out-Null
Write-Host ""
Write-Host "[OK] IME configured for '$TargetUser'"
Write-Host "Summary:"
Write-Host " - Default input: English (US) keyboard"
Write-Host " - Microsoft Bopomofo: starts in English (alphanumeric) mode"
Write-Host " - Ctrl+Space: toggles between English / Chinese within Bopomofo"
Write-Host " - OS-level language hotkeys (Ctrl+Shift / Alt+Shift): disabled"
}
finally {
if ($weLoaded) {
[GC]::Collect(); [GC]::WaitForPendingFinalizers()
[GC]::Collect(); [GC]::WaitForPendingFinalizers()
Start-Sleep -Milliseconds 500
& reg.exe unload $hivePath 2>&1 | Out-Null
if ($LASTEXITCODE -eq 0) { Write-Host "[*] Unloaded $hivePath" }
else { Write-Host "[!] reg unload returned $LASTEXITCODE" }
}
}
Write-Host ""
Write-Host "Note: Settings apply on '$TargetUser' next login. If Bopomofo doesn't pick up"
Write-Host "Ctrl+Space automatically, open 'Microsoft Bopomofo settings' -> 'Keys' and"
Write-Host "confirm 'Chinese/English switch' = Ctrl+Space (this registry write should do it)."