Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
71 changes: 50 additions & 21 deletions src/components/LanguagePicker/useLanguagePicker.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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<LocaleDisplayInfo[]>(
() =>
Expand All @@ -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(
Expand Down