Every collection response json_decodes its whole body for a key only individual documents have
ResponseSubscriber::onRespond()'s Content-Language fallback reads data.attributes.langcode out of the serialized response body when a dynamic-page-cache hit skipped the controller. That fallback was gated on "is this a JSON:API route" (plus an explicit carve-in for jsonapi_menu_items), so every collection response, cold or warm, paid a full Json::decode() of its entire body even though data is a list there and the lookup can never succeed. On a decoupled site, collections are the bulk of traffic and the largest documents, so the discarded decode was pure waste.
Narrow the guard to the two route shapes whose document can actually carry the key: jsonapi..individual and jsonapi..related, per \Drupal\jsonapi\Routing\Routes::getRouteName(). jsonapi_menu_items no longer needs an explicit carve-in: its route is a list, and MenuItemsLanguageSubscriber::onResponseLanguageHeader() (priority -15, after this listener's -10) already owns and overwrites its header, so excluding the route here changes nothing observable. MenuItemsTranslationTest passing untouched proves that.
Add an assertion to testContentLanguageHeaderSurvivesCacheHits() pinning that a collection read is left to core's FinishResponseSubscriber stamp (the negotiated interface language), so a future widening of the guard back to "all JSON:API routes" would be caught.
Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com
Closes #3614411