Accept-Language header VCL features
Last updated May 11, 2018
Fastly provides a number of extensions to VCL, including functions to parse and normalize the Accept-Language
header.
Language lookup
We've implemented Lookup functionality as defined by RFC 4647, section 3.4.
Syntax
accept.language_lookup(<available languages>, <default>, <priority list>)
Argument | Explanation |
---|---|
available languages |
A colon-separated list of languages to choose from. Typically the languages that the origin can provide. For example: en:de:fr:pt:es:zh-CN |
default |
The default language to return if none from the priority list match. For example: en |
priority list |
The Accept-Language header. A comma-separated list of languages, optionally accompanied by weights (q-values). For example: pt-BR,pt;q=0.8,en-US;q=0.6,en;q=0.4 |
Return values
The best matching language (as per the RFC) is returned. If none are found, the default language is returned, unless a weight of zero (q=0
) was indicated by the priority list, in which case NULL
is returned.
Examples
1
2
set req.http.Normalized-Language =
accept.language_lookup("en:de:fr:pt:es:zh-CN", "en", req.http.Accept-Language);
The above would result in Normalized-Language: pt
given an Accept-Language: pt-BR,pt;q=0.8,en-US;q=0.6,en;q=0.4
header.
accept.language_lookup("en", "nl", "en-GB")
results in en
, as each subtag is removed and the match retried when a tag does not match.
accept.language_lookup("en:nl", "nl", "en-GB,nl;q=0.5")
results in en
still, even if nl
is a more exact match, because the q-value of nl is lower, and that has precedence. Exactness just does not come into the equation.
accept.language_lookup("en-US:nl", "nl", "en-GB,nl;q=0.5")
results in nl
, because subtags are not removed from the available languages, only from language tags on the priority list.
accept.language_lookup("en-US:nl", "nl", "en-us,nl;q=0.5")
results in en-US
, as the lookup is case insensitive.
accept.language_lookup("en-US:nl", "nl", "en-GB,nl;q=0")
results in NULL
(the value, not a string) since en-GB
and en
do not match, and nl
(the default) is listed as unacceptable.
If q=0
for the default language is to be ignored, the following VCL can be used:
1
2
3
4
5
6
7
set req.http.Normalized-Language =
accept.language_lookup("en-US:nl", "nl", req.http.Accept-Language);
if (!req.http.Normalized-Language) {
# User will get Dutch even if he doesn't want it!
# (Because none of our languages were acceptable)
set req.http.Normalized-Language = "nl";
}
Language filter (Basic)
We've implemented Basic Filtering functionality as defined by RFC 4647, section 3.3.1. The implementation is not exact when the wildcard tag (*
) is used. If a wildcard is encountered and no matches have been found yet, the default is returned. If there are matches, those are returned and the remainder of the priority list is ignored.
There is no implementation of Extended Filtering, but if you are in need you could always file a feature request with Support.
Syntax
accept.language_filter_basic(<available languages>, <default>, <priority list>, <limit>)
Argument | Explanation |
---|---|
available languages |
A colon-separated list of languages choose from. Typically the languages that the origin can provide. For example: en:de:fr:pt:es:zh-CN |
default |
The default language to return if none from the priority list match. For example: en |
priority list |
The Accept-Language header. A comma-separated list of languages, optionally accompanied by weights (q-values). For example: pt-BR,pt;q=0.8,en-US;q=0.6,en;q=0.4 |
limit |
The maximum amount of languages returned. |
Return values
The best matching language (as per the RFC) is returned. If none are found, the default language is returned, unless a weight of zero (q=0
) was indicated by the priority list, in which case NULL
is returned.
Examples
1
2
set req.http.Filtered-Language =
accept.language_filter_basic("en:de:fr:pt:es:zh-CN", "en", req.http.Accept-Language, 2);
The above would result in Filtered-Language: pt,en
given an Accept-Language: pt-BR,pt;q=0.8,en-US;q=0.6,en;q=0.4
header.
accept.language_filter_basic("en", "nl", "en-GB", 2)
results in en
, as each subtag is removed and the match retried when a tag does not match.
accept.language_filter_basic("en:nl", "nl", "en-GB,nl;q=0.5", 2)
results in en,nl
, even if nl
is a more exact match, because the q-value of nl is lower, and that has precedence. Exactness just does not come into the equation.
accept.language_filter_basic("en-US:nl", "nl", "en-GB,nl;q=0.5", 2)
results in nl
, because subtags are not removed from the available languages during the search.
accept.language_filter_basic("en-US:nl", "nl", "en-us,nl;q=0.5", 2)
results in en-US,nl
, as the lookup is case insensitive.
accept.language_filter_basic("en-US:nl", "nl", "en-GB,nl;q=0", 2)
results in NULL
(the value, not a string) since en-GB
and en
do not match, and nl
(the default) is listed as unacceptable.
If q=0
for the default language is to be ignored, the following VCL can be used:
1
2
3
4
5
6
7
set req.http.Filtered-Language =
accept.language_filter_basic("en-US:nl", "nl", req.http.Accept-Language, 2);
if (!req.http.Filtered-Language) {
# User will get Dutch even if he doesn't want it!
# (Because none of our languages were acceptable)
set req.http.Filtered-Language = "nl";
}
accept.language_filter_basic("en:nl:de:fr", "nl", "en-GB,*;q=0.5", 2)
results in en
and accept.language_filter_basic("en:nl:de:fr", "nl", "*", 2)
results in nl
.