Skip to content

How to Set the System Keyboard Layout in Batch Script

Learn how to programmatically add, remove, and set keyboard layouts in Windows using Batch Script via registry manipulation and PowerShell integration for multilingual environments.

Changing the keyboard layout programmatically is a common requirement for multilingual environments, shared workstations, and automated provisioning of machines for international offices. Instead of asking each new user to manually navigate through the Windows Settings panel, a Batch Script can preconfigure the correct keyboard layout during initial login or machine setup.

In this guide, we will explore how to add, remove, and set keyboard layouts using Batch Script, primarily through registry manipulation and PowerShell integration.

Understanding Keyboard Layout Identifiers

Windows identifies keyboard layouts using a combination of a Language ID and a Keyboard Layout ID, expressed as an 8-character hexadecimal string.

The format is: LLLLKKKK

  • LLLL = Language ID (e.g., 0409 for English US, 0410 for Italian)
  • KKKK = Keyboard Layout ID (e.g., 00000409 for US QWERTY, 00000410 for Italian Standard)

Common keyboard layout codes:

Layout Code
English (US) 00000409
English (UK) 00000809
French (France) 0000040C
German (Germany) 00000407
Italian 00000410
Spanish (Spain) 0000040A
Japanese 00000411
US International 00020409

Method 1: Adding a Keyboard Layout via the Registry

The current user's installed keyboard layouts are stored in the registry at: HKCU\Keyboard Layout\Preload

Each layout is numbered sequentially: 1 is the default (primary) layout, 2 is the secondary, and so on.

Adding a Secondary Layout

@echo off
setlocal

echo Adding German keyboard layout as secondary...

:: The German standard keyboard layout ID
set "layout_id=00000407"
set "reg_path=HKCU\Keyboard Layout\Preload"

:: Scan existing slots to check for duplicates and find the next available slot
set "slot=1"
:check_slots
reg query "%reg_path%" /v %slot% >nul 2>&1
if %errorlevel% neq 0 goto :slot_available
for /f "tokens=2*" %%A in ('reg query "%reg_path%" /v %slot% 2^>nul ^| findstr /i "REG_SZ"') do (
    if /i "%%B"=="%layout_id%" (
        echo [INFO] German layout is already installed in slot %slot%. No changes needed.
        pause
        exit /b 0
    )
)
set /a slot+=1
goto :check_slots

:slot_available
:: Add the layout to the discovered slot
REG ADD "%reg_path%" /v %slot% /t REG_SZ /d "%layout_id%" /f >nul

echo [OK] German layout added in slot %slot%.
echo You may need to log off and back on for the language bar to update.
pause

Setting a New Default Layout

To change the primary (default) keyboard layout, you overwrite slot 1. The script must also handle the previous default to prevent it from being lost, and check for duplicates to prevent the new default from appearing in multiple slots.

@echo off
setlocal

echo Setting French keyboard as the default layout...

:: French standard layout
set "new_default=0000040C"
set "reg_path=HKCU\Keyboard Layout\Preload"

:: Read the current default (slot 1)
set "old_default="
for /f "tokens=2*" %%A in ('reg query "%reg_path%" /v 1 2^>nul ^| findstr /i "REG_SZ"') do set "old_default=%%B"

:: Check if French is already the default
if /i "%old_default%"=="%new_default%" (
    echo [INFO] French layout is already the default. No changes needed.
    pause
    exit /b 0
)

:: Set French as the new default in slot 1
REG ADD "%reg_path%" /v 1 /t REG_SZ /d "%new_default%" /f >nul

:: Check if the new default was already in a secondary slot
:: If so, swap it with the old default to avoid duplicates
set "swapped=0"
set "scan_slot=2"
:scan_slots
reg query "%reg_path%" /v %scan_slot% >nul 2>&1
if %errorlevel% neq 0 goto :scan_done
for /f "tokens=2*" %%A in ('reg query "%reg_path%" /v %scan_slot% 2^>nul ^| findstr /i "REG_SZ"') do (
    if /i "%%B"=="%new_default%" (
        if defined old_default (
            REG ADD "%reg_path%" /v %scan_slot% /t REG_SZ /d "%old_default%" /f >nul
            echo Swapped slot %scan_slot%: replaced %new_default% with %old_default%.
        ) else (
            REG DELETE "%reg_path%" /v %scan_slot% /f >nul 2>&1
        )
        set "swapped=1"
        goto :scan_done
    )
)
set /a scan_slot+=1
goto :scan_slots

