Skip to content

Commit 9ac7ac5

Browse files
authored
Merge pull request #15511 from Anish-Gupta1/Anish_Gupta1/feat#15488
feat: Enhance Language Picker UX for improved discoverability and clarity
2 parents f4c74b4 + 8b78c62 commit 9ac7ac5

File tree

4 files changed

+83
-57
lines changed

4 files changed

+83
-57
lines changed

src/components/LanguagePicker/MenuItem.tsx

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ import type { LocaleDisplayInfo } from "@/lib/types"
77
import { cn } from "@/lib/utils/cn"
88

99
import { CommandItem } from "../ui/command"
10-
import { Tag } from "../ui/tag"
1110

1211
import ProgressBar from "./ProgressBar"
1312

@@ -24,7 +23,6 @@ const MenuItem = ({ displayInfo, ...props }: ItemProps) => {
2423
targetName,
2524
approvalProgress,
2625
wordsApproved,
27-
isBrowserDefault,
2826
} = displayInfo
2927
const { t } = useTranslation("common")
3028
const locale = useLocale()
@@ -64,11 +62,6 @@ const MenuItem = ({ displayInfo, ...props }: ItemProps) => {
6462
>
6563
{targetName}
6664
</p>
67-
{isBrowserDefault && (
68-
<Tag variant="outline" size="small">
69-
{t("page-languages-browser-default")}
70-
</Tag>
71-
)}
7265
</div>
7366
<p className="text-xs uppercase text-body">{sourceName}</p>
7467
</div>

src/components/LanguagePicker/index.tsx

Lines changed: 49 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,16 @@
11
"use client"
22

33
import { useParams } from "next/navigation"
4+
import { useLocale } from "next-intl"
5+
6+
import type { LocaleDisplayInfo } from "@/lib/types"
7+
8+
import { ButtonLink } from "@/components/ui/buttons/Button"
49

510
import { cn } from "@/lib/utils/cn"
611

12+
import { DEFAULT_LOCALE } from "@/lib/constants"
13+
714
import {
815
Command,
916
CommandEmpty,
@@ -12,7 +19,6 @@ import {
1219
CommandList,
1320
} from "../ui/command"
1421
import { Dialog, DialogContent, DialogTrigger } from "../ui/dialog"
15-
import { BaseLink } from "../ui/Link"
1622
import { Popover, PopoverContent, PopoverTrigger } from "../ui/popover"
1723

1824
import MenuItem from "./MenuItem"
@@ -40,7 +46,8 @@ const LanguagePicker = ({
4046
const pathname = usePathname()
4147
const { push } = useRouter()
4248
const params = useParams()
43-
const { disclosure, languages } = useLanguagePicker(handleClose)
49+
const { disclosure, languages, intlLanguagePreference } =
50+
useLanguagePicker(handleClose)
4451
const { isOpen, setValue, onClose, onOpen } = disclosure
4552

4653
/**
@@ -96,6 +103,7 @@ const LanguagePicker = ({
96103
/>
97104

98105
<LanguagePickerFooter
106+
intlLanguagePreference={intlLanguagePreference}
99107
onTranslationProgramClick={handleBaseLinkClose}
100108
/>
101109
</DialogContent>
@@ -124,7 +132,10 @@ const LanguagePicker = ({
124132
}
125133
/>
126134

127-
<LanguagePickerFooter onTranslationProgramClick={handleBaseLinkClose} />
135+
<LanguagePickerFooter
136+
intlLanguagePreference={intlLanguagePreference}
137+
onTranslationProgramClick={handleBaseLinkClose}
138+
/>
128139
</PopoverContent>
129140
</Popover>
130141
)
@@ -185,21 +196,45 @@ const LanguagePickerMenu = ({ languages, onClose, onSelect }) => {
185196
)
186197
}
187198

188-
const LanguagePickerFooter = ({ onTranslationProgramClick }) => {
199+
const LanguagePickerFooter = ({
200+
intlLanguagePreference,
201+
onTranslationProgramClick,
202+
}: {
203+
intlLanguagePreference?: LocaleDisplayInfo
204+
onTranslationProgramClick: () => void
205+
}) => {
189206
const { t } = useTranslation("common")
190-
207+
const locale = useLocale()
208+
console.log({ intlLanguagePreference })
191209
return (
192-
<div className="sticky bottom-0 flex justify-center border-t-2 border-primary bg-primary-low-contrast p-3">
193-
<p className="text-center text-xs text-body">
194-
{t("page-languages-recruit-community")}{" "}
195-
{/* TODO migrate once #13411 is merged */}
196-
<BaseLink
197-
href="/contributing/translation-program"
210+
<div className="sticky bottom-0 flex border-t-2 border-primary bg-primary-low-contrast p-0 pb-1 pt-1">
211+
<div className="flex w-full max-w-sm items-center justify-between px-4">
212+
<div className="flex min-w-0 flex-col items-start">
213+
{locale === DEFAULT_LOCALE ? (
214+
<p className="overflow-hidden text-ellipsis whitespace-nowrap text-xs font-bold text-body">
215+
{intlLanguagePreference
216+
? `${t("page-languages-translate-cta-title")} ${t(`language-${intlLanguagePreference.localeOption}`)}`
217+
: "Translate ethereum.org"}
218+
</p>
219+
) : (
220+
<p className="overflow-hidden text-ellipsis whitespace-nowrap text-xs font-bold text-body">
221+
{t("page-languages-translate-cta-title")}{" "}
222+
{t(`language-${locale}`)}
223+
</p>
224+
)}
225+
<p className="text-xs text-body">
226+
{t("page-languages-recruit-community")}
227+
</p>
228+
</div>
229+
<ButtonLink
230+
className="text-nowrap"
231+
href="/contributing/translation-program/"
232+
size="sm"
198233
onClick={onTranslationProgramClick}
199234
>
200-
{t("common:learn-more")}
201-
</BaseLink>
202-
</p>
235+
{t("get-involved")}
236+
</ButtonLink>
237+
</div>
203238
</div>
204239
)
205240
}

src/components/LanguagePicker/useLanguagePicker.tsx

Lines changed: 33 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -17,52 +17,48 @@ export const useLanguagePicker = (handleClose?: () => void) => {
1717
const { t } = useTranslation("common")
1818
const locale = useLocale()
1919

20-
const languages = useMemo<LocaleDisplayInfo[]>(() => {
21-
const locales = filterRealLocales(LOCALES_CODES)
22-
23-
// Get the preferred languages for the users browser
24-
const navLangs = typeof navigator !== "undefined" ? navigator.languages : []
25-
26-
// For each browser preference, reduce to the most specific match found in `locales` array
27-
const allBrowserLocales: Lang[] = navLangs
28-
.map(
29-
(navLang) =>
30-
locales?.reduce((acc, cur) => {
31-
if (cur.toLowerCase() === navLang.toLowerCase()) return cur
32-
if (
33-
navLang.toLowerCase().startsWith(cur.toLowerCase()) &&
34-
acc !== navLang
35-
)
36-
return cur
37-
return acc
38-
}, "") as Lang
39-
)
40-
.filter((i) => !!i) // Remove those without matches
41-
42-
// Remove duplicate matches
43-
const browserLocales = Array.from(new Set(allBrowserLocales))
44-
45-
return (
20+
// Get the preferred language for the users browser
21+
const [navLang] = typeof navigator !== "undefined" ? navigator.languages : []
22+
const locales = useMemo(() => filterRealLocales(LOCALES_CODES), [])
23+
const intlLocalePreference = useMemo(
24+
() =>
25+
locales?.reduce((acc, cur) => {
26+
if (cur.toLowerCase() === navLang.toLowerCase()) return cur
27+
if (
28+
navLang.toLowerCase().startsWith(cur.toLowerCase()) &&
29+
acc !== navLang
30+
)
31+
return cur
32+
return acc
33+
}, "") as Lang,
34+
[navLang, locales]
35+
)
36+
37+
const languages = useMemo<LocaleDisplayInfo[]>(
38+
() =>
4639
(locales as Lang[])
4740
?.map((localeOption) => {
4841
const displayInfo = localeToDisplayInfo(
4942
localeOption,
5043
locale as Lang,
5144
t
5245
)
53-
const isBrowserDefault = browserLocales.includes(localeOption)
46+
const isBrowserDefault = intlLocalePreference === localeOption
5447
return { ...displayInfo, isBrowserDefault }
5548
})
5649
.sort((a, b) => {
57-
const indexA = browserLocales.indexOf(a.localeOption as Lang)
58-
const indexB = browserLocales.indexOf(b.localeOption as Lang)
59-
if (indexA >= 0 && indexB >= 0) return indexA - indexB
60-
if (indexA >= 0) return -1
61-
if (indexB >= 0) return 1
62-
return b.approvalProgress - a.approvalProgress
63-
}) || []
64-
)
65-
}, [locale, t])
50+
// Always put the browser's preferred language first
51+
if (a.localeOption === intlLocalePreference) return -1
52+
if (b.localeOption === intlLocalePreference) return 1
53+
// Otherwise, sort alphabetically by source name using localeCompare
54+
return a.sourceName.localeCompare(b.sourceName, locale)
55+
}) || [],
56+
[intlLocalePreference, locale, locales, t]
57+
)
58+
59+
const intlLanguagePreference = languages.find(
60+
(lang) => lang.localeOption === intlLocalePreference
61+
)
6662

6763
const { isOpen, setValue, ...menu } = useDisclosure()
6864

@@ -99,5 +95,6 @@ export const useLanguagePicker = (handleClose?: () => void) => {
9995
return {
10096
disclosure: { isOpen, setValue, onOpen, onClose },
10197
languages,
98+
intlLanguagePreference,
10299
}
103100
}

src/intl/en/common.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,7 @@
376376
"page-languages-translated": "translated",
377377
"page-languages-want-more-header": "Want to see ethereum.org in a different language?",
378378
"page-languages-want-more-link": "Translation Program",
379+
"page-languages-translate-cta-title": "Translate to",
379380
"page-languages-want-more-paragraph": "ethereum.org translators are always translating pages in as many languages as possible. To see what they're working on right now or to sign up to join them, read about our",
380381
"page-languages-words": "words",
381382
"page-last-updated": "Page last updated",

0 commit comments

Comments
 (0)