Skip to content

Commit e7e0d45

Browse files
authored
Merge pull request #45 from DeepLcom/release/v1.2.0-publish
Publish v1.2.0: Write API extension + Style Rules CRUD + table-format parity
2 parents 301df74 + 1ef1dcb commit e7e0d45

46 files changed

Lines changed: 4135 additions & 135 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,30 @@ All notable changes to this project will be documented in this file.
55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
66
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
77

8+
## [Unreleased]
9+
10+
## [1.2.0] - 2026-04-25
11+
12+
### Added
13+
14+
- **write**: Japanese (`ja`), Korean (`ko`), and Simplified Chinese (`zh`, `zh-Hans`) are now accepted target languages for `deepl write`.
15+
- **write**: `--tone` and `--style` now apply to Spanish (`es`), Italian (`it`), French (`fr`), and Portuguese (`pt`, `pt-BR`, `pt-PT`) in addition to the previously supported locales. The full value lists on `--tone` and `--style` are unchanged — the same 9 styles and 9 tones are accepted, and the mutual-exclusion rule between `--style` and `--tone` is unchanged. See `docs/API.md` for supported target-language / style / tone combinations.
16+
- **write**: 4xx responses from the Write API that arrive while `--style` or `--tone` is set now carry an explicit recovery hint pointing at `docs/API.md` for supported target-language / style / tone combinations.
17+
- **style-rules**: Full CRUD — `deepl style-rules create|show|update|delete` alongside the existing `list`. `create` requires `--name` and `--language`. `update` accepts `--name` for a rename and `--rules` for replacing configured rules (PUT `/configured_rules`); at least one is required. `--rules` takes a JSON object of category → settings, e.g. `'{"punctuation":{"quotation_mark":"use_guillemets"}}'` — matching the DeepL API's two-level rule shape. `delete` supports `-y`/`--yes`, `--dry-run`, and a TTY confirmation prompt. All new subcommands support `--format text|json`. Text-format output sanitizes control characters from rule names, configured-rule keys/values, and instruction text.
18+
- **style-rules**: Custom instructions management — `deepl style-rules instructions <style-rule-id>` (list, synthesized from the detailed `show` response), plus `add-instruction <style-id> <label> <prompt>`, `update-instruction <style-id> <label> <prompt>`, and `remove-instruction <style-id> <label>` subcommands. `remove-instruction` ships `-y`/`--yes`, `--dry-run`, and a TTY confirmation prompt (custom instructions are user-authored text and deserve confirmation before deletion).
19+
- **style-rules**: `deepl style-rules list` and `deepl style-rules instructions <style-rule-id>` accept `--format table` for aligned column output via `cli-table3`, matching the existing `translate`, `languages`, `usage`, and `cache` commands. In non-TTY output (pipe, redirect, CI), table falls back to plain text with a `WARN` line on stderr — same pattern used by `deepl translate --format table`.
20+
- **examples**: `examples/35-style-rules-crud.sh` — end-to-end style-rules workflow (create → show → update rules → add custom instruction → update instruction → remove instruction → delete rule).
21+
- **examples**: `examples/36-write-extended-languages.sh``deepl write` with Japanese, Korean, and Simplified Chinese targets and tone / style applied to Spanish, Italian, French, and Portuguese variants.
22+
23+
### Fixed
24+
25+
- **languages / cache stats / usage**: `--format table` now actually renders a `cli-table3` table on these commands. Previously the flag was advertised in `--help` but the action handler only branched on `'json'`, so `--format table` silently produced text output. Same non-TTY fallback as the other table commands.
26+
- **sync**: `deepl sync export --output <path>` (and other sync surfaces that call `assertPathWithinRoot`) no longer reject valid output paths under a project root that contains a symlink in its ancestor chain. The containment check now resolves both sides through `fs.realpath` before comparing, so a project under macOS `/tmp` (a symlink to `/private/tmp`) — or any other symlinked directory — works regardless of which form the user types or the engine captures. Symlink-based escape attempts (a symlink inside the project pointing outside) are now also rejected as a defense-in-depth bonus.
27+
28+
### Security
29+
30+
- **api**: Server-returned error messages are now passed through `sanitizeForTerminal` before being interpolated into the user-facing `API error: …` and `Server error (5xx): …` strings emitted from `src/api/http-client.ts`. Defense-in-depth against a buggy or malicious server scribbling ANSI escape codes or other terminal control characters on the user's terminal via the error path. Mirrors the existing TMS-client hardening; no change to error-message wording when the server payload is well-formed.
31+
832
## [1.1.0] - 2026-04-23
933

