Repository: syncthing/syncthing (language: go)

Issue:
lib/api: /svc/lang disregards the passed quality values
### What happened?

The GUI loads its locale preference list from the browser's settings through the `/svc/lang` API endpoint.  The HTTP header value is however treated as being specified in reducing preference order, with the `q=` quality values being discarded.

It should be sorted by decreasing `q` value (where omission means `q=1`), according to the specification at https://httpwg.org/specs/rfc9110.html#field.accept-language

Different browsers may send the HTTP header value with different ordering.  Therefore the sorting should be done in the backend API service.

### Syncthing version

v1.27.10

### Platform & operating system

n/a

### Browser version

Header varies by browser, but service should sort the result

### Relevant log output

_No response_


Discussion / hints:

Fix patch:
diff --git a/gui/default/syncthing/core/localeService.js b/gui/default/syncthing/core/localeService.js
index ed4ae8d023a..94cfb62b4f7 100644
--- a/gui/default/syncthing/core/localeService.js
+++ b/gui/default/syncthing/core/localeService.js
@@ -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
diff --git a/lib/api/api.go b/lib/api/api.go
index f25ec4e1f90..59adad52dfe 100644
--- a/lib/api/api.go
+++ b/lib/api/api.go
@@ -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)
 }
 


Test patch: