Translate Markdown docs into multiple languages using LLMs.
Batteries included: prompt template, CLI, OpenAI/OpenRouter provider switch, and a simple change-detection cache.
- Getting Started – Installation and quick start
- CLI Reference – Complete command-line guide
- Programmatic Usage – Python API and examples
- Caching System – How the cache works
- Custom Prompt – Customize translations
- Error Handling – Failure recovery
- Development Guide – Contributing
- FAQ – Troubleshooting
pip install -e .- Initialize the editable prompt (creates
~/.mdxlate/translation_instruction.txt):
mdx init- Run translations:
export OPENAI_API_KEY=sk-... # or use OPEN_ROUTER_API_KEY when provider=openrouter
mdx run docs_src out --languages de fr --model gpt-4o-miniResult: translated files under out/<lang>/..., preserving the original folder structure.
A cache file .mdxlate.hashes.json is written in docs_src.
mdx run [OPTIONS] DOCS_SRC OUT_DIROptions
--base-language TEXT– Base language (default:en)--languages TEXT...– Target languages, space-separated (default:de)--model TEXT– Model name (default:gpt-4o-mini)--provider [openai|openrouter]– Backend provider (default:openai)--api-key TEXT– API key (overrides env)--api-env-key TEXT– Env var to read (default:OPENAI_API_KEY)--base-url TEXT– Custom base URL (e.g., OpenRouter)--prompt-path PATH– Use a custom prompt file instead of the default--force– Force re-translation, bypassing cache--cache-dir PATH– Directory for cache file (defaults to source directory)
OpenAI (env var):
export OPENAI_API_KEY=sk-...
mdx run docs_src out --languages de fr --model gpt-4o-miniOpenRouter:
export OPEN_ROUTER_API_KEY=or-...
mdx run docs_src out --languages de --provider openrouter --model google/gemini-2.5-proCustom prompt:
mdx run docs_src out --languages de --prompt-path ./my_prompt.txtCustom cache directory (for read-only CI/CD):
mdx run docs_src out --languages de --cache-dir /tmpIf any file fails to translate (e.g., due to API errors, rate limits, or network issues), mdxlate will:
- Continue processing other files instead of crashing
- Save the cache for successful translations
- Generate a failure report at
.mdxlate.failures.jsonwith details about what failed
Example failure report:
{
"failures": [
{
"file": "docs/advanced.md",
"error": "Rate limit exceeded",
"error_type": "RateLimitError"
}
]
}After fixing the issue (e.g., waiting for rate limits to reset), re-run the translation. Only failed files will be retried thanks to the cache.
- Prompt: default lives at
~/.mdxlate/translation_instruction.txt(created bymdx init). You can edit it freely or pass--prompt-path. - Cache: re-translation is skipped if file bytes + prompt content + model + language are unchanged. By default, cache is written to source directory as
.mdxlate.hashes.json. Use--cache-dirfor read-only environments. - Structure: each language gets its own mirror tree under
OUT_DIR/<lang>/.
from pathlib import Path
from mdxlate.start_translation import start_translation
start_translation(
docs_src=Path("docs_src"),
out_dir=Path("out"),
base_language="en",
languages=["de", "fr"],
model="gpt-4o-mini",
provider="openai", # or "openrouter"
api_key=None, # pass explicitly or rely on env
base_url=None,
prompt_path=None,
cache_dir=None, # optional: specify custom cache directory
)- Jekyll – Complete guide for translating Jekyll sites with frontmatter preservation
mdxlate/cli.py– Typer CLI (mdx init,mdx run)mdxlate/client.py–make_client()factory (OpenAI/OpenRouter)mdxlate/translator.py– translation, hashing, and I/Omdxlate/translation_instruction.txt– default prompt template
pip install -e .
pip install ruff mypy pytestThis project uses Ruff for linting and formatting, and Mypy for type checking:
# Lint code
ruff check src tests
# Auto-fix linting issues
ruff check --fix src tests
# Format code
ruff format src tests
# Type check
mypy src --ignore-missing-imports
# Run tests
pytest tests/The .github/workflows/quality.yml workflow runs automatically on every push and PR:
- ✅ Ruff linting
- ✅ Ruff formatting check
- ✅ Mypy type checking
MIT
repo/
pyproject.toml
README.md
src/
mdxlate/
__init__.py
cli.py
client.py
translator.py
start_translation.py
translation_instruction.txt
tests/ # optional
main.py # optional local test runner
---
# pyproject.toml
```toml
[build-system]
requires = ["setuptools>=69", "wheel"]
build-backend = "setuptools.build_meta"
[project]
name = "mdxlate"
version = "0.1.0"
description = "Translate Markdown docs into multiple languages using LLMs."
readme = "README.md"
requires-python = ">=3.10"
license = { text = "MIT" }
authors = [{ name = "Tobias Bück" }]
dependencies = [
"typer>=0.12",
"openai>=1.40",
"tenacity>=8.2",
]
[project.scripts]
mdx = "mdxlate.cli:app"
[tool.setuptools]
package-dir = {"" = "src"}
[tool.setuptools.packages.find]
where = ["src"]
include = ["mdxlate*"]
[tool.setuptools.package-data]
mdxlate = ["translation_instruction.txt"]
[tool.pytest.ini_options]
addopts = "-q"
testpaths = ["tests"]