Skip to content

fix(search-api-graphql): send Vary and Content-Language for Accept-Language - #917

Merged
ddeboer merged 4 commits into
mainfrom
fix/search-vary-content-language
Oct 16, 2026
Merged

ddeboer merged 4 commits into
mainfrom
fix/search-vary-content-language

Conversation

@ddeboer

@ddeboer ddeboer commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

The GraphQL handler’s response depends on Accept-Language, but the handler sent neither Vary nor Content-Language. So a shared cache could serve one requester’s language order to the next.

  • Vary: Accept-Language is appended to every response from the endpoint (appended, so CORS’s Vary: Origin survives).
  • Content-Language lists the displayed language – [0].language – of every localized value in the response, in the order first served. A mixed-language page lists each (en, nl when one item has no English value), as RFC 9110 §8.5 allows. The languages behind [0] are left out, so the header grows with what was requested, not with how many languages the data holds; ADR 0004 already calls [0].language the per-field Content-Language. A response without tagged values sends none.
  • The LanguageString resolvers record their displayed language on a new optional SearchContext.servedLanguages set. The handler creates the set per request, passes it in as Yoga server context, and reads it once execution finishes. I chose this over walking the result JSON, which can’t see through field aliases.
  • Tags come from indexed data, so the handler checks each with Intl.getCanonicalLocales and drops a malformed one (it would make an invalid header and fail the request). Valid tags keep the spelling the body serves them in, so the header and LanguageString.language name the same languages (iw stays iw). Case-only duplicates are folded, and untagged values are skipped whatever their case (UND).

Negotiation still never fails: the und fallback stays.

Fix #800

…nguage

- Append Vary: Accept-Language to every GraphQL response, so a shared
  cache keys on the header that output order and search locale follow
- Collect the languages of served LanguageStrings on an optional
  SearchContext.servedLanguages set; report them in Content-Language
- Leave malformed tags out of the header and fold case-only duplicates,
  since tags come from indexed data
- Canonicalise served tags with Intl.getCanonicalLocales, which rejects
  malformed tags and folds case, instead of a hand-written pattern
- Re-anchor the branch threshold for the removed branches
- Use Intl only to validate tags, so the header names a language the
  way LanguageString.language serves it (iw, not its alias he)
- Skip untagged values in any case (UND), not only lowercase und
…guage

- Record each LanguageString list's [0] language, the value a client
  displays, rather than every language behind it
- Bound the header by what was requested instead of by how many
  languages the data holds
@ddeboer
ddeboer merged commit 1524cca into main Oct 16, 2026
4 checks passed
@ddeboer
ddeboer deleted the fix/search-vary-content-language branch October 16, 2026 12:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Search API varies by Accept-Language without sending Vary or Content-Language

1 participant