```yaml
id: UJ-002
title: Edit an existing language
actors: [administrator]
surface: admin-spa
goal: Administrator changes one language configuration and fresh state returns exact new values.
trigger: Activate Edit for a language at /wp-admin/admin.php?page=international-press-zone#/languages, change fields, then activate Save Changes.
fixtures: [authenticated-admin, editable-nondefault-language]
success_state:
  visible: "Language updated successfully" renders and row shows new values.
  durable: wp_ipz_languages row contains submitted values.
  persistence: Fresh GET /wp-json/international-press-zone/v1/languages/{code} returns updated values.
source_specs: [admin/src/pages/languages.js, includes/API/LanguagesRestController.php, includes/Core/LanguageManager.php]
readiness: draft
canonical: false
blockers: []
```

## Path

| Hop | Evidence |
|---|---|
| UI trigger | `admin/src/pages/languages.js:216` |
| Request construction | `admin/src/pages/languages.js:648` |
| Endpoint auth and parse | `includes/API/LanguagesRestController.php:154` |
| Authoritative write | `includes/Core/LanguageManager.php:451` |
| Response | `includes/API/LanguagesRestController.php:469` |
| Terminal render | `admin/src/pages/languages.js:654` |

## Happy path

### H1
- Setup: Administrator authenticated; one non-default language exists with unique locale.
- Action: Activate `Edit`, change native name, locale, flag, direction, or active state, then activate `Save Changes`.
- Request: `PATCH /wp-json/international-press-zone/v1/languages/{code}`.
- Response: `200` with `{success:true,message:"Language updated successfully.",data:<updated-language>}`.
- Visible: `Language updated successfully` renders; refreshed row shows exact submitted values.
- Durable: Matching `wp_ipz_languages` row contains exact submitted fields and updated timestamp.
- Fresh read: `GET /wp-json/international-press-zone/v1/languages/{code}` returns `200` with exact updated values.
- Forbidden: Language code, other rows, translation rows, and default selection remain unchanged.
- Evidence:
  - Setup: `admin/src/pages/languages.js:411`
  - Action: `admin/src/pages/languages.js:435`
  - Request: `admin/src/pages/languages.js:648`
  - Response: `includes/API/LanguagesRestController.php:469`
  - Visible: `admin/src/pages/languages.js:654`
  - Durable: `includes/Core/LanguageManager.php:451`
  - Fresh read: `includes/API/LanguagesRestController.php:116`
  - Forbidden: `includes/API/LanguagesRestController.php:464`

## Alternate and failure paths

### A1
- Setup: Administrator authenticated; another language already owns submitted locale.
- Action: Submit `Save Changes` using that conflicting locale.
- Request: `PATCH /wp-json/international-press-zone/v1/languages/{code}`.
- Response: `400` with `{code:"invalid_language_data",message:<locale-conflict>,data:{status:400}}`.
- Visible: Locale-conflict message renders in error toast; modal remains available.
- Durable: Target and conflicting `wp_ipz_languages` rows remain unchanged.
- Fresh read: `GET /wp-json/international-press-zone/v1/languages/{code}` returns `200` with original values.
- Forbidden: No language row changes; no duplicate locale exists.
- Evidence:
  - Setup: `includes/Core/LanguageManager.php:420`
  - Action: `admin/src/pages/languages.js:435`
  - Request: `admin/src/pages/languages.js:648`
  - Response: `includes/API/LanguagesRestController.php:475`
  - Visible: `admin/src/pages/languages.js:661`
  - Durable: `includes/Core/LanguageManager.php:420`
  - Fresh read: `includes/API/LanguagesRestController.php:116`
  - Forbidden: `includes/Core/LanguageManager.php:420`

## Permissions and boundaries

### P1
- Setup: No authenticated WordPress session.
- Action: Submit an update payload for an existing code.
- Request: `PATCH /wp-json/international-press-zone/v1/languages/{code}`.
- Response: `401` with `{code:"rest_forbidden",message:"Sorry, you are not allowed to do that.",data:{status:401}}`.
- Visible: REST denial exposes no language mutation result.
- Durable: `wp_ipz_languages` remains unchanged.
- Fresh read: Anonymous mutation retry returns the same `401` denial.
- Forbidden: No row changes; no administrator identity or nonce is disclosed.
- Evidence:
  - Setup: `includes/API/LanguagesRestController.php:736`
  - Action: `includes/API/LanguagesRestController.php:159`
  - Request: `includes/API/LanguagesRestController.php:154`
  - Response: `includes/API/LanguagesRestController.php:736`
  - Visible: `includes/API/LanguagesRestController.php:736`
  - Durable: `includes/API/LanguagesRestController.php:736`
  - Fresh read: `includes/API/LanguagesRestController.php:736`
  - Forbidden: `includes/API/LanguagesRestController.php:736`

## Source specs

- `admin/src/pages/languages.js`
- `includes/API/LanguagesRestController.php`
- `includes/Core/LanguageManager.php`
