# UJ-004 Queue bulk translations and apply callbacks

Audience: AI coding agents first.

- Actor + entry point: WordPress administrator; `wp-admin/admin.php?page=international-press-zone#/translate-posts/posts`.
- Priority: MUST-HAVE.
- Preconditions: Seed two source posts and one missing target language through REST APIs; active key; sufficient credits.
- Test layer: L2.

## Steps

1. Select two posts → bulk bar renders `2 selected`, aggregate estimate, target-language selector, and `Translate Selected` (`admin/src/pages/translate-posts.js:1237-1257`).
2. Activate `Translate Selected` → `Bulk Translation` modal opens with `0 of 2` and `Estimating...` (`admin/src/pages/translate-posts.js:1324-1331`, `admin/src/pages/translate-posts.js:1398-1405`).
3. Observe one `POST /translate-async` per post/language → each queue row changes to checkmark and counter reaches `2 of 2` (`admin/src/pages/translate-posts.js:1345-1356`, `admin/src/pages/translate-posts.js:1470-1480`).
4. Deliver signed callbacks → reload shows both translations applied; no selected rows remain (`admin/src/pages/translate-posts.js:1388-1392`; `docs/contracts/plugin-client-contract.md:293-298`).

## Failure branches

- Auth failure `401` → failed row and visible invalid-key message; later jobs skipped.
- Insufficient credits `402` → visible credits/quota toast, current row failed, remaining rows `Skipped` (`admin/src/pages/translate-posts.js:1357-1375`, `admin/src/pages/translate-posts.js:1460-1465`).
- Rate limit `429` → failed row remains visible; no false completed state.

## Backend touchpoints

Repeated `POST /v1/jobs`; `POST /wp-json/international-press-zone/v1/callback`; plugin `/translate-async` proxy.

