mirror of
https://github.com/Dokploy/dokploy
synced 2025-06-26 18:27:59 +00:00
Merge pull request #421 from PaiJi/feat/add-website-translation-zhHans
feat: add website translation for zh-Hans
This commit is contained in:
commit
0b18f86a91
5
apps/website/app/[locale]/[...rest]/page.tsx
Normal file
5
apps/website/app/[locale]/[...rest]/page.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { notFound } from "next/navigation";
|
||||
|
||||
export default function CatchAll() {
|
||||
notFound();
|
||||
}
|
97
apps/website/app/[locale]/layout.tsx
Normal file
97
apps/website/app/[locale]/layout.tsx
Normal file
@ -0,0 +1,97 @@
|
||||
import clsx from "clsx";
|
||||
import { Inter, Lexend } from "next/font/google";
|
||||
import "@/styles/tailwind.css";
|
||||
import GoogleAnalytics from "@/components/analitycs/google";
|
||||
|
||||
import { NextIntlClientProvider } from "next-intl";
|
||||
import { getMessages } from "next-intl/server";
|
||||
|
||||
import type { Metadata } from "next";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
default: "Dokploy - Effortless Deployment Solutions",
|
||||
template: "%s | Simplify Your DevOps",
|
||||
},
|
||||
alternates: {
|
||||
canonical: "https://dokploy.com",
|
||||
languages: {
|
||||
en: "https://dokploy.com",
|
||||
},
|
||||
},
|
||||
description:
|
||||
"Streamline your deployment process with Dokploy. Effortlessly manage applications and databases on any VPS using Docker and Traefik for improved performance and security.",
|
||||
applicationName: "Dokploy",
|
||||
keywords: [
|
||||
"Dokploy",
|
||||
"Docker",
|
||||
"Traefik",
|
||||
"deployment",
|
||||
"VPS",
|
||||
"application management",
|
||||
"database management",
|
||||
"DevOps",
|
||||
"cloud infrastructure",
|
||||
"UI Self hosted",
|
||||
],
|
||||
referrer: "origin",
|
||||
robots: "index, follow",
|
||||
openGraph: {
|
||||
type: "website",
|
||||
url: "https://dokploy.com",
|
||||
title: "Dokploy - Effortless Deployment Solutions",
|
||||
description:
|
||||
"Simplify your DevOps with Dokploy. Deploy applications and manage databases efficiently on any VPS.",
|
||||
siteName: "Dokploy",
|
||||
images: [
|
||||
{
|
||||
url: "http://dokploy.com/og.png",
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
site: "@Dokploy",
|
||||
creator: "@Dokploy",
|
||||
title: "Dokploy - Simplify Your DevOps",
|
||||
description:
|
||||
"Deploy applications and manage databases with ease using Dokploy. Learn how our platform can elevate your infrastructure management.",
|
||||
images: "https://dokploy.com/og.png",
|
||||
},
|
||||
};
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
display: "swap",
|
||||
variable: "--font-inter",
|
||||
});
|
||||
|
||||
const lexend = Lexend({
|
||||
subsets: ["latin"],
|
||||
display: "swap",
|
||||
variable: "--font-lexend",
|
||||
});
|
||||
|
||||
export default async function RootLayout({
|
||||
children,
|
||||
params,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
params: { locale: string };
|
||||
}) {
|
||||
const { locale } = params;
|
||||
const messages = await getMessages();
|
||||
return (
|
||||
<html
|
||||
lang={locale}
|
||||
className={clsx("h-full scroll-smooth", inter.variable, lexend.variable)}
|
||||
>
|
||||
<GoogleAnalytics />
|
||||
<body className="flex h-full flex-col">
|
||||
<NextIntlClientProvider messages={messages}>
|
||||
{children}
|
||||
</NextIntlClientProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
5
apps/website/app/[locale]/not-found.tsx
Normal file
5
apps/website/app/[locale]/not-found.tsx
Normal file
@ -0,0 +1,5 @@
|
||||
import { SlimLayout } from "@/components/SlimLayout";
|
||||
|
||||
export default function NotFound() {
|
||||
return <SlimLayout />;
|
||||
}
|
25
apps/website/app/[locale]/page.tsx
Normal file
25
apps/website/app/[locale]/page.tsx
Normal file
@ -0,0 +1,25 @@
|
||||
import { CallToAction } from "@/components/CallToAction";
|
||||
import { Faqs } from "@/components/Faqs";
|
||||
import { Footer } from "@/components/Footer";
|
||||
import { Header } from "@/components/Header";
|
||||
import { Hero } from "@/components/Hero";
|
||||
import { PrimaryFeatures } from "@/components/PrimaryFeatures";
|
||||
import { SecondaryFeatures } from "@/components/SecondaryFeatures";
|
||||
import { Testimonials } from "../../components/Testimonials";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div>
|
||||
<Header />
|
||||
<main>
|
||||
<Hero />
|
||||
<PrimaryFeatures />
|
||||
<SecondaryFeatures />
|
||||
<CallToAction />
|
||||
{/* <Testimonials /> */}
|
||||
<Faqs />
|
||||
<Footer />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,85 +1,11 @@
|
||||
import clsx from "clsx";
|
||||
import { Inter, Lexend } from "next/font/google";
|
||||
import "@/styles/tailwind.css";
|
||||
import GoogleAnalytics from "@/components/analitycs/google";
|
||||
import type { Metadata } from "next";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: {
|
||||
default: "Dokploy - Effortless Deployment Solutions",
|
||||
template: "%s | Simplify Your DevOps",
|
||||
},
|
||||
alternates: {
|
||||
canonical: "https://dokploy.com",
|
||||
languages: {
|
||||
en: "https://dokploy.com",
|
||||
},
|
||||
},
|
||||
description:
|
||||
"Streamline your deployment process with Dokploy. Effortlessly manage applications and databases on any VPS using Docker and Traefik for improved performance and security.",
|
||||
applicationName: "Dokploy",
|
||||
keywords: [
|
||||
"Dokploy",
|
||||
"Docker",
|
||||
"Traefik",
|
||||
"deployment",
|
||||
"VPS",
|
||||
"application management",
|
||||
"database management",
|
||||
"DevOps",
|
||||
"cloud infrastructure",
|
||||
"UI Self hosted",
|
||||
],
|
||||
referrer: "origin",
|
||||
robots: "index, follow",
|
||||
openGraph: {
|
||||
type: "website",
|
||||
url: "https://dokploy.com",
|
||||
title: "Dokploy - Effortless Deployment Solutions",
|
||||
description:
|
||||
"Simplify your DevOps with Dokploy. Deploy applications and manage databases efficiently on any VPS.",
|
||||
siteName: "Dokploy",
|
||||
images: [
|
||||
{
|
||||
url: "http://dokploy.com/og.png",
|
||||
},
|
||||
],
|
||||
},
|
||||
twitter: {
|
||||
card: "summary_large_image",
|
||||
site: "@Dokploy",
|
||||
creator: "@Dokploy",
|
||||
title: "Dokploy - Simplify Your DevOps",
|
||||
description:
|
||||
"Deploy applications and manage databases with ease using Dokploy. Learn how our platform can elevate your infrastructure management.",
|
||||
images: "https://dokploy.com/og.png",
|
||||
},
|
||||
type Props = {
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
const inter = Inter({
|
||||
subsets: ["latin"],
|
||||
display: "swap",
|
||||
variable: "--font-inter",
|
||||
});
|
||||
|
||||
const lexend = Lexend({
|
||||
subsets: ["latin"],
|
||||
display: "swap",
|
||||
variable: "--font-lexend",
|
||||
});
|
||||
|
||||
export default function RootLayout({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return (
|
||||
<html
|
||||
lang="en"
|
||||
className={clsx("h-full scroll-smooth ", inter.variable, lexend.variable)}
|
||||
>
|
||||
<GoogleAnalytics />
|
||||
<body className="flex h-full flex-col">{children}</body>
|
||||
</html>
|
||||
);
|
||||
// Since we have a `not-found.tsx` page on the root, a layout file
|
||||
// is required, even if it's just passing children through.
|
||||
export default function RootLayout({ children }: Props) {
|
||||
return children;
|
||||
}
|
||||
|
@ -1,27 +1,13 @@
|
||||
import Link from "next/link";
|
||||
"use client";
|
||||
|
||||
import { SlimLayout } from "../components/SlimLayout";
|
||||
// import { Button } from "../components/Button";
|
||||
import { Logo } from "../components/shared/Logo";
|
||||
import NextError from "next/error";
|
||||
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<SlimLayout>
|
||||
<div className="flex">
|
||||
<Link href="/" aria-label="Home">
|
||||
<Logo className="h-10 w-auto" />
|
||||
</Link>
|
||||
</div>
|
||||
<p className="mt-20 text-sm font-medium text-gray-700">404</p>
|
||||
<h1 className="mt-3 text-lg font-semibold text-gray-900">
|
||||
Page not found
|
||||
</h1>
|
||||
<p className="mt-3 text-sm text-gray-700">
|
||||
Sorry, we couldn’t find the page you’re looking for.
|
||||
</p>
|
||||
{/* <Button href="/" className="mt-10">
|
||||
Go back home
|
||||
</Button> */}
|
||||
</SlimLayout>
|
||||
<html lang="en">
|
||||
<body>
|
||||
<NextError statusCode={404} />
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
}
|
||||
|
@ -1,25 +0,0 @@
|
||||
import { CallToAction } from "../components/CallToAction";
|
||||
import { Faqs } from "../components/Faqs";
|
||||
import { Footer } from "../components/Footer";
|
||||
import { Header } from "../components/Header";
|
||||
import { Hero } from "../components/Hero";
|
||||
import { PrimaryFeatures } from "../components/PrimaryFeatures";
|
||||
import { SecondaryFeatures } from "../components/SecondaryFeatures";
|
||||
import { Testimonials } from "../components/Testimonials";
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<div>
|
||||
<Header />
|
||||
<main>
|
||||
<Hero />
|
||||
<PrimaryFeatures />
|
||||
<SecondaryFeatures />
|
||||
<CallToAction />
|
||||
{/* <Testimonials /> */}
|
||||
<Faqs />
|
||||
<Footer />
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,12 +1,15 @@
|
||||
import { Container } from "@/components/Container";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Link from "next/link";
|
||||
import { Button } from "./ui/button";
|
||||
|
||||
export function CallToAction() {
|
||||
const t = useTranslations("HomePage");
|
||||
const linkT = useTranslations("Link");
|
||||
return (
|
||||
<section
|
||||
id="get-started-today"
|
||||
className="relative overflow-hidden py-10 bg-black border-y border-border/30"
|
||||
className="relative overflow-hidden border-y border-border/30 bg-black py-10"
|
||||
>
|
||||
<svg
|
||||
viewBox="0 0 2000 1000"
|
||||
@ -27,26 +30,23 @@ export function CallToAction() {
|
||||
</radialGradient>
|
||||
</defs>
|
||||
</svg>
|
||||
<Container className="z-30 relative">
|
||||
<Container className="relative z-30">
|
||||
<div className="mx-auto max-w-lg text-center">
|
||||
<h2 className="font-display text-3xl tracking-tight text-white sm:text-4xl">
|
||||
Unlock Your Deployment Potential
|
||||
{t("callToAction.title")}
|
||||
</h2>
|
||||
<p className="mt-4 text-lg tracking-tight text-muted-foreground">
|
||||
Streamline your deployments with our PaaS. Effortlessly manage
|
||||
Docker containers and traffic with Traefik. Boost your
|
||||
infrastructure's efficiency and security today
|
||||
{t("callToAction.des")}
|
||||
</p>
|
||||
|
||||
{/* @ts-expect-error */}
|
||||
<Button className="rounded-full mt-10" asChild>
|
||||
<Button className="mt-10 rounded-full" asChild>
|
||||
<Link
|
||||
href="https://docs.dokploy.com/get-started/installation"
|
||||
href={linkT("docs.install")}
|
||||
aria-label="Dokploy on GitHub"
|
||||
target="_blank"
|
||||
className="flex flex-row gap-2 items-center"
|
||||
className="flex flex-row items-center gap-2"
|
||||
>
|
||||
Get Started Now
|
||||
{t("callToAction.button")}
|
||||
</Link>
|
||||
</Button>
|
||||
</div>
|
||||
|
@ -1,63 +1,57 @@
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Container } from "./Container";
|
||||
|
||||
const faqs = [
|
||||
[
|
||||
{
|
||||
question: "What is dokploy?",
|
||||
answer:
|
||||
"Dokploy is a stable, easy-to-use deployment solution designed to simplify the application management process. Think of Dokploy as a free alternative self-hostable solution to platforms like Heroku, Vercel, and Netlify.",
|
||||
question: "faq.q1",
|
||||
answer: "faq.a1",
|
||||
},
|
||||
{
|
||||
question: "Why Choose Dokploy?",
|
||||
answer: "Simplicity, Flexibility, and Fast",
|
||||
question: "faq.q2",
|
||||
answer: "faq.a2",
|
||||
},
|
||||
{
|
||||
question: "Is free?",
|
||||
answer:
|
||||
"Yes, dokploy is totally free. You can use it for personal projects, small teams, or even for large-scale applications.",
|
||||
question: "faq.q3",
|
||||
answer: "faq.a3",
|
||||
},
|
||||
{
|
||||
question: "Is it open source?",
|
||||
answer: "Yes, dokploy is open source and free to use.",
|
||||
question: "faq.q4",
|
||||
answer: "faq.a4",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
question: "What type of applications can i deploy with dokploy?",
|
||||
answer:
|
||||
"Dokploy is a great choice for any type of application. You can deploy your code to dokploy and manage it from the dashboard. We support a wide range of languages and frameworks, so you can choose the one that best fits your needs.",
|
||||
question: "faq.q5",
|
||||
answer: "faq.a5",
|
||||
},
|
||||
{
|
||||
question: "How do I request a feature or report a bug?",
|
||||
answer:
|
||||
"Currently we are working on fixing bug fixes, but we will be releasing new features soon. You can also request features or report bugs.",
|
||||
question: "faq.q6",
|
||||
answer: "faq.a6",
|
||||
},
|
||||
{
|
||||
question: "Do you track the usage of Dokploy?",
|
||||
answer: "No, we don't track any usage data.",
|
||||
question: "faq.q7",
|
||||
answer: "faq.a7",
|
||||
},
|
||||
],
|
||||
[
|
||||
{
|
||||
question:
|
||||
"Are there any user forums or communities where I can interact with other users?",
|
||||
answer:
|
||||
"Yes, we have active github discussions where you can share ideas, ask for help, and connect with other users.",
|
||||
question: "faq.q8",
|
||||
answer: "faq.a8",
|
||||
},
|
||||
{
|
||||
question: "What types of applications can I deploy with Dokploy?",
|
||||
answer:
|
||||
"Dokploy supports a variety of applications, including those built with Docker, as well as applications from any Git repository, offering custom builds with Nixpacks, Dockerfiles, or Buildpacks like Heroku and Paketo.",
|
||||
question: "faq.q9",
|
||||
answer: "faq.a9",
|
||||
},
|
||||
{
|
||||
question: "How does Dokploy handle database management?",
|
||||
answer:
|
||||
"Dokploy supports multiple database systems including Postgres, MySQL, MariaDB, MongoDB, and Redis, providing tools for easy deployment and management directly from the dashboard.",
|
||||
question: "faq.q10",
|
||||
answer: "faq.a10",
|
||||
},
|
||||
],
|
||||
];
|
||||
|
||||
export function Faqs() {
|
||||
const t = useTranslations("HomePage");
|
||||
return (
|
||||
<section
|
||||
id="faqs"
|
||||
@ -70,11 +64,10 @@ export function Faqs() {
|
||||
id="faq-title"
|
||||
className="font-display text-3xl tracking-tight text-primary sm:text-4xl"
|
||||
>
|
||||
Frequently asked questions
|
||||
{t("faq.title")}
|
||||
</h2>
|
||||
<p className="mt-4 text-lg tracking-tight text-muted-foreground">
|
||||
If you can’t find what you’re looking for, email our support team
|
||||
and if you’re lucky someone will get back to you.
|
||||
{t("faq.des")}
|
||||
</p>
|
||||
</div>
|
||||
<ul className="mx-auto mt-16 grid max-w-2xl grid-cols-1 gap-8 lg:max-w-none lg:grid-cols-3">
|
||||
@ -84,10 +77,10 @@ export function Faqs() {
|
||||
{column.map((faq, faqIndex) => (
|
||||
<li key={faqIndex}>
|
||||
<h3 className="font-display text-lg leading-7 text-primary">
|
||||
{faq.question}
|
||||
{t(faq.question)}
|
||||
</h3>
|
||||
<p className="mt-4 text-sm text-muted-foreground">
|
||||
{faq.answer}
|
||||
{t(faq.answer)}
|
||||
</p>
|
||||
</li>
|
||||
))}
|
||||
|
@ -1,15 +1,19 @@
|
||||
import Link from "next/link";
|
||||
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Container } from "./Container";
|
||||
import { NavLink } from "./NavLink";
|
||||
import { Logo } from "./shared/Logo";
|
||||
|
||||
export function Footer() {
|
||||
const t = useTranslations("HomePage");
|
||||
const linkT = useTranslations("Link");
|
||||
|
||||
return (
|
||||
<footer className="bg-black">
|
||||
<Container>
|
||||
<div className="py-16">
|
||||
<div className="flex flex-col gap-2 items-center">
|
||||
<div className="flex flex-col items-center gap-2">
|
||||
<Logo className="mx-auto h-10 w-auto" />
|
||||
<span className="text-center text-sm font-medium text-primary">
|
||||
Dokploy
|
||||
@ -17,14 +21,11 @@ export function Footer() {
|
||||
</div>
|
||||
|
||||
<nav className="mt-10 text-sm" aria-label="quick links">
|
||||
<div className="-my-1 flex justify-center gap-6 flex-wrap">
|
||||
<NavLink href="/#features">Features</NavLink>
|
||||
<NavLink href="/#faqs">Faqs</NavLink>
|
||||
<NavLink
|
||||
href="https://docs.dokploy.com/get-started/introduction"
|
||||
target="_blank"
|
||||
>
|
||||
Docs
|
||||
<div className="-my-1 flex flex-wrap justify-center gap-6">
|
||||
<NavLink href="/#features">{t("navigation.features")}</NavLink>
|
||||
<NavLink href="/#faqs">{t("navigation.faqs")}</NavLink>
|
||||
<NavLink href={linkT("docs.intro")} target="_blank">
|
||||
{t("navigation.docs")}
|
||||
</NavLink>
|
||||
</div>
|
||||
</nav>
|
||||
@ -57,8 +58,9 @@ export function Footer() {
|
||||
</Link>
|
||||
</div>
|
||||
<p className="mt-6 text-sm text-muted-foreground sm:mt-0">
|
||||
Copyright © {new Date().getFullYear()} Dokploy. All rights
|
||||
reserved.
|
||||
{t("footer.copyright", {
|
||||
year: new Date().getFullYear(),
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</Container>
|
||||
|
@ -1,10 +1,17 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
Select,
|
||||
SelectContent,
|
||||
SelectItem,
|
||||
SelectTrigger,
|
||||
} from "@/components/ui/select";
|
||||
import { Link, useRouter } from "@/i18n/routing";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { Popover, Transition } from "@headlessui/react";
|
||||
import { HeartIcon } from "lucide-react";
|
||||
import Link from "next/link";
|
||||
import { Fragment } from "react";
|
||||
import { useLocale, useTranslations } from "next-intl";
|
||||
import { Fragment, type JSX, type SVGProps } from "react";
|
||||
import { Container } from "./Container";
|
||||
import { NavLink } from "./NavLink";
|
||||
import { trackGAEvent } from "./analitycs";
|
||||
@ -63,7 +70,27 @@ function MobileNavIcon({ open }: { open: boolean }) {
|
||||
);
|
||||
}
|
||||
|
||||
const I18nIcon = (props: JSX.IntrinsicAttributes & SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width={24}
|
||||
height={24}
|
||||
fill="currentColor"
|
||||
stroke="currentColor"
|
||||
strokeWidth={0}
|
||||
viewBox="0 0 512 512"
|
||||
{...props}
|
||||
>
|
||||
<path
|
||||
stroke="none"
|
||||
d="m478.33 433.6-90-218a22 22 0 0 0-40.67 0l-90 218a22 22 0 1 0 40.67 16.79L316.66 406h102.67l18.33 44.39A22 22 0 0 0 458 464a22 22 0 0 0 20.32-30.4zM334.83 362 368 281.65 401.17 362zm-66.99-19.08a22 22 0 0 0-4.89-30.7c-.2-.15-15-11.13-36.49-34.73 39.65-53.68 62.11-114.75 71.27-143.49H330a22 22 0 0 0 0-44H214V70a22 22 0 0 0-44 0v20H54a22 22 0 0 0 0 44h197.25c-9.52 26.95-27.05 69.5-53.79 108.36-31.41-41.68-43.08-68.65-43.17-68.87a22 22 0 0 0-40.58 17c.58 1.38 14.55 34.23 52.86 83.93.92 1.19 1.83 2.35 2.74 3.51-39.24 44.35-77.74 71.86-93.85 80.74a22 22 0 1 0 21.07 38.63c2.16-1.18 48.6-26.89 101.63-85.59 22.52 24.08 38 35.44 38.93 36.1a22 22 0 0 0 30.75-4.9z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
|
||||
function MobileNavigation() {
|
||||
const t = useTranslations("HomePage");
|
||||
const linkT = useTranslations("Link");
|
||||
return (
|
||||
<Popover>
|
||||
<Popover.Button
|
||||
@ -96,16 +123,15 @@ function MobileNavigation() {
|
||||
>
|
||||
<Popover.Panel
|
||||
as="div"
|
||||
className="absolute inset-x-0 top-full mt-4 flex origin-top flex-col rounded-2xl bg-background border border-border p-4 text-lg tracking-tight text-primary shadow-xl ring-1 ring-border/5"
|
||||
className="absolute inset-x-0 top-full mt-4 flex origin-top flex-col rounded-2xl border border-border bg-background p-4 text-lg tracking-tight text-primary shadow-xl ring-1 ring-border/5"
|
||||
>
|
||||
<MobileNavLink href="/#features">Features</MobileNavLink>
|
||||
<MobileNavLink href="/#testimonials">Testimonials</MobileNavLink>
|
||||
<MobileNavLink href="/#faqs">Faqs</MobileNavLink>
|
||||
<MobileNavLink
|
||||
href="https://docs.dokploy.com/get-started/introduction"
|
||||
target="_blank"
|
||||
>
|
||||
Docs
|
||||
<MobileNavLink href="/#features">
|
||||
{t("navigation.features")}
|
||||
</MobileNavLink>
|
||||
{/* <MobileNavLink href="/#testimonials">Testimonials</MobileNavLink> */}
|
||||
<MobileNavLink href="/#faqs">{t("navigation.faqs")}</MobileNavLink>
|
||||
<MobileNavLink href={linkT("docs.intro")} target="_blank">
|
||||
{t("navigation.docs")}
|
||||
</MobileNavLink>
|
||||
</Popover.Panel>
|
||||
</Transition.Child>
|
||||
@ -115,27 +141,54 @@ function MobileNavigation() {
|
||||
}
|
||||
|
||||
export function Header() {
|
||||
const router = useRouter();
|
||||
const locale = useLocale();
|
||||
const t = useTranslations("HomePage");
|
||||
const linkT = useTranslations("Link");
|
||||
|
||||
return (
|
||||
<header className="py-10 bg-background">
|
||||
<header className="bg-background py-10">
|
||||
<Container>
|
||||
<nav className="relative z-50 flex justify-between">
|
||||
<div className="flex items-center md:gap-x-12">
|
||||
<Link href="#" aria-label="Home">
|
||||
<Link href="/" aria-label="Home">
|
||||
<Logo className="h-10 w-auto" />
|
||||
</Link>
|
||||
<div className="hidden md:flex md:gap-x-6">
|
||||
<NavLink href="/#features">Features</NavLink>
|
||||
<NavLink href="/#features">{t("navigation.features")}</NavLink>
|
||||
{/* <NavLink href="/#testimonials">Testimonials</NavLink> */}
|
||||
<NavLink href="/#faqs">Faqs</NavLink>
|
||||
<NavLink
|
||||
href="https://docs.dokploy.com/get-started/introduction"
|
||||
target="_blank"
|
||||
>
|
||||
Docs
|
||||
<NavLink href="/#faqs">{t("navigation.faqs")}</NavLink>
|
||||
<NavLink href={linkT("docs.intro")} target="_blank">
|
||||
{t("navigation.docs")}
|
||||
</NavLink>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-x-2 md:gap-x-5">
|
||||
<Select
|
||||
onValueChange={(locale) => {
|
||||
router.replace("/", {
|
||||
locale: locale as "en" | "zh-Hans",
|
||||
});
|
||||
}}
|
||||
value={locale}
|
||||
>
|
||||
<SelectTrigger
|
||||
className={buttonVariants({
|
||||
variant: "outline",
|
||||
className:
|
||||
" flex items-center gap-2 !rounded-full visited:outline-none focus-within:outline-none focus:outline-none",
|
||||
})}
|
||||
>
|
||||
<I18nIcon width={20} height={20} />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="en">{t("navigation.i18nEn")}</SelectItem>
|
||||
<SelectItem value="zh-Hans">
|
||||
{t("navigation.i18nZh-Hans")}
|
||||
</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
|
||||
<Link
|
||||
className={buttonVariants({
|
||||
variant: "outline",
|
||||
@ -144,10 +197,11 @@ export function Header() {
|
||||
href="https://opencollective.com/dokploy"
|
||||
target="_blank"
|
||||
>
|
||||
<span className="text-sm font-semibold">Support </span>
|
||||
<HeartIcon className="size-4 text-red-500 fill-red-600 animate-heartbeat " />
|
||||
<span className="text-sm font-semibold">
|
||||
{t("navigation.support")}{" "}
|
||||
</span>
|
||||
<HeartIcon className="animate-heartbeat size-4 fill-red-600 text-red-500 " />
|
||||
</Link>
|
||||
{/* @ts-expect-error */}
|
||||
<Button
|
||||
className="rounded-full bg-[#5965F2] hover:bg-[#4A55E0]"
|
||||
asChild
|
||||
@ -156,7 +210,7 @@ export function Header() {
|
||||
href="https://discord.gg/2tBnJ3jDJc"
|
||||
aria-label="Dokploy on GitHub"
|
||||
target="_blank"
|
||||
className="flex flex-row gap-2 items-center text-white"
|
||||
className="flex flex-row items-center gap-2 text-white"
|
||||
>
|
||||
<svg
|
||||
role="img"
|
||||
@ -166,7 +220,7 @@ export function Header() {
|
||||
>
|
||||
<path d="M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z" />
|
||||
</svg>
|
||||
Discord
|
||||
{t("navigation.discord")}
|
||||
</Link>
|
||||
</Button>
|
||||
<div className="-mr-1 md:hidden">
|
||||
|
@ -1,5 +1,6 @@
|
||||
"use client";
|
||||
import { Check, Copy } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import Link from "next/link";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Container } from "./Container";
|
||||
@ -38,6 +39,7 @@ const ProductHunt = () => {
|
||||
};
|
||||
|
||||
export function Hero() {
|
||||
const t = useTranslations("HomePage");
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
@ -47,7 +49,7 @@ export function Hero() {
|
||||
return () => clearTimeout(timer);
|
||||
}, [isCopied]);
|
||||
return (
|
||||
<Container className="pb-16 pt-20 text-center lg:pt-32 bg-black">
|
||||
<Container className="bg-black pb-16 pt-20 text-center lg:pt-32">
|
||||
<div className="absolute inset-0">
|
||||
<svg viewBox="0 0 2000 1000" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="b" x="0" y="0" width="2000" height="1000">
|
||||
@ -68,7 +70,7 @@ export function Hero() {
|
||||
|
||||
<div className="relative">
|
||||
<h1 className="mx-auto max-w-4xl font-display text-5xl font-medium tracking-tight text-muted-foreground sm:text-7xl">
|
||||
Deploy{" "}
|
||||
{t("hero.deploy")}{" "}
|
||||
<span className="relative whitespace-nowrap text-primary">
|
||||
<svg
|
||||
aria-hidden="true"
|
||||
@ -78,18 +80,16 @@ export function Hero() {
|
||||
>
|
||||
<path d="M203.371.916c-26.013-2.078-76.686 1.963-124.73 9.946L67.3 12.749C35.421 18.062 18.2 21.766 6.004 25.934 1.244 27.561.828 27.778.874 28.61c.07 1.214.828 1.121 9.595-1.176 9.072-2.377 17.15-3.92 39.246-7.496C123.565 7.986 157.869 4.492 195.942 5.046c7.461.108 19.25 1.696 19.17 2.582-.107 1.183-7.874 4.31-25.75 10.366-21.992 7.45-35.43 12.534-36.701 13.884-2.173 2.308-.202 4.407 4.442 4.734 2.654.187 3.263.157 15.593-.78 35.401-2.686 57.944-3.488 88.365-3.143 46.327.526 75.721 2.23 130.788 7.584 19.787 1.924 20.814 1.98 24.557 1.332l.066-.011c1.201-.203 1.53-1.825.399-2.335-2.911-1.31-4.893-1.604-22.048-3.261-57.509-5.556-87.871-7.36-132.059-7.842-23.239-.254-33.617-.116-50.627.674-11.629.54-42.371 2.494-46.696 2.967-2.359.259 8.133-3.625 26.504-9.81 23.239-7.825 27.934-10.149 28.304-14.005.417-4.348-3.529-6-16.878-7.066Z" />
|
||||
</svg>
|
||||
<span className="relative"> Anywhere</span>
|
||||
<span className="relative"> {t("hero.anywhere")}</span>
|
||||
</span>{" "}
|
||||
with Total Freedom and Ease.
|
||||
{t("hero.with")}
|
||||
</h1>
|
||||
<p className="mx-auto mt-6 max-w-2xl text-lg tracking-tight text-muted-foreground">
|
||||
Streamline your operations with our all-in-one platform—perfect for
|
||||
managing projects, data, and system health with simplicity and
|
||||
efficiency.
|
||||
{t("hero.des")}
|
||||
</p>
|
||||
<div className="flex flex-col gap-6 md:gap-10">
|
||||
<div className="mt-10 flex justify-center gap-6 items-center flex-wrap md:flex-nowrap">
|
||||
<code className="font-sans border p-3 rounded-xl flex flex-row gap-4 items-center">
|
||||
<div className="mt-10 flex flex-wrap items-center justify-center gap-6 md:flex-nowrap">
|
||||
<code className="flex flex-row items-center gap-4 rounded-xl border p-3 font-sans">
|
||||
curl -sSL https://dokploy.com/install.sh | sh
|
||||
<button
|
||||
type="button"
|
||||
@ -125,13 +125,12 @@ export function Hero() {
|
||||
Discord
|
||||
</Link>
|
||||
</Button> */}
|
||||
{/* @ts-expect-error */}
|
||||
<Button className="rounded-xl" asChild>
|
||||
<Link
|
||||
href="https://github.com/dokploy/dokploy"
|
||||
aria-label="Dokploy on GitHub"
|
||||
target="_blank"
|
||||
className="flex flex-row gap-2 items-center"
|
||||
className="flex flex-row items-center gap-2"
|
||||
>
|
||||
<svg aria-hidden="true" className="h-6 w-6 fill-black">
|
||||
<path d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0 1 12 6.844a9.59 9.59 0 0 1 2.504.337c1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.02 10.02 0 0 0 22 12.017C22 6.484 17.522 2 12 2Z" />
|
||||
@ -141,7 +140,7 @@ export function Hero() {
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="mt-16 flex justify-center gap-x-8 flex-row sm:gap-x-0 sm:gap-y-10 xl:gap-x-12 xl:gap-y-0 rounded-lg">
|
||||
<div className="mt-16 flex flex-row justify-center gap-x-8 rounded-lg sm:gap-x-0 sm:gap-y-10 xl:gap-x-12 xl:gap-y-0">
|
||||
<iframe
|
||||
width="560"
|
||||
height="315"
|
||||
@ -154,8 +153,10 @@ export function Hero() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="mt-16 ">
|
||||
<p className="font-display text-base text-primary">Featured in</p>
|
||||
<div className="mt-16">
|
||||
<p className="font-display text-base text-primary">
|
||||
{t("hero.featuredIn")}
|
||||
</p>
|
||||
<ul className="mt-8 flex items-center justify-center gap-x-8 sm:flex-col sm:gap-x-0 sm:gap-y-10 xl:flex-row xl:gap-x-12 xl:gap-y-0">
|
||||
{[
|
||||
[
|
||||
|
@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import Link from "next/link";
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { trackGAEvent } from "./analitycs";
|
||||
|
||||
export function NavLink({
|
||||
|
@ -5,48 +5,44 @@ import { AnimatePresence, motion } from "framer-motion";
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Container } from "./Container";
|
||||
|
||||
const features = [
|
||||
{
|
||||
title: "Projects",
|
||||
description:
|
||||
"Manage and organize all your projects in one place, keeping detailed track of progress and resource allocation.",
|
||||
title: "primaryFeatures.projects",
|
||||
description: "primaryFeatures.projectsDes",
|
||||
image: "/primary/projects.png",
|
||||
},
|
||||
{
|
||||
title: "Applications & Databases",
|
||||
description:
|
||||
"Centralize control over your applications and databases for enhanced security and efficiency, simplifying access and management across your infrastructure.",
|
||||
title: "primaryFeatures.applications",
|
||||
description: "primaryFeatures.applicationsDes",
|
||||
image: "/primary/applications.png",
|
||||
},
|
||||
{
|
||||
title: "Docker Compose",
|
||||
description:
|
||||
"Native Docker Compose support for manage complex applications and services with ease.",
|
||||
title: "primaryFeatures.compose",
|
||||
description: "primaryFeatures.composeDes",
|
||||
image: "/primary/compose.png",
|
||||
},
|
||||
{
|
||||
title: "Multi Node",
|
||||
description:
|
||||
"Scale applications to multiples nodes using docker swarm to manage the cluster.",
|
||||
title: "primaryFeatures.multinode",
|
||||
description: "primaryFeatures.multinodeDes",
|
||||
image: "/primary/multinode.png",
|
||||
},
|
||||
{
|
||||
title: "Monitoring",
|
||||
description:
|
||||
"Monitor your systems' performance and health in real time, ensuring continuous and uninterrupted operation.",
|
||||
title: "primaryFeatures.monitoring",
|
||||
description: "primaryFeatures.monitoringDes",
|
||||
image: "/primary/monitoring.png",
|
||||
},
|
||||
{
|
||||
title: "Backups",
|
||||
description:
|
||||
"Implement automatic and secure backup solutions to protect your critical data and restore it quickly when necessary.",
|
||||
title: "primaryFeatures.backups",
|
||||
description: "primaryFeatures.backupsDes",
|
||||
image: "/primary/backups.png",
|
||||
},
|
||||
];
|
||||
|
||||
export function PrimaryFeatures() {
|
||||
const t = useTranslations("HomePage");
|
||||
const [tabOrientation, setTabOrientation] = useState<
|
||||
"horizontal" | "vertical"
|
||||
>("horizontal");
|
||||
@ -92,11 +88,10 @@ export function PrimaryFeatures() {
|
||||
<Container className="relative">
|
||||
<div className="max-w-2xl md:mx-auto md:text-center xl:max-w-none">
|
||||
<h2 className="font-display text-3xl tracking-tight text-white sm:text-4xl md:text-5xl">
|
||||
Comprehensive Control for Your Digital Ecosystem
|
||||
{t("primaryFeatures.title")}
|
||||
</h2>
|
||||
<p className="mt-6 text-lg tracking-tight text-muted-foreground">
|
||||
Simplify your project and data management, ensure robust monitoring,
|
||||
and secure your backups—all without the fuss over minute details.
|
||||
{t("primaryFeatures.des")}
|
||||
</p>
|
||||
</div>
|
||||
<Tab.Group
|
||||
@ -118,14 +113,14 @@ export function PrimaryFeatures() {
|
||||
initial={false}
|
||||
key={`feature-${featureIndex}`}
|
||||
className={cn(
|
||||
"group relative rounded-full px-4 py-1 lg:rounded-l-xl lg:rounded-r-none lg:p-6 transition-colors ",
|
||||
"group relative rounded-full px-4 py-1 transition-colors lg:rounded-l-xl lg:rounded-r-none lg:p-6 ",
|
||||
)}
|
||||
>
|
||||
<AnimatePresence>
|
||||
{selectedIndex === featureIndex && (
|
||||
<motion.span
|
||||
layoutId="tab"
|
||||
className="absolute inset-0 z-10 bg-white/5 rounded-full mix-blend-difference lg:rounded-l-xl lg:rounded-r-none"
|
||||
className="absolute inset-0 z-10 rounded-full bg-white/5 mix-blend-difference lg:rounded-l-xl lg:rounded-r-none"
|
||||
initial={{ opacity: 1 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
@ -140,19 +135,19 @@ export function PrimaryFeatures() {
|
||||
<h3>
|
||||
<Tab
|
||||
className={cn(
|
||||
"font-display text-lg ui-not-focus-visible:outline-none text-primary",
|
||||
"font-display text-lg text-primary ui-not-focus-visible:outline-none",
|
||||
)}
|
||||
>
|
||||
<span className="absolute inset-0 rounded-full lg:rounded-l-xl lg:rounded-r-none" />
|
||||
{feature.title}
|
||||
{t(feature.title)}
|
||||
</Tab>
|
||||
</h3>
|
||||
<p
|
||||
className={cn(
|
||||
"mt-2 hidden text-sm lg:block text-muted-foreground",
|
||||
"mt-2 hidden text-sm text-muted-foreground lg:block",
|
||||
)}
|
||||
>
|
||||
{feature.description}
|
||||
{t(feature.description)}
|
||||
</p>
|
||||
</motion.div>
|
||||
))}
|
||||
@ -164,7 +159,7 @@ export function PrimaryFeatures() {
|
||||
<div className="relative sm:px-6 lg:hidden">
|
||||
<div className="absolute -inset-x-4 bottom-[-4.25rem] top-[-6.5rem] bg-white/10 ring-1 ring-inset ring-white/10 sm:inset-x-0 sm:rounded-t-xl" />
|
||||
<p className="relative mx-auto max-w-2xl text-base text-white sm:text-center">
|
||||
{feature.description}
|
||||
{t(feature.description)}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -178,7 +173,7 @@ export function PrimaryFeatures() {
|
||||
bounce: 0.2,
|
||||
duration: 0.6,
|
||||
}}
|
||||
className="mt-10 h-[24rem] lg:h-[40rem] w-[45rem] overflow-hidden rounded-xl shadow-xl border sm:w-auto lg:mt-0 lg:w-[67.8125rem]"
|
||||
className="mt-10 h-[24rem] w-[45rem] overflow-hidden rounded-xl border shadow-xl sm:w-auto lg:mt-0 lg:h-[40rem] lg:w-[67.8125rem]"
|
||||
>
|
||||
<img
|
||||
alt=""
|
||||
|
@ -4,6 +4,7 @@ import { cn } from "@/lib/utils";
|
||||
import { Tab } from "@headlessui/react";
|
||||
import { motion } from "framer-motion";
|
||||
import { Layers, Terminal, Users } from "lucide-react";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Container } from "./Container";
|
||||
interface Feature {
|
||||
name: React.ReactNode;
|
||||
@ -15,10 +16,9 @@ interface Feature {
|
||||
|
||||
const features: Array<Feature> = [
|
||||
{
|
||||
name: "Open Source Templates",
|
||||
summary: "One click to deploy open source templates.",
|
||||
description:
|
||||
"Deploy open source templates with one click, powered by Docker Compose, (Plausible, Calcom, Pocketbase, etc.)",
|
||||
name: "secondaryFeatures.templates",
|
||||
summary: "secondaryFeatures.templatesSummary",
|
||||
description: "secondaryFeatures.templatesDes",
|
||||
image: "/secondary/templates.png",
|
||||
icon: function ReportingIcon() {
|
||||
return (
|
||||
@ -29,11 +29,9 @@ const features: Array<Feature> = [
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Real-Time Traefik Configuration",
|
||||
summary:
|
||||
" Modify Traefik settings on-the-fly via a graphical interface or API.",
|
||||
description:
|
||||
"Users can adjust Traefik's configuration, including middleware, forwarding rules, and SSL certificates through an intuitive interface or API. This feature enables seamless traffic routing and security adjustments without the need to restart services",
|
||||
name: "secondaryFeatures.traefik",
|
||||
summary: "secondaryFeatures.traefikSummary",
|
||||
description: "secondaryFeatures.traefikDes",
|
||||
image: "/secondary/traefik.png",
|
||||
icon: function ReportingIcon() {
|
||||
return (
|
||||
@ -217,11 +215,9 @@ const features: Array<Feature> = [
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "User Permission Management",
|
||||
summary:
|
||||
"Detailed control over user permissions for accessing and managing projects and services.",
|
||||
description:
|
||||
"Allows administrators to define specific roles and permissions for each user, including the ability to create, modify, or delete applications and databases. This feature ensures secure and efficient management of large and diverse teams.",
|
||||
name: "secondaryFeatures.users",
|
||||
summary: "secondaryFeatures.usersSummary",
|
||||
description: "secondaryFeatures.usersDes",
|
||||
image: "/secondary/users.png",
|
||||
icon: function InventoryIcon() {
|
||||
return (
|
||||
@ -232,11 +228,9 @@ const features: Array<Feature> = [
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Terminal Access",
|
||||
summary:
|
||||
"Direct access to each container's and server terminal for advanced management.",
|
||||
description:
|
||||
"Provides an interface to access the command line of any active container, allowing developers to execute commands, manage services, and troubleshoot directly from the dashboard",
|
||||
name: "secondaryFeatures.terminal",
|
||||
summary: "secondaryFeatures.terminalSummary",
|
||||
description: "secondaryFeatures.terminalDes",
|
||||
image: "/secondary/terminal.png",
|
||||
icon: function ContactsIcon() {
|
||||
return (
|
||||
@ -257,18 +251,19 @@ function Feature({
|
||||
feature: Feature;
|
||||
isActive: boolean;
|
||||
}) {
|
||||
const t = useTranslations("HomePage");
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
className,
|
||||
!isActive ? "opacity-75 hover:opacity-100 " : "rounded-xl",
|
||||
" p-4 relative",
|
||||
" relative p-4",
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"size-9 rounded-lg flex items-center justify-center",
|
||||
"flex size-9 items-center justify-center rounded-lg",
|
||||
isActive ? "bg-border" : "bg-muted",
|
||||
)}
|
||||
>
|
||||
@ -277,7 +272,7 @@ function Feature({
|
||||
{isActive && (
|
||||
<motion.span
|
||||
layoutId="bubble"
|
||||
className="absolute inset-0 z-10 bg-white/5 mix-blend-difference rounded-xl"
|
||||
className="absolute inset-0 z-10 rounded-xl bg-white/5 mix-blend-difference"
|
||||
transition={{
|
||||
type: "spring",
|
||||
bounce: 0.2,
|
||||
@ -294,10 +289,10 @@ function Feature({
|
||||
{feature.name}
|
||||
</h3>
|
||||
<p className="mt-2 font-display text-xl text-foreground">
|
||||
{feature.summary}
|
||||
{t(feature.summary)}
|
||||
</p>
|
||||
<p className="mt-4 text-sm text-muted-foreground">
|
||||
{feature.description}
|
||||
{t(feature.description)}
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
@ -327,6 +322,7 @@ function FeaturesMobile() {
|
||||
}
|
||||
|
||||
function FeaturesDesktop() {
|
||||
const t = useTranslations("HomePage");
|
||||
return (
|
||||
<Tab.Group as="div" className="hidden lg:mt-20 lg:block">
|
||||
{({ selectedIndex }) => (
|
||||
@ -340,7 +336,7 @@ function FeaturesDesktop() {
|
||||
name: (
|
||||
<Tab className="ui-not-focus-visible:outline-none">
|
||||
<span className="absolute inset-0" />
|
||||
{feature.name}
|
||||
{t(feature.name)}
|
||||
</Tab>
|
||||
),
|
||||
}}
|
||||
@ -359,7 +355,9 @@ function FeaturesDesktop() {
|
||||
"px-5 transition duration-500 ease-in-out ui-not-focus-visible:outline-none",
|
||||
featureIndex !== selectedIndex && "opacity-60",
|
||||
)}
|
||||
style={{ transform: `translateX(-${selectedIndex * 100}%)` }}
|
||||
style={{
|
||||
transform: `translateX(-${selectedIndex * 100}%)`,
|
||||
}}
|
||||
aria-hidden={featureIndex !== selectedIndex}
|
||||
>
|
||||
<div className="w-[52.75rem] overflow-hidden rounded-xl bg-red-500 shadow-lg shadow-slate-900/5 ring-1 ring-slate-500/10">
|
||||
@ -382,21 +380,20 @@ function FeaturesDesktop() {
|
||||
}
|
||||
|
||||
export function SecondaryFeatures() {
|
||||
const t = useTranslations("HomePage");
|
||||
return (
|
||||
<section
|
||||
id="secondary-features"
|
||||
aria-label="Features for simplifying everyday business tasks"
|
||||
className="pb-14 pt-20 sm:pb-20 sm:pt-32 lg:pb-32 bg-black"
|
||||
className="bg-black pb-14 pt-20 sm:pb-20 sm:pt-32 lg:pb-32"
|
||||
>
|
||||
<Container className="max-w-[95rem]">
|
||||
<div className="mx-auto max-w-2xl md:text-center">
|
||||
<h2 className="font-display text-3xl tracking-tight text-primary sm:text-4xl">
|
||||
Advanced Management Tools
|
||||
{t("secondaryFeatures.title")}
|
||||
</h2>
|
||||
<p className="mt-4 text-lg tracking-tight text-muted-foreground">
|
||||
Elevate your infrastructure with tools that offer precise control,
|
||||
detailed monitoring, and enhanced security, ensuring seamless
|
||||
management and robust performance.
|
||||
{t("secondaryFeatures.des")}
|
||||
</p>
|
||||
</div>
|
||||
<FeaturesMobile />
|
||||
|
@ -1,25 +1,24 @@
|
||||
import Link from "next/link";
|
||||
import { Link } from "@/i18n/routing";
|
||||
import { useTranslations } from "next-intl";
|
||||
import { Footer } from "./Footer";
|
||||
import { Header } from "./Header";
|
||||
|
||||
export function SlimLayout({ children }: { children: React.ReactNode }) {
|
||||
export function SlimLayout() {
|
||||
const t = useTranslations("404");
|
||||
return (
|
||||
<>
|
||||
<div>
|
||||
<Header />
|
||||
</div>
|
||||
<main className="text-center flex-auto items-center flex justify-center">
|
||||
<main className="flex flex-auto items-center justify-center text-center">
|
||||
<div>
|
||||
<h1 className="mb-4 text-6xl font-semibold text-primary">404</h1>
|
||||
<p className="mb-4 text-lg text-muted-foreground">
|
||||
Oops! Looks like you're lost.
|
||||
</p>
|
||||
<p className="mb-4 text-lg text-muted-foreground">{t("title")}</p>
|
||||
<p className="mt-4 text-muted-foreground">
|
||||
Let's get you back{" "}
|
||||
{t("des")}{" "}
|
||||
<Link href="/" className="text-primary">
|
||||
home
|
||||
{t("action")}
|
||||
</Link>
|
||||
.
|
||||
</p>
|
||||
</div>
|
||||
</main>
|
||||
|
@ -46,7 +46,6 @@ const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||
const Comp = asChild ? Slot : "button";
|
||||
return (
|
||||
// @ts-expect-error
|
||||
<Comp
|
||||
className={cn(buttonVariants({ variant, size, className }))}
|
||||
ref={ref}
|
||||
|
160
apps/website/components/ui/select.tsx
Normal file
160
apps/website/components/ui/select.tsx
Normal file
@ -0,0 +1,160 @@
|
||||
"use client";
|
||||
|
||||
import * as SelectPrimitive from "@radix-ui/react-select";
|
||||
import { Check, ChevronDown, ChevronUp } from "lucide-react";
|
||||
import * as React from "react";
|
||||
|
||||
import { cn } from "@/lib/utils";
|
||||
|
||||
const Select = SelectPrimitive.Root;
|
||||
|
||||
const SelectGroup = SelectPrimitive.Group;
|
||||
|
||||
const SelectValue = SelectPrimitive.Value;
|
||||
|
||||
const SelectTrigger = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Trigger
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<SelectPrimitive.Icon asChild>
|
||||
<ChevronDown className="h-4 w-4 opacity-50" />
|
||||
</SelectPrimitive.Icon>
|
||||
</SelectPrimitive.Trigger>
|
||||
));
|
||||
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
||||
|
||||
const SelectScrollUpButton = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.ScrollUpButton
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronUp className="h-4 w-4" />
|
||||
</SelectPrimitive.ScrollUpButton>
|
||||
));
|
||||
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
||||
|
||||
const SelectScrollDownButton = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.ScrollDownButton
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"flex cursor-default items-center justify-center py-1",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<ChevronDown className="h-4 w-4" />
|
||||
</SelectPrimitive.ScrollDownButton>
|
||||
));
|
||||
SelectScrollDownButton.displayName =
|
||||
SelectPrimitive.ScrollDownButton.displayName;
|
||||
|
||||
const SelectContent = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||
>(({ className, children, position = "popper", ...props }, ref) => (
|
||||
<SelectPrimitive.Portal>
|
||||
<SelectPrimitive.Content
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border bg-popover text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
|
||||
position === "popper" &&
|
||||
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||
className,
|
||||
)}
|
||||
position={position}
|
||||
{...props}
|
||||
>
|
||||
<SelectScrollUpButton />
|
||||
<SelectPrimitive.Viewport
|
||||
className={cn(
|
||||
"p-1",
|
||||
position === "popper" &&
|
||||
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
|
||||
)}
|
||||
>
|
||||
{children}
|
||||
</SelectPrimitive.Viewport>
|
||||
<SelectScrollDownButton />
|
||||
</SelectPrimitive.Content>
|
||||
</SelectPrimitive.Portal>
|
||||
));
|
||||
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
||||
|
||||
const SelectLabel = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Label
|
||||
ref={ref}
|
||||
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
||||
|
||||
const SelectItem = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
||||
>(({ className, children, ...props }, ref) => (
|
||||
<SelectPrimitive.Item
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||
<SelectPrimitive.ItemIndicator>
|
||||
<Check className="h-4 w-4" />
|
||||
</SelectPrimitive.ItemIndicator>
|
||||
</span>
|
||||
|
||||
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||
</SelectPrimitive.Item>
|
||||
));
|
||||
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
||||
|
||||
const SelectSeparator = React.forwardRef<
|
||||
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
||||
>(({ className, ...props }, ref) => (
|
||||
<SelectPrimitive.Separator
|
||||
ref={ref}
|
||||
className={cn("-mx-1 my-1 h-px bg-muted", className)}
|
||||
{...props}
|
||||
/>
|
||||
));
|
||||
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
||||
|
||||
export {
|
||||
Select,
|
||||
SelectGroup,
|
||||
SelectValue,
|
||||
SelectTrigger,
|
||||
SelectContent,
|
||||
SelectLabel,
|
||||
SelectItem,
|
||||
SelectSeparator,
|
||||
SelectScrollUpButton,
|
||||
SelectScrollDownButton,
|
||||
};
|
12
apps/website/i18n/request.tsx
Normal file
12
apps/website/i18n/request.tsx
Normal file
@ -0,0 +1,12 @@
|
||||
import { getRequestConfig } from "next-intl/server";
|
||||
import { notFound } from "next/navigation";
|
||||
import { routing } from "./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(`../locales/${locale}.json`)).default,
|
||||
};
|
||||
});
|
16
apps/website/i18n/routing.ts
Normal file
16
apps/website/i18n/routing.ts
Normal file
@ -0,0 +1,16 @@
|
||||
import { createSharedPathnamesNavigation } from "next-intl/navigation";
|
||||
import { defineRouting } from "next-intl/routing";
|
||||
|
||||
export const routing = defineRouting({
|
||||
// A list of all locales that are supported
|
||||
locales: ["en", "zh-Hans"],
|
||||
|
||||
// Used when no locale matches
|
||||
defaultLocale: "en",
|
||||
localePrefix: "as-needed",
|
||||
});
|
||||
|
||||
// Lightweight wrappers around Next.js' navigation APIs
|
||||
// that will consider the routing configuration
|
||||
export const { Link, redirect, usePathname, useRouter } =
|
||||
createSharedPathnamesNavigation(routing);
|
96
apps/website/locales/en.json
Normal file
96
apps/website/locales/en.json
Normal file
@ -0,0 +1,96 @@
|
||||
{
|
||||
"HomePage": {
|
||||
"navigation": {
|
||||
"features": "Features",
|
||||
"faqs": "FAQ",
|
||||
"docs": "Docs",
|
||||
"support": "Support",
|
||||
"discord": "Discord",
|
||||
"i18nButtonPlaceholder": "Language",
|
||||
"i18nEn": "English",
|
||||
"i18nZh-Hans": "简体中文"
|
||||
},
|
||||
"hero": {
|
||||
"deploy": "Deploy",
|
||||
"anywhere": "Anywhere",
|
||||
"with": "with Total Freedom and Ease.",
|
||||
"des": "Streamline your operations with our all-in-one platform—perfect for managing projects, data, and system health with simplicity and efficiency.",
|
||||
"featuredIn": "Featured in"
|
||||
},
|
||||
"primaryFeatures": {
|
||||
"title": "Comprehensive Control for Your Digital Ecosystem",
|
||||
"des": "Simplify your project and data management, ensure robust monitoring, and secure your backups—all without the fuss over minute details.",
|
||||
"projects": "Projects",
|
||||
"projectsDes": "Manage and organize all your projects in one place, keeping detailed track of progress and resource allocation.",
|
||||
"applications": "Applications & Databases",
|
||||
"applicationsDes": "Centralize control over your applications and databases for enhanced security and efficiency, simplifying access and management across your infrastructure.",
|
||||
"compose": "compose",
|
||||
"composeDes": "Native Docker Compose support for manage complex applications and services with ease.",
|
||||
"multinode": "multinode",
|
||||
"multinodeDes": "Scale applications to multiples nodes using docker swarm to manage the cluster.",
|
||||
"monitoring": "Monitoring",
|
||||
"monitoringDes": "Monitor your systems' performance and health in real time, ensuring continuous and uninterrupted operation.",
|
||||
"backups": "Backups",
|
||||
"backupsDes": "Implement automatic and secure backup solutions to protect your critical data and restore it quickly when necessary."
|
||||
},
|
||||
"secondaryFeatures": {
|
||||
"title": "Advanced Management Tools",
|
||||
"des": "Elevate your infrastructure with tools that offer precise control, detailed monitoring, and enhanced security, ensuring seamless management and robust performance.",
|
||||
"templates": "Open Source Templates",
|
||||
"templatesSummary": "One click to deploy open source templates.",
|
||||
"templatesDes": "Deploy open source templates with one click, powered by Docker Compose, (Plausible, Calcom, Pocketbase, etc.)",
|
||||
"traefik": "Real-Time Traefik Configuration",
|
||||
"traefikSummary": "Modify Traefik settings on-the-fly via a graphical interface or API.",
|
||||
"traefikDes": "Users can adjust Traefik's configuration, including middleware, forwarding rules, and SSL certificates through an intuitive interface or API. This feature enables seamless traffic routing and security adjustments without the need to restart services",
|
||||
"users": "User Permission Management",
|
||||
"usersSummary": "Detailed control over user permissions for accessing and managing projects and services.",
|
||||
"usersDes": "Allows administrators to define specific roles and permissions for each user, including the ability to create, modify, or delete applications and databases. This feature ensures secure and efficient management of large and diverse teams.",
|
||||
"terminal": "Terminal Access",
|
||||
"terminalSummary": "Direct access to each container's and server terminal for advanced management.",
|
||||
"terminalDes": "Provides an interface to access the command line of any active container, allowing developers to execute commands, manage services, and troubleshoot directly from the dashboard"
|
||||
},
|
||||
"callToAction": {
|
||||
"title": "Unlock Your Deployment Potential",
|
||||
"des": "Streamline your deployments with our PaaS. Effortlessly manage Docker containers and traffic with Traefik. Boost your infrastructure's efficiency and security today",
|
||||
"button": "Get Started Now"
|
||||
},
|
||||
"faq": {
|
||||
"title": "Frequently asked questions",
|
||||
"des": "If you can’t find what you’re looking for, please submit an issue through our GitHub repository or ask questions on our Discord.",
|
||||
"q1": "What is dokploy?",
|
||||
"a1": "Dokploy is a stable, easy-to-use deployment solution designed to simplify the application management process. Think of Dokploy as a free alternative self-hostable solution to platforms like Heroku, Vercel, and Netlify.",
|
||||
"q2": "Why Choose Dokploy?",
|
||||
"a2": "Simplicity, Flexibility, and Fast",
|
||||
"q3": "Is free?",
|
||||
"a3": "Yes, dokploy is totally free. You can use it for personal projects, small teams, or even for large-scale applications.",
|
||||
"q4": "Is it open source?",
|
||||
"a4": "Yes, dokploy is open source and free to use.",
|
||||
"q5": "What types of languages can i deploy with dokploy?",
|
||||
"a5": "Dokploy do not restrict programming languages. you are free to choose your preferred language and framework.",
|
||||
"q6": "How do I request a feature or report a bug?",
|
||||
"a6": "Currently we are working on fixing bug fixes, but we will be releasing new features soon. You can also request features or report bugs.",
|
||||
"q7": "Do you track the usage of Dokploy?",
|
||||
"a7": "No, we don't track any usage data.",
|
||||
"q8": "Are there any user forums or communities where I can interact with other users?",
|
||||
"a8": "Yes, we have active github discussions where you can share ideas, ask for help, and connect with other users.",
|
||||
"q9": "What types of applications can I deploy with Dokploy?",
|
||||
"a9": "Dokploy supports a variety of applications, including those built with Docker, as well as applications from any Git repository, offering custom builds with Nixpacks, Dockerfiles, or Buildpacks like Heroku and Paketo.",
|
||||
"q10": "How does Dokploy handle database management?",
|
||||
"a10": "Dokploy supports multiple database systems including Postgres, MySQL, MariaDB, MongoDB, and Redis, providing tools for easy deployment and management directly from the dashboard."
|
||||
},
|
||||
"footer": {
|
||||
"copyright": "Copyright © {year} Dokploy. All rights reserved."
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"title": "Oops! Looks like you're lost.",
|
||||
"des": "Let's get you back",
|
||||
"action": "home"
|
||||
},
|
||||
"Link": {
|
||||
"docs": {
|
||||
"intro": "https://docs.dokploy.com/get-started/introduction",
|
||||
"install": "https://docs.dokploy.com/en/docs/core/get-started/introduction"
|
||||
}
|
||||
}
|
||||
}
|
96
apps/website/locales/zh-Hans.json
Normal file
96
apps/website/locales/zh-Hans.json
Normal file
@ -0,0 +1,96 @@
|
||||
{
|
||||
"HomePage": {
|
||||
"navigation": {
|
||||
"features": "特性",
|
||||
"faqs": "FAQ",
|
||||
"docs": "文档",
|
||||
"support": "赞助",
|
||||
"discord": "Discord",
|
||||
"i18nButtonPlaceholder": "语言",
|
||||
"i18nEn": "English",
|
||||
"i18nZh-Hans": "简体中文"
|
||||
},
|
||||
"hero": {
|
||||
"deploy": "部署在",
|
||||
"anywhere": "任何设施之上",
|
||||
"with": "",
|
||||
"des": "以前所未有的简洁和高效提供一站式项目、数据的管理以及系统监控。",
|
||||
"featuredIn": "发布于"
|
||||
},
|
||||
"primaryFeatures": {
|
||||
"title": "全面掌控您的基础设施",
|
||||
"des": "Dokploy 不仅简化您的项目部署和数据管理流程,同时还提供完备的数据备份,一切只在弹指间。",
|
||||
"projects": "项目",
|
||||
"projectsDes": "您所有的项目和所需的一切信息都将归集一处。",
|
||||
"applications": "应用和数据库",
|
||||
"applicationsDes": "Dokploy 通过集中管理您的应用和数据库来提升安全性和效率,并大大简化了跨基础设施的访问和管理。",
|
||||
"compose": "Docker Compose",
|
||||
"composeDes": "Dokploy 提供对复杂应用和服务的原生 Docker Compose 支持。",
|
||||
"multinode": "多节点",
|
||||
"multinodeDes": "Dokploy 使用 Docker Swarm 来管理集群,可自动将应用扩容至多个节点。",
|
||||
"monitoring": "监控",
|
||||
"monitoringDes": "随时掌控你的系统性能和应用健康,确保服务无间断运行。",
|
||||
"backups": "备份",
|
||||
"backupsDes": "使用 Dokploy 的自动备份功能来保护您的数据,并在需要时极速还原。"
|
||||
},
|
||||
"secondaryFeatures": {
|
||||
"title": "先进的管理工具",
|
||||
"des": "借助业界优秀的开源工具,Dokploy 不仅可以为您的基础设施提供精准的控制、详尽的监测、一流的安全,还能确保无缝的体验和强悍的性能。",
|
||||
"templates": "开源模板",
|
||||
"templatesSummary": "一键部署热门开源项目",
|
||||
"templatesDes": "基于 Docker Compose 一键部署热门开源自建项目(Plausible 分析, Calcom 日历, Pocketbase, 等等.)",
|
||||
"traefik": "实时 Traefik 配置",
|
||||
"traefikSummary": "通过图形界面或API动态修改 Traefik 配置",
|
||||
"traefikDes": "用户可通过直观的界面或API调整Traefik的配置,包括中间件、转发规则及SSL证书。此功能使得流量路由和安全设置的调整无需重启服务即可无缝进行。",
|
||||
"users": "多用户权限管理",
|
||||
"usersSummary": "对用户访问和管理项目及服务的权限进行细致控制",
|
||||
"usersDes": "允许管理员为每个用户定义特定角色和权限,包括创建、修改或删除应用程序和数据库的能力。借助这一功能,Dokploy 为大型团队进行安全且高效的管理提供了有力支持。",
|
||||
"terminal": "终端访问",
|
||||
"terminalSummary": "从图形界面直接访问容器或者服务的终端",
|
||||
"terminalDes": "DokPloy 内置的界面可以帮助开发者直接访问容器终端并执行任何命令。 "
|
||||
},
|
||||
"callToAction": {
|
||||
"title": "释放自主部署的全部潜力",
|
||||
"des": "借助我们的 PaaS 软件来简化部署流程,使用 Traefik 轻松管理 Docker 容器和流量,立即提高基础设施的效率和安全性。",
|
||||
"button": "现在就部署"
|
||||
},
|
||||
"faq": {
|
||||
"title": "常问问题",
|
||||
"des": "如果您要寻找的问题不在下方,请通过我们的 GitHub 仓库提交 issue 或在我们的 Discord 提出问题。",
|
||||
"q1": "什么是 dokploy?",
|
||||
"a1": "Dokploy 是一个稳定易用的自部署解决方案,设计用于简化项目部署过程。您可以把 DokPloy 想象成一个自部署的 Vercel/Netlify/Heroku 平台。",
|
||||
"q2": "为什么选择 Dokploy?",
|
||||
"a2": "简洁, 灵活, 还有高效!",
|
||||
"q3": "真的免费?",
|
||||
"a3": "是的, dokploy 完全免费。不管是个人使用还是团队使用,都不收取费用。",
|
||||
"q4": "这是开源的吗?",
|
||||
"a4": "Dokploy 的所有代码皆可在我们的托管平台找到。",
|
||||
"q5": "Dokploy 可以支持部署什么编程语言的项目?",
|
||||
"a5": "Dokploy 对编程语言没有限制, 您可以部署任何您喜爱的语言或者框架。",
|
||||
"q6": "想提出新的功能需求或者遇到了问题?",
|
||||
"a6": "您可以前往我们的 Github 打开一个新的 issue,或者在我们的 GitHub讨论区 或 Discord 中发帖。",
|
||||
"q7": "Dokploy是否内置遥测?",
|
||||
"a7": "不,Dokploy不收集任何使用数据。",
|
||||
"q8": "有什么用户社群可以便于我和其他用户交流吗?",
|
||||
"a8": "您可以在我们的GitHub讨论区自由讨论,如果您愿意还可以加入我们的 Discord。",
|
||||
"q9": "dokploy 可以支持部署哪种类型的应用?",
|
||||
"a9": "Dokploy 支持很多应用,不仅包括 Docker 镜像,也可以从 Git Repository 直接启动,DokPloy 提供了丰富的自定义构建工具,如 Nixpacks, Dockerfiles, 抑或是 Buildpacks (就像 Heroku 和 Paketo)",
|
||||
"q10": "Dokploy 支持哪些数据库?",
|
||||
"a10": "Dokploy 支持 Postgres, MySQL, MariaDB, MongoDB, 以及 Redis。我们还提供方便的图形化管理工具以供使用。"
|
||||
},
|
||||
"footer": {
|
||||
"copyright": "版权属于 © {year} Dokploy, 保留所有权利"
|
||||
}
|
||||
},
|
||||
"404": {
|
||||
"title": "糟糕!看起来你迷路了。",
|
||||
"des": "让我们送你",
|
||||
"action": "回家"
|
||||
},
|
||||
"Link": {
|
||||
"docs": {
|
||||
"intro": "https://docs.dokploy.com/cn/docs/core/get-started/introduction",
|
||||
"install": "https://docs.dokploy.com/cn/docs/core/get-started/introduction"
|
||||
}
|
||||
}
|
||||
}
|
9
apps/website/middleware.ts
Normal file
9
apps/website/middleware.ts
Normal file
@ -0,0 +1,9 @@
|
||||
import createMiddleware from "next-intl/middleware";
|
||||
import { routing } from "./i18n/routing";
|
||||
|
||||
export default createMiddleware(routing);
|
||||
|
||||
export const config = {
|
||||
// Match only internationalized pathnames
|
||||
matcher: ["/((?!_next|.*\\..*).*)"],
|
||||
};
|
@ -1,3 +1,7 @@
|
||||
const createNextIntlPlugin = require("next-intl/plugin");
|
||||
|
||||
const withNextIntl = createNextIntlPlugin();
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
eslint: {
|
||||
@ -8,4 +12,4 @@ const nextConfig = {
|
||||
},
|
||||
};
|
||||
|
||||
module.exports = nextConfig;
|
||||
module.exports = withNextIntl(nextConfig);
|
||||
|
4888
apps/website/package-lock.json
generated
4888
apps/website/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@ -14,16 +14,16 @@
|
||||
"dependencies": {
|
||||
"@headlessui/react": "^1.7.17",
|
||||
"@headlessui/tailwindcss": "^0.2.0",
|
||||
"@radix-ui/react-select": "^2.0.0",
|
||||
"@radix-ui/react-slot": "^1.0.2",
|
||||
"@types/node": "20.4.6",
|
||||
"@types/react": "18.2.18",
|
||||
"@types/react-dom": "18.2.7",
|
||||
"autoprefixer": "^10.4.12",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.0",
|
||||
"framer-motion": "^11.0.24",
|
||||
"lucide-react": "0.364.0",
|
||||
"next": "14.2.2",
|
||||
"next-intl": "^3.19.0",
|
||||
"react": "18.2.0",
|
||||
"react-dom": "18.2.0",
|
||||
"react-ga4": "^2.1.0",
|
||||
@ -34,6 +34,8 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@biomejs/biome": "1.7.0",
|
||||
"@types/react": "18.3.5",
|
||||
"@types/react-dom": "18.3.0",
|
||||
"eslint": "8.45.0",
|
||||
"eslint-config-next": "13.4.16",
|
||||
"prettier": "^3.0.1",
|
||||
|
@ -2,5 +2,7 @@
|
||||
module.exports = {
|
||||
singleQuote: true,
|
||||
semi: false,
|
||||
tabWidth: 4,
|
||||
useTabs: true,
|
||||
plugins: ["prettier-plugin-tailwindcss"],
|
||||
};
|
||||
|
@ -45,5 +45,9 @@
|
||||
},
|
||||
"commitlint": {
|
||||
"extends": ["@commitlint/config-conventional"]
|
||||
},
|
||||
"resolutions": {
|
||||
"@types/react": "18.3.5",
|
||||
"@types/react-dom": "18.3.0"
|
||||
}
|
||||
}
|
||||
|
1589
pnpm-lock.yaml
1589
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user