Language-neutral (und/zxx) content does not satisfy a langCode selector
Language-neutral content (langcode und, "Not specified", or zxx, "Not applicable") belongs to no specific language and, by Drupal convention, therefore to all of them. Core renders it in any language context, and this module already agrees in most places: the jsonapi_menu_items decorator treats neutral entities as matching every request, and the ?include= and /related/ paths serve them because both short-circuit on isTranslatable(), which is FALSE for a locked language.
Strict langCode reads disagreed. An entity whose langcode is und answered ?langCode=fr with 404 and disappeared from every ?langCode-selected collection, because both sites tested for an actual translation in the requested language. The same entity was therefore served when included from another resource and absent from the collection that should list it. "Not specified" is a standard option on every langcode field and is what content predating content_translation typically carries, so this is reachable on ordinary sites.
The individual strict-miss branch now serves the entity when it is language-neutral, and the collection query's strict condition becomes an OR group matching the requested langcode or the two neutral langcodes (a row matches at most one side, so nothing is duplicated). Fallback mode is untouched and was already correct, since core's entity repository resolves neutral entities without help; the new test pins that rather than changing it.
The rule is deliberately read-only. A write's langCode names the translation being written to, so satisfying that selector with a language-neutral entity would let a PATCH land on the entity while the client believes it addressed a translation, which is the failure mode #3614200 exists to prevent. The shared translation resolver the write paths call is therefore left byte-identical, and the neutral rule lives in the read branch only.
Write behavior was characterized against unmodified code and is pinned by a regression test, so a later refactor of that shared resolver cannot quietly change it. For a language-neutral entity with ?langCode=fr: PATCH returns 404, the translation POST returns 422 ("Translation is not enabled for the specified resource", because checkEntityTranslatability() calls isTranslatable(), which is FALSE for a locked language, so the write is rejected before addTranslation() is reached), and DELETE returns 404. The test asserts those exact responses and that nothing changed: the title is untouched and the translation count stays at one.
Requirements are captured in a new OpenSpec change (language-neutral-content) under the translation-endpoints capability.
Co-Authored-By: Claude Fable 5 noreply@anthropic.com
Closes #3614388