Skip to content

Latest commit

 

History

1 Commit

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Language Indicator

A small Windows utility that shows your current keyboard language — and, for CJK layouts, the current input mode — right next to the text caret.

You always know what you are about to type before you type it, without looking away at the taskbar.

The badge beside the caret in VS Code: KO-A in alphanumeric mode, KO-가 in Hangul mode, DE for a German layout

  • Korean: KO-가 / KO-A
  • Japanese: JA-あ / JA-ア / JA-ア / JA-A
  • Chinese: ZH-中 / ZH-A
  • Everything else: the language code — DE, EN, FR, …

The tray icon shows the current language too, so the state is visible even when you are not in a text field.

Why this exists / 만든 이유

English

I often work in both German and Korean on Windows, so I tried a number of existing tools for seeing which input language is currently active. Many of them are good, but some did not behave the way I wanted — so I had an AI build one instead.

The code is 100 % AI-written. The model used was primarily Opus, through Claude Code.

한국어

윈도우 환경에서 독일어와 한국어를 같이 사용하는 경우가 많았습니다. 그래서 현재 언어 상태가 무엇인지 확인하는 프로그램을 이것저것 사용해봤는데요, 좋은 프로그램들이 많았지만 원하는대로 동작하지 않는 경우도 있었습니다. 그래서 AI를 시켜서 만들어보았습니다.

코드는 100% AI로 작성하였습니다. 사용된 모델은 주로 Opus 이고, Claude Code를 사용했음을 알립니다.

Highlights

  • One small executable. No installer, no runtime, no DLLs beyond what ships with Windows. Copy it anywhere and run it.
  • Nearly free at rest. 0.0–0.4 % of one core, and only while a caret is actually present.
  • Never steals focus and never blocks clicks.
  • Works across app frameworks — Win32, Chromium/Electron, UWP/WinUI — through a fallback chain of caret-location strategies.
  • Per-monitor DPI aware, so it lands correctly on mixed-scaling multi-monitor setups.
  • Live configuration. Save config.ini and the change applies immediately.

Requirements

Windows 10 1809 or later, x64. Developed and tested on Windows 11.

Getting started

Download LanguageIndicator.exe and run it. There is no installer.

It runs in the background with a tray icon. Right-click that icon for:

Menu item What it does
Show indicator Toggles the overlay (double-clicking the icon does the same)
Open config file Opens config.ini in Notepad
Run at Windows startup Registers under HKCU — no administrator rights needed
Exit

Configuration

Settings live in config.ini next to the executable. It is created on first run, and saving it applies the changes immediately — no restart.

If the executable's own directory is not writable (when installed under Program Files, for example), %APPDATA%\LanguageIndicator\ is used instead. LanguageIndicator.exe --config prints the path actually in use on its first line.

[display]
mode = always            ; always | onchange (show briefly after a change, then hide)
duration_ms = 1500

[appearance]
font_family = Segoe UI
font_size = 12.5
font_weight = 600        ; 100-900
text_color = FFFFFF      ; RRGGBB
text_opacity = 95        ; 0-100
background_color = 0078D4
background_opacity = 95
corner_radius = 5
padding_x = 7
padding_y = 3
min_width = 22

[position]
anchor = below           ; right | left | above | below
offset_x = 0
offset_y = 6
selection_anchor = end   ; where to sit when a click selects all text: end | start

[animation]
smooth = true            ; glide on short moves, jump on long ones
duration_ms = 90
snap_distance = 120      ; moves longer than this skip the animation

[behavior]
poll_interval_ms = 100
use_uia = true           ; see "Known limitations"

Sizes are given at 100 % scaling and are scaled to your monitor automatically, so there is nothing to adjust for a high-DPI display.

Options added by a newer version are appended to an existing config.ini automatically, leaving your edits untouched.

Invalid values are refused, not ignored

A value outside its accepted range, or one that is not a number where a number is expected, makes the program report the problem and decline to start:

LanguageIndicator did not start, because config.ini contains values that cannot be used:

  [appearance] padding_x = 99999
      expected a number between 0 and 200

  [position] anchor = sideways
      expected one of: right, left, above, below

Silently substituting a default would hide the typo: the setting would simply appear not to work, with nothing to explain why.

Editing the file while the program is running is treated differently — you are probably mid-edit, so it keeps the settings it already has, shows a tray notification naming the offending key, and carries on. Fix the value and the next save applies normally.

To check a file without starting the program:

