Passive language learning inside Claude Code.
Every so often — you choose how often — Claude answers your normal technical question with a language you're learning woven into the response. The code, the commands, and every critical fact stay in English. The language is seasoning.
🌍 langlearn: French sprinkle this turn (light, beginner)
Je regarde ça. (I'm looking at that.) The migration fails because `users.email`
has a UNIQUE constraint but the seed data has two identical rows. Fix:
DELETE FROM users a USING users b WHERE a.id > b.id AND a.email = b.email;
Il faut lancer ça avant la migration. (You need to run that before the migration.)
📚 lancer (- — to run / launch)
New words get logged automatically, and previously-learned ones are fed back into later turns as spaced repetition.
In Claude Code:
/plugin marketplace add nanaknihal/langlearn
/plugin install langlearn
Then tune it with the /lang command:
/lang # status
/lang set fr 20 # 20% of prompts get French
/lang add es Spanish # teach it a new language
/lang off # kill switch
git clone https://github.com/nanaknihal/langlearn.git
cd langlearn && ./install.shThis installs to ~/.langlearn, links lang onto your PATH, and registers the
hook in ~/.claude/settings.json (backing it up first). Restart Claude Code.
Undo with ./uninstall.sh (add --purge to also delete your vocabulary).
Requires: bash, jq, shuf, awk.
On macOS: brew install jq coreutils. On Debian/Ubuntu: apt install jq coreutils.
A UserPromptSubmit hook fires on every prompt and rolls a die. Percentages are
absolute across all languages — French 15% + Spanish 10% means 25% of prompts get
a language and 75% are ordinary English turns.
When a turn triggers, the hook injects instructions telling Claude to weave that language in at the configured dose and difficulty, with a hard rule that the technical content must remain fully understandable from the English alone. It also passes two random previously-learned words back for reuse.
Three anti-overwhelm gates, all tunable: a cooldown between triggered turns, a daily cap, and automatic skipping of slash commands and trivial prompts.
The language never leaks outside the chat — commit messages, file contents, PRs, docs, and published artifacts stay 100% English by instruction.
lang status: probabilities, dose, today's count, vocab size
lang on | off kill switch
lang add <code> <Name> [Script] add a language; Script = non-Latin script name
lang remove <code> remove one (vocabulary is kept)
lang set <code> <percent> chance a prompt gets that language
lang level <code> <beginner|intermediate|advanced>
lang intensity <light|medium|deep> how much language per triggered turn
lang cooldown <minutes> min gap between triggered turns
lang max <n> max triggered turns per day
lang notice <on|off> show/hide the "sprinkle this turn" banner
lang script <code> <both|native|latin>
lang vocab [code] everything you've learned
lang quiz [n] [code] quiz yourself (add --answers to reveal)
lang stats vocab growth over time
lang export [code] Anki-ready CSV
lang add es Spanish # Latin script
lang set es 20
lang add ja Japanese "Kanji/Kana" # non-Latin: adds transliteration to the format
lang add hi Hindi Devanagari
lang add pa Punjabi GurmukhiFor non-Latin languages, lang script <code> latin switches to
transliteration-only — useful when your terminal can't render the script.
| intensity | what you get |
|---|---|
light (default) |
1–2 words woven into the response |
medium |
one full sentence plus a couple of words |
deep |
opening and summary in the language, ~⅓ of the response |
Start at light with a 10-minute cooldown. It should feel like a pleasant
surprise, not a lesson.
Your data lives in ~/.langlearn (override with $LANGLEARN_HOME) and is never
touched by updates:
config.json— all tunables (thelangCLI edits this)vocab.tsv—date · language · word · translit · meaning.state.json— cooldown and daily-count state
MIT