fix: sort language dropdown alphabetically by native name (#7477)

* fix: sort language dropdown alphabetically by native name

Languages in the settings dropdown were ordered by language code,
making it hard to find specific languages. Now sorted alphabetically
by their native display name.

Fixes #3263

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

* test: verify language dropdown is sorted by native name

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
John McLear 2026-04-06 13:28:57 +01:00 committed by GitHub
parent 5320d56b80
commit 6a3094b244
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 38 additions and 2 deletions

View File

@ -106,9 +106,17 @@ const getAllLocales = () => {
// returns a hash of all available languages availables with nativeName and direction
// e.g. { es: {nativeName: "español", direction: "ltr"}, ... }
const getAvailableLangs = (locales:MapArrayType<any>) => {
const result:MapArrayType<string> = {};
const unsorted:MapArrayType<string> = {};
for (const langcode of Object.keys(locales)) {
result[langcode] = languages.getLanguageInfo(langcode);
unsorted[langcode] = languages.getLanguageInfo(langcode);
}
// Sort by native name so the language dropdown is alphabetical
const sorted = Object.entries(unsorted).sort(([, a]: any, [, b]: any) =>
(a.nativeName || '').localeCompare(b.nativeName || '', undefined, {sensitivity: 'base'})
);
const result:MapArrayType<string> = {};
for (const [langcode, info] of sorted) {
result[langcode] = info;
}
return result;
};

View File

@ -0,0 +1,28 @@
'use strict';
const assert = require('assert').strict;
const common = require('../common');
const i18n = require('../../../node/hooks/i18n');
describe(__filename, function () {
before(async function () {
await common.init();
});
it('availableLangs are sorted by nativeName (case-insensitive)', async function () {
const langs = i18n.availableLangs;
assert(langs != null, 'availableLangs should be populated after server init');
const nativeNames: string[] = Object.values(langs).map((info: any) => info.nativeName || '');
assert(nativeNames.length > 1, 'expected more than one language');
for (let i = 1; i < nativeNames.length; i++) {
const cmp = nativeNames[i - 1].localeCompare(nativeNames[i], undefined, {sensitivity: 'base'});
assert(
cmp <= 0,
`languages not sorted: "${nativeNames[i - 1]}" should come before "${nativeNames[i]}" ` +
`(index ${i - 1} vs ${i})`,
);
}
});
});