Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 32 additions & 5 deletions web/src/components/LanguageSwitcher.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
import { Button } from "@nous-research/ui/ui/components/button";
import { Typography } from "@/components/NouiTypography";
import { useI18n } from "@/i18n/context";
import type { Locale } from "@/i18n/types";

const CYCLE: Locale[] = ["en", "zh", "es", "ru", "fr", "de", "ko"];

const FLAGS: Record<Locale, string> = {
en: "🇬🇧",
zh: "🇨🇳",
es: "🇪🇸",
ru: "🇷🇺",
fr: "🇫🇷",
de: "🇩🇪",
ko: "🇰🇷",
};

const LABELS: Record<Locale, string> = {
en: "EN",
zh: "中文",
es: "ES",
ru: "RU",
fr: "FR",
de: "DE",
ko: "한국어",
};

/**
* Compact language toggle — shows a clickable flag that switches between
* English and Chinese. Persists choice to localStorage.
* Compact language toggle — cycles through English, Chinese, Spanish, Russian,
* French, German, and Korean. Persists choice to localStorage.
*/
export function LanguageSwitcher() {
const { locale, setLocale, t } = useI18n();

const toggle = () => setLocale(locale === "en" ? "zh" : "en");
const toggle = () => {
const idx = CYCLE.indexOf(locale);
const next = CYCLE[(idx + 1) % CYCLE.length];
setLocale(next);
};

return (
<Button
Expand All @@ -21,14 +48,14 @@ export function LanguageSwitcher() {
>
<span className="inline-flex items-center gap-1.5">
<span className="text-base leading-none">
{locale === "en" ? "🇬🇧" : "🇨🇳"}
{FLAGS[locale]}
</span>

<Typography
mondwest
className="hidden sm:inline tracking-wide uppercase text-[0.65rem]"
>
{locale === "en" ? "EN" : "中文"}
{LABELS[locale]}
</Typography>
</span>
</Button>
Expand Down
9 changes: 7 additions & 2 deletions web/src/i18n/context.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@ import { createContext, useContext, useState, useCallback, type ReactNode } from
import type { Locale, Translations } from "./types";
import { en } from "./en";
import { zh } from "./zh";
import { ru } from "./ru";
import { es } from "./es";
import { fr } from "./fr";
import { de } from "./de";
import { ko } from "./ko";

const TRANSLATIONS: Record<Locale, Translations> = { en, zh };
const TRANSLATIONS: Record<Locale, Translations> = { en, zh, ru, es, fr, de, ko };
const STORAGE_KEY = "hermes-locale";

function getInitialLocale(): Locale {
try {
const stored = localStorage.getItem(STORAGE_KEY);
if (stored === "en" || stored === "zh") return stored;
if (stored === "en" || stored === "zh" || stored === "es" || stored === "ru" || stored === "fr" || stored === "de" || stored === "ko") return stored;
} catch {
// SSR or privacy mode
}
Expand Down
Loading