Skip to main content

Translate Markdown docs into multiple languages using LLMs with smart caching and custom prompts

Project description

mdxlate

Translate Markdown docs into multiple languages using LLMs.
Batteries included: prompt template, CLI, OpenAI/OpenRouter provider switch, and a simple change-detection cache.

📚 Documentation

Read the full documentation →

Install

pip install -e .

Quick start

  1. Initialize the editable prompt (creates ~/.mdxlate/translation_instruction.txt):
mdx init
  1. 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-mini

Result: translated files under out/<lang>/..., preserving the original folder structure. A cache file .mdxlate.hashes.json is written in docs_src.

CLI

mdx run [OPTIONS] DOCS_SRC OUT_DIR

Options

  • --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)

Examples

OpenAI (env var):

export OPENAI_API_KEY=sk-...
mdx run docs_src out --languages de fr --model gpt-4o-mini

OpenRouter:

export OPEN_ROUTER_API_KEY=or-...
mdx run docs_src out --languages de --provider openrouter --model google/gemini-2.5-pro

Custom prompt:

mdx run docs_src out --languages de --prompt-path ./my_prompt.txt

Custom cache directory (for read-only CI/CD):

mdx run docs_src out --languages de --cache-dir /tmp

Error Handling

If any file fails to translate (e.g., due to API errors, rate limits, or network issues), mdxlate will:

  1. Continue processing other files instead of crashing
  2. Save the cache for successful translations
  3. Generate a failure report at .mdxlate.failures.json with 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.

Behavior

  • Prompt: default lives at ~/.mdxlate/translation_instruction.txt (created by mdx 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-dir for read-only environments.
  • Structure: each language gets its own mirror tree under OUT_DIR/<lang>/.

Programmatic use

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
)

Integrations

  • Jekyll – Complete guide for translating Jekyll sites with frontmatter preservation

Files of interest

  • mdxlate/cli.py – Typer CLI (mdx init, mdx run)
  • mdxlate/client.pymake_client() factory (OpenAI/OpenRouter)
  • mdxlate/translator.py – translation, hashing, and I/O
  • mdxlate/translation_instruction.txt – default prompt template

Development

Setup

pip install -e .
pip install ruff mypy pytest

Code Quality

This 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/

CI/CD

The .github/workflows/quality.yml workflow runs automatically on every push and PR:

  • ✅ Ruff linting
  • ✅ Ruff formatting check
  • ✅ Mypy type checking

License

MIT

Layout

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"]

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mdxlate-1.1.5.tar.gz (17.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mdxlate-1.1.5-py3-none-any.whl (12.0 kB view details)

Uploaded Python 3

File details

Details for the file mdxlate-1.1.5.tar.gz.

File metadata

  • Download URL: mdxlate-1.1.5.tar.gz
  • Upload date:
  • Size: 17.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.22

File hashes

Hashes for mdxlate-1.1.5.tar.gz
Algorithm Hash digest
SHA256 0584a1591b44e814e570de2bf8604fa12ce9818d2dce291c34ca1c264a03a220
MD5 b04eae9d678c2751639c1bf0e62a2a8d
BLAKE2b-256 f55dbf167ce428f0d9455fc30fddd46b881b59fc9dc1dbc6c0185bff7646509c

See more details on using hashes here.

File details

Details for the file mdxlate-1.1.5-py3-none-any.whl.

File metadata

  • Download URL: mdxlate-1.1.5-py3-none-any.whl
  • Upload date:
  • Size: 12.0 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.8.22

File hashes

Hashes for mdxlate-1.1.5-py3-none-any.whl
Algorithm Hash digest
SHA256 46396d6fd259230cd669ec6708250209f123b2e8cf4d465063c6f33b795cee6f
MD5 5a5b8101c20a1710e379ef17d7af4562
BLAKE2b-256 83f7dc57fa8d7d7837f22888980fe344359f48c0486621393852154bf5f0f532

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page