lib/api: Correct ordering of Accept-Language codes by weight (fixes #9670) (#9671)

The preference for languages in the Accept-Language header field
should not be deduced from the listed order, but from the passed
"quality values", according to the HTTP specification:
https://httpwg.org/specs/rfc9110.html#field.accept-language

This implements the parsing of q=values and ordering within the API
backend, to not complicate things further in the GUI code.  Entries
with invalid (unparseable) quality values are discarded completely.

* gui: Fix API endpoint in comment.
This commit is contained in:
André Colomb 2024-09-02 10:15:04 +02:00 committed by GitHub
parent 2fb24dc2cc
commit cb24638ec9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 27 additions and 5 deletions

View File

@ -74,9 +74,9 @@ angular.module('syncthing.core')
}
matching = _availableLocales.filter(function (possibleLang) {
// The langs returned by the /svc/langs call will be in lower
// case. We compare to the lowercase version of the language
// code we have as well.
// The langs returned by the /rest/svc/langs call will be in
// lower case. We compare to the lowercase version of the
// language code we have as well.
possibleLang = possibleLang.toLowerCase();
if (possibleLang.indexOf(browserLang) !== 0) {
// Prefix does not match

View File

@ -1483,11 +1483,33 @@ func (*service) getDeviceID(w http.ResponseWriter, r *http.Request) {
func (*service) getLang(w http.ResponseWriter, r *http.Request) {
lang := r.Header.Get("Accept-Language")
var langs []string
var weights = make(map[string]float64)
for _, l := range strings.Split(lang, ",") {
parts := strings.SplitN(l, ";", 2)
langs = append(langs, strings.ToLower(strings.TrimSpace(parts[0])))
code := strings.ToLower(strings.TrimSpace(parts[0]))
weights[code] = 1.0
if len(parts) < 2 {
continue
}
weight := strings.ToLower(strings.TrimSpace(parts[1]))
if !strings.HasPrefix(weight, "q=") {
continue
}
if q, err := strconv.ParseFloat(weight[2:], 32); err != nil {
// Completely dismiss entries with invalid weight
delete(weights, code)
} else {
weights[code] = q
}
}
var langs = make([]string, 0, len(weights))
for code := range weights {
langs = append(langs, code)
}
// Reorder by descending q value
sort.SliceStable(langs, func(i, j int) bool {
return weights[langs[i]] > weights[langs[j]]
})
sendJSON(w, langs)
}