glot
by MattJeff
README.md
# glot
**AI-native localization for SaaS.** Git is the source of truth, your coding agent is the interface, and every LLM output goes through deterministic code before it touches your catalogs.
`glot` is a Rust CLI + MCP server + optional API/dashboard. It works with the i18n library you already use (next-intl, i18next, FormatJS…) and ships no runtime.
## Why glot (vs Lokalise, Crowdin, Lingo.dev)
| | glot |
|---|---|
| **Deterministic validator** | Every translation is checked for ICU / i18next / MessageFormat 2 syntax, placeholders, CLDR plural categories, glossary terms and length. Failures are retried with the issues in the prompt, then escalated to a fallback model. Nothing invalid is written. |
| **Lockfile** | `glot.lock` hashes source and target per key. Only the delta is translated, and a translation edited by hand is **never** overwritten without `--force`. |
| **Glossary as code** | `glossary.toml` lives in your repo, is reviewed in PRs, and is enforced by the validator (required and forbidden translations, do-not-translate terms). |
| **Living context via MCP** | `glot mcp` gives Claude Code (or any MCP agent) your keys, usages in code (component, UI role, file:line), glossary and notes, so new strings are added and translated while you code. |
| **Visual QA** | `glot-visual-qa` screenshots every route × locale × viewport and flags overflowing, truncated, overlapping, untranslated or tofu text, with optional shorter-text suggestions. |
| **BYO key** | Anthropic, OpenAI, any OpenAI-compatible endpoint (Ollama…). Cost is estimated up front and budgeted. |
| **Open source** | MIT. No per-seat or per-word pricing, no lock-in: your catalogs stay plain JSON in Git. |
## Install
```sh
npx glot-i18n init # no install: npm fetches the binary
npm i -g glot-i18n # `glot` on PATH (macOS, Linux, Windows)
brew install mattjeff/glot/glot # Homebrew (macOS, Linux)
curl -fsSL https://raw.githubusercontent.com/MattJeff/glot/main/integrations/install.sh | sh # ~/.local/bin
cargo install --git https://github.com/MattJeff/glot glot-cli # from source
```
Every prebuilt binary comes from the [GitHub release](https://github.com/MattJeff/glot/releases) and is checked against its SHA-256 before install (pinned in the npm package and the formula; release digests for `install.sh`).
## Quickstart
```sh
glot init # detects your stack, writes glot.toml + agent rules
claude mcp add glot -- glot mcp # give Claude Code live i18n context
export ANTHROPIC_API_KEY=...
glot translate # translates missing/stale strings, validated
glot check # CI-friendly: --format text|json|sarif|github
```
### LLM providers
| Variable | Use |
|---|---|
| `ANTHROPIC_API_KEY` | Anthropic key (default provider; another name via `llm.api_key_env`) |
| `ANTHROPIC_WORKSPACE_ID` | Workspace to bill, required with organization-level keys (`sk-ant-usr-…`). On the server, the per-project setting wins. |
| `OPENAI_API_KEY` | `provider = "openai"` / `"openai-compatible"` |
The system prompt (rules + glossary) is a cached prefix, so repeated batches pay ~10 % of its price. `glot translate --batch` sends the first pass through Anthropic's Message Batches API (-50 %, asynchronous, resumable).
Other commands: `glot status`, `glot extract` (hardcoded strings, missing/unused keys), `glot set <locale> <key> "<value>"`, `glot glossary`, `glot push|pull`.
### GitHub Action
```yaml
on: pull_request
permissions: { contents: write, pull-requests: write }
jobs:
i18n:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
repository: ${{ github.event.pull_request.head.repo.full_name }}
ref: ${{ github.head_ref }}
- uses: MattJeff/glot/action@v0
with:
# Fork PRs get no secrets: only check them, translate on same-repo branches.
command: ${{ github.event.pull_request.head.repo.full_name == github.repository && 'both' || 'check' }}
anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}
```
It validates catalogs (inline annotations), translates the delta, commits catalogs + `glot.lock` to the PR branch and keeps one PR comment up to date with coverage, issues and cost. See [`action/action.yml`](action/action.yml).
### Visual QA
```sh
cd packages/visual-qa && pnpm install && pnpm exec playwright install chromium
node src/cli.ts --base-url http://localhost:3100 --routes /,/login --locales fr,de,ja,ar --suggest
```
Writes `.glot/visual-qa/report.{json,md}` and screenshots; exits 1 on overflow (`--fail-on`). `report.md` is ready to post as a PR comment. Add `data-glot-key="auth.login.cta"` to elements to get ready-to-run `glot set` fixes.
### Task parity (`glot parity`)
Users and agents complete fewer tasks in non-English UIs. `glot parity` measures it for your app: an agent (Claude Sonnet 5.5, screenshot + reduced accessibility tree, click/type/select/scroll) gets each goal **in the tested language** (translated once, cached in `.glot/parity/goals.json`) and tries to complete it in every locale.
```yaml
# glot.tasks.yaml — success conditions use catalog KEYS, so they hold in every language
- id: send_reminder
goal: Invoice INV-1042 is overdue. Send the customer a payment reminder.
start: /invoices
success: { text_visible_key: invoice_actions.reminder_sent } # and/or url_matches, selector
script: # only for --provider mock
- click: { selector: "a[href='/invoices/inv_1042']" }
- click: invoice_actions.send_reminder_button # found by its translated label
```
```sh
cd packages/agent-qa && pnpm install && cd -
glot parity --base-url http://localhost:3000 --locales en,fr,de,ja,ar [--url-pattern "{route}?lang={locale}"] [--locale-cookie NEXT_LOCALE] [--min-parity 90]
glot parity --provider mock # scripted, deterministic, no tokens (CI)
```
Writes `.glot/parity/report.{md,json}` and a screenshot per step: a task × locale matrix (success, steps, time), a parity score per locale (share of the tasks the source locale completes), and findings tied to keys — truncated label, control not found, ambiguous label, broken RTL, untranslated text — with a fix (`glot set fr invoice_actions.send_reminder_button "Relancer"` when the agent looked for "Relancer"). Run it against a production build: dev servers recompile mid-run. The report stays local (no upload endpoint); post `report.md` as a PR comment. Example tasks: `packages/agent-qa/examples/demo-saas.tasks.yaml`.
## Use with AI agents
- **Claude Code**: `claude plugin marketplace add MattJeff/glot && claude plugin install glot@glot` adds a `glot` skill (Claude uses it whenever you ask for a multilingual app), `/glot:i18nize`, `/glot:translate` and the MCP server. MCP only: `claude mcp add glot -- npx -y glot-i18n mcp`.
- **Cursor / Windsurf / Codex**: drop-in rules in [`integrations/agents`](integrations/agents) (`.mdc`, Windsurf rule, `AGENTS.md`); MCP command `npx -y glot-i18n mcp` (stdio, run inside a project with `glot.toml`). Listed in the official MCP registry as `io.github.MattJeff/glot`.
- **Lovable, Bolt, v0, Replit**: paste the [knowledge snippet](integrations/ai-builders-prompt.md) so generated code is i18n-ready, then [Translate this app](integrations/ai-builders.md#translate-this-app): one workflow run does `init` + `i18nize` + `translate` and opens a PR.
[](integrations/ai-builders.md#translate-this-app)
Show your coverage with a [`translated by glot · N languages · X%` badge](integrations/README.md#badge). All integrations: [`integrations/`](integrations).
## Demo
[`examples/demo-saas`](examples/demo-saas) is "Acme Invoices", a small Next.js SaaS with every string hardcoded in English (plurals by concatenation, copy in config objects, fixed-width buttons, ambiguous "Save"/"Archive" labels). [`examples/demo.sh`](examples/demo.sh) takes a throwaway copy from hardcoded English to validated catalogs, with commentary at each step:
```sh
examples/demo.sh # mock LLM, no key needed
DEMO_PAUSE=1 examples/demo.sh # pause between steps for a live demo
GLOT_REAL=1 ANTHROPIC_API_KEY=... examples/demo.sh # real translations
KEEP=1 examples/demo.sh # keep the copy, then `pnpm install && pnpm dev` (port 3200)
```
Steps: `init` → `extract` (117 hardcoded strings) → `i18nize` (24 files rewritten to next-intl, 132 keys) → minimal next-intl wiring + `next dev` → `translate --visual` → `check` → `fit` (German overflow fixed in the running app) → `review` → `glossary mine`. `NO_APP=1` skips the dev server (and thus `--visual`/`fit`). Commands missing from your build are skipped with a notice.
## Architecture
Rust workspace (`crates/core`, `extract`, `llm`, `mcp`, `cli`, `server`), Next.js dashboard in `web/`, Playwright QA in `packages/visual-qa`, GitHub Action in `action/`. Details, file formats and the HTTP API: [ARCHITECTURE.md](ARCHITECTURE.md).
## Local development
```sh
docker compose up -d postgres # postgres://glot:glot@localhost:5433/glot
cargo run -p glot-server # API on :8790
cd web && pnpm install && pnpm dev # dashboard on :3100
```
Tests: `cargo test --workspace` (needs `DATABASE_URL`), `cd web && pnpm lint && pnpm build`, `cd packages/visual-qa && pnpm test`, `cd packages/agent-qa && pnpm test`.
### Docker (full stack)
```sh
GLOT_SECRET_KEY=$(openssl rand -hex 32) docker compose --profile full up -d --build --wait
curl localhost:8790/healthz # ok (migrations run at startup)
open http://localhost:3100 # dashboard; /api and /cdn are proxied to the server
docker compose --profile full down # add -v to drop the database volume
```
Host ports default to 5433 / 8790 / 3100; override with `GLOT_PG_PORT`, `GLOT_API_PORT`, `GLOT_WEB_PORT` (set `GLOT_WEB_ORIGIN` to the web URL if you change `GLOT_WEB_PORT`). The web image bakes the API URL into the Next build (rewrites are resolved at build time), so changing `GLOT_API_URL` means rebuilding `web`. Cold build: ~7 min server (mostly the Rust image pull, ~1.5 min compile), ~2 min web; images ~170 MB (server) and ~440 MB (web).
## Dogfooding
The dashboard in `web/` is translated by glot itself. Any PR touching `web/messages/en.json` runs [`dogfood.yml`](.github/workflows/dogfood.yml), which builds the CLI from the PR and runs our own action in `web/` to translate, validate, commit and comment.
## License
[MIT](LICENSE) © 2026 glot contributors
This server cannot be deployed
Maintenance
ActivityNo data
ResponsivenessNo issues