Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ All notable changes to Tolk are documented in this file.

- **Settings** in the **Tools › Translations** menu, below **Audit**, for users who may edit Tolk's settings.
- **Translated slugs**, a setting that lists the collections whose new localizations get a slug made from their translated title, the way Statamic makes slugs in the target site's language. It is empty by default, so new localizations keep their origin's slug as before, and an existing slug is never changed.
- **Automatic updates**, off by default (the settings' new Automation tab): saving a published entry in its origin updates its localizations in the background, as **Update translations** does, and with **Create missing localizations** also translates it into the sites that have none. They can be limited to some collections, and in collections with revisions published at once. Each update is a run, listed as an automatic update, followed in the origin's banner after the save, and counted under **Automatic updates** in the Overview's usage; the localization's banner says when an automatic update last saved it. With a queue worker the update runs on the queue; without one, in the process that saved the entry, after its response.
- **Reset** on the Overview's usage card, which deletes the month's usage, and on the runs list, which deletes the finished runs; both ask first and are offered to users who may edit Tolk's settings.

### Changed
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ Tolk translates multi-site Statamic content from an origin site into its localiz
- **One field at a time**: "Translate from origin" in a field's actions.
- **In bulk**: a listing action, an Overview of every collection, taxonomy, global set and navigation by language, and runs with an estimate before they start, progress, cancel and resume. No queue or database needed.
- **From the command line**: translate collections, or the whole website into a new language in one command, plus the app's `lang/` files and the texts of forms.
- **Updates, not retranslations**: when the origin changes, only the changed paragraphs are sent again. Text a human edited is never overwritten unless you ask.
- **Updates, not retranslations**: when the origin changes, only the changed paragraphs are sent again. Text a human edited is never overwritten unless you ask. Optionally, saving an entry updates its translations by itself.
- **Quality**: a site profile, instructions, per-language formality and a glossary go with every request; an optional second model reviews each translation and its corrections are applied and listed with reasons.
- **Your own API keys**, from `.env`. Cost estimates before every bulk run, and usage per run and per month.

Expand Down
38 changes: 21 additions & 17 deletions TASKS.md

Large diffs are not rendered by default.

17 changes: 16 additions & 1 deletion docs/bulk-and-runs.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ Runs are started from:
- the **Translate…** action in entry and term listings and in a structured collection's tree view;
- the [Overview](#the-overview), which also translates global sets and navigation;
- an origin entry's **Translate into…** and **Update translations** ([The editor](editor.md#in-the-origin)), a run of that one entry;
- saving a published origin entry, when automatic updates are on ([Automatic updates](#automatic-updates));
- the command line, `tolk:translate` and `tolk:site` ([The command line](cli.md)).

In the CP they need the `run bulk translations` permission, except the origin entry's buttons, which need `translate content`. The commands need no permission.
Expand Down Expand Up @@ -63,6 +64,8 @@ A run started in the CP is driven in one of two ways, decided by `runs.execution

Runs from the command line are driven by the command, with worker processes (see [The command line](cli.md#how-the-command-runs)). Their page shows their progress like any other.

Automatic updates have no page to drive them, so they run on the queue or in the process that saved the entry ([Automatic updates](#automatic-updates)).

### "Keep this tab open"

With the browser driver, the run moves only while its page is open: "Keep this tab open while the run is in progress. Closing it pauses the run until you open it again." The page warns before you leave it while it drives the run. Nothing is lost when it closes: the next time anyone who may see the run opens its page, the run continues. An item the closed page was working on is picked up again after about 80 seconds. A scheduled `tolk:prune` stops a run that made no progress for five minutes, a browser-driven one whose page stayed closed included; its page then offers **Resume** ([Stalled runs](#stalled-runs)).
Expand Down Expand Up @@ -94,9 +97,20 @@ With a `retry_after` of 120, each lane job starts requests for up to 30 seconds

A lane that is killed is started again, up to five times in a row. If no worker works on a queue run for a while, its page shows "This run is not moving" with **Resume** and **Run in this browser instead**, which hands the run to the page (the browser driver) for a site whose queue has no worker after all.

### Automatic updates

With `auto_update` on ([Configuration](configuration.md#automation)), saving a published origin entry starts a run of that entry, as its **Update translations** button does ([The editor](editor.md#automatic-updates)). A run with nothing to translate is not kept. No page drives it:

- **With a queue worker** (`runs.execution` is `auto` or `queue`, and the connection has a worker), a job waits 10 seconds on the queue, so quick saves of the entry make one update, then starts the run with queue lanes, as above.
- **Without one**, or with `runs.execution` set to `browser`, the PHP process that saved the entry runs it after it has sent its response, so the editor does not wait. That process stays busy until the run ends, without PHP's time limit, sleeping through the waits a provider asks for. A web server that stops a request after a fixed time (PHP-FPM's `request_terminate_timeout`) stops the run with it; the run then stalls, and **Resume** on its page finishes it in the browser. Run a queue worker on a site whose entries are long or have many sites.

A save while an earlier run of the entry is still moving waits for that run (up to 15 minutes), so the same fields are not translated twice. A run started from the entry's banner counts too.

On the runs list an automatic run says **Automatic update**, and its page "Started automatically on …, when … saved the entry." It translates as the user who saved the entry: the sites they may not edit are left out. A save without a signed-in user, from code or a command, may update every site.

### Stalled runs

A run whose driver has gone (a closed command, a dead worker) is shown as not moving on its page. `tolk:prune` stops runs that made no progress for `runs.claim_ttl` seconds (5 minutes by default): it marks them aborted and commits what they saved, so their page offers **Resume**, which translates and saves the rest. See [The command line](cli.md#tolkprune).
A run whose driver has gone (a closed command, a dead worker, the process of an automatic update) is shown as not moving on its page. `tolk:prune` stops runs that made no progress for `runs.claim_ttl` seconds (5 minutes by default): it marks them aborted and commits what they saved, so their page offers **Resume**, which translates and saves the rest. See [The command line](cli.md#tolkprune).

## Why one writer

Expand Down Expand Up @@ -143,6 +157,7 @@ With Statamic's git integration, a run makes one commit ("Content translated") w
- **Editor**: the entry and term editor, and **Generate** and **Test connection** in the settings.
- **Runs**: the runs started in the CP (the **Translate…** action, the Overview, **Translate into…** and **Update translations**) or with `--queue`.
- **Command line**: `tolk:translate` and `tolk:site` without `--queue`, `tolk:lang`, `tolk:forms` and `tolk:profile`. A command-line run resumed on its run page counts under Runs from then on.
- **Automatic updates**: the runs started by saving an origin entry ([Automatic updates](#automatic-updates)).
- Users who may edit Tolk's settings can **Reset** the month's usage on the Overview, after a confirmation: the month's file is deleted and counting starts again from zero. A run's page keeps showing the run's own usage.
- Costs are computed from the prices in the model catalog (`models.*.price`). The monthly usage keeps the price of the day of each call; a run's page computes its cost from its tokens at the current prices. A model without a price makes the cost "unknown".
- Tolk sets no budget caps. Set spending limits with the provider; when one is reached, the provider rejects the requests and the run is aborted with that message.
11 changes: 11 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,17 @@ See [Context and glossary](context-and-glossary.md#the-glossary).
| `excluded_fields` | Excluded fields | none | Field handles that are never translated, in every blueprint of entries, terms and global sets. This applies to top-level fields; for a field inside a Replicator, Grid or Bard set, switch off its **Translate automatically** option instead (below). |
| `translated_slugs` | Translated slugs | none | New localizations of these collections get a slug made from their translated title, the way Statamic makes slugs in the target site's language ("Hallo Welt" becomes `hallo-welt`). In every other collection a new localization keeps its origin's slug. An existing slug is never changed. See [Entries](content-types.md#entries). |

### Automation

| Key | Field | Default | Meaning |
| --- | --- | --- | --- |
| `auto_update` | Update translations automatically | off | Each time an entry is saved published in its origin, its localizations are updated in the background: the fields whose origin changed and the fields not translated yet, as **Update translations** does. Fields edited by hand are never overwritten. Every save that changes text spends API credit. See [Automatic updates](editor.md#automatic-updates). |
| `auto_update_create` | Create missing localizations | off | Also translates the entry into the sites of its collection that have no localization of it yet, as **Translate into…** does. New localizations follow `new_localization_status`. Shown only while `auto_update` is on, like the two below; their values are kept while they are hidden. |
| `auto_update_collections` | Collections | none | Only the entries of these collections are updated automatically. Empty: every collection that is not excluded. |
| `auto_update_publish` | Publish updates | off | In collections with revisions, an update of a published localization goes into its working copy and waits until someone publishes it. On: it is published at once, unless the localization already had unpublished changes. |

Entries only: terms, global sets and the navigation are not updated automatically.

### Providers

`providers` (Providers) shows, for Anthropic, OpenAI and Gemini, whether a key is set, whether it is Tolk's own, from the addon's config (`TOLK_*_API_KEY` by default), or comes from `config/ai.php`, which Tolk shares with the rest of the site, and, without a key, which variables to set, with **Test connection**: one small request to the provider's fast model, with a 20-second timeout, answered with the time it took or a readable error. The field stores nothing.
Expand Down
14 changes: 14 additions & 0 deletions docs/editor.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,20 @@ When it has finished, the banner shows a line per site with a link to the locali

You need only `translate content` for this: you follow and cancel the run you started from the entry, and it continues when you open the entry again, without seeing the Runs pages. A cancelled or aborted run is resumed on the Runs pages, which need `run bulk translations`; otherwise start the translation again from the entry.

### Automatic updates

With **Update translations automatically** on in the settings (the Automation tab, see [Configuration](configuration.md#automation)), you do not need the button: each time you save a published entry in its origin, Tolk starts the same run as **Update translations**, into every localization of the entry. It translates the fields whose origin changed and the fields not translated yet, and keeps the fields edited by hand. With **Create missing localizations** on, it also translates the entry into the sites of its collection that have none, as **Translate into…** does.

- The origin's banner says when it happens: "Translations are updated automatically each time this entry is saved", or "…when this entry is published" for a draft, and in a collection with revisions, where Save goes to the working copy and only Publish saves the entry.
- After a save, the banner says "Updating the translations automatically into Greek…" and then shows the run's progress and its result, as for **Update translations**. When the update is done before the banner sees its run, it says "The translations into Greek were updated automatically." With a queue worker the run starts about 10 seconds after the save; when it has not started within half a minute, the banner stops waiting for it, and shows it the next time you open the entry. You can leave the page: the run does not need it.
- A save that changes no text starts nothing. A save while an earlier run of the entry is still going waits for it, so the same fields are not paid for twice.
- The run translates as you: the sites you may not edit are left out. It is listed on the Runs page as an automatic update, and its cost counts under **Automatic updates** on the Overview.
- The localization's banner says when an automatic update last saved it, "Updated automatically from English on 9 Oct 2026, 10:42.", with "not published yet" while the update waits in the working copy of a published localization in a collection with revisions (unless **Publish updates** is on).
- Saves from code (an import, a form handler) count too, when the entry is published.
- Entries only: terms, global sets and the navigation are updated with **Update translations**, bulk runs or the command line.

How the run is driven, with and without a queue worker, is in [Bulk translations and runs](bulk-and-runs.md#automatic-updates).

## Collections with revisions

In the editor, Tolk only fills the form, and Statamic saves it as it always does: on a collection with revisions, your Save goes to the working copy, and publishing stays a separate step. What happens when Tolk saves server-side (Translate into…, bulk runs, the command line) is in [Bulk translations and runs](bulk-and-runs.md#revisions).
2 changes: 1 addition & 1 deletion docs/extending.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ Tolk dispatches three events, all in `Reach\StatamicTolk\Events`:
| Event | When | Properties |
| --- | --- | --- |
| `TargetTranslated` | Translations were saved into a target server-side: by a run, Translate into…, or the command line. | `ref` (a `TargetRef`: `type`, `id`, `site`; as a string `entry::{id}`, `term::{taxonomy}::{slug}::{site}`, `globals::{handle}::{site}`, `nav::{handle}::{site}`, `lang::{group}::{lang}` or `forms::{lang}`), `handles` (the fields saved), `models` (the models called, as `provider:model`), `usage` |
| `RunStarted` | A run moved from pending to running. | `runId`, `driver` (`RunDriver`: `browser`, `queue` or `cli`), `createdBy` (`['id' => …, 'name' => …]`, or `null` from the command line), `counts` (items per status) |
| `RunStarted` | A run moved from pending to running. | `runId`, `driver` (`RunDriver`: `browser`, `queue` or `cli`; `cli` also for an automatic update driven by the process that saved the entry), `createdBy` (`['id' => …, 'name' => …]`, or `null` from the command line and for an automatic update of an entry saved without a signed-in user), `counts` (items per status) |
| `RunFinished` | A run ended: completed, completed with errors, cancelled or aborted. A run that is resumed and ends again fires it again. | `runId`, `status` (`RunStatus`), `counts` (items per status), `usage` |

`usage` is a `UsageMap`; `$event->usage->toArray()` gives, per model, the `input`, `output`, `cache_read`, `cache_write` and `reasoning` tokens and the number of `calls`.
Expand Down
5 changes: 5 additions & 0 deletions docs/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,13 @@ To force one way, set `runs.execution` to `queue` or `browser`. `tolk:doctor` re

- **A queue run**: no worker has worked on it for a while. Start one (`php artisan queue:work`, or check your process manager), then press **Resume**; or press **Run in this browser instead** to let the page drive it.
- **A command-line run**: the command that ran it stopped (closed terminal, Ctrl-C, a crash). Press **Resume** on its run page to continue it in the CP (on the queue when it has a worker, otherwise driven by the page), or start the command again.
- **An automatic update**: the PHP process that saved the entry stopped before the run ended, usually because the web server stops requests after a fixed time. Press **Resume** on its run page, or save the entry again. A queue worker avoids it ([Automatic updates](bulk-and-runs.md#automatic-updates)).
- `tolk:prune` stops runs that made no progress for `runs.claim_ttl` seconds (5 minutes): it marks them aborted and commits what they saved, so **Resume** continues them. Schedule it daily ([`tolk:prune`](cli.md#tolkprune)).

### Saving an entry does not update its translations

Automatic updates need **Update translations automatically** on (the settings' Automation tab), and only these saves count: of an entry in a collection with more than one site, not excluded, and among **Collections** when that list is not empty; in the entry's origin, not in a localization without localizations of its own; while the entry is published (with revisions, when it is published). A save that changes no text starts nothing, and the user who saved must be allowed to edit the localizations. With a queue worker the update starts about 10 seconds after the save. Each update is listed on the Runs page. See [Automatic updates](editor.md#automatic-updates).

### Items fail with "No worker finished this item in 3 attempts."

The process translating the item died three times (a PHP time or memory limit, a killed worker). Check the PHP error log, raise `memory_limit` or `max_execution_time` for the queue worker, or lower the batch sizes; then **Retry failed**.
Expand Down
Loading
Loading