JSON:API Multilingual
Drupal core's JSON:API serves the language-negotiated translation of an entity and exposes no way to address a single translation. JSON:API Multilingual adds per-translation GET, POST, PATCH, and DELETE on the existing resource URLs.
The target language is selected per request, not by URL prefix: a langCode query parameter, the Content-Language header on writes, or Accept-Language for negotiated reads. Responses set Content-Language, advertise available translations as hreflang links, and add Accept-Language to Vary. The contract does not depend on the site's language negotiation, so it behaves the same on any multilingual setup.
Endpoints
- Read a translation:
GET /jsonapi/node/article/{uuid}?langCode=dereturns that translation (404if it does not exist). OmitlangCodeand sendAccept-Languageto negotiate, with fallback to an existing translation. - Create a translation of an existing entity:
POST /jsonapi/node/article/{uuid}withContent-Language: de(409if it already exists; only translatable fields may be written). - Update one translation:
PATCH /jsonapi/node/article/{uuid}?langCode=dechanges that translation only (404if it does not exist; usePOSTto create). - Delete one translation:
DELETE /jsonapi/node/article/{uuid}withContent-Language: de(400for the default translation). Without a language, the whole entity is deleted, matching core.
curl 'https://example.com/jsonapi/node/article/{uuid}?langCode=de' \ -H 'Accept: application/vnd.api+json' # 200 OK, Content-Language: de, data.attributes.langcode == "de"
JSON:API: writes are gated by jsonapi.settings.read_only (405 when read-only) and per-translation entity access is enforced.
JSON:API Multilingual is a forward-compatible implementation of core issue #3199697 and mirrors that merge request's HTTP contract.
Much of the code for this module has been created through AI assistance or generation, and has been spec'ed, reviewed, tested, and updated by real people.