feat(i18n): add i18n support for sidebar

This commit is contained in:
JiPai
2025-03-09 16:53:19 +08:00
parent 3a0dbc26d1
commit 7903ddba89
17 changed files with 229 additions and 78 deletions

View File

@@ -86,6 +86,7 @@ import { Logo } from "../shared/logo";
import { Button } from "../ui/button"; import { Button } from "../ui/button";
import { UpdateServerButton } from "./update-server"; import { UpdateServerButton } from "./update-server";
import { UserNav } from "./user-nav"; import { UserNav } from "./user-nav";
import { useTranslation } from "next-i18next";
// The types of the queries we are going to use // The types of the queries we are going to use
type AuthQueryOutput = inferRouterOutputs<AppRouter>["user"]["get"]; type AuthQueryOutput = inferRouterOutputs<AppRouter>["user"]["get"];
@@ -93,6 +94,7 @@ type AuthQueryOutput = inferRouterOutputs<AppRouter>["user"]["get"];
type SingleNavItem = { type SingleNavItem = {
isSingle?: true; isSingle?: true;
title: string; title: string;
titleKey: string;
url: string; url: string;
icon?: LucideIcon; icon?: LucideIcon;
isEnabled?: (opts: { isEnabled?: (opts: {
@@ -110,6 +112,7 @@ type NavItem =
| { | {
isSingle: false; isSingle: false;
title: string; title: string;
titleKey: string;
icon: LucideIcon; icon: LucideIcon;
items: SingleNavItem[]; items: SingleNavItem[];
isEnabled?: (opts: { isEnabled?: (opts: {
@@ -122,6 +125,7 @@ type NavItem =
// Represents an external link item (used for the help section) // Represents an external link item (used for the help section)
type ExternalLink = { type ExternalLink = {
name: string; name: string;
nameKey: string;
url: string; url: string;
icon: React.ComponentType<{ className?: string }>; icon: React.ComponentType<{ className?: string }>;
isEnabled?: (opts: { isEnabled?: (opts: {
@@ -147,12 +151,14 @@ const MENU: Menu = {
{ {
isSingle: true, isSingle: true,
title: "Projects", title: "Projects",
titleKey: "common.side.projects",
url: "/dashboard/projects", url: "/dashboard/projects",
icon: Folder, icon: Folder,
}, },
{ {
isSingle: true, isSingle: true,
title: "Monitoring", title: "Monitoring",
titleKey: "common.side.monitoring",
url: "/dashboard/monitoring", url: "/dashboard/monitoring",
icon: BarChartHorizontalBigIcon, icon: BarChartHorizontalBigIcon,
// Only enabled in non-cloud environments // Only enabled in non-cloud environments
@@ -161,6 +167,7 @@ const MENU: Menu = {
{ {
isSingle: true, isSingle: true,
title: "Traefik File System", title: "Traefik File System",
titleKey: "common.side.traefik",
url: "/dashboard/traefik", url: "/dashboard/traefik",
icon: GalleryVerticalEnd, icon: GalleryVerticalEnd,
// Only enabled for admins and users with access to Traefik files in non-cloud environments // Only enabled for admins and users with access to Traefik files in non-cloud environments
@@ -173,6 +180,7 @@ const MENU: Menu = {
{ {
isSingle: true, isSingle: true,
title: "Docker", title: "Docker",
titleKey: "common.side.docker",
url: "/dashboard/docker", url: "/dashboard/docker",
icon: BlocksIcon, icon: BlocksIcon,
// Only enabled for admins and users with access to Docker in non-cloud environments // Only enabled for admins and users with access to Docker in non-cloud environments
@@ -182,6 +190,7 @@ const MENU: Menu = {
{ {
isSingle: true, isSingle: true,
title: "Swarm", title: "Swarm",
titleKey: "common.side.swarm",
url: "/dashboard/swarm", url: "/dashboard/swarm",
icon: PieChart, icon: PieChart,
// Only enabled for admins and users with access to Docker in non-cloud environments // Only enabled for admins and users with access to Docker in non-cloud environments
@@ -191,6 +200,7 @@ const MENU: Menu = {
{ {
isSingle: true, isSingle: true,
title: "Requests", title: "Requests",
titleKey: "common.side.requests",
url: "/dashboard/requests", url: "/dashboard/requests",
icon: Forward, icon: Forward,
// Only enabled for admins and users with access to Docker in non-cloud environments // Only enabled for admins and users with access to Docker in non-cloud environments
@@ -259,6 +269,7 @@ const MENU: Menu = {
{ {
isSingle: true, isSingle: true,
title: "Web Server", title: "Web Server",
titleKey: "common.side.web-server",
url: "/dashboard/settings/server", url: "/dashboard/settings/server",
icon: Activity, icon: Activity,
// Only enabled for admins in non-cloud environments // Only enabled for admins in non-cloud environments
@@ -267,12 +278,14 @@ const MENU: Menu = {
{ {
isSingle: true, isSingle: true,
title: "Profile", title: "Profile",
titleKey: "common.side.profile",
url: "/dashboard/settings/profile", url: "/dashboard/settings/profile",
icon: User, icon: User,
}, },
{ {
isSingle: true, isSingle: true,
title: "Remote Servers", title: "Remote Servers",
titleKey: "common.side.remote-servers",
url: "/dashboard/settings/servers", url: "/dashboard/settings/servers",
icon: Server, icon: Server,
// Only enabled for admins // Only enabled for admins
@@ -281,6 +294,7 @@ const MENU: Menu = {
{ {
isSingle: true, isSingle: true,
title: "Users", title: "Users",
titleKey: "common.side.users",
icon: Users, icon: Users,
url: "/dashboard/settings/users", url: "/dashboard/settings/users",
// Only enabled for admins // Only enabled for admins
@@ -289,6 +303,7 @@ const MENU: Menu = {
{ {
isSingle: true, isSingle: true,
title: "SSH Keys", title: "SSH Keys",
titleKey: "common.side.ssh-keys",
icon: KeyRound, icon: KeyRound,
url: "/dashboard/settings/ssh-keys", url: "/dashboard/settings/ssh-keys",
// Only enabled for admins and users with access to SSH keys // Only enabled for admins and users with access to SSH keys
@@ -297,6 +312,7 @@ const MENU: Menu = {
}, },
{ {
title: "AI", title: "AI",
titleKey: "common.side.ai",
icon: BotIcon, icon: BotIcon,
url: "/dashboard/settings/ai", url: "/dashboard/settings/ai",
isSingle: true, isSingle: true,
@@ -305,6 +321,7 @@ const MENU: Menu = {
{ {
isSingle: true, isSingle: true,
title: "Git", title: "Git",
titleKey: "common.side.git",
url: "/dashboard/settings/git-providers", url: "/dashboard/settings/git-providers",
icon: GitBranch, icon: GitBranch,
// Only enabled for admins and users with access to Git providers // Only enabled for admins and users with access to Git providers
@@ -314,6 +331,7 @@ const MENU: Menu = {
{ {
isSingle: true, isSingle: true,
title: "Registry", title: "Registry",
titleKey: "common.side.registry",
url: "/dashboard/settings/registry", url: "/dashboard/settings/registry",
icon: Package, icon: Package,
// Only enabled for admins // Only enabled for admins
@@ -322,6 +340,7 @@ const MENU: Menu = {
{ {
isSingle: true, isSingle: true,
title: "S3 Destinations", title: "S3 Destinations",
titleKey: "common.side.s3-destinations",
url: "/dashboard/settings/destinations", url: "/dashboard/settings/destinations",
icon: Database, icon: Database,
// Only enabled for admins // Only enabled for admins
@@ -331,6 +350,7 @@ const MENU: Menu = {
{ {
isSingle: true, isSingle: true,
title: "Certificates", title: "Certificates",
titleKey: "common.side.certificates",
url: "/dashboard/settings/certificates", url: "/dashboard/settings/certificates",
icon: ShieldCheck, icon: ShieldCheck,
// Only enabled for admins // Only enabled for admins
@@ -339,6 +359,7 @@ const MENU: Menu = {
{ {
isSingle: true, isSingle: true,
title: "Cluster", title: "Cluster",
titleKey: "common.side.cluster",
url: "/dashboard/settings/cluster", url: "/dashboard/settings/cluster",
icon: Boxes, icon: Boxes,
// Only enabled for admins in non-cloud environments // Only enabled for admins in non-cloud environments
@@ -347,6 +368,7 @@ const MENU: Menu = {
{ {
isSingle: true, isSingle: true,
title: "Notifications", title: "Notifications",
titleKey: "common.side.notifications",
url: "/dashboard/settings/notifications", url: "/dashboard/settings/notifications",
icon: Bell, icon: Bell,
// Only enabled for admins // Only enabled for admins
@@ -355,6 +377,7 @@ const MENU: Menu = {
{ {
isSingle: true, isSingle: true,
title: "Billing", title: "Billing",
titleKey: "common.side.billing",
url: "/dashboard/settings/billing", url: "/dashboard/settings/billing",
icon: CreditCard, icon: CreditCard,
// Only enabled for admins in cloud environments // Only enabled for admins in cloud environments
@@ -365,16 +388,19 @@ const MENU: Menu = {
help: [ help: [
{ {
name: "Documentation", name: "Documentation",
nameKey: "common.side.documentation",
url: "https://docs.dokploy.com/docs/core", url: "https://docs.dokploy.com/docs/core",
icon: BookIcon, icon: BookIcon,
}, },
{ {
name: "Support", name: "Support",
nameKey: "common.side.support",
url: "https://discord.gg/2tBnJ3jDJc", url: "https://discord.gg/2tBnJ3jDJc",
icon: CircleHelp, icon: CircleHelp,
}, },
{ {
name: "Sponsor", name: "Sponsor",
nameKey: "common.side.sponsor",
url: "https://opencollective.com/dokploy", url: "https://opencollective.com/dokploy",
icon: ({ className }) => ( icon: ({ className }) => (
<HeartIcon <HeartIcon
@@ -493,6 +519,7 @@ function LogoWrapper() {
} }
function SidebarLogo() { function SidebarLogo() {
const { t } = useTranslation("common");
const { state } = useSidebar(); const { state } = useSidebar();
const { data: isCloud } = api.settings.isCloud.useQuery(); const { data: isCloud } = api.settings.isCloud.useQuery();
const { data: user } = api.user.get.useQuery(); const { data: user } = api.user.get.useQuery();
@@ -689,7 +716,9 @@ function SidebarLogo() {
side={"right"} side={"right"}
className="w-80" className="w-80"
> >
<DropdownMenuLabel>Pending Invitations</DropdownMenuLabel> <DropdownMenuLabel>
{t("common.side.invitations.pending-invitations")}
</DropdownMenuLabel>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
{invitations && invitations.length > 0 ? ( {invitations && invitations.length > 0 ? (
invitations.map((invitation) => ( invitations.map((invitation) => (
@@ -702,16 +731,23 @@ function SidebarLogo() {
{invitation?.organization?.name} {invitation?.organization?.name}
</div> </div>
<div className="text-xs text-muted-foreground"> <div className="text-xs text-muted-foreground">
Expires:{" "} {t("common.side.invitations.expires", {
{new Date(invitation.expiresAt).toLocaleString()} expireDate: new Date(
invitation.expiresAt,
).toLocaleString(),
})}
</div> </div>
<div className="text-xs text-muted-foreground"> <div className="text-xs text-muted-foreground">
Role: {invitation.role} {t("common.side.invitations.role", {
role: invitation.role,
})}
</div> </div>
</DropdownMenuItem> </DropdownMenuItem>
<DialogAction <DialogAction
title="Accept Invitation" title={t("common.side.invitations.accept-invitation")}
description="Are you sure you want to accept this invitation?" description={t(
"common.side.invitations.confirm-accept-invitation",
)}
type="default" type="default"
onClick={async () => { onClick={async () => {
const { error } = const { error } =
@@ -721,24 +757,31 @@ function SidebarLogo() {
if (error) { if (error) {
toast.error( toast.error(
error.message || "Error accepting invitation", error.message ||
t(
"common.side.invitations.error-accepting-invitation",
),
); );
} else { } else {
toast.success("Invitation accepted successfully"); toast.success(
t(
"common.side.invitations.invitation-accepted",
),
);
await refetchInvitations(); await refetchInvitations();
await refetch(); await refetch();
} }
}} }}
> >
<Button size="sm" variant="secondary"> <Button size="sm" variant="secondary">
Accept Invitation {t("common.side.invitations.accept-invitation")}
</Button> </Button>
</DialogAction> </DialogAction>
</div> </div>
)) ))
) : ( ) : (
<DropdownMenuItem disabled> <DropdownMenuItem disabled>
No pending invitations {t("common.side.invitations.no-pending-invitations")}
</DropdownMenuItem> </DropdownMenuItem>
)} )}
</div> </div>
@@ -752,6 +795,8 @@ function SidebarLogo() {
} }
export default function Page({ children }: Props) { export default function Page({ children }: Props) {
const { t } = useTranslation("common");
const [defaultOpen, setDefaultOpen] = useState<boolean | undefined>( const [defaultOpen, setDefaultOpen] = useState<boolean | undefined>(
undefined, undefined,
); );
@@ -818,7 +863,7 @@ export default function Page({ children }: Props) {
</SidebarHeader> </SidebarHeader>
<SidebarContent> <SidebarContent>
<SidebarGroup> <SidebarGroup>
<SidebarGroupLabel>Home</SidebarGroupLabel> <SidebarGroupLabel>{t("common.side.home")}</SidebarGroupLabel>
<SidebarMenu> <SidebarMenu>
{filteredHome.map((item) => { {filteredHome.map((item) => {
const isSingle = item.isSingle !== false; const isSingle = item.isSingle !== false;
@@ -851,7 +896,7 @@ export default function Page({ children }: Props) {
className={cn(isActive && "text-primary")} className={cn(isActive && "text-primary")}
/> />
)} )}
<span>{item.title}</span> <span>{t(item.titleKey)}</span>
</Link> </Link>
</SidebarMenuButton> </SidebarMenuButton>
) : ( ) : (
@@ -907,7 +952,7 @@ export default function Page({ children }: Props) {
</SidebarMenu> </SidebarMenu>
</SidebarGroup> </SidebarGroup>
<SidebarGroup> <SidebarGroup>
<SidebarGroupLabel>Settings</SidebarGroupLabel> <SidebarGroupLabel>{t("common.side.settings")}</SidebarGroupLabel>
<SidebarMenu className="gap-2"> <SidebarMenu className="gap-2">
{filteredSettings.map((item) => { {filteredSettings.map((item) => {
const isSingle = item.isSingle !== false; const isSingle = item.isSingle !== false;
@@ -940,7 +985,7 @@ export default function Page({ children }: Props) {
className={cn(isActive && "text-primary")} className={cn(isActive && "text-primary")}
/> />
)} )}
<span>{item.title}</span> <span>{t(item.titleKey)}</span>
</Link> </Link>
</SidebarMenuButton> </SidebarMenuButton>
) : ( ) : (
@@ -996,7 +1041,7 @@ export default function Page({ children }: Props) {
</SidebarMenu> </SidebarMenu>
</SidebarGroup> </SidebarGroup>
<SidebarGroup className="group-data-[collapsible=icon]:hidden"> <SidebarGroup className="group-data-[collapsible=icon]:hidden">
<SidebarGroupLabel>Extra</SidebarGroupLabel> <SidebarGroupLabel>{t("common.side.extra")}</SidebarGroupLabel>
<SidebarMenu> <SidebarMenu>
{help.map((item: ExternalLink) => ( {help.map((item: ExternalLink) => (
<SidebarMenuItem key={item.name}> <SidebarMenuItem key={item.name}>
@@ -1010,7 +1055,7 @@ export default function Page({ children }: Props) {
<span className="mr-2"> <span className="mr-2">
<item.icon className="h-4 w-4" /> <item.icon className="h-4 w-4" />
</span> </span>
<span>{item.name}</span> <span>{t(item.nameKey)}</span>
</a> </a>
</SidebarMenuButton> </SidebarMenuButton>
</SidebarMenuItem> </SidebarMenuItem>

View File

@@ -55,7 +55,7 @@ export async function getServerSideProps(
return { return {
props: { props: {
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
...(await serverSideTranslations(locale, ["settings"])), ...(await serverSideTranslations(locale, ["common", "settings"])),
}, },
}; };
} }

View File

@@ -2,6 +2,7 @@ import { ShowBilling } from "@/components/dashboard/settings/billing/show-billin
import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { appRouter } from "@/server/api/root"; import { appRouter } from "@/server/api/root";
import { getLocale, serverSideTranslations } from "@/utils/i18n";
import { IS_CLOUD } from "@dokploy/server/constants"; import { IS_CLOUD } from "@dokploy/server/constants";
import { validateRequest } from "@dokploy/server/lib/auth"; import { validateRequest } from "@dokploy/server/lib/auth";
import { createServerSideHelpers } from "@trpc/react-query/server"; import { createServerSideHelpers } from "@trpc/react-query/server";
@@ -30,6 +31,7 @@ export async function getServerSideProps(
}; };
} }
const { req, res } = ctx; const { req, res } = ctx;
const locale = getLocale(req.cookies);
const { user, session } = await validateRequest(req); const { user, session } = await validateRequest(req);
if (!user || user.role === "member") { if (!user || user.role === "member") {
return { return {
@@ -59,6 +61,7 @@ export async function getServerSideProps(
return { return {
props: { props: {
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
...(await serverSideTranslations(locale, ["common", "settings"])),
}, },
}; };
} }

View File

@@ -2,6 +2,7 @@ import { ShowCertificates } from "@/components/dashboard/settings/certificates/s
import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { appRouter } from "@/server/api/root"; import { appRouter } from "@/server/api/root";
import { getLocale, serverSideTranslations } from "@/utils/i18n";
import { validateRequest } from "@dokploy/server"; import { validateRequest } from "@dokploy/server";
import { createServerSideHelpers } from "@trpc/react-query/server"; import { createServerSideHelpers } from "@trpc/react-query/server";
import type { GetServerSidePropsContext } from "next"; import type { GetServerSidePropsContext } from "next";
@@ -24,6 +25,7 @@ export async function getServerSideProps(
ctx: GetServerSidePropsContext<{ serviceId: string }>, ctx: GetServerSidePropsContext<{ serviceId: string }>,
) { ) {
const { req, res } = ctx; const { req, res } = ctx;
const locale = await getLocale(req.cookies);
const { user, session } = await validateRequest(req); const { user, session } = await validateRequest(req);
if (!user || user.role === "member") { if (!user || user.role === "member") {
return { return {
@@ -51,6 +53,7 @@ export async function getServerSideProps(
return { return {
props: { props: {
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
...(await serverSideTranslations(locale, ["common", "settings"])),
}, },
}; };
} }

View File

@@ -2,6 +2,7 @@ import { ShowNodes } from "@/components/dashboard/settings/cluster/nodes/show-no
import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { appRouter } from "@/server/api/root"; import { appRouter } from "@/server/api/root";
import { getLocale, serverSideTranslations } from "@/utils/i18n";
import { IS_CLOUD, validateRequest } from "@dokploy/server"; import { IS_CLOUD, validateRequest } from "@dokploy/server";
import { createServerSideHelpers } from "@trpc/react-query/server"; import { createServerSideHelpers } from "@trpc/react-query/server";
import type { GetServerSidePropsContext } from "next"; import type { GetServerSidePropsContext } from "next";
@@ -25,6 +26,7 @@ export async function getServerSideProps(
ctx: GetServerSidePropsContext<{ serviceId: string }>, ctx: GetServerSidePropsContext<{ serviceId: string }>,
) { ) {
const { req, res } = ctx; const { req, res } = ctx;
const locale = await getLocale(req.cookies);
if (IS_CLOUD) { if (IS_CLOUD) {
return { return {
redirect: { redirect: {
@@ -58,6 +60,7 @@ export async function getServerSideProps(
return { return {
props: { props: {
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
...(await serverSideTranslations(locale, ["common", "settings"])),
}, },
}; };
} }

View File

@@ -2,6 +2,7 @@ import { ShowDestinations } from "@/components/dashboard/settings/destination/sh
import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { appRouter } from "@/server/api/root"; import { appRouter } from "@/server/api/root";
import { getLocale, serverSideTranslations } from "@/utils/i18n";
import { validateRequest } from "@dokploy/server"; import { validateRequest } from "@dokploy/server";
import { createServerSideHelpers } from "@trpc/react-query/server"; import { createServerSideHelpers } from "@trpc/react-query/server";
import type { GetServerSidePropsContext } from "next"; import type { GetServerSidePropsContext } from "next";
@@ -25,6 +26,7 @@ export async function getServerSideProps(
ctx: GetServerSidePropsContext<{ serviceId: string }>, ctx: GetServerSidePropsContext<{ serviceId: string }>,
) { ) {
const { req, res } = ctx; const { req, res } = ctx;
const locale = await getLocale(req.cookies);
const { user, session } = await validateRequest(req); const { user, session } = await validateRequest(req);
if (!user || user.role === "member") { if (!user || user.role === "member") {
return { return {
@@ -52,6 +54,7 @@ export async function getServerSideProps(
return { return {
props: { props: {
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
...(await serverSideTranslations(locale, ["common", "settings"])),
}, },
}; };
} }

View File

@@ -2,6 +2,7 @@ import { ShowGitProviders } from "@/components/dashboard/settings/git/show-git-p
import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { appRouter } from "@/server/api/root"; import { appRouter } from "@/server/api/root";
import { getLocale, serverSideTranslations } from "@/utils/i18n";
import { validateRequest } from "@dokploy/server"; import { validateRequest } from "@dokploy/server";
import { createServerSideHelpers } from "@trpc/react-query/server"; import { createServerSideHelpers } from "@trpc/react-query/server";
import type { GetServerSidePropsContext } from "next"; import type { GetServerSidePropsContext } from "next";
@@ -9,68 +10,72 @@ import type { ReactElement } from "react";
import superjson from "superjson"; import superjson from "superjson";
const Page = () => { const Page = () => {
return ( return (
<div className="flex flex-col gap-4 w-full"> <div className="flex flex-col gap-4 w-full">
<ShowGitProviders /> <ShowGitProviders />
</div> </div>
); );
}; };
export default Page; export default Page;
Page.getLayout = (page: ReactElement) => { Page.getLayout = (page: ReactElement) => {
return <DashboardLayout metaName="Git Providers">{page}</DashboardLayout>; return <DashboardLayout metaName="Git Providers">{page}</DashboardLayout>;
}; };
export async function getServerSideProps( export async function getServerSideProps(
ctx: GetServerSidePropsContext<{ serviceId: string }>, ctx: GetServerSidePropsContext<{ serviceId: string }>
) { ) {
const { user, session } = await validateRequest(ctx.req); const { user, session } = await validateRequest(ctx.req);
if (!user) { if (!user) {
return { return {
redirect: { redirect: {
permanent: true, permanent: true,
destination: "/", destination: "/",
}, },
}; };
} }
const { req, res } = ctx; const { req, res } = ctx;
const helpers = createServerSideHelpers({ const locale = await getLocale(req.cookies);
router: appRouter, const helpers = createServerSideHelpers({
ctx: { router: appRouter,
req: req as any, ctx: {
res: res as any, req: req as any,
db: null as any, res: res as any,
session: session as any, db: null as any,
user: user as any, session: session as any,
}, user: user as any,
transformer: superjson, },
}); transformer: superjson,
await helpers.user.get.prefetch(); });
try { await helpers.user.get.prefetch();
await helpers.project.all.prefetch(); try {
await helpers.settings.isCloud.prefetch(); await helpers.project.all.prefetch();
if (user.role === "member") { await helpers.settings.isCloud.prefetch();
const userR = await helpers.user.one.fetch({ if (user.role === "member") {
userId: user.id, const userR = await helpers.user.one.fetch({
}); userId: user.id,
});
if (!userR?.canAccessToGitProviders) { if (!userR?.canAccessToGitProviders) {
return { return {
redirect: { redirect: {
permanent: true, permanent: true,
destination: "/", destination: "/",
}, },
}; };
} }
} }
return { return {
props: { props: {
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
}, ...(await serverSideTranslations(locale, ["common", "settings"])),
}; },
} catch (_error) { };
return { } catch (_error) {
props: {}, return {
}; props: {
} ...(await serverSideTranslations(locale, ["common", "settings"])),
},
};
}
} }

View File

@@ -21,6 +21,7 @@ import {
import { Switch } from "@/components/ui/switch"; import { Switch } from "@/components/ui/switch";
import { appRouter } from "@/server/api/root"; import { appRouter } from "@/server/api/root";
import { api } from "@/utils/api"; import { api } from "@/utils/api";
import { getLocale, serverSideTranslations } from "@/utils/i18n";
import { validateRequest } from "@dokploy/server"; import { validateRequest } from "@dokploy/server";
import { zodResolver } from "@hookform/resolvers/zod"; import { zodResolver } from "@hookform/resolvers/zod";
import { createServerSideHelpers } from "@trpc/react-query/server"; import { createServerSideHelpers } from "@trpc/react-query/server";
@@ -180,6 +181,7 @@ export async function getServerSideProps(
ctx: GetServerSidePropsContext<{ serviceId: string }>, ctx: GetServerSidePropsContext<{ serviceId: string }>,
) { ) {
const { req, res } = ctx; const { req, res } = ctx;
const locale = await getLocale(req.cookies);
const { user, session } = await validateRequest(ctx.req); const { user, session } = await validateRequest(ctx.req);
if (!user) { if (!user) {
return { return {
@@ -214,6 +216,7 @@ export async function getServerSideProps(
return { return {
props: { props: {
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
...(await serverSideTranslations(locale, ["common", "settings"])),
}, },
}; };
} }

View File

@@ -2,6 +2,7 @@ import { ShowNotifications } from "@/components/dashboard/settings/notifications
import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { appRouter } from "@/server/api/root"; import { appRouter } from "@/server/api/root";
import { getLocale, serverSideTranslations } from "@/utils/i18n";
import { validateRequest } from "@dokploy/server"; import { validateRequest } from "@dokploy/server";
import { createServerSideHelpers } from "@trpc/react-query/server"; import { createServerSideHelpers } from "@trpc/react-query/server";
import type { GetServerSidePropsContext } from "next"; import type { GetServerSidePropsContext } from "next";
@@ -25,6 +26,7 @@ export async function getServerSideProps(
ctx: GetServerSidePropsContext<{ serviceId: string }>, ctx: GetServerSidePropsContext<{ serviceId: string }>,
) { ) {
const { req, res } = ctx; const { req, res } = ctx;
const locale = await getLocale(req.cookies);
const { user, session } = await validateRequest(req); const { user, session } = await validateRequest(req);
if (!user || user.role === "member") { if (!user || user.role === "member") {
return { return {
@@ -52,6 +54,7 @@ export async function getServerSideProps(
return { return {
props: { props: {
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
...(await serverSideTranslations(locale, ["common", "settings"])),
}, },
}; };
} }

View File

@@ -66,7 +66,7 @@ export async function getServerSideProps(
return { return {
props: { props: {
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
...(await serverSideTranslations(locale, ["settings"])), ...(await serverSideTranslations(locale, ["common", "settings"])),
}, },
}; };
} }

View File

@@ -2,6 +2,7 @@ import { ShowRegistry } from "@/components/dashboard/settings/cluster/registry/s
import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { appRouter } from "@/server/api/root"; import { appRouter } from "@/server/api/root";
import { serverSideTranslations, getLocale } from "@/utils/i18n";
import { validateRequest } from "@dokploy/server"; import { validateRequest } from "@dokploy/server";
import { createServerSideHelpers } from "@trpc/react-query/server"; import { createServerSideHelpers } from "@trpc/react-query/server";
import type { GetServerSidePropsContext } from "next"; import type { GetServerSidePropsContext } from "next";
@@ -25,6 +26,7 @@ export async function getServerSideProps(
ctx: GetServerSidePropsContext<{ serviceId: string }>, ctx: GetServerSidePropsContext<{ serviceId: string }>,
) { ) {
const { req, res } = ctx; const { req, res } = ctx;
const locale = await getLocale(req.cookies);
const { user, session } = await validateRequest(req); const { user, session } = await validateRequest(req);
if (!user || user.role === "member") { if (!user || user.role === "member") {
return { return {
@@ -51,6 +53,7 @@ export async function getServerSideProps(
return { return {
props: { props: {
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
...(await serverSideTranslations(locale, ["common", "settings"])),
}, },
}; };
} }

View File

@@ -115,7 +115,7 @@ export async function getServerSideProps(
return { return {
props: { props: {
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
...(await serverSideTranslations(locale, ["settings"])), ...(await serverSideTranslations(locale, ["common", "settings"])),
}, },
}; };
} }

View File

@@ -62,7 +62,7 @@ export async function getServerSideProps(
return { return {
props: { props: {
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
...(await serverSideTranslations(locale, ["settings"])), ...(await serverSideTranslations(locale, ["common", "settings"])),
}, },
}; };
} }

View File

@@ -2,6 +2,7 @@ import { ShowDestinations } from "@/components/dashboard/settings/ssh-keys/show-
import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { appRouter } from "@/server/api/root"; import { appRouter } from "@/server/api/root";
import { getLocale, serverSideTranslations } from "@/utils/i18n";
import { validateRequest } from "@dokploy/server"; import { validateRequest } from "@dokploy/server";
import { createServerSideHelpers } from "@trpc/react-query/server"; import { createServerSideHelpers } from "@trpc/react-query/server";
import type { GetServerSidePropsContext } from "next"; import type { GetServerSidePropsContext } from "next";
@@ -34,6 +35,7 @@ export async function getServerSideProps(
}; };
} }
const { req, res } = ctx; const { req, res } = ctx;
const locale = await getLocale(req.cookies);
const helpers = createServerSideHelpers({ const helpers = createServerSideHelpers({
router: appRouter, router: appRouter,
ctx: { ctx: {
@@ -67,11 +69,14 @@ export async function getServerSideProps(
return { return {
props: { props: {
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
...(await serverSideTranslations(locale, ["common", "settings"])),
}, },
}; };
} catch (_error) { } catch (_error) {
return { return {
props: {}, props: {
...(await serverSideTranslations(locale, ["common", "settings"])),
},
}; };
} }
} }

View File

@@ -3,6 +3,7 @@ import { ShowUsers } from "@/components/dashboard/settings/users/show-users";
import { DashboardLayout } from "@/components/layouts/dashboard-layout"; import { DashboardLayout } from "@/components/layouts/dashboard-layout";
import { appRouter } from "@/server/api/root"; import { appRouter } from "@/server/api/root";
import { getLocale, serverSideTranslations } from "@/utils/i18n";
import { validateRequest } from "@dokploy/server"; import { validateRequest } from "@dokploy/server";
import { createServerSideHelpers } from "@trpc/react-query/server"; import { createServerSideHelpers } from "@trpc/react-query/server";
import type { GetServerSidePropsContext } from "next"; import type { GetServerSidePropsContext } from "next";
@@ -27,6 +28,7 @@ export async function getServerSideProps(
ctx: GetServerSidePropsContext<{ serviceId: string }>, ctx: GetServerSidePropsContext<{ serviceId: string }>,
) { ) {
const { req, res } = ctx; const { req, res } = ctx;
const locale = await getLocale(req.cookies);
const { user, session } = await validateRequest(req); const { user, session } = await validateRequest(req);
if (!user || user.role === "member") { if (!user || user.role === "member") {
@@ -55,6 +57,7 @@ export async function getServerSideProps(
return { return {
props: { props: {
trpcState: helpers.dehydrate(), trpcState: helpers.dehydrate(),
...(await serverSideTranslations(locale, ["common", "settings"])),
}, },
}; };
} }

View File

@@ -1 +1,37 @@
{} {
"common.side.home": "Home",
"common.side.settings": "Settings",
"common.side.extra": "Extra",
"common.side.projects": "Projects",
"common.side.monitoring": "Monitoring",
"common.side.traefik": "Traefik File System",
"common.side.docker": "Docker",
"common.side.swarm": "Swarm",
"common.side.requests": "Requests",
"common.side.web-server": "Web Server",
"common.side.profile": "Profile",
"common.side.remote-servers": "Remote Servers",
"common.side.users": "Users",
"common.side.ssh-keys": "SSH Keys",
"common.side.ai": "AI",
"common.side.git": "Git",
"common.side.registry": "Registry",
"common.side.s3-destinations": "S3 Destinations",
"common.side.certificates": "Certificates",
"common.side.cluster": "Cluster",
"common.side.notifications": "Notifications",
"common.side.billing": "Billing",
"common.side.documentation": "Documentation",
"common.side.support": "Support",
"common.side.sponsor": "Sponsor",
"common.side.invitations.pending-invitations": "Pending Invitations",
"common.side.invitations.no-pending-invitations": "No pending invitations",
"common.side.invitations.accept-invitation": "Accept Invitation",
"common.side.invitations.confirm-accept-invitation": "Are you sure you want to accept this invitation?",
"common.side.invitations.error-accepting-invitation": "Error accepting invitation",
"common.side.invitations.invitation-accepted": "Invitation accepted successfully",
"common.side.invitations.expires": "Expires: {{expireDate}}",
"common.side.invitations.role": "Role: {{role}}"
}

View File

@@ -1 +1,37 @@
{} {
"common.side.home": "主页",
"common.side.settings": "设置",
"common.side.extra": "其他",
"common.side.projects": "项目",
"common.side.monitoring": "监控",
"common.side.traefik": "Traefik",
"common.side.docker": "Docker",
"common.side.swarm": "Swarm",
"common.side.requests": "请求",
"common.side.web-server": "本地配置",
"common.side.profile": "个人资料",
"common.side.remote-servers": "远程服务器",
"common.side.users": "用户",
"common.side.ssh-keys": "SSH 密钥",
"common.side.ai": "AI",
"common.side.git": "Git 集成",
"common.side.registry": "注册表",
"common.side.s3-destinations": "S3 存储",
"common.side.certificates": "证书",
"common.side.cluster": "集群",
"common.side.notifications": "通知",
"common.side.billing": "账单",
"common.side.documentation": "文档",
"common.side.support": "支持",
"common.side.sponsor": "赞助",
"common.side.invitations.pending-invitations": "待处理邀请",
"common.side.invitations.no-pending-invitations": "没有待处理的邀请",
"common.side.invitations.accept-invitation": "接受邀请",
"common.side.invitations.confirm-accept-invitation": "您确定要接受此邀请吗?",
"common.side.invitations.error-accepting-invitation": "接受邀请时出错",
"common.side.invitations.invitation-accepted": "邀请已成功接受",
"common.side.invitations.expires": "有效期:{{expireDate}}",
"common.side.invitations.role": "角色:{{role}}"
}