Accept-Language
Pick the language of LanguageProperties with the HTTP Accept-Language header instead of the lang parameter. Accept-Language: fr-CH, fr;q=0.9, en;q=0.8 on Retrieve Entity gives the same answer as lang=fr-CH,fr;q=0.9,en;q=0.8. Off by default.
At a glance
- Setting:
extensions.accept_language.mode,"disabled"(default) or"enabled" - Environment variable:
ANDROMEDA__EXTENSIONS__ACCEPT_LANGUAGE__MODE - Advertised as:
acceptLanguageincontextSourceExtras.extensions - Context sources: the header is never forwarded. Temporal requests pass the language on as
lang. - When off: the header is ignored, as a conformant broker ignores it
Turning it on
[extensions.accept_language]
mode = "enabled"
Using it
Take an entity whose street holds a French and a Dutch name:
{
"id": "urn:ngsi-ld:Vehicle:A4567",
"type": "Vehicle",
"street": {
"type": "LanguageProperty",
"languageMap": {
"fr": "Grand Place",
"nl": "Grote Markt"
}
}
}
Ask for Swiss French, then French, then English:
GET /ngsi-ld/v1/entities/urn:ngsi-ld:Vehicle:A4567
Accept: application/json
Accept-Language: fr-CH, fr;q=0.9, en;q=0.8
HTTP/1.1 200 OK
Content-Type: application/json
Vary: accept-language
{
"id": "urn:ngsi-ld:Vehicle:A4567",
"type": "Vehicle",
"street": {
"type": "Property",
"value": "Grand Place",
"lang": "fr"
}
}
There is no fr-CH, so fr is the best match. Every LanguageProperty comes back as a plain Property holding the string for the best matching language, with a non-reified lang member naming the language it picked. That is the same shape the specification shows for lang=fr. Without the header, or with the extension off, street comes back as the whole languageMap.
The header is read only on operations where lang sets the language of the response:
- Retrieve Entity
- Query Entities, by
GETand byPOST /entityOperations/query - Retrieve and Query Temporal Evolution, by
GETand byPOST /temporal/entityOperations/query - Query Context Source Registrations
Nothing else reads it. Writes, subscriptions and notifications are never affected, and a malformed header on any other operation is ignored.
lang wins. If the URL has a lang parameter, or the query body has a lang member, the header is ignored. It only fills in when neither names a language.
The header follows the same grammar as lang. Ranking by q, excluding with q=0, the * wildcard and falling back to any supported language all work as they do for lang. Empty list elements are skipped, and a header that names no language leaves the response unfiltered.
Every response of the operations above carries Vary: accept-language, whether or not the request had the header, because the chosen representation depends on it either way. That keeps caches from serving one client's language to another.
What gets refused
Context sources
This broker applies the filter to the answer it renders, after merging. On the temporal operations the header's languages become the query's lang, so a forwarded temporal request carries them as a lang parameter, which every source understands. The header itself is never forwarded.
Why it is an extension
The specification never mentions Accept-Language. It carries the language filter in the lang parameter alone, or in the lang member of a Query, and Accept-Language isn't among the headers it lists. Without a filter, an entity is returned with its whole languageMap, and HTTP lets a server ignore Accept-Language. So a conformant broker ignores it.
There is a practical reason to keep it off by default too. Browsers send the header on every request, and a broker that read it unasked would collapse languageMaps for clients that never wanted a filter.
Empty list elements in the header are skipped.