language-selector.tsx67 lines · main
1'use client';
2
3import { useEffect, useState } from 'react';
4
5/**
6 * Footer language selector. English is the main (default) language; the others
7 * are written in their OWN language so a speaker recognises their own.
8 *
9 * Scope note: this control records the visitor's preferred locale in the
10 * standard `NEXT_LOCALE` cookie so the choice is remembered and is ready for
11 * i18n. It does NOT yet translate page text — full multilingual content is a
12 * separate, larger build. Until then, picking a language is remembered but the
13 * copy stays English.
14 */
15const LANGUAGES: readonly { code: string; label: string }[] = [
16 { code: 'en', label: 'English' },
17 { code: 'de', label: 'Deutsch' },
18 { code: 'fr', label: 'Français' },
19 { code: 'es', label: 'Español' },
20 { code: 'nl', label: 'Nederlands' },
21 { code: 'tr', label: 'Türkçe' },
22];
23
24function readLocaleCookie(): string {
25 if (typeof document === 'undefined') return 'en';
26 const match = document.cookie.match(/(?:^|;\s*)NEXT_LOCALE=([^;]+)/);
27 return match?.[1] ? decodeURIComponent(match[1]) : 'en';
28}
29
30export function LanguageSelector() {
31 // Default to English on the server + first paint; sync to the saved choice
32 // after mount (cookies aren't readable during SSR here).
33 const [locale, setLocale] = useState('en');
34
35 useEffect(() => {
36 setLocale(readLocaleCookie());
37 }, []);
38
39 function onChange(event: React.ChangeEvent<HTMLSelectElement>) {
40 const next = event.target.value;
41 setLocale(next);
42 // Site-wide, one year. Lax so normal top-level navigations carry it.
43 document.cookie = `NEXT_LOCALE=${encodeURIComponent(next)}; path=/; max-age=31536000; samesite=lax`;
44 }
45
46 return (
47 <label className="inline-flex items-center" title="language">
48 <span className="sr-only">language</span>
49 <select
50 value={locale}
51 onChange={onChange}
52 aria-label="select language"
53 className="cursor-pointer border-0 bg-transparent py-0.5 pl-0 pr-5 font-mono text-[10px] text-[var(--color-text-muted)] outline-none transition-colors hover:text-[var(--color-text)]"
54 >
55 {LANGUAGES.map((language) => (
56 <option
57 key={language.code}
58 value={language.code}
59 className="bg-[var(--color-surface)] text-[var(--color-text)]"
60 >
61 {language.label}
62 </option>
63 ))}
64 </select>
65 </label>
66 );
67}