```yaml
id: UJ-019
title: Queue selected string translations
actors: [administrator]
surface: admin-spa
goal: Administrator queues selected discovered strings for selected target languages.
trigger: Select strings and activate bulk Generate on themes or plugins translations page.
fixtures: [authenticated-admin, discovered-strings, active-target-language, available-translation-service]
success_state:
  visible: N translation jobs queued
  durable: One job per selected string and target is pending.
  persistence: Fresh job list returns queued job identities.
source_specs: [admin/src/pages/string-translate.js, includes/API/TranslateController.php, includes/API/TranslateJobsController.php]
readiness: draft
canonical: false
blockers: []
```

## Path

| Hop | Evidence |
|---|---|
| UI trigger | `admin/src/pages/string-translate.js:124` |
| Request construction | `admin/src/pages/string-translate.js:1856` |
| Endpoint auth and parse | `includes/API/TranslateController.php:84` |
| Authoritative write | `includes/API/TranslateController.php:418` |
| Response | `includes/API/TranslateController.php:454` |
| Terminal render | `admin/src/pages/string-translate.js:1867` |

## Happy path

### H1
- Setup: Administrator authenticated; two named untranslated strings and one active target exist; translation service is available.
- Action: Select both strings, select target, and activate bulk Generate.
- Request: `POST /wp-json/international-press-zone/v1/translate-async` for first selected string.
- Response: `200` with `{success:true,jobs:[<job>],already_queued:[]}`.
- Visible: `2 translation jobs queued` renders after both per-string requests complete.
- Durable: Two pending jobs identify selected strings and target with no duplicate identity.
- Fresh read: `GET /wp-json/international-press-zone/v1/translate-jobs` returns both pending jobs.
- Forbidden: Unselected strings, other targets, source strings, settings, and licenses remain unchanged.
- Evidence:
  - Setup: `admin/src/pages/string-translate.js:1849`
  - Action: `admin/src/pages/string-translate.js:1854`
  - Request: `admin/src/pages/string-translate.js:1856`
  - Response: `includes/API/TranslateController.php:454`
  - Visible: `admin/src/pages/string-translate.js:1867`
  - Durable: `includes/API/TranslateController.php:418`
  - Fresh read: `includes/API/TranslateJobsController.php:78`
  - Forbidden: `admin/src/pages/string-translate.js:1850`

## Alternate and failure paths

### A1
- Setup: Administrator authenticated; selected string already has pending job for target.
- Action: Activate bulk Generate for same string and target.
- Request: `POST /wp-json/international-press-zone/v1/translate-async`.
- Response: `200` with `{success:true,jobs:[],already_queued:[<job>]}`.
- Visible: `1 translation jobs queued` renders after accepted request.
- Durable: Original pending job remains sole job for string and target.
- Fresh read: Job-list GET returns one matching pending job.
- Forbidden: No duplicate job or translation row is created.
- Evidence:
  - Setup: `includes/API/TranslateController.php:396`
  - Action: `admin/src/pages/string-translate.js:1854`
  - Request: `admin/src/pages/string-translate.js:1856`
  - Response: `includes/API/TranslateController.php:454`
  - Visible: `admin/src/pages/string-translate.js:1867`
  - Durable: `includes/API/TranslateController.php:396`
  - Fresh read: `includes/API/TranslateJobsController.php:78`
  - Forbidden: `includes/API/TranslateController.php:396`

## Permissions and boundaries

### P1
- Setup: Actor lacks translation-management capability; selected strings and target exist.
- Action: Submit one selected-string queue request.
- Request: `POST /wp-json/international-press-zone/v1/translate-async`.
- Response: `403` with `{code:"rest_forbidden",message:"Sorry, you are not allowed to do that.",data:{status:403}}`.
- Visible: REST denial exposes no job payload.
- Durable: Job list remains unchanged.
- Fresh read: Protected job-list request returns permission denial.
- Forbidden: No job or translation row is created.
- Evidence:
  - Setup: `includes/API/TranslateController.php:232`
  - Action: `includes/API/TranslateController.php:92`
  - Request: `includes/API/TranslateController.php:84`
  - Response: `includes/API/TranslateController.php:232`
  - Visible: `includes/API/TranslateController.php:232`
  - Durable: `includes/API/TranslateController.php:232`
  - Fresh read: `includes/API/TranslateJobsController.php:78`
  - Forbidden: `includes/API/TranslateController.php:232`

## Source specs

- `admin/src/pages/string-translate.js`
- `includes/API/TranslateController.php`
- `includes/API/TranslateJobsController.php`