1034
### Added

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
1.1.0
1+
1.2.0

docs/API.md

Lines changed: 208 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# DeepL CLI - API Reference
22

3-
**Version**: 1.1.0
4-
**Last Updated**: April 23, 2026
3+
**Version**: 1.2.0
4+
**Last Updated**: April 25, 2026
55

66
Complete reference for all DeepL CLI commands, options, and configuration.
77

@@ -664,15 +664,15 @@ deepl write [OPTIONS] TEXT
664664

665665
#### Description
666666

667-
Enhance text quality with AI-powered grammar checking, style improvement, and tone adjustment. Supports 8 languages.
667+
Enhance text quality with AI-powered grammar checking, style improvement, and tone adjustment. Supports 14 target languages.
668668

669669
**File Detection:** The command automatically detects if the text argument is a file path. If a file exists at that path, it operates on the file; otherwise, it treats the argument as text to improve.
670670

671671
#### Options
672672

673673
**Language:**
674674

675-
- `--lang, -l LANG` - Target language: `de`, `en`, `en-GB`, `en-US`, `es`, `fr`, `it`, `pt`, `pt-BR`, `pt-PT`. Optional — omit to auto-detect the language and rephrase in the original language.
675+
- `--lang, -l LANG` - Target language: `de`, `en`, `en-GB`, `en-US`, `es`, `fr`, `it`, `ja`, `ko`, `pt`, `pt-BR`, `pt-PT`, `zh`, `zh-Hans`. Optional — omit to auto-detect the language and rephrase in the original language.
676676
- `--to LANG` - Long-only alias of `--lang`. Accepts the same language values. Provided for muscle-memory consistency with `deepl translate --to`; the short form `-t` is intentionally **not** bound here (it would collide with `deepl translate -t, --to`). Specifying both `--to` and `--lang` with different values exits with a `ValidationError`.
677677

678678
**Style Options (mutually exclusive with tone):**
@@ -695,6 +695,16 @@ Enhance text quality with AI-powered grammar checking, style improvement, and to
695695
- `diplomatic` - More careful and tactful
696696
- `prefer_enthusiastic`, `prefer_friendly`, etc. - Soft preferences
697697

698+
**Supported target-language / style-and-tone combinations:**
699+
700+
| Target language | `--style` | `--tone` |
701+
|-----------------|:---------:|:--------:|
702+
| `en`, `en-GB`, `en-US`, `de` |||
703+
| `es`, `fr`, `it`, `pt`, `pt-BR`, `pt-PT` |||
704+
| `ja`, `ko`, `zh`, `zh-Hans` |||
705+
706+
When `--style` or `--tone` is set for a target language that does not support it, the server returns a 4xx; the CLI converts that response into a `ValidationError` (exit code 6) that names the unsupported combination and points back to this table.
707+
698708
**Output Options:**
699709

700710
- `--alternatives, -a` - Show all improvement alternatives
@@ -2000,7 +2010,7 @@ Show cache statistics (status, entries count, size, percentage used).
20002010

20012011
**Options:**
20022012

2003-
- `--format <format>` - Output format: `text`, `json`, `table` (default: `text`)
2013+
- `--format <format>` - Output format: `text`, `json`, `table` (default: `text`). In non-TTY output, `table` falls back to `text` with a `WARN` line on stderr.
20042014