:scan_done
:: If the new default was not found in any secondary slot,
:: the old default needs to be added to the next available slot
if "%swapped%"=="0" (
    if defined old_default (
        REG ADD "%reg_path%" /v %scan_slot% /t REG_SZ /d "%old_default%" /f >nul
        echo Previous default ^(%old_default%^) moved to slot %scan_slot%.
    )
)

echo [SUCCESS] Default keyboard set to French.
echo A logoff may be required for this to take full effect.
pause

Method 2: Complete Keyboard Layout Reset Script

If the goal is to ensure a machine has exactly one keyboard layout (removing all extras), this cleanup script handles the complete process.

@echo off
setlocal enabledelayedexpansion

echo Resetting Keyboard Layouts to US English Only...

:: Desired layout
set "target=00000409"
set "reg_path=HKCU\Keyboard Layout\Preload"

:: 1. Delete all existing preload entries
::    Scan dynamically rather than assuming a fixed upper limit
set "slot=1"
:delete_loop
reg query "%reg_path%" /v !slot! >nul 2>&1
if !errorlevel! neq 0 goto :delete_done
REG DELETE "%reg_path%" /v !slot! /f >nul 2>&1
set /a slot+=1
goto :delete_loop
:delete_done

:: 2. Set US English as the sole layout
REG ADD "%reg_path%" /v 1 /t REG_SZ /d "%target%" /f >nul

:: 3. Clear any substitute layouts (used by some IMEs)
::    Delete the entire key and recreate it empty
REG DELETE "HKCU\Keyboard Layout\Substitutes" /f >nul 2>&1
REG ADD "HKCU\Keyboard Layout\Substitutes" /f >nul 2>&1

echo [OK] All keyboard layouts removed except US English.
echo Log off required for change to be reflected in the language bar.
pause

Why We Clean Substitutes

Some Input Method Editors (IMEs), especially for East Asian languages, register themselves not in the Preload key but in Keyboard Layout\Substitutes. Failing to clean this key can result in phantom Japanese or Chinese input methods appearing in the language bar even after you clear Preload.

Method 3: Immediate Layout Change via PowerShell (No Logoff)

For scripts that need the change applied to the current session immediately (e.g., during a live demonstration), we can use PowerShell to call the Windows international settings cmdlets.

@echo off
setlocal

echo Applying keyboard layout change immediately...

:: Add French layout and set it as default, effective immediately
powershell -NoProfile -Command ^
  "$langList = New-WinUserLanguageList 'fr-FR';" ^
  "$langList.Add('en-US');" ^
  "Set-WinUserLanguageList $langList -Force"

if %errorlevel% neq 0 (
    echo [ERROR] Failed to apply keyboard layout change.
    echo         Ensure you are running Windows 10 or later.
    pause
    exit /b 1
)

echo [OK] French set as primary, English as secondary.
echo Change is effective immediately in this session.
pause

How This Works

  • New-WinUserLanguageList 'fr-FR': Creates a new language list with French as the first (primary) entry.
  • .Add('en-US'): Appends English as a secondary language. PowerShell automatically converts the language tag string into a WinUserLanguage object.
  • Set-WinUserLanguageList: Applies the list instantly. The language bar reflects the change without logging off.

Method 4: Applying Layouts for All New Users (Default Profile)

To set the keyboard layout for users who have not yet logged into the machine (new accounts), you must modify the Default User profile's registry hive.

@echo off
setlocal

:: Verify Admin Rights
net session >nul 2>&1
if %errorlevel% neq 0 (
    echo [ERROR] Administrator privileges are required to modify the default user profile.
    pause
    exit /b 1
)

echo Loading default user registry hive...