LanguageIndicator.exe --config
Setting Accepted
mode always, onchange
duration_ms 100 – 60000
font_size 5 – 96
font_weight 100 – 900
text_color, background_color six hex digits (FFFFFF or #FFFFFF)
text_opacity, background_opacity 0 – 100
corner_radius 0 – 64
padding_x, padding_y 0 – 200
min_width 0 – 500
anchor right, left, above, below
offset_x, offset_y −500 – 500
selection_anchor end, start
smooth, use_uia true, false
[animation] duration_ms 10 – 2000
snap_distance 0 – 10000
poll_interval_ms 16 – 2000

Command line

Command Purpose
--config Config file path and the values in effect
--languages Installed keyboard layouts and the label each produces
--probe Live view of language, IME state and caret coordinates, per application
--selftest Verifies the build, environment and settings, then exits (exit code 0 on success)

Exit codes: 0 success, 1 selftest failure or an instance already running, 2 the settings file was rejected.

The binary is a GUI-subsystem application, so PowerShell does not wait for it: the prompt comes back before the diagnostic output is printed, which makes it look as though the program is still running. It is not — it has already exited. Pipe it if you want the shell to wait:

.\LanguageIndicator.exe --config | Out-Host

--probe is the tool to reach for when the badge misbehaves in a particular application: it shows which strategy located the caret, and — when coordinates were found but discarded — why they were rejected.

Building

Visual Studio 2022 with the Desktop development with C++ workload. There are no external dependencies.

The simplest route, from any terminal:

powershell -ExecutionPolicy Bypass -File tools\build.ps1

That script locates Visual Studio, puts its bundled CMake on PATH for the current process, and builds. -Configuration Debug and -Clean are available.

A standard Visual Studio installation does not put cmake on the system PATH — it lives inside the VS directory — so a plain terminal will not find it. To run CMake yourself, either use the script above or start from Developer PowerShell for VS 2022 in the Start menu.

Presets

Two generators are configured. Both use the same MSVC toolchain with the same flags and produce equivalent binaries; they differ only in what drives the build.

Preset Generator Output
vs2022 Visual Studio 17 2022 build\vs2022\<config>\ Multi-config, and produces a .sln you can open and debug in the IDE. CMake finds Visual Studio on its own, so it works from any shell.
ninja-release, ninja-debug Ninja build\ninja-<config>\ Much faster, but single-config, no .sln, and requires a Developer PowerShell because Ninja invokes cl.exe directly and needs the compiler environment.
# Visual Studio generator
cmake --preset vs2022
cmake --build --preset vs2022-release

# Ninja — from Developer PowerShell for VS 2022
cmake --preset ninja-release
cmake --build --preset ninja-release

Ninja builds noticeably faster, which is worth having while iterating.

The result is LanguageIndicator.exe, linked against the static CRT.

How it works

Three questions have to be answered continuously.

What is the input language? The keyboard layout of the foreground thread (GetKeyboardLayout), mapped to an ISO 639 code. Layouts are per-thread, so the foreground thread must be asked specifically.

What is the IME conversion mode? WM_IME_CONTROL / IMC_GETCONVERSIONMODE, sent with SendMessageTimeout so an unresponsive application cannot hang the indicator. The message goes to the focused control, not the top-level window — the conversion mode belongs to the input context, and the input context belongs to whatever actually receives input. Readings are validated against the language (Korean cannot have the katakana bit set) and transient failures are held over, because applications do return garbage during transitions.

Where is the caret? A fallback chain, because no single API covers everything:

  1. GetGUIThreadInfo — synchronous and cheap; classic Win32 controls.
  2. MSAA OBJID_CARET.
  3. UI Automation TextPattern.

Steps 2 and 3 are cross-process COM calls that block if the target application stops pumping messages, so they run on a dedicated STA worker thread; the main thread only ever reads finished results. Every candidate rectangle is validated — a caret is tall and narrow, and some providers return the geometry of an off-screen element instead.

Updates are driven by SetWinEventHook (foreground, focus, text-selection changes) rather than by polling, with a low-frequency timer only for the IME conversion mode, which has no event to subscribe to.

The overlay is a layered, click-through, never-activated window rendered with Direct2D and DirectWrite, composited via UpdateLayeredWindow.

Known limitations

  • Elevated windows. An unelevated process cannot read the caret or IME state of a window running as administrator (Windows UIPI). The last known state is kept instead.
  • Console windows. Windows Terminal and friends sometimes return implausible conversion modes; the validator rejects them and only the language code is shown.
  • Secure desktops. Nothing is displayed over UAC prompts or the lock screen — by design.
  • use_uia = false disables selection detection, so clicking a field that selects all its text places the badge wherever that application keeps its caret (Chrome keeps it at the start). Applications that MSAA cannot serve show nothing at all. Leave it on unless you are isolating a problem.
  • Unsigned binaries. The executable is not code-signed, so SmartScreen will warn on first run. Security software may also treat it as suspicious: reading other processes' input state through global hooks and the accessibility APIs is, by behaviour alone, hard to tell apart from software you would not want. Nothing here records or transmits keystrokes — the source is short enough to check.

Design notes

docs/PLAN.md is the working record kept while building this: the technology choice and why, the risky parts, and a round-by-round log of what each application actually did when measured. Most of the non-obvious code here exists because of something in that file.

License

MIT — see LICENSE.

About

Shows your current keyboard language — and IME input mode for CJK layouts — right next to the text caret. Windows, single exe, no dependencies.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages