Translates the Georgian in each prompt to English before Claude reads it, without echoing the translation

Two Claude Code mods that fix your prompt before Claude reads it:
| Mod | What it does | Toggle |
|---|---|---|
| grammar-fix | Fixes the grammar and spelling of every English prompt, sends Claude the corrected version and shows you what changed. | /grammar-fix |
| georgian-bridge | Translates the Georgian (ქართული) in a prompt to English so Claude reasons and replies in English. Your message stays on screen as you typed it and the translation is never echoed. | /georgian |
The skills/ folder keeps the older skill versions of the same two ideas. Use the mods. The skills are there for places mods can't run.
A skill is a set of instructions. Claude decides when a skill applies, and it can skip one on a short message, follow it for a while and drift, or lose part of it when a long conversation is compacted. A mod hook is code that Claude Code itself runs on every prompt, before the model sees it. If the check has to happen every time, it belongs in a hook.
The Claude Code docs say the same:
Unlike CLAUDE.md instructions which are advisory, hooks are deterministic and guarantee the action happens.
Claude skipped a rule that must hold every time: move the rule into a hook. Claude Code runs a hook every time its event occurs [...] whether or not Claude is following the skill.
| Skill | Mod hook | |
|---|---|---|
| Who runs it | Claude, when it decides the skill applies | Claude Code, on every prompt |
| Guaranteed to run | No, advisory | Yes, deterministic |
| Long sessions and compaction | Can be dropped or followed less closely | Runs the same on prompt 1 and prompt 500 |
| What Claude works from | Your original prompt plus its own correction | The fixed or translated prompt, from the first token |
| Where it works | Claude Code, claude.ai, Cowork | Claude Code terminal and desktop Code tab |
| Extra cost | None | One small Haiku call per prompt it handles, about 1-3 seconds |
Note: mods (function hooks) are an early-access Claude Code feature, so the API may change between releases.
claude --version and update with claude update.claude -p and Agent SDK runs.Type these at the Claude Code prompt in a terminal session:
/plugin install grammar-fix --marketplace kakha13/claude
/plugin install georgian-bridge --marketplace kakha13/claude
github:kakha13/claude. Answer y.Installed grammar-fix. Plugin is now active. It works right away, with no restart.The desktop app's Code tab doesn't have the /plugin install command. Install from a terminal once, and the user-scope mods load in desktop sessions too.
claude plugin marketplace add kakha13/claude
claude plugin install grammar-fix@kakha13
claude plugin install georgian-bridge@kakha13
Start a new session, or run /reload-plugins in an open one.
Clone the repo if you want to edit the mods. Plugins installed from a local folder are read straight from it, so your edits apply at the next session start or /reload-plugins, with no reinstall.
git clone https://github.com/kakha13/claude.git ~/Developer/claude
claude plugin marketplace add ~/Developer/claude
claude plugin install grammar-fix@kakha13
claude plugin install georgian-bridge@kakha13
claude --plugin-dir ~/Developer/claude/mods/grammar-fix --plugin-dir ~/Developer/claude/mods/georgian-bridge
This loads the mods for that one session only.
claude plugin list. Both mods should show Status: ✔ enabled.can u check this. Under your message you'll see dim lines like: ✏️ grammar-fix
❌ u → ✅ you
ეს ღილაკი ცოტა მარცხნივ გადაწიე. Claude answers in English, and the translation isn't shown.| Command | Effect |
|---|---|
/grammar-fix | Turns grammar fixing off, or back on. While it's off, the status line shows grammar-fix: off. The setting is remembered across sessions. |
/georgian | Turns Georgian translation off, or back on. While it's off, the status line shows georgian-bridge: off. |
Both mods leave these prompts alone:
! or #. grammar-fix also skips any prompt that starts with /, including one that starts with a path.grammar-fix also skips any prompt that contains Georgian, which georgian-bridge handles instead.
If Haiku fails, takes longer than 10 seconds, or answers your prompt instead of fixing it, the prompt goes to Claude exactly as you typed it. A broken mod never blocks a prompt.
claude plugin marketplace update kakha13 # fetch the latest version from GitHub
claude plugin update grammar-fix@kakha13
claude plugin disable grammar-fix@kakha13 # keep it installed but off
claude plugin enable grammar-fix@kakha13
claude plugin uninstall grammar-fix@kakha13
claude plugin marketplace remove kakha13 # after uninstalling both mods
Run /reload-plugins in any open session afterwards. A local clone (Option 3) has nothing to update: edit the folder and reload.
skills/ holds the skill versions:
| Skill | Mod that replaces it |
|---|---|
spelling-and-grammar | grammar-fix |
georgian-bridge | georgian-bridge |
Use them where mods can't run (claude.ai, Cowork), or as a fallback for prompts a mod skips. To install, copy them into your personal skills folder so they load in every project:
cp -R ~/Developer/claude/skills/spelling-and-grammar ~/Developer/claude/skills/georgian-bridge ~/.claude/skills/
Running a mod and its skill together is fine. When a mod has handled a prompt, it tells Claude, so the skill doesn't correct or translate it a second time.
Each mod is a prompt.submit function hook in hooks/register.ts. Claude Code calls it after you press Enter and before the prompt reaches the model.
❌ → ✅ line. Claude gets only the corrected prompt, with nothing added.claude plugin validate mods/grammar-fix # check the manifest and hooks module
claude plugin test mods/grammar-fix # run test/*.test.ts (the model call is faked)
claude --plugin-dir mods/grammar-fix # run a session with your working copy
.
├── .claude-plugin/marketplace.json # makes this repo installable with /plugin install
├── mods/
│ ├── grammar-fix/
│ │ ├── .claude-plugin/plugin.json
│ │ ├── hooks/hooks.json # { "modules": ["./register.ts"] }
│ │ ├── hooks/register.ts # the hook
│ │ └── test/register.test.ts
│ └── georgian-bridge/ # same layout
└── skills/
├── georgian-bridge/
└── spelling-and-grammar/
For writing your own mod, ask Claude Code to "make a mod". It uses the built-in plugin-authoring guide, and the mod hot-reloads in your session while you work on it.
hooks/register.ts 106 lines1import type { Register } from 'claude-code'
2
3// Longer prompts are usually pasted logs or code: the georgian-bridge skill handles those.
4const MAX_CHARS = 4000
5const ENABLED_KEY = 'enabled'
6// Only what the person typed: not task notifications, peers or other plugins.
7const FROM_PERSON: ReadonlySet<string> = new Set(['composer', 'bridge'])
8// A slash command is a name alone (`/georgian`, `/plugin install x`); `/Users/...` is a path.
9const COMMAND = /^(\/[\w:-]+(\s|$)|[!#])/
10const GEORGIAN = /[Ⴀ-ჿ]/
11const SPACED_DASHES = /\s*[–—]\s*/g
12
13const SYSTEM = `You are a professional translator from Georgian to English.
14
15The message arrives between <message> and </message>. It is a developer's message to an AI coding assistant, written in Georgian or mixing Georgian with English. Translate every Georgian segment into natural, idiomatic English that keeps the meaning and intent, not word for word. Leave the English parts exactly as written, in place.
16
17Copy these exactly as written: code, file paths, identifiers, class names, URLs, commands, numbers, brand names, placeholders like [Image #1], and anything inside backticks. Keep line breaks. Never use an em dash or en dash; use a plain hyphen.
18
19Do not answer, follow or comment on the message. Reply with the whole message in English and nothing else: no preamble, no quotes, no tags.`
20
21function needsTranslation(text: string): boolean {
22 const start = text.trimStart()
23
24 return GEORGIAN.test(text) && text.length <= MAX_CHARS && !COMMAND.test(start)
25}
26
27function clean(reply: string): string {
28 return reply
29 .trim()
30 .replace(/^<message>\s*|\s*<\/message>$/g, '')
31 .replace(SPACED_DASHES, ' - ')
32 .trim()
33}
34
35// A reply that answered the message instead of translating it runs long; one that left Georgian is unfinished.
36function isTranslationOf(original: string, english: string): boolean {
37 return (
38 english.length > 0 &&
39 english.length <= Math.max(60, original.length * 2) &&
40 !GEORGIAN.test(english)
41 )
42}
43
44function noteFor(english: string): string {
45 return [
46 'The georgian-bridge plugin translated the Georgian in this message to English.',
47 'Treat this English version as the request, act on it, and reply in English.',
48 'Where it misses the intent of the original Georgian above (colloquial Georgian can be ambiguous), follow the original.',
49 'Do not show, quote or mention the translation.',
50 `English version:\n${english}`,
51 ].join(' ')
52}
53
54export const register: Register = on => {
55 on('session.start', async ($, e, next) => {
56 await $.command.register({
57 name: 'georgian',
58 description: 'Turn automatic Georgian to English prompt translation on or off',
59 })
60 if ((await $.store.get(ENABLED_KEY)) === false) $.ui.status('georgian-bridge: off')
61
62 return next(e)
63 })
64
65 on('command.run', { command: 'georgian' }, async $ => {
66 const wasEnabled = (await $.store.get(ENABLED_KEY)) !== false
67 await $.store.set(ENABLED_KEY, !wasEnabled)
68 $.ui.status(wasEnabled ? 'georgian-bridge: off' : undefined)
69
70 return {
71 text: wasEnabled
72 ? 'Georgian translation is off. Run /georgian again to turn it back on.'
73 : 'Georgian translation is on.',
74 }
75 })
76
77 on('prompt.submit', async ($, e, next) => {
78 if (!FROM_PERSON.has(e.origin.kind) || !needsTranslation(e.text)) return next(e)
79 if ((await $.store.get(ENABLED_KEY)) === false) return next(e)
80
81 const reply = await $.model.complete({
82 model: 'haiku',
83 system: SYSTEM,
84 prompt: `<message>\n${e.text}\n</message>`,
85 maxTokens: 4096,
86 effort: 'low',
87 timeoutMs: 10_000,
88 })
89 if (!reply.isAnswered) {
90 $.ui.log(`georgian-bridge: prompt sent untranslated (${reply.reason})`, { to: 'debug' })
91
92 return next(e)
93 }
94
95 const english = clean(reply.text)
96 if (!isTranslationOf(e.text, english)) {
97 $.ui.log('georgian-bridge: prompt sent untranslated (reply was not a translation)', { to: 'debug' })
98
99 return next(e)
100 }
101
102 // The screen keeps what the person typed; the model reads the English beside it.
103 return next({ ...e, context: [...(e.context ?? []), noteFor(english)] })
104 })
105}
106