Progressive language-immersion drip for Claude Code: preprompt a % of each reply in a target language, monitor the real ratio, ramp it up as you keep up.
1
stars
19
commits
Python
primary language
Aug 20, 2026
updated
/ˈlæŋɡwə ʒɛm/
A progressive language-immersion drip for Claude Code. It preprompts, forces and monitors how much of each reply comes back in a target language, and ramps that share up on evidence you're keeping up, not on a calendar.
Standalone: one Python file, stdlib only, no pip installs. A blend drip, not a hard language switch.
Four layers, wired as three Claude Code hooks plus a statusline segment:
languageme hook injects the drip into
every new session: blend ~N% of your prose into the target, keep the rest in
your usual reply language. It states N as a concrete per-paragraph quantum
(a model cannot self-calibrate a raw percentage) and tells you to judge each
reply against the target, not against your own earlier replies. N lives in
state. The target is confined to chat prose, never code, commits, filenames,
tool arguments, or any clause where a wrong word breaks something.languageme nudge closes the loop the
one-shot preprompt cannot. Before each reply it injects one live line from the
previous turn's measured blend: last reply 80% vs ~16% target, well over, one short phrase per paragraph. This re-asserts the target every turn (immune
to context growth) and corrects both overshoot and full dropout, the two ways
an open-loop drip drifts.languageme monitor reads the finished transcript,
measures the real target-language share you produced (char-weighted,
per-token classifier that reads the intra-sentence blend, no ML dependency),
tracks whether you asked for help or wrote the language yourself, and moves N
per the ramp policy. The ramp reads the session-cumulative blend and gates on
a band: too much target counts as noncompliance, not mastery, so overshoot
never ramps N up.A statusline segment rides along so you always see where the dial is.
Reading sv 12%↑ ~11:
sv : target language12% : current blend target (what the preprompt aims for)↑ : the gauge just moved up on the last turn (↓ down, nothing = steady)~11 : the real share of target language measured on the last turn (~ =
roughly). No ~N yet means no turn has been measured.install wires it non-destructively: if you already have a statusline it wraps
it and appends the segment, if you don't it adds one. It never edits or replaces
your statusline.
The blend only ever grows when the real measured share holds the target and you are not asking for help.
./languageme init sv # seed Swedish at 10%
./languageme install # wire hooks + statusline (edits ~/.claude/settings.json)
./languageme status # target, blend %, recent measurements, ramp state
The preprompt loads at SessionStart, so the drip starts on your next Claude
Code session. To activate it in the current one without leaving, run /clear
(it re-fires SessionStart). Until the first turn is measured the statusline
shows the target with no ~N, and the gauge stays put; the ~N and any ramp
appear once a turn has run under the drip.
Put it on your PATH if you like: ln -s "$PWD/languageme" ~/bin/languageme.
The hooks call it by absolute path, so a symlink is optional.
Switch target any time: languageme lang sv|es|lv. The gauge and ramp follow
the new language.
languageme ramp <mode>)languageme bump +5.Natural-language overrides always win, both directions, and they are language-aware. Say "too much swedish" (or the target's own words, "för mycket svenska", "demasiado español", "trop de français") in chat and the next monitor pass steps the blend down; say "more swedish" / "mer svenska" / "más español" and it steps up. Asking to switch back ("in english", "en français") or to gloss a word ("what does X mean") counts as needing help and holds the ramp. Ordinary coding talk ("translate the config", "the query got slower") does not.
languageme help <mode>)word (translation) on first use,
fades as they recur.Add languageme phonetic on to layer a short pronunciation respelling on the
tricky words (the å/ä/ö vowels, sj/tj/rs clusters, silent letters), e.g.
sköldpadda (turtle, "HYULD-pad-da"). Plain learner respelling, not IPA, only
where the spelling hides the sound. Off by default; ignored when help is off.
Two levels, so you can always shut it off:
languageme off pauses the drip (no preprompt, no
measuring; the statusline shows sv off). languageme on resumes it. The
state persists across sessions.export LANGUAGEME_OFF=1 fully disables it for that
shell or session, overriding state no matter what. The hook injects nothing,
the monitor does nothing, and the statusline falls back to pure passthrough
(your original line, no segment). The guaranteed off switch.init <lang> seed a target language (sv, lv, de, es, it, ...)
status full state readout
statusline [--wrap] compact segment; --wrap runs the wrapped statusline too
hook [SessionStart] print the preprompt
monitor [--session P] [Stop] measure a turn, ramp the %
[--full] [--quiet] re-measure whole transcript / silence the report
set pct|goal|floor|step <n> tune the numbers
bump [+n] nudge the blend now
goal <n> ceiling to ramp toward
help on|off|auto inline-gloss policy
phonetic on|off pronunciation cues on tricky words (å/ä/ö...)
ramp mastery|calendar|manual
lang [<code>] show / switch target
off | on pause / resume the drip (persists)
install | uninstall wire / unwire hooks + statusline
update self-update (git pull the tool's checkout)
Measurable targets: sv, es, lv. Others get the drip but no % yet.
Hard kill for a single shell/session (overrides everything): LANGUAGEME_OFF=1
Lives in $LANGUAGEME_HOME (default ~/.languageme/state.json). The code is
portable; the state is the only thing that mutates. install backs up your
settings to settings.json.languageme.bak before its first edit.
LEXICONS /
LANG_CHARS registries; the target is scored against all the others. Adding a
language is those two entries, nothing else. The drip and preprompt work for
any language out of the box, only the measurement half is lexicon-bound (an
unmeasured target just holds the % at 0).install appends its hook entries and wraps (never replaces) your statusline;
it leaves every existing hook and statusline untouched. uninstall removes
only its own and restores the original statusline.flock-serialized so parallel Claude Code
sessions can't clobber each other's measurements.MIT. See LICENSE. Use it, fork it, ship it.
19 commits
Python
100.0%
Progressive language-immersion drip for Claude Code: preprompt a % of each reply in a target language, monitor the real ratio, ramp it up as you keep up.
1
stars
19
commits
Python
primary language
Aug 20, 2026
updated
/ˈlæŋɡwə ʒɛm/
A progressive language-immersion drip for Claude Code. It preprompts, forces and monitors how much of each reply comes back in a target language, and ramps that share up on evidence you're keeping up, not on a calendar.
Standalone: one Python file, stdlib only, no pip installs. A blend drip, not a hard language switch.
Four layers, wired as three Claude Code hooks plus a statusline segment:
languageme hook injects the drip into
every new session: blend ~N% of your prose into the target, keep the rest in
your usual reply language. It states N as a concrete per-paragraph quantum
(a model cannot self-calibrate a raw percentage) and tells you to judge each
reply against the target, not against your own earlier replies. N lives in
state. The target is confined to chat prose, never code, commits, filenames,
tool arguments, or any clause where a wrong word breaks something.languageme nudge closes the loop the
one-shot preprompt cannot. Before each reply it injects one live line from the
previous turn's measured blend: last reply 80% vs ~16% target, well over, one short phrase per paragraph. This re-asserts the target every turn (immune
to context growth) and corrects both overshoot and full dropout, the two ways
an open-loop drip drifts.languageme monitor reads the finished transcript,
measures the real target-language share you produced (char-weighted,
per-token classifier that reads the intra-sentence blend, no ML dependency),
tracks whether you asked for help or wrote the language yourself, and moves N
per the ramp policy. The ramp reads the session-cumulative blend and gates on
a band: too much target counts as noncompliance, not mastery, so overshoot
never ramps N up.A statusline segment rides along so you always see where the dial is.
Reading sv 12%↑ ~11:
sv : target language12% : current blend target (what the preprompt aims for)↑ : the gauge just moved up on the last turn (↓ down, nothing = steady)~11 : the real share of target language measured on the last turn (~ =
roughly). No ~N yet means no turn has been measured.install wires it non-destructively: if you already have a statusline it wraps
it and appends the segment, if you don't it adds one. It never edits or replaces
your statusline.
The blend only ever grows when the real measured share holds the target and you are not asking for help.
./languageme init sv # seed Swedish at 10%
./languageme install # wire hooks + statusline (edits ~/.claude/settings.json)
./languageme status # target, blend %, recent measurements, ramp state
The preprompt loads at SessionStart, so the drip starts on your next Claude
Code session. To activate it in the current one without leaving, run /clear
(it re-fires SessionStart). Until the first turn is measured the statusline
shows the target with no ~N, and the gauge stays put; the ~N and any ramp
appear once a turn has run under the drip.
Put it on your PATH if you like: ln -s "$PWD/languageme" ~/bin/languageme.
The hooks call it by absolute path, so a symlink is optional.
Switch target any time: languageme lang sv|es|lv. The gauge and ramp follow
the new language.
languageme ramp <mode>)languageme bump +5.Natural-language overrides always win, both directions, and they are language-aware. Say "too much swedish" (or the target's own words, "för mycket svenska", "demasiado español", "trop de français") in chat and the next monitor pass steps the blend down; say "more swedish" / "mer svenska" / "más español" and it steps up. Asking to switch back ("in english", "en français") or to gloss a word ("what does X mean") counts as needing help and holds the ramp. Ordinary coding talk ("translate the config", "the query got slower") does not.
languageme help <mode>)word (translation) on first use,
fades as they recur.Add languageme phonetic on to layer a short pronunciation respelling on the
tricky words (the å/ä/ö vowels, sj/tj/rs clusters, silent letters), e.g.
sköldpadda (turtle, "HYULD-pad-da"). Plain learner respelling, not IPA, only
where the spelling hides the sound. Off by default; ignored when help is off.
Two levels, so you can always shut it off:
languageme off pauses the drip (no preprompt, no
measuring; the statusline shows sv off). languageme on resumes it. The
state persists across sessions.export LANGUAGEME_OFF=1 fully disables it for that
shell or session, overriding state no matter what. The hook injects nothing,
the monitor does nothing, and the statusline falls back to pure passthrough
(your original line, no segment). The guaranteed off switch.init <lang> seed a target language (sv, lv, de, es, it, ...)
status full state readout
statusline [--wrap] compact segment; --wrap runs the wrapped statusline too
hook [SessionStart] print the preprompt
monitor [--session P] [Stop] measure a turn, ramp the %
[--full] [--quiet] re-measure whole transcript / silence the report
set pct|goal|floor|step <n> tune the numbers
bump [+n] nudge the blend now
goal <n> ceiling to ramp toward
help on|off|auto inline-gloss policy
phonetic on|off pronunciation cues on tricky words (å/ä/ö...)
ramp mastery|calendar|manual
lang [<code>] show / switch target
off | on pause / resume the drip (persists)
install | uninstall wire / unwire hooks + statusline
update self-update (git pull the tool's checkout)
Measurable targets: sv, es, lv. Others get the drip but no % yet.
Hard kill for a single shell/session (overrides everything): LANGUAGEME_OFF=1
Lives in $LANGUAGEME_HOME (default ~/.languageme/state.json). The code is
portable; the state is the only thing that mutates. install backs up your
settings to settings.json.languageme.bak before its first edit.
LEXICONS /
LANG_CHARS registries; the target is scored against all the others. Adding a
language is those two entries, nothing else. The drip and preprompt work for
any language out of the box, only the measurement half is lexicon-bound (an
unmeasured target just holds the % at 0).install appends its hook entries and wraps (never replaces) your statusline;
it leaves every existing hook and statusline untouched. uninstall removes
only its own and restores the original statusline.flock-serialized so parallel Claude Code
sessions can't clobber each other's measurements.MIT. See LICENSE. Use it, fork it, ship it.
19 commits
Python
100.0%