From 745e4e7106952301c14d6bcf0e58e61b5ea54409 Mon Sep 17 00:00:00 2001 From: alexsparkes Date: Sat, 24 Jan 2026 21:24:20 +0000 Subject: [PATCH] feat(Language): add search functionality and system language detection to language options --- src/features/misc/sections/Language.jsx | 80 ++++++++++++++++++- .../components/Sections/ChooseLanguage.jsx | 72 +++++++++++++++-- src/i18n/locales/achievements/ar.json | 2 +- src/i18n/locales/ar.json | 5 +- src/i18n/locales/arz.json | 5 +- src/i18n/locales/az.json | 5 +- src/i18n/locales/azb.json | 5 +- src/i18n/locales/bn.json | 5 +- src/i18n/locales/de_DE.json | 5 +- src/i18n/locales/el.json | 5 +- src/i18n/locales/en_GB.json | 5 +- src/i18n/locales/en_US.json | 5 +- src/i18n/locales/es.json | 5 +- src/i18n/locales/es_419.json | 5 +- src/i18n/locales/et.json | 5 +- src/i18n/locales/fa.json | 5 +- src/i18n/locales/fr.json | 5 +- src/i18n/locales/hu.json | 5 +- src/i18n/locales/id_ID.json | 5 +- src/i18n/locales/ja.json | 5 +- src/i18n/locales/lt.json | 5 +- src/i18n/locales/lv.json | 5 +- src/i18n/locales/nl.json | 5 +- src/i18n/locales/no.json | 5 +- src/i18n/locales/peo.json | 5 +- src/i18n/locales/pt.json | 5 +- src/i18n/locales/pt_BR.json | 5 +- src/i18n/locales/ru.json | 5 +- src/i18n/locales/sl.json | 5 +- src/i18n/locales/sv.json | 5 +- src/i18n/locales/ta.json | 5 +- src/i18n/locales/tr_TR.json | 5 +- src/i18n/locales/uk.json | 5 +- src/i18n/locales/vi.json | 5 +- src/i18n/locales/zh_CN.json | 5 +- src/i18n/locales/zh_Hant.json | 5 +- 36 files changed, 276 insertions(+), 43 deletions(-) diff --git a/src/features/misc/sections/Language.jsx b/src/features/misc/sections/Language.jsx index 04104007..23ac212c 100644 --- a/src/features/misc/sections/Language.jsx +++ b/src/features/misc/sections/Language.jsx @@ -2,7 +2,8 @@ import { useState, useEffect, useRef, useMemo } from 'react'; import { useT, useTranslation } from 'contexts/TranslationContext'; import variables from 'config/variables'; -import { MdOutlineOpenInNew } from 'react-icons/md'; +import { MdOutlineOpenInNew, MdSearch } from 'react-icons/md'; +import { TextField, InputAdornment } from '@mui/material'; import { Radio } from 'components/Form/Settings'; @@ -10,12 +11,14 @@ import languages from '@/i18n/languages.json'; const LanguageOptions = () => { const t = useT(); - const { language: currentLanguage } = useTranslation(); + const { language: currentLanguage, changeLanguage } = useTranslation(); const loadingText = t('modals.main.loading'); const offlineText = t('modals.main.marketplace.offline.description'); const languageTitle = t('modals.main.settings.sections.language.title'); const quoteTitle = t('modals.main.settings.sections.language.quote'); + const [searchQuery, setSearchQuery] = useState(''); + // Create language options with both translated and native names const languageOptions = useMemo(() => { // Convert currentLanguage to ISO format (e.g., "de_DE" -> "de-DE") @@ -24,7 +27,7 @@ const LanguageOptions = () => { // Use Intl.DisplayNames to get language names in the current language const displayNames = new Intl.DisplayNames([currentLanguageISO], { type: 'language' }); - return languages.map((lang) => { + const mappedLanguages = languages.map((lang) => { const nativeName = lang.name; // Convert language code to ISO format for Intl.DisplayNames @@ -56,10 +59,33 @@ const LanguageOptions = () => { return { name: displayName, value: lang.value, + nativeName, + searchText: `${nativeName} ${translatedName || ''}`.toLowerCase(), }; }); + + // Sort alphabetically by native name + return mappedLanguages.sort((a, b) => a.nativeName.localeCompare(b.nativeName)); }, [currentLanguage]); + // Filter languages based on search query + const filteredLanguages = useMemo(() => { + if (!searchQuery.trim()) return languageOptions; + const query = searchQuery.toLowerCase(); + return languageOptions.filter(lang => lang.searchText.includes(query)); + }, [languageOptions, searchQuery]); + + // Detect system language + const systemLanguage = useMemo(() => { + const browserLang = navigator.language.replace('-', '_'); + // Check exact match first, then base language + return languages.find(l => l.value === browserLang) || + languages.find(l => l.value.startsWith(browserLang.split('_')[0])); + }, []); + + // Find current language option for display + const currentLangOption = languageOptions.find(l => l.value === currentLanguage); + const [quoteLanguages, setQuoteLanguages] = useState([ { name: loadingText, @@ -136,8 +162,54 @@ const LanguageOptions = () => { +
+ setSearchQuery(e.target.value)} + variant="outlined" + size="small" + InputProps={{ + startAdornment: ( + + + + ), + }} + sx={{ + width: '250px', + '& .MuiOutlinedInput-root': { + borderRadius: '24px', + backgroundColor: 'rgba(255, 255, 255, 0.08)', + '& fieldset': { + border: 'none', + }, + '&:hover fieldset': { + border: 'none', + }, + '&.Mui-focused fieldset': { + border: 'none', + }, + }, + }} + /> + {currentLangOption && ( +
+ {t('modals.main.settings.sections.language.current')}: {currentLangOption.nativeName} +
+ )} +
+ {systemLanguage && systemLanguage.value !== currentLanguage && ( + + )}
- +
{quoteTitle} diff --git a/src/features/welcome/components/Sections/ChooseLanguage.jsx b/src/features/welcome/components/Sections/ChooseLanguage.jsx index 971d4adc..d53de184 100644 --- a/src/features/welcome/components/Sections/ChooseLanguage.jsx +++ b/src/features/welcome/components/Sections/ChooseLanguage.jsx @@ -1,5 +1,6 @@ -import { useMemo } from 'react'; -import { MdOutlineOpenInNew } from 'react-icons/md'; +import { useState, useMemo } from 'react'; +import { MdOutlineOpenInNew, MdSearch } from 'react-icons/md'; +import { TextField, InputAdornment } from '@mui/material'; import languages from '@/i18n/languages.json'; import { useT, useTranslation } from 'contexts/TranslationContext'; import variables from 'config/variables'; @@ -9,15 +10,17 @@ import { Header, Content } from '../Layout'; function ChooseLanguage() { const t = useT(); - const { language: currentLanguage } = useTranslation(); + const { language: currentLanguage, changeLanguage } = useTranslation(); const title = t('modals.welcome.sections.language.title'); const description = t('modals.welcome.sections.language.description'); + const [searchQuery, setSearchQuery] = useState(''); + const languageOptions = useMemo(() => { const currentLanguageISO = currentLanguage.replace('_', '-'); const displayNames = new Intl.DisplayNames([currentLanguageISO], { type: 'language' }); - return languages.map((lang) => { + const mappedLanguages = languages.map((lang) => { const nativeName = lang.name; const isoCode = lang.value.replace('_', '-'); @@ -44,10 +47,29 @@ function ChooseLanguage() { return { name: displayName, value: lang.value, + nativeName, + searchText: `${nativeName} ${translatedName || ''}`.toLowerCase(), }; }); + + // Sort alphabetically by native name + return mappedLanguages.sort((a, b) => a.nativeName.localeCompare(b.nativeName)); }, [currentLanguage]); + // Filter languages based on search query + const filteredLanguages = useMemo(() => { + if (!searchQuery.trim()) return languageOptions; + const query = searchQuery.toLowerCase(); + return languageOptions.filter(lang => lang.searchText.includes(query)); + }, [languageOptions, searchQuery]); + + // Detect system language + const systemLanguage = useMemo(() => { + const browserLang = navigator.language.replace('-', '_'); + return languages.find(l => l.value === browserLang) || + languages.find(l => l.value.startsWith(browserLang.split('_')[0])); + }, []); + return (
} /> + {systemLanguage && systemLanguage.value !== currentLanguage && ( + + )} + setSearchQuery(e.target.value)} + variant="outlined" + size="small" + fullWidth + InputProps={{ + startAdornment: ( + + + + ), + }} + sx={{ + marginBottom: 2, + '& .MuiOutlinedInput-root': { + borderRadius: '24px', + backgroundColor: 'rgba(255, 255, 255, 0.08)', + '& fieldset': { + border: 'none', + }, + '&:hover fieldset': { + border: 'none', + }, + '&.Mui-focused fieldset': { + border: 'none', + }, + }, + }} + />
- +
); diff --git a/src/i18n/locales/achievements/ar.json b/src/i18n/locales/achievements/ar.json index 6859159a..0ec5e920 100644 --- a/src/i18n/locales/achievements/ar.json +++ b/src/i18n/locales/achievements/ar.json @@ -7,7 +7,7 @@ "name": "MU3T4B", "description": "Opened 1337 tabs" }, - "10tabs": { + "tentabs": { "name": "10/10 IGN", "description": "Opened 10 tabs" }, diff --git a/src/i18n/locales/ar.json b/src/i18n/locales/ar.json index 979fbd2c..04345b32 100644 --- a/src/i18n/locales/ar.json +++ b/src/i18n/locales/ar.json @@ -476,7 +476,10 @@ }, "language": { "title": "اللغة", - "quote": "لغة الاقتباس" + "quote": "لغة الاقتباس", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "سجل التغييرات", diff --git a/src/i18n/locales/arz.json b/src/i18n/locales/arz.json index 0f0984e7..7e464d46 100644 --- a/src/i18n/locales/arz.json +++ b/src/i18n/locales/arz.json @@ -478,7 +478,10 @@ }, "language": { "title": "Language", - "quote": "Quote language" + "quote": "Quote language", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Change Log", diff --git a/src/i18n/locales/az.json b/src/i18n/locales/az.json index cd7e4395..9cafbab9 100644 --- a/src/i18n/locales/az.json +++ b/src/i18n/locales/az.json @@ -476,7 +476,10 @@ }, "language": { "title": "Dil", - "quote": "Sitat dili" + "quote": "Sitat dili", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Dəyişikliklər Siyahısı", diff --git a/src/i18n/locales/azb.json b/src/i18n/locales/azb.json index 5119798c..802c85c7 100644 --- a/src/i18n/locales/azb.json +++ b/src/i18n/locales/azb.json @@ -476,7 +476,10 @@ }, "language": { "title": "Dil", - "quote": "Sitat dili" + "quote": "Sitat dili", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Dəyişikliklər Siyahısı", diff --git a/src/i18n/locales/bn.json b/src/i18n/locales/bn.json index d99827cc..02a396d2 100644 --- a/src/i18n/locales/bn.json +++ b/src/i18n/locales/bn.json @@ -476,7 +476,10 @@ }, "language": { "title": "Language", - "quote": "Quote language" + "quote": "Quote language", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Change Log", diff --git a/src/i18n/locales/de_DE.json b/src/i18n/locales/de_DE.json index fc88a9b2..23fd79e7 100644 --- a/src/i18n/locales/de_DE.json +++ b/src/i18n/locales/de_DE.json @@ -476,7 +476,10 @@ }, "language": { "title": "Sprache", - "quote": "Zitat-Sprache" + "quote": "Zitat-Sprache", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Änderungen", diff --git a/src/i18n/locales/el.json b/src/i18n/locales/el.json index 9871d072..31f5b6a1 100644 --- a/src/i18n/locales/el.json +++ b/src/i18n/locales/el.json @@ -478,7 +478,10 @@ }, "language": { "title": "Language", - "quote": "Quote language" + "quote": "Quote language", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Change Log", diff --git a/src/i18n/locales/en_GB.json b/src/i18n/locales/en_GB.json index 351afab1..00b6b69c 100644 --- a/src/i18n/locales/en_GB.json +++ b/src/i18n/locales/en_GB.json @@ -478,7 +478,10 @@ }, "language": { "title": "Language", - "quote": "Quote language" + "quote": "Quote language", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Change Log", diff --git a/src/i18n/locales/en_US.json b/src/i18n/locales/en_US.json index ae799dda..eb6822f5 100644 --- a/src/i18n/locales/en_US.json +++ b/src/i18n/locales/en_US.json @@ -478,7 +478,10 @@ }, "language": { "title": "Language", - "quote": "Quote language" + "quote": "Quote language", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Change Log", diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index ec31c28b..c49a9d6c 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json @@ -476,7 +476,10 @@ }, "language": { "title": "Idioma", - "quote": "Idioma de las citas" + "quote": "Idioma de las citas", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Registro de cambios", diff --git a/src/i18n/locales/es_419.json b/src/i18n/locales/es_419.json index f16d6eb5..ef2b7152 100644 --- a/src/i18n/locales/es_419.json +++ b/src/i18n/locales/es_419.json @@ -476,7 +476,10 @@ }, "language": { "title": "Idioma", - "quote": "Idioma de las citaciones" + "quote": "Idioma de las citaciones", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Registro de cambios", diff --git a/src/i18n/locales/et.json b/src/i18n/locales/et.json index ddce32e0..1d69effe 100644 --- a/src/i18n/locales/et.json +++ b/src/i18n/locales/et.json @@ -476,7 +476,10 @@ }, "language": { "title": "Keel", - "quote": "Tsitaadi keel" + "quote": "Tsitaadi keel", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Muudatuste logi", diff --git a/src/i18n/locales/fa.json b/src/i18n/locales/fa.json index 59796e42..1f19b62e 100644 --- a/src/i18n/locales/fa.json +++ b/src/i18n/locales/fa.json @@ -478,7 +478,10 @@ }, "language": { "title": "Language", - "quote": "Quote language" + "quote": "Quote language", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Change Log", diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index f46b613a..f19ac842 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json @@ -476,7 +476,10 @@ }, "language": { "title": "Langue", - "quote": "Langue des citations" + "quote": "Langue des citations", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Journal des modifications", diff --git a/src/i18n/locales/hu.json b/src/i18n/locales/hu.json index 351afab1..00b6b69c 100644 --- a/src/i18n/locales/hu.json +++ b/src/i18n/locales/hu.json @@ -478,7 +478,10 @@ }, "language": { "title": "Language", - "quote": "Quote language" + "quote": "Quote language", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Change Log", diff --git a/src/i18n/locales/id_ID.json b/src/i18n/locales/id_ID.json index 274fea02..77cc1284 100644 --- a/src/i18n/locales/id_ID.json +++ b/src/i18n/locales/id_ID.json @@ -476,7 +476,10 @@ }, "language": { "title": "Bahasa", - "quote": "Bahasa untuk kutipan" + "quote": "Bahasa untuk kutipan", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Changelog", diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index ad7c7534..862f1f4a 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json @@ -478,7 +478,10 @@ }, "language": { "title": "Language", - "quote": "Quote language" + "quote": "Quote language", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Change Log", diff --git a/src/i18n/locales/lt.json b/src/i18n/locales/lt.json index 056af7df..3caa6207 100644 --- a/src/i18n/locales/lt.json +++ b/src/i18n/locales/lt.json @@ -476,7 +476,10 @@ }, "language": { "title": "Kalba", - "quote": "Citatų kalba" + "quote": "Citatų kalba", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Pakeitimų žurnalas", diff --git a/src/i18n/locales/lv.json b/src/i18n/locales/lv.json index ad21d1c2..c7635b44 100644 --- a/src/i18n/locales/lv.json +++ b/src/i18n/locales/lv.json @@ -476,7 +476,10 @@ }, "language": { "title": "Valoda", - "quote": "Citātu valoda" + "quote": "Citātu valoda", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Izmaiņu žurnāls", diff --git a/src/i18n/locales/nl.json b/src/i18n/locales/nl.json index 8cebab42..8734da98 100644 --- a/src/i18n/locales/nl.json +++ b/src/i18n/locales/nl.json @@ -476,7 +476,10 @@ }, "language": { "title": "Taal", - "quote": "Quote language" + "quote": "Quote language", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Change Log", diff --git a/src/i18n/locales/no.json b/src/i18n/locales/no.json index ed0d3bef..30aa1e12 100644 --- a/src/i18n/locales/no.json +++ b/src/i18n/locales/no.json @@ -476,7 +476,10 @@ }, "language": { "title": "Språk", - "quote": "Quote language" + "quote": "Quote language", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Change Log", diff --git a/src/i18n/locales/peo.json b/src/i18n/locales/peo.json index 1fb2662a..c6e6b5ee 100644 --- a/src/i18n/locales/peo.json +++ b/src/i18n/locales/peo.json @@ -478,7 +478,10 @@ }, "language": { "title": "Language", - "quote": "Quote language" + "quote": "Quote language", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Change Log", diff --git a/src/i18n/locales/pt.json b/src/i18n/locales/pt.json index daa08b34..5cc29cdb 100644 --- a/src/i18n/locales/pt.json +++ b/src/i18n/locales/pt.json @@ -476,7 +476,10 @@ }, "language": { "title": "Idioma", - "quote": "Idioma das citações" + "quote": "Idioma das citações", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Registo de alterações", diff --git a/src/i18n/locales/pt_BR.json b/src/i18n/locales/pt_BR.json index b1167b4d..bc63ea57 100644 --- a/src/i18n/locales/pt_BR.json +++ b/src/i18n/locales/pt_BR.json @@ -476,7 +476,10 @@ }, "language": { "title": "Idioma", - "quote": "Idioma das citações" + "quote": "Idioma das citações", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Registo de alterações", diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json index 3d6effea..0f54e3bb 100644 --- a/src/i18n/locales/ru.json +++ b/src/i18n/locales/ru.json @@ -476,7 +476,10 @@ }, "language": { "title": "Язык", - "quote": "Язык цитат" + "quote": "Язык цитат", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Журнал изменений", diff --git a/src/i18n/locales/sl.json b/src/i18n/locales/sl.json index 0c509355..82ab92f7 100644 --- a/src/i18n/locales/sl.json +++ b/src/i18n/locales/sl.json @@ -476,7 +476,10 @@ }, "language": { "title": "Jezik", - "quote": "Jezik citata" + "quote": "Jezik citata", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Dnevnik sprememb", diff --git a/src/i18n/locales/sv.json b/src/i18n/locales/sv.json index 6d5f6835..b5f05fc4 100644 --- a/src/i18n/locales/sv.json +++ b/src/i18n/locales/sv.json @@ -478,7 +478,10 @@ }, "language": { "title": "Language", - "quote": "Quote language" + "quote": "Quote language", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Change Log", diff --git a/src/i18n/locales/ta.json b/src/i18n/locales/ta.json index bf86aa93..c4ccfebc 100644 --- a/src/i18n/locales/ta.json +++ b/src/i18n/locales/ta.json @@ -476,7 +476,10 @@ }, "language": { "title": "மொழி", - "quote": "மேற்கோள் மொழி" + "quote": "மேற்கோள் மொழி", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "பதிவை மாற்றவும்", diff --git a/src/i18n/locales/tr_TR.json b/src/i18n/locales/tr_TR.json index 5b82d2a4..401829f0 100644 --- a/src/i18n/locales/tr_TR.json +++ b/src/i18n/locales/tr_TR.json @@ -476,7 +476,10 @@ }, "language": { "title": "Dil", - "quote": "Alıntı dili" + "quote": "Alıntı dili", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Değişiklik Günlüğü", diff --git a/src/i18n/locales/uk.json b/src/i18n/locales/uk.json index ee961986..a25f5d76 100644 --- a/src/i18n/locales/uk.json +++ b/src/i18n/locales/uk.json @@ -476,7 +476,10 @@ }, "language": { "title": "Мова", - "quote": "Мова цитат" + "quote": "Мова цитат", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Журнал змін", diff --git a/src/i18n/locales/vi.json b/src/i18n/locales/vi.json index c53aa096..adc4f49e 100644 --- a/src/i18n/locales/vi.json +++ b/src/i18n/locales/vi.json @@ -478,7 +478,10 @@ }, "language": { "title": "Language", - "quote": "Quote language" + "quote": "Quote language", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Change Log", diff --git a/src/i18n/locales/zh_CN.json b/src/i18n/locales/zh_CN.json index 82ebe8d7..1e5d9c6e 100644 --- a/src/i18n/locales/zh_CN.json +++ b/src/i18n/locales/zh_CN.json @@ -476,7 +476,10 @@ }, "language": { "title": "语言", - "quote": "名言语言" + "quote": "名言语言", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "更新日志", diff --git a/src/i18n/locales/zh_Hant.json b/src/i18n/locales/zh_Hant.json index 651de479..61f7bcfe 100644 --- a/src/i18n/locales/zh_Hant.json +++ b/src/i18n/locales/zh_Hant.json @@ -478,7 +478,10 @@ }, "language": { "title": "Language", - "quote": "Quote language" + "quote": "Quote language", + "search": "Search languages...", + "current": "Current", + "use_system": "Use system language" }, "changelog": { "title": "Change Log",