Upgraded the language selector to cope with many more languages

This commit is contained in:
Colin Dawson 2026-02-03 18:32:32 +00:00
parent 8cbdf0fedf
commit f6c0097110

View File

@ -1,4 +1,5 @@
import { NavDropdown } from "react-bootstrap";
import { useState } from "react";
import { NavDropdown, Modal, Form } from "react-bootstrap";
import { availableLocales } from "../../../i18n/generatedLocales";
import i18n from "../../../i18n/i18n";
import profileService from "../../profile/services/profileService";
@ -39,9 +40,25 @@ function formatLocaleLabel(locale: string) {
export function LanguageSelectorMenuItem() {
const current = i18n.language;
const [showModal, setShowModal] = useState(false);
const [search, setSearch] = useState("");
const currentFlag = flagEmoji(current);
const currentLabel = formatLocaleLabel(current);
// ⭐ Primary languages you want to show in the main dropdown
const primaryLocales = ["en-GB", "en-US", "fr-FR", "fr-CA", "hi-IN", "ur-PK"];
// ⭐ Build the shortlist
const baseLang = current.split("-")[0];
const baseLangLocale = availableLocales.find((l) => l.startsWith(baseLang));
const shortlist = new Set(
[current, baseLangLocale, ...primaryLocales].filter(Boolean) as string[],
);
const visibleLocales = availableLocales.filter((l) => shortlist.has(l));
async function handleSelect(locale: string) {
try {
await profileService.patchMyProfile({
@ -49,15 +66,20 @@ export function LanguageSelectorMenuItem() {
});
} catch (err) {
console.error("Failed to update preferred locale", err);
// Optional: show toast or revert language
}
i18n.changeLanguage(locale);
}
// ⭐ Filter for modal search
const filteredLocales = availableLocales.filter((locale) =>
formatLocaleLabel(locale).toLowerCase().includes(search.toLowerCase()),
);
return (
<>
<NavDropdown align="end" title={`${currentFlag} ${currentLabel}`}>
{availableLocales.map((locale) => {
{visibleLocales.map((locale) => {
const flag = flagEmoji(locale);
const label = formatLocaleLabel(locale);
@ -71,6 +93,50 @@ export function LanguageSelectorMenuItem() {
</NavDropdown.Item>
);
})}
<NavDropdown.Divider />
<NavDropdown.Item onClick={() => setShowModal(true)}>
More languages
</NavDropdown.Item>
</NavDropdown>
{/* ⭐ Modal for full searchable list */}
<Modal show={showModal} onHide={() => setShowModal(false)} centered>
<Modal.Header closeButton>
<Modal.Title>Select Language</Modal.Title>
</Modal.Header>
<Modal.Body>
<Form.Control
type="text"
placeholder="Search languages…"
value={search}
onChange={(e) => setSearch(e.target.value)}
className="mb-3"
/>
{filteredLocales.map((locale) => {
const flag = flagEmoji(locale);
const label = formatLocaleLabel(locale);
return (
<div
key={locale}
className="mb-2"
style={{ cursor: "pointer" }}
onClick={() => {
handleSelect(locale);
setShowModal(false);
}}
>
{flag} {label}
</div>
);
})}
</Modal.Body>
</Modal>
</>
);
}
export default LanguageSelectorMenuItem;