feat(i18n): add search/filter to the language picker
The language list now has a search input in both the header dropdown (pinned above the scrolling list) and the inline list used in the mobile nav sheet. Filtering matches the English name, native name, or locale code, and shows a localized empty state. Adds nav.searchLanguages and nav.noLanguages to en/es (other locales fall back to English).
This commit is contained in:
parent
bd9eaf9b5f
commit
281a4c6ba5
3 changed files with 50 additions and 3 deletions
|
|
@ -1,3 +1,4 @@
|
|||
import { useState } from 'react';
|
||||
import { Languages } from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import {
|
||||
|
|
@ -61,14 +62,33 @@ interface LanguageToggleProps {
|
|||
* Language toggle dropdown for switching between English and Spanish
|
||||
*/
|
||||
export function LanguageToggle({ compact = false, inline = false, className, variant = 'ghost' }: LanguageToggleProps) {
|
||||
const { language, setLanguage } = useTranslation();
|
||||
const { language, setLanguage, t } = useTranslation();
|
||||
const [query, setQuery] = useState('');
|
||||
|
||||
const safeQuery = query.trim().toLowerCase();
|
||||
const filteredLanguages = safeQuery
|
||||
? languages.filter(
|
||||
(l) =>
|
||||
l.name.toLowerCase().includes(safeQuery) ||
|
||||
l.nativeName.toLowerCase().includes(safeQuery) ||
|
||||
l.code.toLowerCase().includes(safeQuery)
|
||||
)
|
||||
: languages;
|
||||
|
||||
const currentLanguage = languages.find(l => l.code === language) || languages[0];
|
||||
|
||||
if (inline) {
|
||||
return (
|
||||
<div className={cn('flex flex-col', className)}>
|
||||
{languages.map((lang) => (
|
||||
<div className="px-1 pb-2">
|
||||
<input
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
placeholder={t('nav.searchLanguages')}
|
||||
className="h-9 w-full rounded-lg border border-border bg-background px-3 text-sm outline-none focus:border-primary"
|
||||
/>
|
||||
</div>
|
||||
{filteredLanguages.map((lang) => (
|
||||
<button
|
||||
key={lang.code}
|
||||
type="button"
|
||||
|
|
@ -90,6 +110,11 @@ export function LanguageToggle({ compact = false, inline = false, className, var
|
|||
{language === lang.code && <span className="ml-auto text-primary">✓</span>}
|
||||
</button>
|
||||
))}
|
||||
{filteredLanguages.length === 0 && (
|
||||
<div className="px-2 py-1.5 text-sm text-muted-foreground">
|
||||
{t('nav.noLanguages')}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -116,7 +141,20 @@ export function LanguageToggle({ compact = false, inline = false, className, var
|
|||
</Button>
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent align="end" className="w-48">
|
||||
{languages.map((lang) => (
|
||||
<div
|
||||
className="sticky top-0 z-10 -mx-1 -mt-1 mb-1 border-b bg-popover p-1"
|
||||
onKeyDown={(e) => e.stopPropagation()}
|
||||
onPointerDown={(e) => e.stopPropagation()}
|
||||
>
|
||||
<input
|
||||
autoFocus
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
placeholder={t('nav.searchLanguages')}
|
||||
className="h-8 w-full rounded-sm bg-transparent px-2 text-sm outline-none placeholder:text-muted-foreground"
|
||||
/>
|
||||
</div>
|
||||
{filteredLanguages.map((lang) => (
|
||||
<DropdownMenuItem
|
||||
key={lang.code}
|
||||
onClick={() => setLanguage(lang.code)}
|
||||
|
|
@ -137,6 +175,11 @@ export function LanguageToggle({ compact = false, inline = false, className, var
|
|||
)}
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
{filteredLanguages.length === 0 && (
|
||||
<div className="px-2 py-1.5 text-sm text-muted-foreground">
|
||||
{t('nav.noLanguages')}
|
||||
</div>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenu>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -57,6 +57,8 @@ const en = {
|
|||
|
||||
// Navigation
|
||||
nav: {
|
||||
searchLanguages: 'Search languages...',
|
||||
noLanguages: 'No languages found',
|
||||
home: 'Home',
|
||||
// Kept to one word so the mobile tab bar labels stay on a single line.
|
||||
map: 'Spots',
|
||||
|
|
|
|||
|
|
@ -58,6 +58,8 @@ const es = {
|
|||
|
||||
// Navigation
|
||||
nav: {
|
||||
searchLanguages: 'Buscar idiomas...',
|
||||
noLanguages: 'No se encontraron idiomas',
|
||||
home: 'Inicio',
|
||||
map: 'Spots',
|
||||
lab: 'El Lab',
|
||||
|
|
|
|||
Loading…
Reference in a new issue