20052015
##### `clear`
20062016

@@ -2139,7 +2149,7 @@ Display your DeepL API character usage and remaining quota. Helps you monitor co
21392149

21402150
#### Options
21412151

2142-
- `--format FORMAT` - Output format: `text`, `json`, `table` (default: `text`)
2152+
- `--format FORMAT` - Output format: `text`, `json`, `table` (default: `text`). In non-TTY output, `table` falls back to `text` with a `WARN` line on stderr.
21432153

21442154
#### Examples
21452155

@@ -2215,7 +2225,7 @@ You can filter to show only source languages, only target languages, or both (de
22152225

22162226
- `--source, -s` - Show only source languages
22172227
- `--target` - Show only target languages
2218-
- `--format FORMAT` - Output format: `text`, `json`, `table` (default: `text`)
2228+
- `--format FORMAT` - Output format: `text`, `json`, `table` (default: `text`). In non-TTY output, `table` falls back to `text` with a `WARN` line on stderr.
22192229

22202230
#### Examples
22212231

@@ -2462,7 +2472,7 @@ deepl auth clear
24622472

24632473
### style-rules
24642474

2465-
Manage DeepL style rules (Pro API only). Style rules are created via the DeepL web UI and applied to translations using their ID.
2475+
Manage DeepL style rules (Pro API only). Style rules carry a list of configured rule ids and optional custom instructions, and apply to translations via their style id.
24662476

24672477
#### Synopsis
24682478

@@ -2481,30 +2491,208 @@ List all available style rules.
24812491
- `--detailed` - Show detailed information including configured rules and custom instructions
24822492
- `--page NUMBER` - Page number for pagination
24832493
- `--page-size NUMBER` - Number of results per page (1-25)
2484-
- `--format FORMAT` - Output format: `text`, `json` (default: `text`)
2494+
- `--format FORMAT` - Output format: `text`, `json`, `table` (default: `text`). In non-TTY output, `table` falls back to `text` with a `WARN` line on stderr.
24852495

24862496
**Examples:**
24872497

24882498
```bash
2489-
# List all style rules
24902499
deepl style-rules list
2491-
2492-
# List with details
24932500
deepl style-rules list --detailed
2494-
2495-
# JSON output
24962501
deepl style-rules list --format json
2497-
2498-
# Pagination
2502+
deepl style-rules list --format table
24992503
deepl style-rules list --page 1 --page-size 10
25002504
```
25012505

2506+
##### `create`
2507+
2508+
Create a new style rule list.
2509+
2510+
**Options:**
2511+
2512+
- `--name NAME` - Style rule name (required)
2513+
- `--language LANG` - Target language (required)
2514+
- `--rules JSON` - Configured rules as a JSON object of category → settings (optional). The DeepL API models configured rules as a two-level dictionary; arrays are not accepted. See "Configured rules shape" below.
2515+
- `--format FORMAT` - Output format: `text`, `json` (default: `text`)
2516+
2517+
**Examples:**
2518+
2519+
```bash
2520+
deepl style-rules create --name "Corporate" --language en
2521+
deepl style-rules create --name "Québécois" --language fr \
2522+
--rules '{"punctuation":{"quotation_mark":"use_guillemets"}}'
2523+
```
2524+
2525+
**Configured rules shape:**
2526+
2527+
The DeepL API expects `configured_rules` as a nested object — a category name maps to a settings object, and each setting maps to a string value. Empty rules are `{}`. Example for fr-CA:
2528+
2529+
```json
2530+
{
2531+
"punctuation": {
2532+
"quotation_mark": "use_guillemets",
2533+
"spacing_and_punctuation": "do_not_use_space"
2534+
},
2535+
"spelling_and_grammar": {
2536+
"accents_and_cedillas": "use_even_on_capital_letters"
2537+
}
2538+
}
2539+
```
2540+
2541+
The available categories, settings, and accepted values are defined by the DeepL API; consult DeepL's API documentation for the current rule schema.
2542+
2543+
##### `show`
2544+
2545+
Show a single style rule.
2546+
2547+
**Arguments:**
2548+
2549+
- `<id>` - Style rule id (required)
2550+
2551+
**Options:**
2552+
2553+
- `--detailed` - Include configured rules and custom instructions
2554+
- `--format FORMAT` - Output format: `text`, `json` (default: `text`)
2555+
2556+
**Examples:**
2557+
2558+
```bash
2559+
deepl style-rules show sr-abc123
2560+
deepl style-rules show sr-abc123 --detailed --format json
2561+
```
2562+
2563+
##### `update`
2564+
2565+
Update a style rule — rename and/or replace configured rules. At least one of `--name` / `--rules` is required. When both are provided, the rename (PATCH) runs first, then the rules replacement (PUT `/configured_rules`).
2566+
2567+
**Arguments:**
2568+
2569+
- `<id>` - Style rule id (required)
2570+
2571+
**Options:**
2572+
2573+
- `--name NAME` - New name
2574+
- `--rules JSON` - Replace configured rules with a JSON object of category → settings (see "Configured rules shape" under `create`)
2575+
- `--format FORMAT` - Output format: `text`, `json` (default: `text`)
2576+
2577+
**Examples:**
2578+
2579+
```bash
2580+
deepl style-rules update sr-abc123 --name "Renamed"
2581+
deepl style-rules update sr-abc123 --rules '{"punctuation":{"quotation_mark":"use_guillemets"}}'
2582+
deepl style-rules update sr-abc123 --name "New" --rules '{"spelling_and_grammar":{"accents_and_cedillas":"use_even_on_capital_letters"}}'
2583+
```
2584+
2585+
##### `delete`
2586+
2587+
Delete a style rule. Prompts for confirmation on a TTY; use `--yes` to skip or `--dry-run` to preview.
2588+
2589+
**Arguments:**
2590+
2591+
- `<id>` - Style rule id (required)
2592+
2593+
**Options:**
2594+
2595+
- `-y`, `--yes` - Skip confirmation prompt
2596+
- `--dry-run` - Show what would be deleted without performing the operation
2597+
2598+
**Examples:**
2599+
2600+
```bash
2601+
deepl style-rules delete sr-abc123
2602+
deepl style-rules delete sr-abc123 --yes
2603+
deepl style-rules delete sr-abc123 --dry-run
2604+
```
2605+
2606+
##### `instructions`
2607+
2608+
List custom instructions attached to a style rule. Synthesized from the detailed `show` response.
2609+
2610+
**Arguments:**
2611+
2612+
- `<style-id>` - Style rule id (required)
2613+
2614+
**Options:**
2615+
2616+
- `--format FORMAT` - Output format: `text`, `json`, `table` (default: `text`). In non-TTY output, `table` falls back to `text` with a `WARN` line on stderr.
2617+
2618+
**Examples:**
2619+
2620+
```bash
2621+
deepl style-rules instructions sr-abc123
2622+
deepl style-rules instructions sr-abc123 --format json
2623+
deepl style-rules instructions sr-abc123 --format table
2624+
```
2625+
2626+
##### `add-instruction`
2627+
2628+
Add a custom instruction to a style rule.
2629+
2630+
**Arguments:**
2631+
2632+
- `<style-id>` - Style rule id (required)
2633+
- `<label>` - Instruction label, unique within the rule (required)
2634+
- `<prompt>` - Instruction prompt text (required)
2635+
2636+
**Options:**
2637+
2638+
- `--source-language LANG` - Source language code (optional)
2639+
- `--format FORMAT` - Output format: `text`, `json` (default: `text`)
2640+
2641+
**Examples:**
2642+
2643+
```bash
2644+
deepl style-rules add-instruction sr-abc123 tone "Be formal"
2645+
deepl style-rules add-instruction sr-abc123 register "Use first person" --source-language en
2646+
```
2647+
2648+
##### `update-instruction`
2649+
2650+
Update the prompt and/or source language of an existing custom instruction. The label cannot be changed (it is the identifier); rename by removing and re-adding.
2651+
2652+
**Arguments:**
2653+
2654+
- `<style-id>` - Style rule id (required)
2655+
- `<label>` - Instruction label (required)
2656+
- `<prompt>` - New instruction prompt text (required)
2657+
2658+
**Options:**
2659+
2660+
- `--source-language LANG` - Source language code (optional)
2661+
- `--format FORMAT` - Output format: `text`, `json` (default: `text`)
2662+
2663+
**Examples:**
2664+
2665+
```bash
2666+
deepl style-rules update-instruction sr-abc123 tone "Be friendlier"
2667+
```
2668+
2669+
##### `remove-instruction`
2670+
2671+
Remove a custom instruction from a style rule. Prompts for confirmation on a TTY.
2672+
2673+
**Arguments:**
2674+
2675+
- `<style-id>` - Style rule id (required)
2676+
- `<label>` - Instruction label (required)
2677+
2678+
**Options:**
2679+
2680+
- `-y`, `--yes` - Skip confirmation prompt
2681+
- `--dry-run` - Show what would be removed without performing the operation
2682+
2683+
**Examples:**
2684+
2685+
```bash
2686+
deepl style-rules remove-instruction sr-abc123 tone --yes
2687+
deepl style-rules remove-instruction sr-abc123 tone --dry-run
2688+
```
2689+
25022690
#### Notes
25032691

2504-
- Style rules are created and managed via the DeepL web interface, not through the API
2505-
- Style rules are Pro API only and datacenter-specific (EU and US rules don't cross)
2506-
- Use the style ID from `style-rules list` with `deepl translate --style-id <uuid>`
2507-
- Style rules force the `quality_optimized` model type
2692+
- Style rules are Pro API only and datacenter-specific (EU and US rules don't cross).
2693+
- Use the style id with `deepl translate --style-id <uuid>` to apply a rule at translation time.
2694+
- Style rules force the `quality_optimized` model type.
2695+
- Text-format output of stored user strings (rule names, instruction labels, instruction prompts) passes through a terminal-escape sanitizer to prevent injection of control characters from the API response. JSON-format output preserves raw strings (JSON-escaped at the encoding layer).
25082696

25092697
### admin
25102698

examples/15-glossaries.sh

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,27 @@ echo
5151
# BASIC GLOSSARY OPERATIONS
5252
# ═══════════════════════════════════════════════════════
5353

54+
# Idempotent setup: previously-aborted runs may have left glossaries with
55+
# these demo names on the server (the script renames tech-terms-demo →
56+
# tech-terms-renamed → tech-final, so any of those three may be lingering),
57+
# and multiple aborted runs can leave duplicates of the same name. Delete
58+
# every matching glossary by UUID via the JSON listing so the run starts
59+
# from a clean slate.
60+
echo "0. Pre-run cleanup of any leftover demo glossaries"
61+
if command -v jq &>/dev/null; then
62+
DEMO_NAMES='tech-terms-demo tech-terms-renamed tech-final business-terms-demo multi-demo'
63+
for name in $DEMO_NAMES; do
64+
deepl glossary list --format json 2>/dev/null \
65+
| jq -r --arg n "$name" '.[] | select(.name == $n) | .glossary_id' 2>/dev/null \
66+
| while read -r id; do
67+
[[ -n "$id" ]] && deepl glossary delete "$id" --yes 2>/dev/null || true
68+
done
69+
done
70+
else
71+
echo " (jq not installed — skipping pre-run cleanup; install jq for full idempotency)"
72+
fi
73+
echo
74+
5475
# Example 1: Create a glossary
5576
echo "1. Create tech glossary (EN → DE)"
5677
deepl glossary create tech-terms-demo en de "$SAMPLE_DIR/tech-glossary.tsv"

0 commit comments

Comments
 (0)