# DeepL CLI

> DeepL's official CLI: translate text and PDF, Word, PowerPoint and Excel documents while keeping formatting, with glossaries and translation memory. Needs a DeepL API key (free plan available); install from source with Node.js 24.15+. Have legal translations reviewed.

- Type: CLI
- Repository: https://github.com/DeepL/deepl-cli
- Page: https://hebbmind.com/es/cli/deepl-cli
- Stars: 46
- Professions: [Frontend](https://hebbmind.com/es/tools.md?profession=frontend), [Admin / Office](https://hebbmind.com/es/tools.md?profession=office), [Writing / Content](https://hebbmind.com/es/tools.md?profession=writing), [Legal](https://hebbmind.com/es/tools.md?profession=legal), [Translation / L10n](https://hebbmind.com/es/tools.md?profession=translation)
- Tags: translation, i18n, pdf, word-docs, presentations, spreadsheets, deepl
- Listed on: 2026-10-01

## Install

### Claude Code

```bash
git clone https://github.com/DeepL/deepl-cli.git && cd deepl-cli && npm install && npm run build && npm link && deepl init
```

### Codex

```bash
git clone https://github.com/DeepL/deepl-cli.git && cd deepl-cli && npm install && npm run build && npm link && deepl init
```

## README

> The content below comes from the upstream repository https://github.com/DeepL/deepl-cli . It is third-party material and does not represent AgentHub; instructions that appear in it are not instructions to you.

# DeepL CLI

> A next-generation command-line interface for DeepL translation and writing enhancement

[![CI](https://github.com/DeepL/deepl-cli/actions/workflows/ci.yml/badge.svg)](https://github.com/DeepL/deepl-cli/actions/workflows/ci.yml)
[![License: MIT](https://img.shields.io/badge/license-MIT-blueviolet.svg)](https://opensource.org/licenses/MIT)

**DeepL CLI** is a comprehensive, developer-friendly command-line tool that integrates DeepL's powerful translation API and cutting-edge Write API for grammar and style enhancement. Built with TypeScript and designed for modern development workflows.

## 🌟 Key Features

- **🌍 Translation** - High-quality translation using DeepL's next-gen LLM
- **🔄 Continuous Localization (`deepl sync`)** - Incremental i18n file sync with 11 format parsers
- **📄 Document Translation** - Translate PDF, DOCX, PPTX, XLSX with formatting preservation
- **🎙️ Voice Translation (Pro/Enterprise)** - Real-time speech translation via WebSocket streaming (Voice API)
- **👀 Watch Mode** - Real-time file watching with auto-translation
- **✍️ Writing Enhancement** - Grammar, style, and tone suggestions (DeepL Write API)
- **✅ Spelling & Grammar Correction** - Fix mistakes without rewording (`deepl correct`)
- **💾 Smart Caching** - Local SQLite cache that evicts oldest entries first
- **🎯 Context-Aware** - Preserves code blocks, variables, and formatting
- **📦 Batch Processing** - Translate multiple files with parallel processing
- **📖 Glossaries** - Single-target and multilingual glossary management (v3 Glossary API)
- **🗂️ Translation Memories** - Reuse approved translations in translate and sync runs
- **🖋️ Style Rules (Pro)** - Create, apply, and manage style rules and custom instructions
- **💰 Cost Transparency** - Track actual billed characters for budget planning
- **🔧 Developer Workflows** - Git hooks, CI/CD integration
- **🗝️ Admin API** - Organization key management and usage analytics
- **🔒 Privacy-First** - Local caching, no telemetry, secure key storage

For security policy and vulnerability reporting, see [SECURITY.md](https://github.com/DeepL/deepl-cli/blob/main/SECURITY.md).

## 📋 Table of Contents

- [Installation](#-installation)
- [Upgrading from 1.x](#-upgrading-from-1x)
- [Quick Start](#-quick-start)
- [Global Options](#-global-options)
  - [Verbose Mode](#verbose-mode)
  - [Quiet Mode](#quiet-mode)
  - [Command Suggestions](#command-suggestions)
  - [Custom Configuration Files](#custom-configuration-files)
  - [Configuration Paths](#configuration-paths)
- [Usage](#-usage)
  - **Core Commands:** [Translation](#translation) | [Writing Enhancement](#writing-enhancement) | [Spelling and Grammar Correction](#spelling-and-grammar-correction) | [Voice Translation](#voice-translation)
  - **Resources:** [Glossaries](#glossaries) | [Translation Memories](#translation-memories)
  - **Workflow:** [Continuous Localization (sync)](#continuous-localization-deepl-sync) | [Watch Mode](#watch-mode) | [Git Hooks](#git-hooks)
  - **Configuration:** [Setup Wizard](#setup-wizard) | [Authentication](#authentication) | [Configure Defaults](#configure-defaults) | [Proxy Configuration](#proxy-configuration) | [Retry and Timeout Configuration](#retry-and-timeout-configuration) | [Cache Management](#cache-management) | [Style Rules](#style-rules)
  - **Information:** [Usage Statistics](#api-usage-statistics) | [Language Detection](#language-detection) | [Languages](#supported-languages) | [Shell Completion](#shell-completion)
  - **Administration:** [Admin API](#admin-api)
- [Development](#-development)
- [Architecture](#-architecture)
- [Testing](#-testing)
- [Documentation](#-documentation)
- [Environment Variables](#-environment-variables)
- [Security & Privacy](#-security--privacy)
- [Contributing](#-contributing)
- [License](#-license)

## 📦 Installation

> **Prerequisite:** Node.js **24.15.0 or later**. The cache uses Node's built-in [`node:sqlite`](https://nodejs.org/api/sqlite.html) module — no native compilation, no build toolchain needed. 24.15.0 is the release where `node:sqlite` stopped being experimental; Homebrew installs a suitable Node for you.

### Homebrew (macOS / Linux)

```bash
brew install deepl/tap/deepl
```

This installs the `deepl` command and everything it needs, including Node.

### npm

```bash
npm install -g @deepl/cli

# Verify installation
deepl --version
```

The package is scoped, but the command is just `deepl`.

### From Source

```bash
# Clone the repository
git clone https://github.com/DeepL/deepl-cli.git
cd deepl-cli

# Install dependencies
npm install

# Build the project
npm run build

# Link for global usage
npm link

# Verify installation
deepl --version
```

## 🔀 Upgrading from 1.x

2.0.0 requires Node 24.15.0, renames the package to `@deepl/cli`, removes several
deprecated flags, and changes a number of exit codes and output shapes that scripts
read. If you have a pipeline built on 1.x, read
**[docs/MIGRATION.md](https://github.com/DeepL/deepl-cli/blob/main/docs/MIGRATION.md)** before upgrading — it lists every
change that needs action, with a before/after for each.

## 🚀 Quick Start

### 1. Get Your DeepL API Key

Sign up for a [DeepL API account](https://www.deepl.com/pro-api) and get your authentication key.

### 2. Set Up Your Environment

Use the interactive setup wizard:

```bash
deepl init
```

Or set your API key directly:

```bash
# Piping from stdin keeps the key out of process listings and shell history
echo "YOUR_API_KEY" | deepl auth set-key --from-stdin

# Or from a file, keeping the key out of shell history
deepl auth set-key --from-stdin < ~/.deepl-api-key
```

Or use an environment variable:

```bash
export DEEPL_API_KEY=YOUR_API_KEY
```

### 3. Translate Your First Text

```bash
deepl translate "Hello, world!" --to es
# Output:
# ¡Hola, mundo!

# Short alias: t (and w for write)
deepl t "Hello, world!" --to es
```

## 🔧 Global Options

DeepL CLI supports global flags that work with all commands:

| Flag            | Short | Description                                                  |
| --------------- | ----- | ------------------------------------------------------------ |
| `--version`     | `-V`  | Show version number                                          |
| `--quiet`       | `-q`  | Suppress non-essential output                                |
| `--verbose`     | `-v`  | Show extra information                                       |
| `--config FILE` | `-c`  | Use alternate configuration file                             |
| `--no-input`    |       | Disable all interactive prompts (abort instead of prompting) |
| `--timeout MS`  |       | HTTP request timeout in milliseconds (default 30000)         |
| `--max-retries N` |     | Retries per failed request (default 3)                       |

### Verbose Mode

The `--verbose` (or `-v`) flag shows extra information such as detected source language, timing, and cache status. Useful for debugging and understanding translation behavior.

```bash
$ deepl --verbose translate "Hello" --to es
```

### Quiet Mode

The `--quiet` (or `-q`) flag suppresses all non-essential output, showing only errors and essential results. Perfect for scripts, CI/CD pipelines, and automation.

```bash
# Normal mode
$ deepl translate "Hello" --to es
Hola

# Quiet mode - same result, without the status lines on stderr
$ deepl --quiet translate "Hello" --to es
Hola

# Suppress progress indicators in batch operations
$ deepl --quiet translate docs/ --to es --output docs-es/
# Shows only final statistics, no spinners or progress updates
```

**What's suppressed in quiet mode:**

- ❌ Informational messages (`Note: "zz-zz" is not in the bundled language list; deferring to the API.`)
- ❌ Success confirmations (`✓ Cache enabled`)
- ❌ **Warnings** — including the notice that `--api-url` points somewhere that is not a DeepL endpoint, and the `file exceeds 100 KiB, using document API instead` fallback
- ❌ Progress spinners and status updates
- ❌ Decorative output

**What's always shown:**

- ✅ Errors, with their `Suggestion:` line
- ✅ Essential command output (translation results, JSON data, statistics)

Warnings are suppressed along with everything else, so run a new pipeline once without `--quiet` before trusting it — that is where you would see a misdirected `--api-url` or a silent fallback to the document API.

**Use cases:**

- **CI/CD pipelines**: Clean output for log parsing
- **Scripting**: Extract just the translation result
- **Automation**: Reduce noise in automated workflows
- **Parsing**: Easier to parse machine-readable output

```bash
# Example: Use in scripts
TRANSLATION=$(deepl --quiet translate "Hello" --to es)
echo "Result: $TRANSLATION"  # Result: Hola

# Example: CI/CD pipeline
deepl --quiet translate docs/ --to es,fr,de --output i18n/
# Returns exit code 0 on success, shows only errors if they occur
```

See [docs/API.md#global-options](https://github.com/DeepL/deepl-cli/blob/main/docs/API.md#global-options) for complete documentation.

### Command Suggestions

Mistype a command? The CLI suggests the closest match:

```bash
$ deepl transalte "Hello" --to es
# Unknown command: transalte
# Did you mean: deepl translate?
#
# Run deepl --help to see available commands.
```

### Custom Configuration Files

The `--config` (or `-c`) flag allows you to use alternate configuration files for different projects, environments, or accounts:

```bash
# Use work configuration
$ deepl --config ~/.deepl-work.json translate "Hello" --to es

# Use project-specific configuration
$ deepl --config ./project/.deepl.json translate docs/ --to fr --output docs-fr/

# Use test environment configuration
$ deepl -c /path/to/test-config.json usage
```

**Use cases:**

- **Multiple API keys**: Switch between free and paid accounts
- **Project isolation**: Different settings per project (glossaries, formality defaults, etc.)
- **Team configurations**: Share standardized configs via version control
- **Environment separation**: Separate configs for dev/staging/production
- **Testing**: Use test configurations without affecting default settings

**Precedence**: `--config` replaces the config _file_ only. The cache location is unaffected — it still follows `DEEPL_CONFIG_DIR` > legacy `~/.deepl-cli/` > XDG directories (see below).

### Configuration Paths

The CLI follows the [XDG Base Directory Specification](https://specifications.freedesktop.org/basedir-spec/latest/):

| Priority | Condition              | Config path                              | Cache path                           |
| -------- | ---------------------- | ---------------------------------------- | ------------------------------------ |
| 1        | `DEEPL_CONFIG_DIR` set | `$DEEPL_CONFIG_DIR/config.json`          | `$DEEPL_CONFIG_DIR/cache.db`         |
| 2        | `~/.deepl-cli/` exists | `~/.deepl-cli/config.json`               | `~/.deepl-cli/cache.db`              |
| 3        | XDG env vars set       | `$XDG_CONFIG_HOME/deepl-cli/config.json` | `$XDG_CACHE_HOME/deepl-cli/cache.db` |
| 4        | Default                | `~/.config/deepl-cli/config.json`        | `~/.cache/deepl-cli/cache.db`        |

Existing `~/.deepl-cli/` installations continue to work with no changes needed.

See [docs/API.md#global-options](https://github.com/DeepL/deepl-cli/blob/main/docs/API.md#global-options) for more details.

## 📖 Usage

### Translation

#### Basic Text Translation

```bash
# Simple translation
deepl translate "Hello world" --to ja
# こんにちは世界

# Specify source language explicitly
deepl translate "Bonjour" --from fr --to en
# Hello

# Multiple target languages (each result is prefixed with its language)
deepl translate "Good morning" --to es,fr,de
# [es] Buenos días
# [fr] Bonjour
# [de] Guten Morgen

# Read from stdin
echo "Hello world" | deepl translate --to es
cat input.txt | deepl translate --to ja
```

#### File Translation

**Text Files:** `.txt`, `.md`, `.html`, `.htm`, `.srt`, `.xlf`, `.xliff`

**Structured Files (i18n):** `.json`, `.yaml`, `.yml`

Structured files are parsed to extract only string values. Keys, nesting, non-string values (numbers, booleans, null), indentation, and YAML comments are all preserved. Ideal for translating i18n locale files.

```bash
# Translate a JSON locale file
deepl translate en.json --to es --output es.json

# Translate a YAML locale file (comments preserved)
deepl translate en.yaml --to de --output de.yaml

# Multiple targets
deepl translate locales/en.json --to es,fr,de --output locales/
```

**Smart Caching for Text Files:**

Small text-based files (under 100 KiB) automatically use the cached text translation API for faster performance and reduced API calls. Larger files automatically fall back to the document translation API (not cached).

- **Cached formats:** `.txt`, `.md`, `.html`, `.htm`, `.srt`, `.xlf`, `.xliff` (files under 100 KiB only)
- **Structured formats:** `.json`, `.yaml`, `.yml` — parsed and translated via batch text API (no size limit)
- **Large file fallback:** Files ≥100 KiB use document API (not cached, always makes API calls)
- **Binary formats:** `.pdf`, `.docx`, `.pptx`, `.xlsx` always use document API (not cached)
- **Performance:** Only small text files (<100 KiB) benefit from instant cached translations
- **Cost savings:** Only small text files avoid repeated API calls

```bash
# Single file translation (uses cache for small text files)
deepl translate README.md --to es --output README.es.md
# Translated README.md -> README.es.md

# Output directory (names the file <name>.<lang>.<ext>)
deepl translate README.md --to es --output ./translated/
# Translated README.md -> translated/README.es.md

# Multiple target languages (creates README.es.md, README.fr.md, etc.)
deepl translate docs.md --to es,fr,de --output ./translated/
# Translated docs.md to 3 languages:
#   [es] ./translated/docs.es.md
#   [fr] ./translated/docs.fr.md
#   [de] ./translated/docs.de.md

# With code preservation (preserves code blocks in markdown)
deepl translate tutorial.md --to ja --output tutorial.ja.md --preserve-code

# Large text file (over 100 KiB) - automatic fallback with warning
deepl translate large-document.txt --to es --output large-document.es.txt
# ⚠ File exceeds 100 KiB limit for cached translation (150.5 KiB), using document API instead
# Translated large-document.txt -> large-document.es.txt
```

#### Document Translation

**Supported Document Formats:** `.pdf`, `.docx`, `.doc`, `.pptx`, `.xlsx`, `.html`, `.htm`, `.txt`, `.srt`, `.xlf`, `.xliff`, `.jpg`, `.jpeg`, `.png`

Translate complete documents while preserving formatting, structure, and layout:

```bash
# Translate PDF document
deepl translate document.pdf --to es --output document.es.pdf
# Uploading document...
# Document queued for translation...
# Translating document (est. 5s remaining)...
# Downloading translated document...
# ✔ Document translated successfully!
# Translated document.pdf -> document.es.pdf
# Billed characters: 1,234

# Translate PowerPoint presentation
deepl translate presentation.pptx --to fr --output presentation.fr.pptx

# Translate Excel spreadsheet
deepl translate report.xlsx --to de --output report.de.xlsx

# Translate HTML file
deepl translate website.html --to ja --output website.ja.html

# With formality setting
deepl translate contract.pdf --to de --formality more --output contract.de.pdf

# Specify source language
deepl translate document.pdf --from en --to es --output document.es.pdf

# Apply a glossary (needs a source language: --from, or defaults.sourceLang)
deepl translate report.docx --from en --to de --glossary tech-terms --output report.de.docx

# Repeat --glossary for up to 5; entries merge, and a term defined in
# several is resolved by the API, not by flag order
deepl translate report.docx --from en --to de --output report.de.docx \
  --glossary base-terms --glossary project-overrides

# Convert PDF to DOCX during translation (ONLY supported conversion)
deepl translate document.pdf --to es --output-format docx --output document.es.docx
# Translates PDF to Spanish and converts to editable Word format

# Note: DeepL API only supports PDF → DOCX conversion
# All other format conversions (DOCX→PDF, HTML→TXT, etc.) are NOT supported
# See examples/06-document-format-conversion.sh for details
```

**Document Translation Features:**

- ✅ **Preserves Formatting** - Maintains fonts, styles, colors, and layout
- ✅ **Format Conversion** - PDF → DOCX conversion only (convert PDFs to editable Word documents)
- ✅ **Progress Tracking** - Real-time status updates during translation
- ✅ **Large Files** - Handles documents up to 30MB
- ✅ **Cost Tracking** - Shows billed characters after translation
- ✅ **Glossaries** - `--glossary` applies to documents too, repeatable up to 5 (needs a source language: `--from`, or `defaults.sourceLang`). Translation memories are not supported for documents.
- ✅ **Async Processing** - Documents are translated on DeepL servers with polling

**Supported Formats:**

- `.pdf` - PDF documents
- `.docx`, `.doc` - Microsoft Word
- `.pptx` - Microsoft PowerPoint
- `.xlsx` - Microsoft Excel
- `.html`, `.htm` - HTML files
- `.txt` - Plain text files
- `.srt` - Subtitle files
- `.xlf`, `.xliff` - XLIFF localization files
- `.jpg`, `.jpeg` - JPEG images
- `.png` - PNG images

**Note:** Document translation uses DeepL's async translation API. The CLI automatically handles upload, polling, and download. Translation time varies based on document size and complexity.

#### Batch Translation (Directory Processing)

Translate multiple files in parallel with progress indicators:

```bash
# Translate all files in a directory
deepl translate ./docs --to es --output ./docs-es
# Scanning files...
# Translating files: 10/10
# ✔ Translation complete!
#
# Translation Statistics:
#   Total files: 10
#   ✓ Successful: 10

# Translate specific file types with glob pattern
deepl translate ./docs --to fr --output ./docs-fr --pattern "*.md"
# Only translates markdown files

# Non-recursive (current directory only)
deepl translate ./docs --to de --output ./docs-de --no-recursive

# Custom concurrency (default: 5)
deepl translate ./large-docs --to ja --output ./large-docs-ja --concurrency 10
# Faster processing with more parallel translations
```

**Performance Optimization:**

The CLI automatically optimizes batch translations by grouping multiple texts into single API requests (up to 50 texts per batch). This significantly reduces API overhead and improves translation speed compared to translating texts individually.

- ✅ **Automatic batching** - Groups translations into efficient batches
- ✅ **Cache-aware** - Only translates uncached texts
- ✅ **Smart splitting** - Respects DeepL API batch size limits (50 texts/request)
- ✅ **Parallel processing** - Multiple batches processed concurrently

This optimization is automatic and transparent - no configuration needed.

#### Advanced Translation Options

```bash
# Preserve code blocks and variables
deepl translate tutorial.md --preserve-code --to ja --output tutorial.ja.md

# Preserve line breaks and whitespace formatting
deepl translate document.txt --preserve-formatting --to es --output document.es.txt

# Enable document minification for PPTX/DOCX (reduces file size)
deepl translate presentation.pptx --enable-minification --to de --output presentation.de.pptx

# Set formality level (default, more, less, prefer_more, prefer_less, formal, informal)
deepl translate "How are you?" --formality more --to de --output formal.txt
# More formal: Wie geht es Ihnen?

deepl translate "How are you?" --formality less --to de --output casual.txt
# Less formal: Wie geht's?

# Add context for better translation quality (helps with ambiguous terms)
deepl translate "bank" --context "This document is about financial institutions" --to es
# Translation considers financial context → "banco" (financial institution)

deepl translate "bank" --context "This document is about rivers and geography" --to es
# Translation considers geographical context → "orilla" (riverbank)

# Combine context with other options
deepl translate "How are you?" --context "Formal business email" --formality more --to de

# Choose model type for quality vs. speed trade-offs
deepl translate "Long document text..." --to ja --model-type quality_optimized
# Best translation quality

deepl translate "Real-time chat message" --to es --model-type latency_optimized
# Faster response time, slightly lower quality

deepl translate "Important email" --to de --model-type prefer_quality_optimized
# Prefer quality, fall back to latency if unavailable

# Custom instructions for tailored translations (repeatable, max 10)
deepl translate "Click Save to confirm" --to de \
  --custom-instruction "This is a software UI string" \
  --custom-instruction "Keep it concise"

# Combine custom instructions with other options
deepl translate "Meeting at the bank" --to es \
  --custom-instruction "This is about financial institutions" \
  --formality more

# Apply a style rule (Pro API only - get IDs with: deepl style-rules list)
deepl translate "Hello" --to de --style-id "abc-123-def-456"

# Custom API endpoint (for DeepL Pro accounts or testing)
deepl translate "Hello" --to es --api-url https://api.deepl.com/v2

# Track actual billed characters for cost transparency
deepl translate "Hello, world!" --to es --show-billed-characters
# Output:
# Hola, mundo!
#
# Billed characters: 13

# Table output format - structured view for multiple languages
deepl translate "Hello, world!" --to es,fr,de --format table
# ┌──────────┬──────────────────────────────────────────────────────────────────────┐
# │ Language │ Translation                                                          │
# ├──────────┼──────────────────────────────────────────────────────────────────────┤
# │ es       │ ¡Hola, mundo!                                                        │
# │ fr       │ Bonjour le monde!                                                    │
# │ de       │ Hallo, Welt!                                                         │
# └──────────┴──────────────────────────────────────────────────────────────────────┘

# Table format with cost tracking (adds Characters column)
deepl translate "Cost analysis" --to es,fr,de --format table --show-billed-characters --no-cache
# ┌──────────┬────────────────────────────────────────────────────────────┬────────────┐
# │ Language │ Translation                                                │ Characters │
# ├──────────┼────────────────────────────────────────────────────────────┼────────────┤
# │ es       │ Análisis de costes                                         │ 14         │
# │ fr       │ Analyse des coûts                                          │ 14         │
# │ de       │ Kostenanalyse                                              │ 14         │
# └──────────┴────────────────────────────────────────────────────────────┴────────────┘

# Preview what would be translated without making API calls (file/directory mode)
deepl translate ./docs --to es --dry-run

# Tag handling pins v2 (better structure handling); opt back into v1 explicitly
deepl translate page.html --to es --tag-handling html --tag-handling-version v1

# Advanced XML/HTML tag handling (requires --tag-handling xml)
# Control automatic XML structure detection
deepl translate "<doc><p>Hello</p></doc>" --to es --tag-handling xml --outline-detection false

# Specify tags that split sentences (like <br/> and <hr/>)
deepl translate "<div>First sentence<br/>Second sentence</div>" --to es --tag-handling xml --splitting-tags "br,hr"

# Preserve non-translatable content (code blocks, preformatted text)
deepl translate "<doc><code>let x = 1;</code><p>Text</p></doc>" --to es --tag-handling xml --non-splitting-tags "code,pre"

# Ignore specific tags and their content (scripts, styles)
deepl translate page.html --to es --tag-handling xml --ignore-tags "script,style,noscript" --output page.es.html

# Combine multiple XML tag handling options for fine-tuned control
deepl translate complex.xml --to de --tag-handling xml \
  --outline-detection false \
  --splitting-tags "br,hr,div" \
  --non-splitting-tags "code,pre,kbd" \
  --ignore-tags "script,style" \
  --output complex.de.xml
```

`--format table` draws the box only when stdout is a terminal. Redirected or captured (`--format table > out.txt`, `$(…)`), it falls back to plain `[lang] text` lines and says so on stderr, so log scrapers and screen readers get parseable output instead of box-drawing characters. Use `--format json` when a script needs a stable shape.

**XML Tag Handling Use Cases:**

Advanced XML/HTML tag handling is perfect for:

- 🌐 Localizing HTML websites while preserving structure
- 📚 Translating technical documentation with code blocks
- 📄 Processing custom XML formats with specific content rules
- 🔒 Protecting non-translatable content (scripts, styles, code)
- ✂️ Fine-tuned control over sentence splitting for better context

See [examples/09-xml-tag-handling.sh](https://github.com/DeepL/deepl-cli/blob/main/examples/09-xml-tag-handling.sh) for comprehensive XML tag handling examples with real-world scenarios.

**Model Types:**

- `quality_optimized` - Best translation quality, standard latency
- `prefer_quality_optimized` - Prefer quality, fallback to latency if unavailable
- `latency_optimized` - Faster responses, slightly lower quality (ideal for real-time use)

There is no CLI default: when `--model-type` is omitted, the API server selects the model.

See [examples/08-model-type-selection.sh](https://github.com/DeepL/deepl-cli/blob/main/examples/08-model-type-selection.sh) for a complete example with different model types.

### Writing Enhancement

Improve your writing with AI-powered grammar, style, and tone suggestions using the **DeepL Write API**.

The `--lang` flag is optional. If omitted, DeepL auto-detects the language and rephrases in the original language. Generic codes `en` and `pt` are also accepted (mapped to `en-us` and `pt-br` respectively).

```bash
# Auto-detect language (--lang is optional)
deepl write "This is a sentence."

# Specify language explicitly
deepl write "This is a sentence." --lang en-us

# Use generic language code (en maps to en-us, pt maps to pt-br)
deepl write "This is a sentence." --lang en

# Apply business writing style
deepl write "We want to tell you about our new product." --lang en-us --style business

# Apply academic writing style
deepl write "This shows that the method works." --lang en-us --style academic

# Apply casual tone
deepl write "That is interesting." --lang en-us --style casual

# Use confident tone
deepl write "I think this will work." --lang en-us --tone confident

# Use diplomatic tone
deepl write "Try something else." --lang en-us --tone diplomatic

# Show all alternative improvements
deepl write "This is good." --tone enthusiastic --alternatives

# Improve files and save to output
deepl write input.txt --lang en-us --output improved.txt

# Edit file in place
deepl write document.md --lang en-us --in-place

# Interactive mode - choose from multiple style alternatives
# Generates improvements with simple, business, academic, and casual styles
deepl write "Text to improve." --lang en-us --interactive

# Interactive mode with file
deepl write document.md --lang en-us --interactive --in-place

# Interactive mode with specific style (single option)
deepl write "Text to improve." --lang en-us --style business --interactive

# Check if text needs improvement (exit code 0 if no changes needed)
deepl write document.md --lang en-us --check

# Machine-readable check result for CI (exit code unchanged)
deepl write document.md --check --format json
# {"ok":true,"mode":"write","needsChanges":true,"changes":3,"file":"/abs/path/document.md"}

# Auto-fix files in place
deepl write document.md --lang en-us --fix

# Auto-fix with backup
deepl write document.md --lang en-us --fix --backup

# Show diff between original and improved
deepl write file.txt --lang en-us --diff

# Show diff for plain text
deepl write "This text could be better." --lang en-us --diff

# Machine-readable diff (uncoloured patch)
deepl write file.txt --diff --format json
# {"ok":true,"original":"...","improved":"...","diff":"Index: text\n===...\n"}

# Every alternative the API offered, as an array
deepl write "This is good." --alternatives --format json
# {"ok":true,"original":"This is good.","alternatives":["This is fine.","This works."]}

# Bypass cache for this request
deepl write "Fresh improvement please." --lang en-us --no-cache
```

**Failures under `--format json`.** Every command with a JSON mode writes a single error envelope to **stdout** — not stderr — so one redirection captures both outcomes. Discriminate on `ok`:

```json
{ "ok": false, "error": { "code": "ValidationError", "message": "…", "suggestion": "…" }, "exitCode": 6 }
```

The non-zero exit code remains the failure signal, and stderr carries only human-facing warnings. `--check` is not a failure: it emits `ok: true` with a `needsChanges` verdict and exits 0 or 8.

**Supported Languages:**

- German (`de`)
- English (`en`) - generic, defaults to American English
- English - British (`en-gb`)
- English - American (`en-us`)
- Spanish (`es`)
- French (`fr`)
- Italian (`it`)
- Japanese (`ja`)
- Korean (`ko`)
- Portuguese (`pt`) - generic, defaults to Brazilian Portuguese
- Portuguese - Brazilian (`pt-br`)
- Portuguese - European (`pt-pt`)
- Chinese (`zh`) - generic, defaults to Simplified Chinese
- Chinese - Simplified (`zh-hans`)

**Writing Styles:**

- `default` - No style modification (API default)
- `simple` - Easy-to-read, accessible language
- `business` - Professional, formal business tone
- `academic` - Scholarly, research-oriented style
- `casual` - Relaxed, conversational tone
- `prefer_*` prefix - Apply style only if language supports it

**Tones:**

- `default` - No tone modification (API default)
- `enthusiastic` - Energetic and positive
- `friendly` - Warm and approachable
- `confident` - Assertive and certain
- `diplomatic` - Tactful and considerate
- `prefer_*` prefix - Apply tone only if language supports it

**Interactive Mode:**

When using `--interactive` without specifying a style or tone, the CLI automatically generates **4 different alternatives** by calling the DeepL Write API with different writing styles (simple, business, academic, casual). You can then choose which version works best for your needs:

```
? Choose an improvement (4 alternatives):
❯ Keep original - "This text could be better improved with..."
  Simple - "This text needs improvement with better..."
  Business - "We recommend enhancing this text through..."
  Academic - "It is advisable to improve this text via..."
  Casual - "You should make this text better by..."
```

If you specify a style or tone with `--interactive`, you'll get a simple confirm/reject prompt for that single suggestion.

**Note:** You cannot combine `--style` and `--tone` in a single request. Choose one or the other.

### Spelling and Grammar Correction

Fix spelling and grammar without rewording, using the Write API's dedicated correct endpoint. Unlike `deepl write`, which may rephrase for style, `deepl correct` leaves your wording alone.

```bash
# Correct a sentence (language auto-detected)
deepl correct "This is an test."
# This is a test.

# c is an alias of correct
deepl c "Their going too the store."

# Grammar gate for CI: exit code 8 if corrections are needed
deepl correct README.md --check

# Fix a file in place, keeping a backup
deepl correct essay.md --fix --backup

# Review the correction as a diff first
deepl correct document.txt --diff

# Pipe from stdin
cat notes.txt | deepl correct
```

`correct` supports the same languages and workflow flags as `write` — `--check`, `--fix`, `--backup`, `--diff`, `--alternatives`, `--interactive`, `--output`, `--in-place`, `--no-cache`, `--format json`, and `--to` as an alias of `--lang` — but not `--style`/`--tone`.

### Voice Translation

Translate audio in real-time using the DeepL Voice API. Supports multiple audio formats with automatic content type detection.

```bash
# Translate an audio file
deepl voice recording.ogg --to de

# Multiple target languages (max 5)
deepl voice meeting.mp3 --to de,fr,es

# Specify source language
deepl voice audio.flac --to ja --from en

# Pipe from stdin (content-type required)
cat audio.pcm | deepl voice - --to es --content-type 'audio/pcm;encoding=s16le;rate=16000'

# JSON output
deepl voice speech.ogg --to de --format json

# Disable live streaming (plain text at end)
deepl voice speech.ogg --to de --no-stream

# Set source language detection mode (auto or fixed)
deepl voice speech.ogg --to de --from en --source-language-mode fixed

# Customize chunking behavior
deepl voice large-recording.ogg --to de --chunk-size 12800 --chunk-interval 100

# Adjust reconnection attempts (default: 3)
deepl voice speech.ogg --to de --max-reconnect-attempts 5
```

**Supported audio formats:** OGG (Opus), WebM (Opus), FLAC, MP3, PCM (16kHz s16le), Matroska (Opus)

Automatic WebSocket reconnection is enabled by default (up to 3 attempts). Disable with `--no-reconnect` or adjust with `--max-reconnect-attempts <n>`.

**Note:** The Voice API requires a DeepL Pro or Enterprise plan.

### Continuous Localization (`deepl sync`)

Incremental i18n translation engine: scans your source locale files, diffs them against `.deepl-sync.lock`, translates only new and changed strings, and writes format-preserving target files. Supports 11 formats: JSON, YAML, TOML, Gettext PO, Android XML, iOS Strings, Xcode String Catalog (`.xcstrings`), ARB, XLIFF, Java Properties, and Laravel PHP arrays.

```bash
# One-time setup - generates .deepl-sync.yaml (framework auto-detection)
deepl sync init

# Translate new and changed keys across all configured locales
deepl sync

# Preview changes without making API calls
deepl sync --dry-run

# CI gate - exit 10 if translations are missing or outdated (no API calls)
deepl sync --frozen

# Per-locale coverage report
deepl sync status

# Check placeholder, format-string, and HTML-tag integrity
deepl sync validate

# Terminology-consistency audit across locales
deepl sync audit

# Export source strings to XLIFF 1.2 for CAT tool handoff
deepl sync export --output handoff.xlf

# Resolve git merge conflicts in .deepl-sync.lock
deepl sync resolve

# Optional TMS integration (requires a tms: block in .deepl-sync.yaml)
deepl sync push
deepl sync pull

# .deepl-sync.yaml picks the TMS host, so an environment-supplied TMS_API_KEY
# only goes to a host you have approved. Approve one up front:
deepl config set tms.allowedServers tms.example.com
```

Because `tms.server` is chosen by `.deepl-sync.yaml` in the checkout while `TMS_API_KEY`/`TMS_TOKEN` come from your environment, `push`/`pull` will not send an environment-supplied credential to a hostname you have not approved: it asks once in a terminal (recording the answer in your **user** config, outside the repo) and fails closed with exit 7 under `--no-input` or in CI. Both commands also print the destination origin on success. See [TMS destination trust](https://github.com/DeepL/deepl-cli/blob/main/docs/SYNC.md#tms-destination-trust).

See **[docs/SYNC.md](https://github.com/DeepL/deepl-cli/blob/main/docs/SYNC.md)** for configuration (`.deepl-sync.yaml`), the lockfile model, CI/CD recipes, and the TMS REST contract, and [docs/API.md#sync](https://github.com/DeepL/deepl-cli/blob/main/docs/API.md#sync) for the full flag reference.

### Watch Mode

Monitor files or directories for changes and automatically translate them in real-time. Perfect for keeping documentation and localization files in sync.

```bash
# Watch a single file
deepl watch README.md --to es,fr,de

# Watch a directory (all supported files)
deepl watch docs/ --to ja --output docs-i18n/

# Watch with pattern filtering
deepl watch src/locales/ --pattern "*.json" --to es,fr,de

# Watch markdown files only
deepl watch docs/ --pattern "*.md" --to ja

# Auto-commit translations to git
deepl watch docs/ --to es --auto-commit

# Custom debounce delay (default: 500ms)
deepl watch src/ --to es --debounce 1000

# Preview what would be watched without starting the watcher
deepl watch docs/ --to es --dry-run

# Only watch git-staged files (useful in pre-commit workflows)
deepl watch . --to es --git-staged

# With formality and code preservation
deepl watch docs/ --to de --formality more --preserve-code
```

**Features:**

- 🔄 Real-time monitoring with debouncing
- 📁 Watch files or entire directories
- 🎯 Glob pattern filtering (e.g., `*.md`, `*.json`)
- 🔀 Multiple target languages
- 💾 Auto-commit to git (optional)
- 📌 Git-staged filtering for pre-commit workflows
- ⚡ Smart debouncing to avoid redundant translations
- 🗂️ Output mirrors the source tree: a file in a subdirectory of the watched path is written to the matching subdirectory under `--output`, so two same-named files in different directories never overwrite each other

**Example output:**

```
👀 Watching for changes...
Path: docs/
Targets: es, fr, ja
Output: docs/translations
Pattern: *.md

📝 Change detected: docs/README.md
✓ Translated docs/README.md to 3 languages
  → [es] docs/translations/README.es.md
  → [fr] docs/translations/README.fr.md
  → [ja] docs/translations/README.ja.md

Press Ctrl+C to stop
```

See [examples/19-watch-mode.sh](https://github.com/DeepL/deepl-cli/blob/main/examples/19-watch-mode.sh) for a complete watch mode example with multiple scenarios.

### Git Hooks

Automate translation validation in your git workflow with pre-commit, pre-push, commit-msg, and post-commit hooks.

```bash
# Install hooks
deepl hooks install pre-commit
deepl hooks install pre-push
deepl hooks install commit-msg
deepl hooks install post-commit

# List hook installation status
deepl hooks list

# Show path to hook file
deepl hooks path pre-commit

# Uninstall a hook
deepl hooks uninstall pre-commit
```

**What the hooks do:**

- **pre-commit**: Checks if staged files include translatable content (`.md`, `.txt` files) and validates translations are up-to-date
- **pre-push**: Validates all translations in the repository before pushing to remote
- **commit-msg**: Validates or augments commit messages with translation context
- **post-commit**: Runs post-commit translation tasks (e.g., auto-translate changed files)

**Features:**

- 🔒 Safe installation with automatic backup of existing hooks
- 🚧 Confirmation before writing an executable outside the working tree — a repository-local `core.hooksPath` travels with the repository, so an install that would land outside the checkout names the configured value and the resolved directory and asks first (`--yes` to accept, exit `6` and nothing written if declined). Paths inside the working tree, linked worktrees, and submodules are unaffected
- 🎯 Only validates changed files (pre-commit)
- ⚡ Lightweight and fast
- 🔧 Customizable hook scripts
- 🗑️ Clean uninstallation with backup restoration

**Hook Status Example:**

```bash
$ deepl hooks list

Git Hooks Status:

  ✓ pre-commit      installed
  ! pre-push        installed, content does not match its recorded hash
  ? commit-msg      installed, no hash recorded (legacy marker)
  ✗ post-commit     not installed

The content of a hook no longer matches the hash its marker records. That
is expected if you edited the hook yourself. If you did not, replace it:
  deepl hooks install <hook-type>
A recorded hash detects a change made after the marker was written. It
cannot establish that a hook came from this CLI.
```


> The README is long; the above is its first 40000 characters. Full content: https://github.com/DeepL/deepl-cli
