feat(i18n): add locale switcher
This commit is contained in:
@@ -0,0 +1,53 @@
|
|||||||
|
"use client"
|
||||||
|
|
||||||
|
import { useLocale, useTranslations } from "next-intl"
|
||||||
|
import { useRouter, usePathname } from "@/i18n/navigation"
|
||||||
|
import { Languages } from "lucide-react"
|
||||||
|
import { Button } from "@/components/ui/button"
|
||||||
|
import {
|
||||||
|
DropdownMenu,
|
||||||
|
DropdownMenuContent,
|
||||||
|
DropdownMenuItem,
|
||||||
|
DropdownMenuTrigger,
|
||||||
|
} from "@/components/ui/dropdown-menu"
|
||||||
|
import { routing } from "@/i18n/routing"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export function LocaleSwitch() {
|
||||||
|
const currentLocale = useLocale()
|
||||||
|
const router = useRouter()
|
||||||
|
const pathname = usePathname()
|
||||||
|
|
||||||
|
const t = useTranslations('languages');
|
||||||
|
const localeLabels: Record<string, string> = {
|
||||||
|
de: t('german'),
|
||||||
|
en: t('english'),
|
||||||
|
}
|
||||||
|
|
||||||
|
function switchLocale(newLocale: string) {
|
||||||
|
router.replace(pathname, { locale: newLocale })
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<DropdownMenu>
|
||||||
|
<DropdownMenuTrigger asChild>
|
||||||
|
<Button variant="outline" size="icon">
|
||||||
|
<Languages className="size-5" />
|
||||||
|
<span className="sr-only">Toggle language</span>
|
||||||
|
</Button>
|
||||||
|
</DropdownMenuTrigger>
|
||||||
|
<DropdownMenuContent align="end">
|
||||||
|
{routing.locales.map((locale) => (
|
||||||
|
<DropdownMenuItem
|
||||||
|
key={locale}
|
||||||
|
onClick={() => switchLocale(locale)}
|
||||||
|
className={currentLocale === locale ? "font-medium" : ""}
|
||||||
|
>
|
||||||
|
{localeLabels[locale]}
|
||||||
|
</DropdownMenuItem>
|
||||||
|
))}
|
||||||
|
</DropdownMenuContent>
|
||||||
|
</DropdownMenu>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import Link from "next/link"
|
|||||||
import { cn } from "@/lib/utils"
|
import { cn } from "@/lib/utils"
|
||||||
import { ThemeSwitch } from "./theme-switch"
|
import { ThemeSwitch } from "./theme-switch"
|
||||||
import { useTranslations } from "next-intl"
|
import { useTranslations } from "next-intl"
|
||||||
|
import { LocaleSwitch } from "./locale-switch"
|
||||||
|
|
||||||
export default function Navbar() {
|
export default function Navbar() {
|
||||||
const [scrolled, setScrolled] = useState(false);
|
const [scrolled, setScrolled] = useState(false);
|
||||||
@@ -55,7 +56,10 @@ export default function Navbar() {
|
|||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<ThemeSwitch/>
|
<div className="flex gap-3">
|
||||||
|
<ThemeSwitch/>
|
||||||
|
<LocaleSwitch/>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</header>
|
</header>
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
"use client"
|
"use client"
|
||||||
|
|
||||||
import * as React from "react"
|
|
||||||
import { Moon, Sun } from "lucide-react"
|
import { Moon, Sun } from "lucide-react"
|
||||||
import { useTheme } from "next-themes"
|
import { useTheme } from "next-themes"
|
||||||
|
|
||||||
@@ -11,9 +10,17 @@ import {
|
|||||||
DropdownMenuItem,
|
DropdownMenuItem,
|
||||||
DropdownMenuTrigger,
|
DropdownMenuTrigger,
|
||||||
} from "@/components/ui/dropdown-menu"
|
} from "@/components/ui/dropdown-menu"
|
||||||
|
import { useTranslations } from "next-intl"
|
||||||
|
|
||||||
export function ThemeSwitch() {
|
export function ThemeSwitch() {
|
||||||
const { setTheme } = useTheme()
|
const { setTheme } = useTheme();
|
||||||
|
|
||||||
|
const t = useTranslations('themes');
|
||||||
|
const themeLabels: Record<string, string> = {
|
||||||
|
light: t('light'),
|
||||||
|
dark: t('dark'),
|
||||||
|
system: t('system'),
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
@@ -26,13 +33,13 @@ export function ThemeSwitch() {
|
|||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem onClick={() => setTheme("light")}>
|
<DropdownMenuItem onClick={() => setTheme("light")}>
|
||||||
Light
|
{themeLabels['light']}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => setTheme("dark")}>
|
<DropdownMenuItem onClick={() => setTheme("dark")}>
|
||||||
Dark
|
{themeLabels['dark']}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => setTheme("system")}>
|
<DropdownMenuItem onClick={() => setTheme("system")}>
|
||||||
System
|
{themeLabels['system']}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|||||||
+1
-1
@@ -2,6 +2,6 @@ import { defineRouting } from "next-intl/routing";
|
|||||||
|
|
||||||
export const routing = defineRouting({
|
export const routing = defineRouting({
|
||||||
locales: ["de", "en"],
|
locales: ["de", "en"],
|
||||||
defaultLocale: "de",
|
defaultLocale: "en",
|
||||||
localePrefix: "as-needed",
|
localePrefix: "as-needed",
|
||||||
});
|
});
|
||||||
@@ -3,5 +3,14 @@
|
|||||||
"home": "Startseite",
|
"home": "Startseite",
|
||||||
"aboutMe": "Über Mich",
|
"aboutMe": "Über Mich",
|
||||||
"projects": "Projekte"
|
"projects": "Projekte"
|
||||||
|
},
|
||||||
|
"languages": {
|
||||||
|
"english": "Englisch",
|
||||||
|
"german": "Deutsch"
|
||||||
|
},
|
||||||
|
"themes": {
|
||||||
|
"light": "Hell",
|
||||||
|
"dark": "Dunkel",
|
||||||
|
"system": "System"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+15
-6
@@ -1,7 +1,16 @@
|
|||||||
{
|
{
|
||||||
"pages": {
|
"pages": {
|
||||||
"home": "Home",
|
"home": "Home",
|
||||||
"aboutMe": "About Me",
|
"aboutMe": "About Me",
|
||||||
"projects": "Projects"
|
"projects": "Projects"
|
||||||
}
|
},
|
||||||
}
|
"languages": {
|
||||||
|
"german": "German",
|
||||||
|
"english": "English"
|
||||||
|
},
|
||||||
|
"themes": {
|
||||||
|
"light": "Light",
|
||||||
|
"dark": "Dark",
|
||||||
|
"system": "System"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user