Gettext MCP Server
A Model Context Protocol server for GNU gettext .po / .pot files. Twelve structured tools cover the full translation lifecycle: read, edit, manage fuzzy flags and metadata, handle plural forms and contexts. An optional web UI ships in the same binary.
Why this exists
.po files are deceptively annoying. They look like flat text, but a real catalog mixes multi-line msgstr continuations, escaped quotes and newlines, plural arrays (msgstr[0], msgstr[1], ...), context-disambiguated duplicates (msgctxt), obsolete entries marked with #~, and a handful of comment types (#., #:, #,, #|) that each mean something different. Asking a model to edit one with Edit/Bash means either reading the whole file into context (token-expensive for any non-trivial catalog) or hand-rolling fragile regexes that break the moment a string crosses a line. Even one stray quote in msgstr quietly corrupts the file for msgfmt.
A structured MCP server sidesteps all of that. The model calls get_translation / upsert_translation / set_fuzzy with typed arguments; the server parses, escapes, and serializes correctly, persists to disk, and only ships the entries the model actually asked for back across the wire.
Features
- 12 MCP tools covering translation CRUD, fuzzy/flag management, header metadata, plural forms, and context disambiguation
- Single-file, directory, and dynamic-path operating modes
- Optional web UI for browsing and editing the catalog from a browser
- Round-trip-safe PO parser/serializer: preserves comments, source references, flag order, header ordering, and obsolete
#~entries - Thread-safe in-memory store (
Arc<RwLock>), changes flushed to disk on every write - Tested with Claude Code, Cursor, VS Code + Copilot, Windsurf, and Zed; should work with any MCP client
Install
# or, from a checkout:
The binary lands at ~/.cargo/bin/gettext-mcp.
Configure
To pin the server to a specific catalog, pass the path:
Add to .cursor/mcp.json:
Add to ~/.codeium/windsurf/mcp_config.json:
Add to .vscode/mcp.json:
Add to Zed settings (settings.json):
gettext-mcp speaks JSON-RPC over stdio. Point your client at the binary:
command: gettext-mcp
args: [optional path to a .po file or gettext directory]
transport: stdio
Modes
The first CLI argument decides how the server resolves the path parameter on each tool call.
- Single-file mode —
gettext-mcp /path/to/messages.po. The given file becomes the default;pathis optional on every tool call. - Directory mode —
gettext-mcp /path/to/locale. The server scans the tree at startup for all.po/.potfiles (e.g.{lang}/LC_MESSAGES/*.po). Tools accept relative paths within the base directory;list_filesenumerates what was discovered. - Dynamic mode —
gettext-mcpwith no argument. Every tool call must supply an absolutepath. Useful for ad-hoc, multi-project use.
Path validation rejects .. traversal, and in directory mode every path is canonicalized to stay inside the base directory.
Tools
| Tool | Description |
|---|---|
list_translations |
List translation entries with optional case-insensitive substring query and limit. |
get_translation |
Get a single translation entry by msgid (and optional msgctxt). |
upsert_translation |
Create or update a translation entry. Preserves existing flags/comments when updating. |
delete_translation |
Clear the translation (msgstr) for an entry without removing the key. |
delete_key |
Remove every entry (across all contexts) with the given msgid. |
set_comment |
Set or clear the translator comment for an entry. Pass comment: null to clear. |
set_fuzzy |
Toggle the fuzzy flag on a translation entry. |
set_flag |
Add or remove an arbitrary flag (e.g. c-format, no-wrap) on an entry. |
list_metadata |
List all PO header metadata entries (Language, Plural-Forms, etc.). |
set_header |
Set or remove a single PO header entry. Pass value: null to remove. |
list_contexts |
List all distinct msgctxt values used in the file. |
list_files |
List all .po/.pot files discovered in directory mode. |
Every tool accepts an optional path parameter (required in dynamic mode). upsert_translation additionally accepts msgid_plural and msgstr_plural for plural forms, plus flags for arbitrary flag arrays.
Web UI
Set WEB_PORT to spawn an Axum-backed web UI alongside the MCP server:
WEB_PORT=8787
# open http://127.0.0.1:8787
Optional WEB_HOST overrides the bind address (default 127.0.0.1). CORS is restricted to localhost. The UI offers:
- Translation browser with search and translated/fuzzy/untranslated status
- Inline editor for
msgid,msgstr, plural forms, and translator comments - Fuzzy flag toggle
- File switcher and per-file coverage stats in directory mode
- Language list with add/remove operations
Build & Test
Logs go to stderr — required, because stdout is the MCP stdio transport.
PO format support
The parser and serializer round-trip everything the GNU gettext spec defines:
# Translator comment
#. Extracted comment
#: source/file.py:42
#, fuzzy, c-format
#| msgid "Previous original"
msgctxt "context"
msgid "Original text"
msgid_plural "Original texts"
msgstr[0] "Singular translation"
msgstr[1] "Plural translation"
Obsolete entries (#~), multi-line string continuations, header ordering, and arbitrary flag combinations are preserved on write.
Architecture
Three layers, plus an optional web frontend:
src/
├── main.rs # CLI args, spawns MCP + optional web server
├── lib.rs # Public re-exports
├── store.rs # PO parser, serializer, GettextStore, GettextStoreManager
├── mcp_server.rs # 12 MCP tools + ServerHandler stdio wiring
└── web/mod.rs # Axum REST API + embedded SPA
- Store —
Arc<RwLock<GettextFile>>per file. Entries keyed by(msgid, msgctxt)in anIndexMapfor stable order. Writes flushed to disk immediately. - MCP server — Per-tool
*Paramsstructs deriveJsonSchemaviaschemars;ServerHandlerwirestools/listandtools/callover stdio. - Web — Axum router with
/api/*REST endpoints mirroring the MCP tools, plus an embedded single-page UI.
See CLAUDE.md for deeper architectural notes (entry keying, metadata preservation rules, thread-safety model).
Examples
Sample catalogs in examples/:
sample_fr.po— French translations with contexts, plurals, and flagssample_plurals.po— complex plural forms
Contributing
See CONTRIBUTING.md for development setup, testing, and PR requirements.
Security
See SECURITY.md for the vulnerability reporting process.
License
Licensed under either of
- Apache License, Version 2.0 (LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
- MIT license (LICENSE-MIT or http://opensource.org/licenses/MIT)
at your option.
Related
- Model Context Protocol — open protocol for AI tool integration
- GNU gettext — the format and reference toolchain
- xcstrings-mcp — sibling MCP server for Apple String Catalogs