# StringLane in Claude Code

> Install the plugin: an MCP server over your locale files, four slash commands, the skills, and a write guard that asks before Claude hand-edits a locale file. What the guard covers, what it deliberately does not, and how to remove it.

Source: https://stringlane.app/docs/cli/claude-code
Last updated: 2026-09-14

Claude Code is one of eight hosts StringLane installs into, and one of three that take it as a plugin. This page is that plugin. For the other seven, and for what each one's write guard can actually do, see [Agent hosts](/docs/cli/agent-hosts).

Four things, and the second is why this is a plugin rather than a line of configuration.

**An MCP server** over your localization files: inspect the project, plan what needs translating, validate a set of translations, apply them, add keys and locales.

**A write guard.** An MCP server only ever sees its own tool calls. It has no way to know an agent just wrote a locale file with `Write`, or with a shell command, and that has happened more than once in this project's history. The guard is a hook, so it sees both.

**Four commands**, listed below.

**Three skills**, for the parts no tool call finishes on its own: what each format can and cannot say, what adding a locale costs beyond the file, and what adding a string costs beyond the base locale.

## Install

```
/plugin marketplace add thebedcoder/stringlane-plugin
/plugin install stringlane@stringlane
```

Both lines are needed: the first registers the marketplace, the second installs from it. `/plugin marketplace add` clones a git repository, so the plugin lives in a public mirror rather than on npm.

Then:

```
/stringlane:setup
```

The plugin brings the MCP server and the write guard with it, but both call a `stringlane` binary that has to exist on your machine. `/stringlane:setup` finds out whether it does — and whether the copy it found is new enough to answer `stringlane hook`, which is the failure that looks exactly like working. It reads and reports; the one thing it can write is a package install, and it asks first.

Then, in a project with localization files:

```
/stringlane:init
```

It detects the project, shows you the config before writing it, reports what is translated and what is not, and offers one concrete piece of work. Every write is a separate yes.

## The four commands

| Command | What it does |
|---|---|
| `/stringlane:setup` | Checks that StringLane actually works in this session, and fixes what does not |
| `/stringlane:init` | Sets the project up and reaches a first validated translation |
| `/stringlane:translate <locale>` | Plan, validate, apply, for one language |
| `/stringlane:review` | Reviews existing translations for problems a validator cannot see |

On Codex CLI the same four arrive as skills, under the same names with a `stringlane-` prefix, because Codex does not load a `commands/` directory.

## What the guard does

Two halves, covering different things on purpose.

**Before an edit** — `Write`, `Edit`, `MultiEdit`, `NotebookEdit` — StringLane is asked whether the file is one it manages. If it is, **you** get asked, with the sanctioned route named. It never refuses on its own, and anything it cannot decide is allowed. A guard that blocks when it is confused is a guard that gets turned off, and then it guards nothing.

**After any tool runs**, including a shell command, StringLane compares your locale files against a baseline taken when the session started, and reports what changed. It cannot block, because the write already happened. It tells you and the agent, and points at `stringlane check`.

> **Prevention covers `Write`, `Edit`, `MultiEdit` and `NotebookEdit` only.** A shell command is covered *after the fact*, not prevented. Nothing in a `Bash` tool call says which file it will touch — the path can be computed — so there is nothing to decide about before it runs. No completeness should be read into the prevention half.

It also will not notice a locale file written into a directory your project keeps no locale files in, and it cannot stop an agent that removes the hook. It is a guard against a confused agent, which is what actually goes wrong.

## Adding a language is two jobs

StringLane creates `app_de.arb`, or `values-de/strings.xml`, or the entry in your string catalog. The other half is in your app, and it is the half that decides whether anyone ever sees the translation:

- **Flutter** — if your `MaterialApp` lists locales as `AppLocalizations.supportedLocales`, there is nothing to do. If it lists them literally, German is not selectable until you add it. Then `CFBundleLocalizations` in `Info.plist` on iOS, or the App Store will not list the language.
- **iOS** — Xcode records localizations in the project file. An `de.lproj` folder Xcode does not know about is a correct file that never ships.
- **Android** — the folder qualifier is usually enough. But a `localeFilters` or `resourceConfigurations` list in your Gradle file strips the language out of the APK with a green build, and appearing in Settings has its own locale config.
- **i18next** — `supportedLngs` accepts every language by default. If you have set it, a language missing from the array is rejected and falls back, and your JSON file sits on disk unread.

And in all four, a language picker with a hardcoded list.

Every one of these is optional, so none can be assumed. Miss one and you get the same symptom as having no translation at all, with a fully translated file in the repository.

The plugin's `locale-wiring` skill carries this per platform, so Claude raises the wiring instead of reporting the locale as done. `/stringlane:init` also scans for *yours* while it is already reading your codebase, and offers to write down what it found — the absences included, so a later session does not go looking for a picker you have never had. It goes in `.stringlane/locale-wiring.md`, and it asks first. Nothing here edits your app for you.

## Turn the guard off

The off switch matters as much as the guard, because a hook you cannot remove gets removed by uninstalling everything.

Take out just the hooks, leaving the MCP server:

```sh
stringlane setup hooks --host claude-code --remove
```

It removes StringLane's entries and leaves every other hook in that file exactly as it was. Or disable the whole plugin with `/plugin`.

To see what would be installed without installing it:

```sh
stringlane setup hooks --host claude-code
```

That prints the exact JSON and the exact file it would go in, and writes nothing.

## Without the plugin

The plugin is a convenience. The same guard installs into any project:

```sh
stringlane setup hooks --host claude-code --scope project --apply
```

`--scope project` writes `.claude/settings.json` in the repository, so your team gets the same guard. `--scope user` covers every project you open. Both back up what was there first and print where the backup went.

Claude Code is the only host where `--apply` writes. Everywhere else the setup commands print the change and you paste it, for the reason given in [Agent hosts](/docs/cli/agent-hosts).

## Windows

The MCP server and the hooks work. The bundled resolver does not: it is a POSIX shell script, and Windows will not run it. That costs one convenience, not the plugin. Install the CLI yourself once and everything else behaves identically:

```sh
npm install -g @stringlane/cli
```

## Frequently asked questions

### What does the StringLane write guard actually block?

It asks you before Write, Edit, MultiEdit or NotebookEdit changes a file it recognises as a locale file. It never refuses on its own, and anything it cannot decide is allowed. A shell command cannot be prevented, because nothing in the tool call says which file it will touch, so those are reported after the fact instead.

### How do I remove the StringLane hooks from Claude Code?

Run stringlane setup hooks --host claude-code --remove. It takes out StringLane entries and leaves every other hook in that file exactly as it was. Disabling the whole plugin with /plugin also works.
