From b51c6a8f5845e083d107973ca58085ab7e0bbd73 Mon Sep 17 00:00:00 2001
From: Mauricio Siu <47042324+Siumauricio@users.noreply.github.com>
Date: Wed, 5 Nov 2025 01:11:41 -0600
Subject: [PATCH] refactor: remove internationalization middleware and related
localization files; update layout and components to use new translation
utility
---
apps/website/app/[locale]/layout.tsx | 88 ------
.../app/{[locale] => }/contact/page.tsx | 78 ++---
apps/website/app/layout.tsx | 24 +-
apps/website/app/{[locale] => }/page.tsx | 5 +-
apps/website/components/CallToAction.tsx | 2 +-
apps/website/components/Faqs.tsx | 2 +-
apps/website/components/Footer.tsx | 57 +---
apps/website/components/Header.tsx | 49 ++--
apps/website/components/Hero.tsx | 2 +-
apps/website/components/NavLink.tsx | 2 +-
apps/website/components/SecondaryFeatures.tsx | 2 +-
apps/website/components/SlimLayout.tsx | 2 +-
apps/website/components/pricing.tsx | 2 +-
.../website/components/secondary-features.tsx | 2 +-
apps/website/components/sponsors.tsx | 2 +-
apps/website/i18n/request.tsx | 15 -
apps/website/i18n/routing.ts | 16 -
apps/website/lib/intl.ts | 22 ++
apps/website/locales/es.json | 272 -----------------
apps/website/locales/fr.json | 266 -----------------
apps/website/locales/zh-Hans.json | 277 ------------------
apps/website/middleware.ts | 14 -
apps/website/next.config.js | 18 +-
23 files changed, 115 insertions(+), 1104 deletions(-)
delete mode 100644 apps/website/app/[locale]/layout.tsx
rename apps/website/app/{[locale] => }/contact/page.tsx (79%)
rename apps/website/app/{[locale] => }/page.tsx (80%)
delete mode 100644 apps/website/i18n/request.tsx
delete mode 100644 apps/website/i18n/routing.ts
create mode 100644 apps/website/lib/intl.ts
delete mode 100644 apps/website/locales/es.json
delete mode 100644 apps/website/locales/fr.json
delete mode 100644 apps/website/locales/zh-Hans.json
delete mode 100644 apps/website/middleware.ts
diff --git a/apps/website/app/[locale]/layout.tsx b/apps/website/app/[locale]/layout.tsx
deleted file mode 100644
index 8a7729e..0000000
--- a/apps/website/app/[locale]/layout.tsx
+++ /dev/null
@@ -1,88 +0,0 @@
-import { Inter, Lexend } from "next/font/google";
-import "@/styles/tailwind.css";
-import "react-photo-view/dist/react-photo-view.css";
-import { Footer } from "@/components/Footer";
-import { Header } from "@/components/Header";
-import type { Metadata } from "next";
-import { setRequestLocale } from "next-intl/server";
-
-export const metadata: Metadata = {
- metadataBase: new URL("https://dokploy.com"),
- title: {
- default: "Dokploy - Effortless Deployment Solutions",
- template: "%s | Simplify Your DevOps",
- },
- icons: {
- icon: "icon.svg",
- apple: "apple-touch-icon.png",
- },
- 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: "https://dokploy.com/og.png",
- },
- {
- url: "https://dokploy.com/icon.svg",
- width: 24,
- height: 24,
- alt: "Dokploy Logo",
- },
- ],
- },
- 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",
- },
-};
-
-export default async function RootLayout({
- children,
- params,
-}: {
- children: React.ReactNode;
- params: { locale: string };
-}) {
- const { locale } = await params;
- setRequestLocale(locale);
- return (
-
-
- {children}
-
-
- );
-}
diff --git a/apps/website/app/[locale]/contact/page.tsx b/apps/website/app/contact/page.tsx
similarity index 79%
rename from apps/website/app/[locale]/contact/page.tsx
rename to apps/website/app/contact/page.tsx
index c15c006..7c2343e 100644
--- a/apps/website/app/[locale]/contact/page.tsx
+++ b/apps/website/app/contact/page.tsx
@@ -1,7 +1,6 @@
"use client";
import { useState } from "react";
-import { useTranslations } from "next-intl";
import { Container } from "@/components/Container";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
@@ -26,7 +25,6 @@ interface ContactFormData {
}
export default function ContactPage() {
- const t = useTranslations("Contact");
const [isSubmitting, setIsSubmitting] = useState(false);
const [isSubmitted, setIsSubmitted] = useState(false);
const [formData, setFormData] = useState({
@@ -43,24 +41,24 @@ export default function ContactPage() {
const newErrors: Record = {};
if (!formData.inquiryType) {
- newErrors.inquiryType = t("errors.inquiryTypeRequired");
+ newErrors.inquiryType = "Please select what we can help you with";
}
if (!formData.firstName.trim()) {
- newErrors.firstName = t("errors.firstNameRequired");
+ newErrors.firstName = "First name is required";
}
if (!formData.lastName.trim()) {
- newErrors.lastName = t("errors.lastNameRequired");
+ newErrors.lastName = "Last name is required";
}
if (!formData.email.trim()) {
- newErrors.email = t("errors.emailRequired");
+ newErrors.email = "Email is required";
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(formData.email)) {
- newErrors.email = t("errors.emailInvalid");
+ newErrors.email = "Please enter a valid email address";
}
if (!formData.company.trim()) {
- newErrors.company = t("errors.companyRequired");
+ newErrors.company = "Company name is required";
}
if (!formData.message.trim()) {
- newErrors.message = t("errors.messageRequired");
+ newErrors.message = "Message is required";
}
setErrors(newErrors);
@@ -86,14 +84,12 @@ export default function ContactPage() {
});
if (response.ok) {
- // Track successful form submission
trackGAEvent({
action: "Contact Form Submitted",
category: "Contact",
label: formData.inquiryType,
});
- // Reset form and show success
setFormData({
inquiryType: "",
firstName: "",
@@ -109,7 +105,7 @@ export default function ContactPage() {
}
} catch (error) {
console.error("Error submitting form:", error);
- alert(t("errorMessage"));
+ alert("There was an error sending your message. Please try again.");
} finally {
setIsSubmitting(false);
}
@@ -117,7 +113,6 @@ export default function ContactPage() {
const handleInputChange = (field: keyof ContactFormData, value: any) => {
setFormData((prev) => ({ ...prev, [field]: value }));
- // Clear error when user starts typing
if (errors[field]) {
setErrors((prev) => {
const newErrors = { ...prev };
@@ -133,14 +128,15 @@ export default function ContactPage() {
- {t("successTitle")}
+ Thank you for contacting us!
- {t("successMessage")}
+ We've received your message and will get back to you as soon as
+ possible.
@@ -167,10 +163,11 @@ export default function ContactPage() {
- {t("title")}
+ Contact Us
- {t("description")}
+ Get in touch with our team. We're here to help with any questions
+ about Dokploy.
@@ -180,7 +177,7 @@ export default function ContactPage() {
htmlFor="inquiryType"
className="block text-sm font-medium text-foreground"
>
- {t("fields.inquiryType.label")}{" "}
+ What can we help you with today?{" "}
*
{errors.inquiryType && (
@@ -220,8 +209,7 @@ export default function ContactPage() {
htmlFor="firstName"
className="block text-sm font-medium text-foreground"
>
- {t("fields.firstName.label")}{" "}
-
*
+ First Name
*
handleInputChange("firstName", e.target.value)
}
- placeholder={t("fields.firstName.placeholder")}
+ placeholder="Your first name"
/>
{errors.firstName && (
{errors.firstName}
@@ -242,8 +230,7 @@ export default function ContactPage() {
htmlFor="lastName"
className="block text-sm font-medium text-foreground"
>
- {t("fields.lastName.label")}{" "}
-
*
+ Last Name
*
handleInputChange("lastName", e.target.value)
}
- placeholder={t("fields.lastName.placeholder")}
+ placeholder="Your last name"
/>
{errors.lastName && (
{errors.lastName}
@@ -265,15 +252,14 @@ export default function ContactPage() {
htmlFor="email"
className="block text-sm font-medium text-foreground"
>
- {t("fields.email.label")}{" "}
-
*
+ Email
*
handleInputChange("email", e.target.value)}
- placeholder={t("fields.email.placeholder")}
+ placeholder="your.email@company.com"
/>
{errors.email && (
{errors.email}
@@ -285,15 +271,14 @@ export default function ContactPage() {
htmlFor="company"
className="block text-sm font-medium text-foreground"
>
- {t("fields.company.label")}{" "}
-
*
+ Company Name
*
handleInputChange("company", e.target.value)}
- placeholder={t("fields.company.placeholder")}
+ placeholder="Your company name"
/>
{errors.company && (
{errors.company}
@@ -305,14 +290,13 @@ export default function ContactPage() {
htmlFor="message"
className="block text-sm font-medium text-foreground"
>
- {t("fields.message.label")}{" "}
-
*
+ How can we help?
*
diff --git a/apps/website/app/layout.tsx b/apps/website/app/layout.tsx
index 7f3c600..6ac6010 100644
--- a/apps/website/app/layout.tsx
+++ b/apps/website/app/layout.tsx
@@ -1,10 +1,12 @@
import clsx from "clsx";
import type { Metadata } from "next";
-import { NextIntlClientProvider } from "next-intl";
-import { getMessages } from "next-intl/server";
import { Inter, Lexend } from "next/font/google";
import type { ReactNode } from "react";
import { GoogleAnalytics } from "@next/third-parties/google";
+import "@/styles/tailwind.css";
+import "react-photo-view/dist/react-photo-view.css";
+import { Header } from "@/components/Header";
+import { Footer } from "@/components/Footer";
type Props = {
children: ReactNode;
@@ -44,18 +46,10 @@ const lexend = Lexend({
});
// 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 async function RootLayout({
- children,
- params,
-}: {
- children: ReactNode;
- params: { locale: string };
-}) {
- const { locale } = params;
- const messages = await getMessages();
+export default function RootLayout({ children }: { children: ReactNode }) {
return (
-
+
+
{children}
-
+
+
);
diff --git a/apps/website/app/[locale]/page.tsx b/apps/website/app/page.tsx
similarity index 80%
rename from apps/website/app/[locale]/page.tsx
rename to apps/website/app/page.tsx
index c7d65a2..37c0216 100644
--- a/apps/website/app/[locale]/page.tsx
+++ b/apps/website/app/page.tsx
@@ -7,11 +7,8 @@ import { Pricing } from "@/components/pricing";
import { SecondaryFeaturesSections } from "@/components/secondary-features";
import { Sponsors } from "@/components/sponsors";
import { StatsSection } from "@/components/stats";
-import { setRequestLocale } from "next-intl/server";
-export default async function Home({ params }: { params: { locale: string } }) {
- const { locale } = await params;
- setRequestLocale(locale);
+export default function Home() {
return (
diff --git a/apps/website/components/CallToAction.tsx b/apps/website/components/CallToAction.tsx
index 232fa74..294eff7 100644
--- a/apps/website/components/CallToAction.tsx
+++ b/apps/website/components/CallToAction.tsx
@@ -1,5 +1,5 @@
import { Container } from "@/components/Container";
-import { useTranslations } from "next-intl";
+import { useTranslations } from "@/lib/intl";
import Link from "next/link";
import { Button } from "./ui/button";
diff --git a/apps/website/components/Faqs.tsx b/apps/website/components/Faqs.tsx
index dba71a4..6340ead 100644
--- a/apps/website/components/Faqs.tsx
+++ b/apps/website/components/Faqs.tsx
@@ -4,7 +4,7 @@ import {
AccordionItem,
AccordionTrigger,
} from "@/components/ui/accordion";
-import { useTranslations } from "next-intl";
+import { useTranslations } from "@/lib/intl";
import { Container } from "./Container";
const faqs = [
diff --git a/apps/website/components/Footer.tsx b/apps/website/components/Footer.tsx
index a63177d..0a895b6 100644
--- a/apps/website/components/Footer.tsx
+++ b/apps/website/components/Footer.tsx
@@ -1,13 +1,6 @@
"use client";
-import {
- Select,
- SelectContent,
- SelectItem,
- SelectTrigger,
-} from "@/components/ui/select";
-import { Link, useRouter } from "@/i18n/routing";
-import { useLocale, useTranslations } from "next-intl";
+import Link from "next/link";
import type { SVGProps } from "react";
import { Container } from "./Container";
import { NavLink } from "./NavLink";
@@ -33,11 +26,6 @@ const I18nIcon = (props: JSX.IntrinsicAttributes & SVGProps) => (
);
export function Footer() {
- const router = useRouter();
- const locale = useLocale();
- const t = useTranslations("HomePage");
- const linkT = useTranslations("Link");
-
return (
- {t("footer.copyright", {
- year: new Date().getFullYear(),
- })}
+ {`Copyright © ${new Date().getFullYear()} Dokploy. All rights reserved.`}
diff --git a/apps/website/components/Header.tsx b/apps/website/components/Header.tsx
index 0fdeb5f..da1122d 100644
--- a/apps/website/components/Header.tsx
+++ b/apps/website/components/Header.tsx
@@ -1,10 +1,9 @@
"use client";
-import { Link } from "@/i18n/routing";
+import Link from "next/link";
import { cn } from "@/lib/utils";
import { Popover, Transition } from "@headlessui/react";
import { ChevronRight, HeartIcon } from "lucide-react";
-import { useTranslations } from "next-intl";
import { Fragment, type JSX, type SVGProps } from "react";
import { Container } from "./Container";
import { NavLink } from "./NavLink";
@@ -84,8 +83,6 @@ const I18nIcon = (props: JSX.IntrinsicAttributes & SVGProps) => (
);
function MobileNavigation() {
- const t = useTranslations("HomePage");
- const linkT = useTranslations("Link");
return (
-
- {t("navigation.pricing")}
+ Pricing
+ FAQ
+
+ Docs
- {t("navigation.faqs")}
-
- {t("navigation.docs")}
-
- {t("navigation.blog")}
- {t("navigation.contact")}
-
+ Blog
+ Contact
+