BiDi · terminals · Claude Code

No terminal renders Hebrew correctly. Here is what it took to fix it.

Half the world's terminal emulators have no bidirectional text engine at all, and the ones that do disagree about what a paragraph is. This is a field report from a year of making an AI coding agent readable in Hebrew, Arabic and Persian: what breaks, why the obvious fixes are wrong, one measured result that killed our favourite idea, and the two stupid bugs that cost us the most.

1. The thing that actually breaks

Type a sentence that mixes Hebrew and English into most terminals and the words survive, but their order does not. A file path lands on the wrong side of the sentence. A version number jumps to the far edge of the line. Parentheses point the wrong way. Nothing is corrupted, every character is intact, and the line is still unreadable.

This is not a font problem and it is not an encoding problem. It is the Unicode bidirectional algorithm, and whether your terminal implements it.

2. Why terminals are structurally bad at this

A browser lays out a paragraph. It knows where the paragraph starts and ends, so it can run the full bidirectional algorithm over the whole thing, work out the nesting levels, and reorder runs of text before painting.

A terminal emulator is a grid of cells. Row 24, column 60 holds one character. The program writing to it moves a cursor around and paints cells, sometimes repainting the same row many times per second. There is no paragraph. There is barely a line, because the application can rewrite half of it a moment later.

So the terminal has two bad options. Either it ignores direction entirely and paints characters in the order they arrive, which is what most do, or it guesses a paragraph boundary and reorders, which breaks the moment an application assumes it can address cells by column. Terminal emulator authors have mostly chosen the first, and they are not being lazy: doing this correctly conflicts with what a terminal is.

3. Which terminals work, tested

We tested these against mixed Hebrew and English output rather than trusting documentation.

TerminalBidirectional textVerdict
Konsole (KDE)Reorders, and honours a right-to-left mark at position 0 to set line directionusable
Apple TerminalNone at allbroken
iTerm2Mirrors Hebrew, reversing letter order inside wordsbroken
WezTermShaping present but incorrect for this casebroken
Windows TerminalNo paragraph reorderingbroken

The practical consequence is uncomfortable and worth stating plainly: on macOS today there is no native terminal that renders mixed Hebrew and English correctly. We shipped macOS support, then withdrew it, because no amount of work on our side fixes a host that will not reorder. That is why the tool we ended up building is Windows and Linux only, and why Mac users get pointed at a different setup instead of a broken one.

Testing note that cost us three releases. Never test a keyboard or direction change on a machine with leftover state. A previous layout can linger on the display server, and an existing terminal window can adopt an old backend, so a contaminated test shows a fix working when a clean test shows it failing. Shut everything down between runs.

4. Why an AI agent makes it worse

A shell prints a line and moves on. An AI coding agent runs a full text user interface: spinners, progress, collapsible trees, a status line that repaints continuously. Every repaint is another chance to get direction wrong, and any mistake is visible while you are reading.

One detail did more damage than the rest. Claude Code prefixes each assistant message with a bullet character. A bullet is directionally neutral, and Konsole's heuristic treats the first non-whitespace character of a line as the anchor for that line's direction. A neutral character anchors left-to-right. So every Hebrew reply had its first line stuck in the wrong direction while the rest of the message was fine. One character, at position 0, breaking the most visible line of every answer.

5. What we built, and one thing we refused to do

The fix is a wrapper. Claude Code runs inside a pseudo terminal, its output stream passes through a transform before reaching the emulator, and the transform injects Unicode direction control characters:

The interesting part is what the transform deliberately does not do. It never substitutes characters. Browser-layer right-to-left extensions often swap arrows, turning a right arrow into a left arrow inside a Hebrew paragraph. For prose in a document that is correct behaviour. In a terminal it is destructive, because the tree renderers, box drawing and status indicators of a text user interface rely on the exact glyph they emitted. Direction comes from direction marks only, never from swapping characters. There is a comment in our injector telling the next person not to add a substitution table, because it looks like an obvious improvement and it is not.

6. The display stack rabbit hole

Getting the right terminal onto Windows means running it under the Linux subsystem, and that is where a second class of problem lives. A sequence of findings, each of which looked like a different bug:

None of this is Hebrew-specific. It is what it costs to put a specific terminal emulator in front of a user who just wants to type in their own language.

7. A measured negative result: the Hebrew tax

Hebrew is expensive. The same answer costs roughly 31% more output tokens in Hebrew than in English, because of how the tokenizer splits the script. For anyone on a usage limit that is a real tax on working in their own language.

So we built an optimizer: strip diacritics and invisible marks from prompts, collapse whitespace while leaving code blocks untouched, and wrap the request in a short English frame, keeping the user's words verbatim.

Then we measured it, paired A/B, on two models including extended thinking at maximum effort. The net effect on output tokens was statistically indistinguishable from zero. On one configuration it came out at minus 0.5%, with a confidence interval from minus 5.8% to plus 4.8%, while the English frame adds input tokens.

The reason is structural, and obvious in hindsight. The cost lives in the output tokenizer, the answer has to stay in Hebrew because that is the entire point, and the model already reasons efficiently regardless of the prompt's language. There was nothing for the lever to grab. We shipped it off by default and wrote up the negative result instead of quietly deleting it.

Why publish a failed optimization? Because the next person with this idea will otherwise spend the same weeks on it. The Hebrew tax is real, it is worth attacking, and prompt-side rewriting is not the way to attack it.

8. The two dumbest bugs, which cost the most

After all of the above, the two bugs that hurt real users most were not about bidirectional text at all.

A codepage. The folder picker wrote the chosen project path to a small file as UTF-8. The launcher script read it back with the console's default codepage, which on a Hebrew Windows is 862, not UTF-8. Any project folder with a Hebrew name came back as mojibake, failed its existence check, and the launcher fell back to the home directory. The user picked a folder and the tool opened somewhere else, with no error. One missing line fixed it.

A trailing backslash. On Windows, a backslash immediately before a closing quote escapes that quote. So passing a folder that ends in a backslash, which is exactly what a drive root looks like, collapses the rest of the command line into the argument. We measured it: a child process received one argument instead of three. The terminal then reported that it could not find a path, and the agent never started. The fix is to strip the trailing separator and restore a bare drive letter as a real directory.

Both had been shipping for months behind the interesting problems. If you build tooling for other people's machines, the boring path handling deserves the same suspicion as the algorithm.

9. What we still want from upstream

Everything above is a workaround. The real fixes belong in two places: terminal emulators growing a bidirectional engine that survives cell addressing, and text user interface frameworks not assuming that column order equals reading order. Until then, right-to-left users get a wrapper, and the wrapper has to be maintained against every upstream repaint change.

If you read Hebrew, Arabic, Persian or Urdu and you have given up on the terminal, it is worth another look. If you maintain a terminal emulator, the test is one line of mixed text, and it takes ten seconds to find out where you stand.

The tools

Both are free, open source, and the whole wrapper is readable in a single directory.

Kivun Terminal, right-to-left Launchpad CLI, left-to-right

Windows and Linux. If it saves you an afternoon, a star on the repo is how other people find it.

Referenced from the source
  • The bidirectional wrapper, including the comment asking you not to add character substitution: kivun-claude-bidi
  • The Hebrew tax measurement and why the optimizer ships disabled: same repository, pull requests 84 and 102
  • Hebrew version of the practical fix guide: Claude בעברית הפוכה