:: Determine the default profile path from the registry
set "default_profile="
for /f "usebackq tokens=2*" %%A in (`reg query "HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList" /v Default 2^>nul ^| findstr /i "REG_SZ"`) do call set "default_profile=%%B"

if not defined default_profile (
    echo [ERROR] Could not determine the default user profile path.
    pause
    exit /b 1
)

set "hive_path=%default_profile%\NTUSER.DAT"
set "hive_key=HKU\DefaultUserTemp"

if not exist "%hive_path%" (
    echo [ERROR] Default user hive not found: %hive_path%
    pause
    exit /b 1
)

:: Load the default user's NTUSER.DAT under a temporary key name
REG LOAD "%hive_key%" "%hive_path%" >nul 2>&1
if %errorlevel% neq 0 (
    echo [ERROR] Failed to load the default user registry hive.
    echo         The hive may already be loaded or locked by another process.
    pause
    exit /b 1
)

:: Set the keyboard layout for new users (Italian)
REG ADD "%hive_key%\Keyboard Layout\Preload" /v 1 /t REG_SZ /d "00000410" /f >nul

:: Unload the hive
REG UNLOAD "%hive_key%" >nul 2>&1
if %errorlevel% neq 0 (
    echo [WARNING] Failed to unload the registry hive immediately.
    echo           This can happen if Registry Editor or another tool has the key open.
    echo           The hive will be released when those handles are closed.
) else (
    echo [OK] Registry hive unloaded successfully.
)

echo [OK] New user accounts will default to the Italian keyboard layout.
pause

Common Mistakes

The Wrong Way: Confusing Language ID with Layout ID

:: WRONG - 0409 is only a language ID (4 digits), not a full layout code
REG ADD "HKCU\Keyboard Layout\Preload" /v 1 /t REG_SZ /d "0409" /f

The Wrong Way: Expecting Immediate Effect from Registry Changes

:: WRONG - The user expects the language bar to change instantly
REG ADD "HKCU\Keyboard Layout\Preload" /v 1 /t REG_SZ /d "0000040C" /f
echo French layout is now active. Start typing!

Registry modifications to the Preload key only take effect after a logoff/logon cycle. For immediate changes, use the PowerShell Set-WinUserLanguageList approach from Method 3.

The Wrong Way: Blindly Overwriting Existing Slots

:: WRONG - Overwrites whatever was in slot 2 without checking
REG ADD "HKCU\Keyboard Layout\Preload" /v 2 /t REG_SZ /d "00000407" /f

The Wrong Way: Using Unreliable Filters When Parsing Registry Output

:: WRONG - findstr /i "1" matches any line containing the character "1"
for /f "tokens=3" %%A in ('reg query "HKCU\Keyboard Layout\Preload" /v 1 2^>nul ^| findstr /i "1"') do set "old_default=%%A"

Best Practices

  1. Use full 8-character layout codes: Always specify the complete LLLLKKKK hex string when writing to the registry.
  2. Check for duplicates before adding: Always scan existing slots to verify the layout is not already installed before adding it, and when changing the default, handle the displaced layout to prevent duplicate entries.
  3. Use PowerShell for immediate changes: If a logoff is unacceptable, the Set-WinUserLanguageList cmdlet provides instant session-level results on Windows 10 and later.
  4. Clean the Substitutes key: When performing a full keyboard reset, always clear the Keyboard Layout\Substitutes registry key to prevent ghost IME entries.
  5. Use tokens=2* with findstr /i "REG_SZ" for registry parsing: This combination reliably captures the value data from reg query output regardless of the value name or data containing digits or spaces.
  6. Use the registry to resolve the default profile path: Do not hardcode C:\Users\Default. Query HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList for the Default value to support non-standard installations.

Conclusion

Setting the system keyboard layout in Batch Script involves writing the correct 8-character hexadecimal layout identifier into the HKCU\Keyboard Layout\Preload registry key. While registry changes require a logoff to take effect, embedding PowerShell's Set-WinUserLanguageList cmdlet provides an immediate, session-level alternative for Windows 10 and later. By combining these approaches with default user profile modification, administrators can fully automate keyboard configuration for both existing and future user accounts on any machine.

Navigation