diff --git a/src/components/LanguagePicker/useLanguagePicker.tsx b/src/components/LanguagePicker/useLanguagePicker.tsx index 01733d819e0..34da208be57 100644 --- a/src/components/LanguagePicker/useLanguagePicker.tsx +++ b/src/components/LanguagePicker/useLanguagePicker.tsx @@ -17,22 +17,33 @@ export const useLanguagePicker = (handleClose?: () => void) => { const { t } = useTranslation("common") const locale = useLocale() - // Get the preferred language for the users browser - const [navLang] = typeof navigator !== "undefined" ? navigator.languages : [] const locales = useMemo(() => filterRealLocales(LOCALES_CODES), []) - const intlLocalePreference = useMemo( - () => - locales?.reduce((acc, cur) => { - if (cur.toLowerCase() === navLang.toLowerCase()) return cur - if ( - navLang.toLowerCase().startsWith(cur.toLowerCase()) && - acc !== navLang - ) - return cur - return acc - }, "") as Lang, - [navLang, locales] - ) + + // Find all matching browser language preferences in order + const intlLocalePreferences = useMemo(() => { + // Get the preferred languages for the users browser + const navLangs = typeof navigator !== "undefined" ? navigator.languages : [] + const preferences: Lang[] = [] + + for (const navLang of navLangs) { + const match = locales?.find((locale) => { + // Exact match first + if (locale.toLowerCase() === navLang.toLowerCase()) return true + // Then partial match (e.g., 'en-US' matches 'en') + if (navLang.toLowerCase().startsWith(locale.toLowerCase())) return true + return false + }) as Lang | undefined + + if (match && !preferences.includes(match)) { + preferences.push(match) + } + } + + return preferences + }, [locales]) + + // Keep the first preference for backward compatibility + const intlLocalePreference = intlLocalePreferences[0] || "" const languages = useMemo( () => @@ -43,17 +54,35 @@ export const useLanguagePicker = (handleClose?: () => void) => { locale as Lang, t ) - const isBrowserDefault = intlLocalePreference === localeOption - return { ...displayInfo, isBrowserDefault } + const isBrowserDefault = intlLocalePreferences.includes( + localeOption as Lang + ) + return { + ...displayInfo, + isBrowserDefault, + } }) .sort((a, b) => { - // Always put the browser's preferred language first - if (a.localeOption === intlLocalePreference) return -1 - if (b.localeOption === intlLocalePreference) return 1 + const aPreferenceIndex = intlLocalePreferences.indexOf( + a.localeOption as Lang + ) + const bPreferenceIndex = intlLocalePreferences.indexOf( + b.localeOption as Lang + ) + + // First, sort by browser preferences (all browser preferences come first) + if (a.isBrowserDefault && !b.isBrowserDefault) return -1 + if (!a.isBrowserDefault && b.isBrowserDefault) return 1 + + // If both are browser preferences, sort by preference order + if (a.isBrowserDefault && b.isBrowserDefault) { + return aPreferenceIndex - bPreferenceIndex + } + // Otherwise, sort alphabetically by source name using localeCompare return a.sourceName.localeCompare(b.sourceName, locale) }) || [], - [intlLocalePreference, locale, locales, t] + [intlLocalePreferences, locale, locales, t] ) const intlLanguagePreference = languages.find(