Repository: syncthing/syncthing (language: go)

Issue:
The Web UI defaults to Filipino on Finnish browsers.
### What happened?

On a browser with the language set to Finnish (fi), Syncthing's Web UI mistakenly sets the language to Filipino (fil).

Reproduction steps:
- Clear the cookies for the web UI or use a blank browser profile.
- Set the browser's primary language to Finnish.
- Open the Web UI.

### Syncthing version

v1.27.10

### Platform & operating system

Linux (64-bit Intel/AMD)

### Browser version

Mozilla Firefox 129.0.1 and Chromium 127.0.6533.99

### 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 8486df76888..ed4ae8d023a 100644
--- a/gui/default/syncthing/core/localeService.js
+++ b/gui/default/syncthing/core/localeService.js
@@ -59,30 +59,35 @@ angular.module('syncthing.core')
                         // Find the first language in the list provided by the user's browser
                         // that is a prefix of a language we have available. That is, "en"
                         // sent by the browser will match "en" or "en-US", while "zh-TW" will
-                        // match only "zh-TW" and not "zh-CN".
+                        // match only "zh-TW" and not "zh" or "zh-CN".
 
                         var i,
-                            lang,
+                            browserLang,
                             matching,
-                            locale = _defaultLocale;
+                            locale = _defaultLocale; // Fallback if nothing matched
 
                         for (i = 0; i < langs.length; i++) {
-                            lang = langs[i];
+                            browserLang = langs[i];
 
-                            if (lang.length < 2) {
+                            if (browserLang.length < 2) {
                                 continue;
                             }
 
                             matching = _availableLocales.filter(function (possibleLang) {
-                                // The langs returned by the /rest/langs call will be in lower
+                                // 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.
                                 possibleLang = possibleLang.toLowerCase();
-                                if (possibleLang.length > lang.length) {
-                                    return possibleLang.indexOf(lang) === 0;
-                                } else {
-                                    return lang.indexOf(possibleLang) === 0;
+                                if (possibleLang.indexOf(browserLang) !== 0) {
+                                    // Prefix does not match
+                                    return false;
                                 }
+                                if (possibleLang.length > browserLang.length) {
+                                    // Must match up to the next hyphen separator
+                                    return possibleLang[browserLang.length] === '-';
+                                }
+                                // Same length, exact match
+                                return true;
                             });
 
                             if (matching.length >= 1) {
@@ -90,7 +95,6 @@ angular.module('syncthing.core')
                                 break;
                             }
                         }
-                        // Fallback if nothing matched
                         useLocale(locale);
                     });
                 }


Test patch: