From 399ab88f5f93a015b2b2f414019549e62d490eaf Mon Sep 17 00:00:00 2001 From: Benjamin Admin Date: Wed, 29 Apr 2026 18:23:05 +0200 Subject: [PATCH] Translate language dropdown tabs + add info box in 26 languages - Tab labels (Muttersprache/Schulsprache) now translate with selected language e.g. Turkish: "Ana dilim" / "Okul dili" - Info box below tabs explains each setting in the user's native language e.g. "Evde konustunuz dil. Tum menuler bu dilde gorunecektir." - Wider dropdown (w-72) to fit translated text - All 26 European languages covered Co-Authored-By: Claude Opus 4.6 (1M context) --- studio-v2/components/LanguageDropdown.tsx | 91 ++++++++++++++--------- 1 file changed, 55 insertions(+), 36 deletions(-) diff --git a/studio-v2/components/LanguageDropdown.tsx b/studio-v2/components/LanguageDropdown.tsx index a5f777f..7522ed3 100644 --- a/studio-v2/components/LanguageDropdown.tsx +++ b/studio-v2/components/LanguageDropdown.tsx @@ -5,11 +5,41 @@ import { useLanguage } from '@/lib/LanguageContext' import { useTheme } from '@/lib/ThemeContext' import { Language } from '@/lib/i18n' -interface LanguageDropdownProps { - className?: string +/** Tab labels + info text in all 26 languages */ +const TAB_LABELS: Record = { + de: { native: 'Muttersprache', school: 'Schulsprache', info_native: 'Die Sprache, die Sie zu Hause sprechen. Alle Menus und Erklaerungen erscheinen in dieser Sprache.', info_school: 'Die Sprache, in der Ihr Kind in der Schule unterrichtet wird.' }, + en: { native: 'My language', school: 'School language', info_native: 'The language you speak at home. All menus and explanations will appear in this language.', info_school: 'The language your child is taught in at school.' }, + tr: { native: 'Ana dilim', school: 'Okul dili', info_native: 'Evde konustunuz dil. Tum menuler ve aciklamalar bu dilde gorunecektir.', info_school: 'Cocugunuzun okulda ogretildigi dil.' }, + ar: { native: '\u0644\u063a\u062a\u064a', school: '\u0644\u063a\u0629 \u0627\u0644\u0645\u062f\u0631\u0633\u0629', info_native: '\u0627\u0644\u0644\u063a\u0629 \u0627\u0644\u062a\u064a \u062a\u062a\u062d\u062f\u062b\u0647\u0627 \u0641\u064a \u0627\u0644\u0645\u0646\u0632\u0644. \u0633\u062a\u0638\u0647\u0631 \u062c\u0645\u064a\u0639 \u0627\u0644\u0642\u0648\u0627\u0626\u0645 \u0628\u0647\u0630\u0647 \u0627\u0644\u0644\u063a\u0629.', info_school: '\u0627\u0644\u0644\u063a\u0629 \u0627\u0644\u062a\u064a \u064a\u062a\u0639\u0644\u0645 \u0628\u0647\u0627 \u0637\u0641\u0644\u0643 \u0641\u064a \u0627\u0644\u0645\u062f\u0631\u0633\u0629.' }, + uk: { native: '\u041c\u043e\u044f \u043c\u043e\u0432\u0430', school: '\u041c\u043e\u0432\u0430 \u0448\u043a\u043e\u043b\u0438', info_native: '\u041c\u043e\u0432\u0430, \u044f\u043a\u043e\u044e \u0432\u0438 \u0440\u043e\u0437\u043c\u043e\u0432\u043b\u044f\u0454\u0442\u0435 \u0432\u0434\u043e\u043c\u0430. \u0412\u0441\u0456 \u043c\u0435\u043d\u044e \u0431\u0443\u0434\u0443\u0442\u044c \u0446\u0456\u0454\u044e \u043c\u043e\u0432\u043e\u044e.', info_school: '\u041c\u043e\u0432\u0430, \u044f\u043a\u043e\u044e \u0432\u0430\u0448\u0430 \u0434\u0438\u0442\u0438\u043d\u0430 \u043d\u0430\u0432\u0447\u0430\u0454\u0442\u044c\u0441\u044f \u0432 \u0448\u043a\u043e\u043b\u0456.' }, + ru: { native: '\u041c\u043e\u0439 \u044f\u0437\u044b\u043a', school: '\u042f\u0437\u044b\u043a \u0448\u043a\u043e\u043b\u044b', info_native: '\u042f\u0437\u044b\u043a, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0432\u044b \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u0435 \u0434\u043e\u043c\u0430. \u0412\u0441\u0435 \u043c\u0435\u043d\u044e \u0431\u0443\u0434\u0443\u0442 \u043d\u0430 \u044d\u0442\u043e\u043c \u044f\u0437\u044b\u043a\u0435.', info_school: '\u042f\u0437\u044b\u043a, \u043d\u0430 \u043a\u043e\u0442\u043e\u0440\u043e\u043c \u0432\u0430\u0448 \u0440\u0435\u0431\u0435\u043d\u043e\u043a \u0443\u0447\u0438\u0442\u0441\u044f \u0432 \u0448\u043a\u043e\u043b\u0435.' }, + pl: { native: 'Moj jezyk', school: 'Jezyk szkolny', info_native: 'Jezyk, ktorym mowisz w domu. Wszystkie menu beda w tym jezyku.', info_school: 'Jezyk, w ktorym uczy sie Twoje dziecko w szkole.' }, + fr: { native: 'Ma langue', school: 'Langue scolaire', info_native: 'La langue que vous parlez a la maison. Tous les menus apparaitront dans cette langue.', info_school: 'La langue dans laquelle votre enfant est enseigne a l\'ecole.' }, + es: { native: 'Mi idioma', school: 'Idioma escolar', info_native: 'El idioma que habla en casa. Todos los menus apareceran en este idioma.', info_school: 'El idioma en el que su hijo es ensenado en la escuela.' }, + it: { native: 'La mia lingua', school: 'Lingua scolastica', info_native: 'La lingua che parli a casa. Tutti i menu appariranno in questa lingua.', info_school: 'La lingua in cui tuo figlio viene insegnato a scuola.' }, + pt: { native: 'Minha lingua', school: 'Lingua escolar', info_native: 'A lingua que voce fala em casa. Todos os menus aparecerao neste idioma.', info_school: 'A lingua em que seu filho e ensinado na escola.' }, + nl: { native: 'Mijn taal', school: 'Schooltaal', info_native: 'De taal die u thuis spreekt. Alle menu\'s verschijnen in deze taal.', info_school: 'De taal waarin uw kind op school les krijgt.' }, + ro: { native: 'Limba mea', school: 'Limba scolara', info_native: 'Limba pe care o vorbiti acasa. Toate meniurile vor aparea in aceasta limba.', info_school: 'Limba in care copilul dvs. invata la scoala.' }, + el: { native: '\u0397 \u03b3\u03bb\u03ce\u03c3\u03c3\u03b1 \u03bc\u03bf\u03c5', school: '\u03a3\u03c7\u03bf\u03bb\u03b9\u03ba\u03ae \u03b3\u03bb\u03ce\u03c3\u03c3\u03b1', info_native: '\u0397 \u03b3\u03bb\u03ce\u03c3\u03c3\u03b1 \u03c0\u03bf\u03c5 \u03bc\u03b9\u03bb\u03ac\u03c4\u03b5 \u03c3\u03c4\u03bf \u03c3\u03c0\u03af\u03c4\u03b9.', info_school: '\u0397 \u03b3\u03bb\u03ce\u03c3\u03c3\u03b1 \u03c3\u03c4\u03b7\u03bd \u03bf\u03c0\u03bf\u03af\u03b1 \u03b4\u03b9\u03b4\u03ac\u03c3\u03ba\u03b5\u03c4\u03b1\u03b9 \u03c4\u03bf \u03c0\u03b1\u03b9\u03b4\u03af \u03c3\u03b1\u03c2.' }, + bg: { native: '\u041c\u043e\u044f\u0442 \u0435\u0437\u0438\u043a', school: '\u0423\u0447\u0438\u043b\u0438\u0449\u0435\u043d \u0435\u0437\u0438\u043a', info_native: '\u0415\u0437\u0438\u043a\u044a\u0442, \u043a\u043e\u0439\u0442\u043e \u0433\u043e\u0432\u043e\u0440\u0438\u0442\u0435 \u0443 \u0434\u043e\u043c\u0430.', info_school: '\u0415\u0437\u0438\u043a\u044a\u0442, \u043d\u0430 \u043a\u043e\u0439\u0442\u043e \u0434\u0435\u0442\u0435\u0442\u043e \u0432\u0438 \u0443\u0447\u0438 \u0432 \u0443\u0447\u0438\u043b\u0438\u0449\u0435.' }, + hr: { native: 'Moj jezik', school: 'Skolski jezik', info_native: 'Jezik koji govorite kod kuce. Svi izbornici ce biti na ovom jeziku.', info_school: 'Jezik na kojem se vase dijete poducava u skoli.' }, + cs: { native: 'Muj jazyk', school: 'Skolni jazyk', info_native: 'Jazyk, kterym mluvite doma. Vsechny nabidky budou v tomto jazyce.', info_school: 'Jazyk, ve kterem se vase dite uci ve skole.' }, + hu: { native: 'Az en nyelvem', school: 'Iskolai nyelv', info_native: 'A nyelv, amelyet otthon beszel. Minden menu ezen a nyelven jelenik meg.', info_school: 'A nyelv, amelyen gyermeke az iskolaban tanul.' }, + sv: { native: 'Mitt sprak', school: 'Skolsprak', info_native: 'Spraket du talar hemma. Alla menyer visas pa detta sprak.', info_school: 'Spraket som ditt barn undervisas pa i skolan.' }, + da: { native: 'Mit sprog', school: 'Skolesprog', info_native: 'Det sprog, du taler derhjemme. Alle menuer vises pa dette sprog.', info_school: 'Det sprog, dit barn undervises pa i skolen.' }, + fi: { native: 'Oma kieleni', school: 'Koulun kieli', info_native: 'Kieli, jota puhut kotona. Kaikki valikot nakyvet talla kielella.', info_school: 'Kieli, jolla lastasi opetetaan koulussa.' }, + sk: { native: 'Moj jazyk', school: 'Skolsky jazyk', info_native: 'Jazyk, ktorym hovorite doma.', info_school: 'Jazyk, v ktorom sa vase dieta uci v skole.' }, + sl: { native: 'Moj jezik', school: 'Solski jezik', info_native: 'Jezik, ki ga govorite doma.', info_school: 'Jezik, v katerem se vas otrok uci v soli.' }, + lt: { native: 'Mano kalba', school: 'Mokyklos kalba', info_native: 'Kalba, kuria kalbate namuose.', info_school: 'Kalba, kuria jusu vaikas mokosi mokykloje.' }, + lv: { native: 'Mana valoda', school: 'Skolas valoda', info_native: 'Valoda, kuru runajat majas.', info_school: 'Valoda, kura jusu berns macas skola.' }, + et: { native: 'Minu keel', school: 'Kooli keel', info_native: 'Keel, mida raagite kodus.', info_school: 'Keel, milles teie last koolis opetatakse.' }, } -export function LanguageDropdown({ className = '' }: LanguageDropdownProps) { +function getLabels(lang: string) { + return TAB_LABELS[lang] || TAB_LABELS['en'] +} + +export function LanguageDropdown({ className = '' }: { className?: string }) { const { language, setLanguage, schoolLanguage, setSchoolLanguage, availableLanguages } = useLanguage() const { isDark } = useTheme() const [isOpen, setIsOpen] = useState(false) @@ -17,39 +47,30 @@ export function LanguageDropdown({ className = '' }: LanguageDropdownProps) { const dropdownRef = useRef(null) useEffect(() => { - function handleClickOutside(event: MouseEvent) { - if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { - setIsOpen(false) - } - } - document.addEventListener('mousedown', handleClickOutside) - return () => document.removeEventListener('mousedown', handleClickOutside) + const h = (e: MouseEvent) => { if (dropdownRef.current && !dropdownRef.current.contains(e.target as Node)) setIsOpen(false) } + document.addEventListener('mousedown', h) + return () => document.removeEventListener('mousedown', h) }, []) useEffect(() => { - function handleEscape(event: KeyboardEvent) { - if (event.key === 'Escape') setIsOpen(false) - } - document.addEventListener('keydown', handleEscape) - return () => document.removeEventListener('keydown', handleEscape) + const h = (e: KeyboardEvent) => { if (e.key === 'Escape') setIsOpen(false) } + document.addEventListener('keydown', h) + return () => document.removeEventListener('keydown', h) }, []) const nativeLang = availableLanguages[language] const schoolLang = availableLanguages[schoolLanguage] const activeLang = mode === 'native' ? language : schoolLanguage + const labels = getLabels(language) const handleSelect = (lang: Language) => { - if (mode === 'native') { - setLanguage(lang) - } else { - setSchoolLanguage(lang) - } + if (mode === 'native') setLanguage(lang) + else setSchoolLanguage(lang) setIsOpen(false) } return (
- {/* Trigger: shows both native + school language */} - {/* Dropdown */} {isOpen && ( -
- {/* Tab switcher: Muttersprache / Schulsprache */} + {/* Tabs — translated labels */}
+ {/* Info box — explains what this setting does */} +
+ 💡 {mode === 'native' ? labels.info_native : labels.info_school} +
+ {/* Language list */} -
    +
      {(Object.keys(availableLanguages) as Language[]).map((lang) => { const langInfo = availableLanguages[lang] const isSelected = lang === activeLang @@ -118,8 +139,6 @@ export function LanguageDropdown({ className = '' }: LanguageDropdownProps) { ? isDark ? 'bg-white/20 text-white' : 'bg-indigo-100 text-slate-900' : isDark ? 'text-white/80 hover:bg-white/10' : 'text-slate-700 hover:bg-slate-100' }`} - role="option" - aria-selected={isSelected} > {langInfo.flag} {langInfo.name}