# AI Babel Enchanter Documentation

Installation steps and a complete reference for every setting, with concrete examples — the same level of detail our support uses to help customers. **AI Babel Enchanter rewrites, magnifies and translates your Magento catalog at scale**, driven by reusable profiles, with a dry-run preview, a real side-by-side **AI Compare** bench, and full rollback so nothing is ever a one-way door.

> **New in v3.4.0 — your texts stay where you put them.** Declare a field **Final** and the AI never touches it again, at no cost, not even after a change of prompt or model. An hourly background check notices when a text this module wrote is no longer on the product and puts it back. And **Apply to already processed** extends an improved prompt to the products already done.
>
> **Does your catalogue come from an importer?** That importer and AI Babel Enchanter write to the same product fields, and when they collide nothing raises an error — your paid-for copy simply disappears. Read this first: **[Babel and importers](https://codingrow.com/ai-babel-enchanter/importers)**.

---

## Installation

### Requirements

Magento 2.4.x — tested on 2.4.9, compatible with previous 2.4.* releases. PHP 8.1–8.5. Compatible with both the default Luma theme and the Hyvä theme — the module works entirely in the admin and writes standard catalog attributes, so the storefront renders your improved content with no template changes. Depends on the free `codingrow/module-core` module, installed automatically. Requires an API key from at least one AI provider for the magnify/translate steps (Anthropic, OpenAI, Google or OpenRouter) and, optionally, a machine-translation key (DeepL). All AI usage is billed to you directly by the provider — Codingrow never marks it up.

### Setup steps

1. Add the credentials you'll receive by email to `auth.json` in your Magento project root:
   ```json
   { "http-basic": { "repo.codingrow.com": { "username": "...", "password": "..." } } }
   ```
2. `composer config repositories.codingrow composer https://repo.codingrow.com`
3. `composer require codingrow/module-ai-babel-enchanter`
4. `bin/magento module:enable Codingrow_BabelEnchanter`
5. `bin/magento setup:upgrade && bin/magento setup:di:compile && bin/magento cache:flush`
   (on a **production** deployment `setup:di:compile` is required, and if you deploy static content you must run `setup:static-content:deploy` too; on a developer setup you can skip both)
6. Paste your license key into **Admin → Stores → Configuration → Codingrow → AI Babel Enchanter → License → License Key**, save, then flush the cache.

### Upgrading from 2.x

v3.0.0 replaces the old single **Enhancement/Translation provider** configuration with the **AI — Models** pool. After `setup:upgrade`, open **AI — Models** and add your provider keys there (see below). Your profiles, generated content and rollback history are untouched.

### Uninstallation

Set **Enable** to **No** and save to stop all processing immediately. Your catalog content stays exactly as it is — use rollback first if you want to revert to the original text. To remove the module entirely: `bin/magento module:disable Codingrow_BabelEnchanter` then `composer remove codingrow/module-ai-babel-enchanter`.

---

## AI — Models: your reusable credential pool

![AI — Models pool with the Max tokens box and cost estimators](img/01-ai-models.png)

Open the **AI — Models** tab. This is where you register every AI you want to use, **once**, and then reference them anywhere. Each row is one credential:

| Field | What it does |
|---|---|
| **Name** | A custom label (e.g. "Sonnet quality", "Gemini bulk"). Free text — rename anytime. |
| **Provider** | Anthropic, OpenAI, Google (Gemini), OpenRouter or DeepL. A link right below always points to the correct key page for the selected provider. |
| **API key** | Your key, stored **encrypted**; never shown again in clear (masked as `******`). |
| **Model** | Type the model id, or click **Load models** — the module calls the provider's own model list with your key and fills a dropdown with only the models that actually **respond 200 within a few seconds**, each annotated with its **response speed**, sorted fastest-first. Picking one also fills the Name for you. |
| **Capability** | `translate`, `enhancement` or `both` — decides in which per-step dropdowns this AI appears. |
| **Default** | One credential is the default: the general fallback, and the default **judge** in AI Compare. |
| **Fallback order** | The order the failover chain tries credentials in. |
| **Enabled** | Whether the credential is part of the chain and the dropdowns. |

**Failover, built in.** If a run hits a dead key — out of credit, rate-limited, an auth error — the module doesn't burn the whole queue against it: it marks that credential *cooling* for the rest of the run and **continues on the next credential in the chain**, across providers. One line to recover from "the Anthropic balance ran out at 2 a.m."

### Max tokens — the one setting that decides whether the copy comes out complete

Every AI has a **Max tokens** field, shown in a prominent box because it matters more than it looks. It is the **ceiling** on the tokens the model may *generate* — a **limit, not a target**: you pay only for what's produced, so a higher cap does **not** cost more when the prompt keeps the copy short. Set it too low, though, and the response is **cut short**; with "thinking" models (e.g. Claude Sonnet 5) the model can spend the whole budget reasoning and return **nothing at all**, leaving the product on its original text. The **default of 4000** covers the full package (description + short description + title + meta). If you ever see short, empty or untranslated output, this is the first thing to check.

### Advanced (per credential)

- **Extended thinking** — off by default. Extended reasoning helps on hard reasoning tasks but does **not** improve sales copy, while it costs more tokens and time; keep it off for generation. (The AI Compare **judge** turns it on by itself, and gracefully falls back if the chosen model doesn't support it.)
- **Omit sampling params** — for models that reject `temperature`/`top_p` ≠ default. The module also auto-heals this at runtime.
- **Price €/1M in / out** — optional. Fill these to feed the **cost estimate** in AI Compare with your exact prices; leave empty to use the built-in indicative price list.

---

## AI Compare — try every model on *your* products, then let an AI judge decide

![AI Compare running: the live matrix, the judging skeleton and the timer](img/02-ai-compare-run.png)

This is the feature that turns "which model should I use?" from a guess into an experiment. Open a profile → **AI Compare** tab:

1. **Tick the models** you want to compare (any of your enabled AIs) and pick **how many sample products** (1–5) from the profile's categories.
2. **Run comparison.** The module magnifies each sample product **with each model** — no writes, it's a dry-run — and fills a **matrix**: one column per model, one row per product. Each cell shows the generated title with a **col.row reference** (e.g. `2.1`), the **generation time**, an **indicative € cost** (from the real API token counts), and a **Preview** button that opens the fully rendered result in a new tab, titled so you always know which cell it is.
3. **The judge scores everyone.** Pick the **Judge** in the dropdown — your most capable model — and it rates every output on a rubric (Italian/target-language quality, HTML formatting, contextualized measures, premium register, JSON validity, SEO) and picks a **winner**. The verdict table adds **Avg time** and **Avg cost** per model, so you see **quality, speed and cost on a single line**.

![The verdict table: per-model scores, winner, average time and average cost](img/03-ai-compare-verdict.png)

**"All models judge!" — the panel.** Prefer several opinions? Choose **All models judge (panel)** and *every* selected model returns its own verdict — one table each, stacked. It's a great way to see each model's taste (and its bias — a model tends to flatter itself, so for a neutral call pick a judge that isn't in the race).

**It waits with you, and you can stop it.** While a judge is thinking, a live **skeleton table with a big central timer** shows it's working (no "is it frozen?" moment); as each verdict lands it appears and the skeleton moves to the next judge. A **Stop** button interrupts the whole run — the current call is aborted — at any time.

**Nothing is lost.** The last comparison is **saved on the profile**: reopen the tab and the whole matrix and verdicts come back, with a *"Last comparison saved: …"* timestamp — no re-run, no extra cost.

![The saved comparison reopened, with the timestamp banner and the stacked panel verdicts](img/04-ai-compare-saved.png)

> **The judge writes in your admin language.** Scores are numbers, but the judge's *notes* and *rationale* are written in your **backend (admin) language**, because they're for you — not part of the product copy.

---

## Before → after: one real product

AI Babel Enchanter takes a bare, imported, English product record and turns it into rich, on-brand Italian selling copy — title included — with editorial HTML, contextualized measures and SEO meta. Nothing invented: only the real data, magnified.

![Before/after of one product: raw import vs. the magnified premium description](img/07-before-after.png)

**Before** (raw import):

> **Name:** `Writing Desk Solid Mango Wood`
> **Description:** "This one drawer writing desk is constructed from 100% solid mango wood and has a french grey hand-painted base and a stained oak-ish top. Other features include hand-carved flute legs…"

**After** (magnified, per a "premium artisan furniture" persona):

> **Name:** *Scrivania in Mango Massello Bicolore con Gambe Scanalate e Cassetto a Conchiglia*
> **Description (rendered HTML):** an evocative hero opening, colored micro-subtitles, bold **contextualized measures** ("con i suoi 110 cm di larghezza trova posto anche in ambienti raccolti…"), highlight marks, a callout on the hand-crafted care, a pull-quote — all in warm, premium Italian.
> **Short description:** a 200–300-char SEO synthesis with the key measures in bold. **Meta title = the new name**; **meta description** ≤ 155 chars for the Google snippet.

The exact look is driven entirely by your **persona prompt** (see below) — tone, structure, even the color palette of the HTML are yours to set.

---

## User guide

### Profiles, categories and the persona prompt

Everything is organized into **profiles**. A profile targets a slice of the catalog by **category** (set in **Settings**), lists the **attributes** it may touch, and holds the **pipeline** of groups and steps. The heart of a profile is the **persona prompt** — free-text instructions that set the voice, rules and (for rich HTML copy) the exact formatting the model must produce.

> **Example persona prompt:** "You write for a premium outdoor-gear store. Be concise and confident, lead with the benefit, use British English, never invent specifications, keep every description under 90 words."

Profiles are **anti-revert**: a product already processed isn't re-processed on the next run unless you reset it, so re-running is safe and only picks up new or reset products.

### Groups and steps — model and prompt *per step*

Inside a profile you define **groups** of **steps** that run in order. The two step types are **Magnify** (rewrite/expand with an enhancement AI) and **Translate** (produce the target-language versions). New in v3.0.0, **each step carries its own prompt and its own model** (chosen from the pool, filtered by capability) — so you can, for example, magnify with a top-tier model and translate 500k low-value titles with a cheap fast one, all in one pipeline. Each group can be enabled/disabled independently, and each group scopes its steps to a chosen set of **attributes** (only the base ones by default — `name`, `description`, `short_description`, `meta_description` — with a "show all" toggle for power users). The normal pipeline is **Magnify → Translate**: the source is improved first, then the *improved* text is what gets translated, so every store view inherits the better copy.

**Language follows the persona.** Output is written in the language your persona/brief asks for; the store-view language is only the default when the persona doesn't specify. Your curated copy is never silently left in the source language.

### The magnify scaffolding is editable (advanced)

![Settings → the editable technical default that wraps your persona](img/06-magnify-template.png)

Under **Settings → Magnify prompt (technical default)** you can view and edit the technical instructions the module wraps around your persona (language rule + expansion), with a **Reset to default** button. You normally never touch this — the operational prompt goes in the profile step. The final **JSON output format is enforced by the module** and appended last, so a bad edit here can never break the module's parsing.

### Cross-sell, related and up-sell (AI relations)

Three independent toggles let a Magnify step propose **cross-sell**, **related** and **up-sell** products. The model only ever suggests **real SKUs from your catalog** — it cannot invent a product — and writes them into Magento's native links, so they appear in the standard storefront blocks.

### Dry-run preview — and a live rendered preview in the article

Before anything is written, **Preview** a step: the module generates the proposed content for a sample product and shows the raw and **rendered** result (with a Google-style SERP snippet) — nothing is saved. And on each article's **Enrichments & Translations** page there's now a **live, formatted HTML preview** (images stripped) that updates as you edit the fields, plus a direct **link to the product on the storefront** — no more copy-pasting into an external tool to see how it looks.

![The Content page: the live rendered preview and the storefront link](img/08-content-preview.png)

### "Enrichments & Translations" grid

![The Enrichments & Translations working grid](img/05-enrichments-grid.png)

The **"Enrichments & Translations"** tab is the working grid: every generated field for every product and language, paginated and filterable. **Edit** any value inline before or after it's applied — the AI output is a starting point, not a lock — and each field keeps a link back to the **original** with a one-click **Restore original**. This is the human-in-the-loop layer over the automated pipeline.

### Texts you declare final (v3.4.0)

Sooner or later there is a product whose description you write yourself: the flagship item, the one the owner cares about, the one where the AI copy was good but not *right*. You fix it by hand, and from that moment you want one guarantee — **nobody rewrites this again**. That guarantee is the **Final** checkbox.

Open **Enrichments & Translations**, open a product, and under every field there is a checkbox labelled **"Final — never regenerate this text"**. Tick it and that one field is declared yours.

![The Enrichments & Translations editor with a "Final — never regenerate this text" checkbox under each field](img/09-final-checkbox.png)

**What "final" means, precisely.** For that field, on that product, in that store view:

- the AI is **never called** for it again — not on the next run, not after you change the persona prompt, not after you switch model, not after a cache flush, not when you press **Apply to already processed**. It costs **nothing**, for ever. And if *every* field of a product is declared final, the AI call for that product **is not made at all**.
- if the value disappears from the product — an importer overwrote it, someone restored the original, a migration wiped it — the background check **puts your declared text back**, again at no cost.
- it wins over the module's own automatic rules. The meta title, for example, is normally forced to follow the product name, always; declare the meta title final and **your** text wins even over that.
- it applies to **that one field only**. The other fields of the same product keep being generated normally. Declaring the description final does **not** freeze the meta title, and it does **not** take the product out of the profile.

**Editing in the grid ticks it for you.** As soon as you type into a field in the grid, its Final checkbox ticks itself. It is the behaviour the interface implies — you went in and corrected that text, so the correction is the whole point — and before v3.4.0 it did not happen: a hand-corrected text survived until the next prompt change and was then silently regenerated over the top. If you *want* a corrected text to be regenerated later, untick the box before saving.

> **Example.** You sell handmade furniture. For the Mango writing desk you rewrite the short description yourself, because the AI did not know the drawer front is carved from a single piece. You tick **Final** on `short_description` and save. Three weeks later you rewrite the persona prompt to a warmer register and press **Apply to already processed**: every other field of every other product is regenerated, the desk's description and meta are regenerated too — and its short description is exactly the sentence you wrote, untouched, with no AI call billed for it.

**The one thing to remember:** a final field is the *only* thing in the module that survives a change of persona prompt or model. Everything else is rebuilt from the saved result, and the saved result is filed under a key that includes the whole prompt — change the prompt and none of the saved results can be reused.

### Texts that come back by themselves (v3.4.0)

Here is a failure that used to pass unnoticed. A product that has been processed **never re-enters the queue on its own** — on purpose, so that re-running a profile is cheap and safe. The side effect: if something overwrites a generated text *after* the product was processed, that product is never looked at again. The storefront keeps showing the overwritten text, the queue says *done*, the history still holds the generated copy, and **no error is raised anywhere**. We found it on a real catalogue: 318 products showing English titles and descriptions in an Italian shop, for weeks, with the Italian text still sitting in the module's own history.

v3.4.0 closes this. A background check runs hourly and asks one question per saved result, **reading only the database and never calling an AI to ask it**: *is the text I wrote still on the product?* When the answer is no, the product goes back into the queue with the cheapest treatment that fits:

| What the check finds on a product | What it does | What it costs |
|---|---|---|
| at least one of the missing fields is **declared final** | puts your declared text straight back | **nothing**, always |
| the fields changed, but the **source text is unchanged** | reuses the saved result — no AI call | **nothing** |
| the fields changed **and the source text really changed** | regenerates them: this is genuinely new product material | **a paid AI call** — subject to the cap |

**It works in slices, and this matters.** Each run examines **500 saved results** and remembers where it stopped, carrying on from there an hour later. The comparison costs two queries per row, so sweeping a large catalogue every single hour would be a burden nobody asked for. The whole catalogue is still covered — just spread over time. On a big catalogue that means a text which went missing may wait hours, and on a very large one a couple of days, before the check reaches it. **It is not stuck; it has not got there yet.** If you need an answer now, `bin/magento babel:reapply` looks at whatever you point it at, immediately.

You control it under **Stores → Configuration → Codingrow → AI Babel Enchanter → Keeping the texts in place**:

![The "Keeping the texts in place" configuration group with "Put back texts that disappeared" and "At most, per check"](img/10-keeping-texts-in-place.png)

- **Put back texts that disappeared** — Yes by default. Set it to No and nothing is ever put back automatically; you keep the `babel:reapply` command for manual passes.
- **At most, per check** — 200 by default. Anything above the cap is picked up by the following runs. Set it to **0** to switch the background check off.

**Why the cap counts products and not AI calls.** "This one costs nothing" is a *prediction* based on the saved result. If you changed the prompt or the model in the meantime, that saved result can no longer be reused and a restore that looked free turns into a paid one. Capping products keeps the bill bounded even when the prediction is wrong. Free restores are processed first, so the cheap work always gets done before the cap is reached.

**What it does not do, and what it does.** It never touches prices, stock, images or any attribute outside the ones the profile manages, and it never enrols a product that was never processed. But be clear on the part that surprises people: it puts the **product** back in the queue, and the run then rewrites **every field that profile manages**. So if you had corrected one of those fields on the product page, that correction is replaced. That is not a bug, it is the two-channel rule: a correction you want kept belongs in the grid, or gets the **Final** tick.

From the command line:

```
bin/magento babel:reapply                 # report only — says what it would do, changes nothing
bin/magento babel:reapply --confirm       # actually requeues
bin/magento babel:reapply --only-free     # only the restores that cost nothing
bin/magento babel:reapply --profile=3 --limit=500
```

Without `--confirm` the command **writes nothing**: it is the safe way to see the state of a catalogue before deciding.

### Applying a new prompt to the products already done (v3.4.0)

Profiles are deliberately anti-revert: a product already processed is not processed again, which is what makes re-running a profile safe and cheap. The flip side is that when you improve the persona prompt, the improvement only reaches products processed *from that moment on* — everything already done keeps the old copy. Until v3.4.0 the only way to include them was `babel:run --force` from the command line.

Now the profile page has a button for it:

![The profile action bar with the "Apply to already processed" button](img/08-apply-already-processed.png)

**Apply to already processed** puts the products marked *done*, *partial* and *skipped* back into the queue for that profile. The confirmation states the cost in plain words before anything happens:

> *Apply the current settings to the products already processed as well? Products whose source text has not changed are reused at no cost. The others are rewritten by the AI, which your provider bills you for, so after changing the persona or the instructions expect the whole profile to be rewritten.*

That last clause is not boilerplate. Every magnified result is filed under a key that includes **the provider, the model and the entire system prompt**. Change the persona, change the template, change the model, and **not one magnify result in the catalogue can be reused** — and the translations follow, because what they translate has just changed. So pressing this button after a prompt change means *"regenerate this whole profile, and pay for it"*. Before a prompt change, on an untouched catalogue, the same button is nearly free.

Use it when: you have improved the prompt and want the old products brought up to the new standard; you have added an attribute to a group and want it filled in everywhere; you fixed a mapping mistake.

Do not use it when: you only want to repair texts that went missing — that is the background check above, and it is far cheaper.

Fields declared **Final** are not regenerated by this button either.

### If your catalogue is imported: read this first

If products arrive in Magento from a supplier feed, an ERP, a PIM or any scheduled import, **that importer and Babel write to the same fields**. Whether the two coexist peacefully depends on how the importer is configured and on how many store views you have — and when they do not coexist, the symptom is not an error message, it is simply your paid-for copy quietly disappearing.

The acceptance test is one minute long: enrich one product, run the import **without changing anything in the feed**, reload the product. If Babel's text is still there, that importer is compatible. If it is not, it is not compatible, and no setting fixes it.

→ **[Babel and importers — the full page](https://codingrow.com/ai-babel-enchanter/importers)** ([Markdown](https://codingrow.com/ai-babel-enchanter/importers.md)) — phase-by-phase timelines, the single-store-view caveat, where Babel compares, the worst case, and the acceptance test.

### Rollback

Every value the module writes is reversible. Beyond per-field **restore original**, the `babel:rollback` CLI reverts content in bulk — by profile, by product, or the whole run — back to the text (and url_key/301 redirects and relations) that was in place before the module touched it. A full-catalog run is never a one-way door.

### Queue, robustness and the credit circuit-breaker

Long runs go through a **queue** you can watch, pause, resume and **clear**. Robustness is built in:

- **Real provider errors are visible** in the queue (e.g. *"credit balance too low"*), not a generic HTTP code.
- **Empty or truncated AI responses fail loudly** — the item is marked *failed* with the real reason, and is **never silently written with the original (untranslated) text**.
- The **circuit-breaker** (under **Resilience**) pauses after N provider errors, waits a **cooldown**, and can **auto-retry** transient failures; combined with the pool's **failover**, a run survives a dead key by moving to the next.

### Merchant listings: returns and shipping (v3.5.0)

Besides the texts, the module can publish the product's structured data (**SEO Rendering → Structured data
(JSON-LD Product/Offer)**). Google also reads a **return policy** and **shipping details** from that block
to show them in its free merchant listings, and Search Console reports them as missing when they are
absent — "Missing field `hasMerchantReturnPolicy`", "Missing field `shippingDetails`". Neither can be
derived from the catalogue: they are commercial terms, declared under **Merchant listings (returns and
shipping)**, and both blocks are **off by default**.

![Merchant listings group in the admin: publish the return policy, countries, return window, days, return method and who pays; then publish shipping details with countries, cost and preparation/delivery times](img/11-merchant-listings.png)

| Field | What it declares | Notes |
|---|---|---|
| **Publish the return policy** | `hasMerchantReturnPolicy` | Off by default. |
| **Countries the policy applies to** | `applicableCountry` | Picked from Magento's country list. Required by Google — without it the block is skipped. |
| **Returns accepted** | `returnPolicyCategory` | Within a number of days / no time limit / not accepted. |
| **Days to return** | `merchantReturnDays` | Only for the finite window. In the EU the legal minimum is 14. |
| **How the goods come back** | `returnMethod` | By mail/courier, in store, at a drop-off point. |
| **Who pays the return** | `returnFees` | Free, a fixed fee (with its amount), or the customer's responsibility. |
| **Publish shipping details** | `shippingDetails` | Off by default. |
| **Countries you ship to** | `shippingDestination` → `DefinedRegion` | Same country list. Required. |
| **Shipping cost** | `shippingRate` → `MonetaryAmount` | Store currency; `0` for free shipping. |
| **Preparation / delivery time** | `deliveryTime` → `handlingTime` / `transitTime` | Working days, minimum and maximum; filling only one end uses it for both. |

Two rules are deliberate. A block whose required fields are incomplete is **dropped whole** rather than
published half-true — Google compares what you declare with what the customer finds on your site, so a
wrong declaration is worth less than a missing one. And "returns not accepted" publishes neither a return
method nor who pays: there is no return to describe.

The offer also carries **`validFrom`**, the date the published price is valid from (the start of the
special price, otherwise the product's creation date). That one needs no configuration.

#### Values that come from the product (v3.6.0)

These terms are rarely the same for the whole catalogue: preparation and delivery times differ between
your own warehouse and dropshipping, shipping depends on how bulky the item is, the return window on
what is being sold. And the figure usually **already exists on the product**, in an attribute your
importer or your team fills in.

![The fields that take their value from a product attribute, with the attribute picker and the free-text code box](img/12-merchant-attributes.png)

*Days to return*, *Who pays the return*, *Shipping cost*, *Preparation time* and *Delivery time* each
have a **"take it from a product attribute"** picker below them, listing the catalogue's attributes —
**custom ones included** — plus a box to type a code the list does not offer (the box wins over the
picker). The resolution order is: the hand-typed code, then the picked attribute, then the fixed value.

| Situation | What is published |
|---|---|
| The product has the attribute filled in | The product's value wins |
| The product does not have it | The fixed value configured above |
| The attribute holds something that is not a number | Back to the fixed value — never a broken figure |

A product attribute holds **one** number, not a range, so for the times that number is used as both the
minimum and the maximum. For *Who pays the return* the attribute may hold a schema.org value or simply
`free` / `gratis` / `yes` (free return) or `no` / `0` / `customer` (the customer pays). Attributes not
visible on the storefront work too — the common case for importer-filled fields.

---

## Estimating the cost of a full-catalog run

AI Compare already shows you a **real, per-product € cost** for each model on your own products (from the actual API token counts). For planning a whole catalog, the **Cost estimators** on the AI — Models page do the math per product:

- **Magnify** (LLM tokens): `input_tokens/1M × input_price + output_tokens/1M × output_price`.
- **Translate** (characters, DeepL): `characters/1M × char_price × languages`.

Per product, `cost = magnify + translate`; multiply by the number of products for the catalog. Indicative €/million:

| Magnify model | Input €/M tok | Output €/M tok |
|---|---|---|
| Gemini Flash-Lite | ~0.10 | ~0.40 |
| OpenAI gpt-4o-mini | ~0.14 | ~0.55 |
| Claude Haiku 4.5 | ~0.90 | ~4.60 |
| Claude Sonnet 5 | ~3.00 | ~15.00 |

Translation with DeepL is about **€20 per 1M characters**. All figures are indicative — real numbers depend on content length, the models you pick, and provider cache/batch discounts. With the built-in **cache**, unchanged products aren't re-billed on later passes.

---

## Command line (CLI)

| Command | What it does |
|---|---|
| `bin/magento babel:run` | Runs a profile (magnify/translate pipeline) over its target products; the standard way to launch a full-catalog pass. |
| `bin/magento babel:rollback` | Reverts generated content back to the original — scoped by profile, product, or the entire run. |
| `bin/magento babel:queue` | Inspects and controls the processing queue: status, pause, resume. |
| `bin/magento babel:reapply` | Finds generated texts that are no longer on the products and puts them back. **Reports only unless you pass `--confirm`**; `--only-free` restricts it to the restores that cost nothing, `--profile` and `--limit` narrow the pass. |

---
© Codingrow (a brand of Revera SRLS). Sold worldwide. Licensed per domain.
