feat: Add language switcher component to footer
This commit is contained in:
1 parent
66cc854c2c
commit
d787a95657
29 files changed
+53
-867
No files matched your search
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -3,10 +3,8 @@ import { Inter } from "next/font/google";
|
||||
import "./globals.css";
|
||||
import { ThemeProvider } from "@/components/theme-provider";
|
||||
import StyledComponentsRegistry from "@/lib/styled-components-registry";
|
||||
import {NextIntlClientProvider} from 'next-intl';
|
||||
import Footer from "@/components/footer";
|
||||
import { Navigation } from "@/components/navigation";
|
||||
import { getMessages } from "next-intl/server";
|
||||
|
||||
const inter = Inter({ subsets: ["latin"] });
|
||||
|
||||
@@ -18,36 +16,31 @@ export const metadata: Metadata = {
|
||||
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
params: {locale}
|
||||
}: Readonly<{
|
||||
children: React.ReactNode;
|
||||
params: {locale: string};
|
||||
}>) {
|
||||
const messages = await getMessages();
|
||||
|
||||
return (
|
||||
<html lang={locale} suppressHydrationWarning>
|
||||
<html suppressHydrationWarning>
|
||||
<head>
|
||||
<link rel="me" href="https://fosstodon.org/@zenbrowser"></link>
|
||||
<link rel="alternate" type="application/rss+xml" title="Zen Browser Release Notes" href="https://www.zen-browser.app/feed.xml" />
|
||||
</head>
|
||||
<body className={inter.className}>
|
||||
<NextIntlClientProvider messages={messages} locale={locale}>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="dark"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<StyledComponentsRegistry>
|
||||
<div>
|
||||
{children}
|
||||
<Footer />
|
||||
<Navigation /> {/* At the bottom of the page */}
|
||||
</div>
|
||||
</StyledComponentsRegistry>
|
||||
</ThemeProvider>
|
||||
</NextIntlClientProvider>
|
||||
<ThemeProvider
|
||||
attribute="class"
|
||||
defaultTheme="dark"
|
||||
enableSystem
|
||||
disableTransitionOnChange
|
||||
>
|
||||
<StyledComponentsRegistry>
|
||||
<div>
|
||||
{children}
|
||||
<Footer />
|
||||
<Navigation /> {/* At the bottom of the page */}
|
||||
</div>
|
||||
</StyledComponentsRegistry>
|
||||
</ThemeProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -5,7 +5,6 @@ import TextReveal from "./ui/text-reveal";
|
||||
import { DiscordLogoIcon, GitHubLogoIcon } from "@radix-ui/react-icons";
|
||||
import { MastodonLogo } from "./icons/mastodon";
|
||||
import { Button } from "./ui/button";
|
||||
import LocaleSwitcher from "./locale-switcher";
|
||||
|
||||
export default function Footer() {
|
||||
return (
|
||||
@@ -27,8 +26,6 @@ export default function Footer() {
|
||||
<MastodonLogo className="w-5 h-5" />
|
||||
</a>
|
||||
</div>
|
||||
<h2 className="text-md font-bold opacity-80 mt-6">Change Language</h2>
|
||||
<LocaleSwitcher />
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col md:flex-row">
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
"use client";
|
||||
import { SUPPORTED_LANGUAGES, usePathname, useRouter } from '@/i18n/routing';
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from './ui/select';
|
||||
|
||||
export default function LocaleSwitcher() {
|
||||
const router = useRouter();
|
||||
const pathname = usePathname();
|
||||
const onLocaleChange = (value: string) => {
|
||||
router.push(pathname, { locale: value });
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="mt-5 md:w-80">
|
||||
<Select
|
||||
onValueChange={onLocaleChange}
|
||||
>
|
||||
<SelectTrigger className="w-[180px]">
|
||||
<SelectValue placeholder="Select Language" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{SUPPORTED_LANGUAGES.map((lang) => (
|
||||
<SelectItem key={lang} value={lang}>
|
||||
{lang}
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
@@ -11,18 +11,9 @@ import { ScrollArea } from './ui/scroll-area'
|
||||
import Logo from './logo'
|
||||
import { ny } from '@/lib/utils'
|
||||
import { components } from './navigation'
|
||||
import { useTranslations } from 'next-intl'
|
||||
|
||||
export function MobileNav() {
|
||||
const [open, setOpen] = React.useState(false);
|
||||
const t = useTranslations('mobile-nav');
|
||||
const t2 = useTranslations('navigation');
|
||||
|
||||
const translatedComponents = components.map(component => ({
|
||||
title: t2(component.titleKey),
|
||||
href: component.href,
|
||||
description: t2(component.descriptionKey),
|
||||
}));
|
||||
|
||||
return (
|
||||
<Sheet open={open} onOpenChange={setOpen}>
|
||||
@@ -34,7 +25,7 @@ export function MobileNav() {
|
||||
className="mr-2 px-0 ml-auto text-base hover:bg-transparent focus-visible:bg-transparent focus-visible:ring-0 focus-visible:ring-offset-0"
|
||||
>
|
||||
<SidebarOpen className="size-6" />
|
||||
<span className="sr-only">{t('mobile-menu-button')}</span>
|
||||
<span className="sr-only">Toggle Menu</span>
|
||||
</Button>
|
||||
</div>
|
||||
</SheetTrigger>
|
||||
@@ -52,27 +43,27 @@ export function MobileNav() {
|
||||
href="/download"
|
||||
onOpenChange={setOpen}
|
||||
>
|
||||
{t('mobile-download-link')}
|
||||
Download
|
||||
</MobileLink>
|
||||
<MobileLink
|
||||
href="/themes"
|
||||
onOpenChange={setOpen}
|
||||
>
|
||||
{t('mobile-theme-store-link')}
|
||||
Theme Store
|
||||
</MobileLink>
|
||||
<MobileLink
|
||||
href="/release-notes"
|
||||
onOpenChange={setOpen}
|
||||
>
|
||||
{t('mobile-release-notes-link')}
|
||||
Release Notes
|
||||
</MobileLink>
|
||||
<MobileLink
|
||||
href="https://patreon.com/zen_browser?utm_medium=unknown&utm_source=join_link&utm_campaign=creatorshare_creator&utm_content=copyLink"
|
||||
onOpenChange={setOpen}
|
||||
>
|
||||
{t('mobile-donate-link')} {"<"}3
|
||||
Donate {"<"}3
|
||||
</MobileLink>
|
||||
{translatedComponents.map(({title, href, description}) => (
|
||||
{components.map(({title, href, description}) => (
|
||||
<MobileLink
|
||||
href={href}
|
||||
key={href}
|
||||
|
||||
@@ -17,46 +17,36 @@ import { ModeToggle } from "./mode-toggle"
|
||||
import { MobileNav } from "./mobile-nav"
|
||||
import { HeartIcon } from "lucide-react"
|
||||
import { HeartFilledIcon } from "@radix-ui/react-icons"
|
||||
import { useTranslations } from "next-intl";
|
||||
import { unstable_setRequestLocale } from "next-intl/server"
|
||||
|
||||
export const components: { titleKey: string; href: string; descriptionKey: string }[] = [
|
||||
export const components: { title: string; href: string; description: string }[] = [
|
||||
{
|
||||
titleKey: "nav-components-privacy-policy",
|
||||
title: "nav-components-privacy-policy",
|
||||
href: "/privacy-policy",
|
||||
descriptionKey: "nav-components-privacy-policy-text",
|
||||
description: "nav-components-privacy-policy-text",
|
||||
},
|
||||
{
|
||||
titleKey: "nav-components-discord",
|
||||
title: "nav-components-discord",
|
||||
href: "https://discord.com/servers/mauro-s-little-sweatshop-1088172780480114748",
|
||||
descriptionKey: "nav-components-discord-text"
|
||||
description: "nav-components-discord-text"
|
||||
},
|
||||
{
|
||||
titleKey: "nav-components-source-code",
|
||||
title: "nav-components-source-code",
|
||||
href: "https://github.com/zen-browser",
|
||||
descriptionKey: "nav-components-source-code-text"
|
||||
description: "nav-components-source-code-text"
|
||||
},
|
||||
{
|
||||
titleKey: "nav-components-branding-assets",
|
||||
title: "nav-components-branding-assets",
|
||||
href: "/branding-assets",
|
||||
descriptionKey: "nav-components-branding-assets-text"
|
||||
description: "nav-components-branding-assets-text"
|
||||
},
|
||||
{
|
||||
titleKey: "nav-components-documentation",
|
||||
title: "nav-components-documentation",
|
||||
href: "https://docs.zen-browser.app/",
|
||||
descriptionKey: "nav-components-documentation-text"
|
||||
description: "nav-components-documentation-text"
|
||||
}
|
||||
]
|
||||
|
||||
export function Navigation() {
|
||||
const t = useTranslations('navigation');
|
||||
|
||||
const translatedComponents = components.map(component => ({
|
||||
title: t(component.titleKey),
|
||||
href: component.href,
|
||||
description: t(component.descriptionKey),
|
||||
}));
|
||||
|
||||
return (
|
||||
<div className="bg-background z-40 top-0 left-0 w-full flex fixed border-b border-grey p-2 items-center justify-center">
|
||||
<MobileNav />
|
||||
@@ -68,7 +58,7 @@ export function Navigation() {
|
||||
</NavigationMenuLink>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger>{t('nav-getting-started')}</NavigationMenuTrigger>
|
||||
<NavigationMenuTrigger>Getting Started</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul className="grid gap-3 p-6 md:w-[400px] lg:w-[500px] lg:grid-cols-[.75fr_1fr]">
|
||||
<li className="row-span-3">
|
||||
@@ -79,22 +69,23 @@ export function Navigation() {
|
||||
>
|
||||
<Logo />
|
||||
<div className="mb-2 mt-4 text-lg font-medium">
|
||||
{t('nav-gs-zen-heading')}
|
||||
Zen Browser
|
||||
</div>
|
||||
<p className="text-sm leading-tight text-muted-foreground">
|
||||
{t('nav-gs-zen-text')}
|
||||
Firefox based browser with a focus on privacy and
|
||||
customization.
|
||||
</p>
|
||||
</a>
|
||||
</NavigationMenuLink>
|
||||
</li>
|
||||
<ListItem href="/download" title={t('nav-gs-download-li')}>
|
||||
{t('nav-gs-download-li-text')}
|
||||
<ListItem href="/download" title="Download">
|
||||
Start using Zen Browser today with just a few clicks.
|
||||
</ListItem>
|
||||
<ListItem href="/themes" title={t('nav-gs-themes-li')}>
|
||||
{t('nav-gs-themes-li-text')}
|
||||
<ListItem href="/themes" title="Themes Store">
|
||||
Customize your browser with a variety of themes!
|
||||
</ListItem>
|
||||
<ListItem href="/release-notes" title={t('nav-gs-release-li')}>
|
||||
{t('nav-gs-release-text')}
|
||||
<ListItem href="/release-notes" title="Release Notes">
|
||||
Stay up to date with the latest changes.
|
||||
</ListItem>
|
||||
</ul>
|
||||
</NavigationMenuContent>
|
||||
@@ -102,30 +93,30 @@ export function Navigation() {
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger>
|
||||
<HeartFilledIcon className="text-red-500" />
|
||||
<span className="ml-2">{t('nav-donate')}</span>
|
||||
<span className="ml-2">Donate</span>
|
||||
</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px] ">
|
||||
<ListItem
|
||||
title={t('nav-donate-patreon-li')}
|
||||
title="Patreon"
|
||||
href="https://patreon.com/zen_browser?utm_medium=unknown&utm_source=join_link&utm_campaign=creatorshare_creator&utm_content=copyLink"
|
||||
>
|
||||
{t('nav-donate-patreon-li-text')}
|
||||
Support us on Patreon and get exclusive rewards and keep the project alive.
|
||||
</ListItem>
|
||||
<ListItem
|
||||
title={t('nav-donate-ko-fi-li')}
|
||||
title="Ko-Fi"
|
||||
href="https://ko-fi.com/zen_browser?utm_medium=unknown&utm_source=join_link&utm_campaign=creatorshare_creator&utm_content=copyLink"
|
||||
>
|
||||
{t('nav-donate-ko-fi-li-text')}
|
||||
Ko-fi is a way to support us with a one-time donation and help us keep the project alive.
|
||||
</ListItem>
|
||||
</ul>
|
||||
</NavigationMenuContent>
|
||||
</NavigationMenuItem>
|
||||
<NavigationMenuItem>
|
||||
<NavigationMenuTrigger>{t('nav-useful-links')}</NavigationMenuTrigger>
|
||||
<NavigationMenuTrigger>{"Useful Links"}</NavigationMenuTrigger>
|
||||
<NavigationMenuContent>
|
||||
<ul className="grid w-[400px] gap-3 p-4 md:w-[500px] md:grid-cols-2 lg:w-[600px] ">
|
||||
{translatedComponents.map((component) => (
|
||||
{components.map((component) => (
|
||||
<ListItem
|
||||
key={component.title}
|
||||
title={component.title}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
import { getThemeAuthorLink, getThemeFromId, getThemeMarkdown, ZenTheme } from "@/lib/themes";
|
||||
import { Button } from "./ui/button";
|
||||
import Markdown from "react-markdown";
|
||||
import '../app/[locale]/privacy-policy/markdown.css';
|
||||
import '../app/privacy-policy/markdown.css';
|
||||
import { ChevronLeft, LoaderCircleIcon } from "lucide-react";
|
||||
|
||||
export default async function ThemePage({ themeID }: { themeID: string }) {
|
||||
|
||||
-12
@@ -1,12 +0,0 @@
|
||||
import {notFound} from 'next/navigation';
|
||||
import {getRequestConfig} from 'next-intl/server';
|
||||
import {routing} from './i18n/routing';
|
||||
|
||||
export default getRequestConfig(async ({locale}) => {
|
||||
// Validate that the incoming `locale` parameter is valid
|
||||
if (!routing.locales.includes(locale as any)) notFound();
|
||||
|
||||
return {
|
||||
messages: (await import(`../messages/${locale}.json`)).default
|
||||
};
|
||||
});
|
||||
@@ -1,17 +0,0 @@
|
||||
import {defineRouting} from 'next-intl/routing';
|
||||
import {createSharedPathnamesNavigation} from 'next-intl/navigation';
|
||||
export const SUPPORTED_LANGUAGES = ['en', 'de'];
|
||||
|
||||
export const routing = defineRouting({
|
||||
// A list of all locales that are supported
|
||||
locales: SUPPORTED_LANGUAGES,
|
||||
|
||||
// Used when no locale matches
|
||||
defaultLocale: 'en',
|
||||
localePrefix: "always",
|
||||
});
|
||||
|
||||
// Lightweight wrappers around Next.js' navigation APIs
|
||||
// that will consider the routing configuration
|
||||
export const {Link, redirect, usePathname, useRouter} =
|
||||
createSharedPathnamesNavigation(routing);
|
||||
@@ -1,9 +0,0 @@
|
||||
import createMiddleware from 'next-intl/middleware';
|
||||
import {routing} from './i18n/routing';
|
||||
|
||||
export default createMiddleware(routing);
|
||||
|
||||
export const config = {
|
||||
// Match only internationalized pathnames and all paths that dont start with static
|
||||
matcher: ['/', '/:path((?!_next|next|static).*)*'],
|
||||
};
|
||||
Reference in new issue
Block a user