ADR-213: Approval preview lines are in the acting user's language 

Status

Accepted

Date

2026-10-06

Authors

Netresearch DTT GmbH

Context 

A run that needs a human approval suspends, and every tool that implements ToolPreviewInterface describes the pending call in lines of text (ADR-136). The lines are produced at the moment of suspension, persisted in the suspended state, shown on the approval card after a per-viewer authorisation, and produced a second time when the run resumes, to compare them with what the approver was shown (ADR-184).

All of them are English literals. For a German editor that makes the card a mix: the buttons and the labels around it are German, the lines in the middle read New page under page [2] "Open":, navigation title: and first among the subpages. The editorial guidelines for the assistant (rules 14 to 18) ask for approval texts in the editor's language, in editor vocabulary, in a fixed order, without internal field names.

Two constraints follow from the design above and decide where the language can come from:

  • The lines are compared byte for byte at resume. Their language must be the same at suspend and at resume, on the synchronous path and in the queue worker, or every approval of a localised tool bounces as stale.
  • The run owner and the person who approves can be different people (ADR-130, ADR-133). One set of lines is persisted per pending call.

Decision 

The language of a preview line is the language of the run's acting backend user, the lang column of the user the run executes as, read from the ToolExecutionContext (ADR-083). Never the ambient $GLOBALS['LANG'], which belongs to whoever runs the call, and never the viewer's language.

  • A user without a lang value gets English, which is what TYPO3 itself does for such a user. A language without a catalogue of its own gets the English source text.
  • When the viewer's language differs from the acting user's, the card shows the lines in the acting user's language, complete and in one language. It does not mix, and it does not retranslate. In the usual case, an editor who starts the run in the chat and approves it there, the two are the same person.
  • When the acting user changes their language between suspend and resume, the re-computed lines differ from the persisted ones and the approval bounces once, with the current lines shown again (ADR-184). That is the existing staleness path doing its job; no special case.

The texts live in the extension's catalogue, locallang.xlf and de.locallang.xlf, under approvalPreview.*. A closed enum, ApprovalPreviewLabel, names every entry, and ApprovalPreviewTranslator resolves one for a given user. Tools do not build a sentence from English words: they pick labels and pass the values. Quotation marks, the word for "empty" and every sentence are catalogue entries, so a language decides its own.

A unit test walks the enum and fails when a label has no English or no German text, when the two texts take different placeholders, when the German text is the English one, when a text names an internal field or tool, or when the catalogue holds an approvalPreview.* entry that no label names.

The order of the lines follows rule 16 of the guidelines: what changes, where, the current state, the new state, the consequences. The last line is "Technical details": UIDs and table names, for support (rules 10 and 26). It is not an optional extra: it keeps the approval bound to the exact records the call names, because the lines above it name pages by title and two pages can share one (ADR-184).

What stays English. A refusal line is the string the tool's execute() hands the model as well; it is shared on purpose, and a refusal at preview time tells the approver the call would fail, not what it would do. The tool results that go back to the model stay English too; they are not approval text.

Scope of this decision. It is applied to create_page_draft, move_page and delete_record. The other tools that implement ToolPreviewInterface keep their English lines until they are converted to the same mechanism; each of them needs its own list of labels and its own expected text, and the guidelines' rules 19 to 21 (consequences) need to be checked against what each tool actually reads.

No new reads. The consequence lines show what the tools already read for their plan: translations, subpages, the number of records stored on a page, the number of references, recoverability. A line the tool cannot know is not shown. In particular delete_record does not say anything about redirects: it does not look at them, and whether core creates one on a delete is not something this decision verifies.

Considered alternatives 

English only (the status quo). Rejected: it contradicts rule 17 and mixes languages on every German card.

The viewer's language at render time. The right answer to the second constraint, and the larger change: the persisted state would have to hold label identifiers and values instead of text, the comparator would compare those, and both card renderers would translate at display time. The loop, the state encoding and the chat panels all change. Not rejected as a goal; rejected as part of this step, because it is a format change of persisted state and the acting user is the viewer in the common case. If approvals by someone other than the run owner turn out to be common, this is the next step.

The ambient backend language ($GLOBALS['LANG']). Rejected: it is the language of the process that happens to run the call, and it differs between the request that suspends and the worker that resumes (ADR-083).

Consequences 

● A German editor reads a German approval card for these three tools: the heading names the editorial action, the lines carry no field name, and the consequences of a delete are listed line by line.

● The English text is the catalogue's source text, so an installation in any other language sees the same English lines as before in structure, and a translation file for that language works without code.

◐ The lines of these three tools changed in wording and order. A caller that matched on the old English strings, rather than showing the lines, has to change; none in this repository did except the tools' own tests.

◐ A viewer whose language differs from the run owner's reads the owner's language (see above).

✕ The other tools' previews are still English, so a card for one of them still mixes languages until it is converted.

✕ A refusal shown in a preview is still English.