diff --git a/apps/fronted/next.config.mjs b/apps/fronted/next.config.mjs index 5ae9bbd..074cbda 100644 --- a/apps/fronted/next.config.mjs +++ b/apps/fronted/next.config.mjs @@ -5,26 +5,17 @@ const withNextIntl = createNextIntlPlugin(); /** @type {import('next').NextConfig} */ const config = { typescript: { - ignoreBuildErrors: true - }, - async redirects() { - return [ - { - source: "/", - destination: "/en", - permanent: true - } - ]; + ignoreBuildErrors: true, }, async rewrites() { return [ { source: "/generate-pdf", destination: - "http://1255612844-0z3iovadu8.ap-chengdu.tencentscf.com/generate-pdf" - } + "http://1255612844-0z3iovadu8.ap-chengdu.tencentscf.com/generate-pdf", + }, ]; - } + }, }; export default withNextIntl(config); diff --git a/apps/fronted/src/app/(public)/[locale]/layout.tsx b/apps/fronted/src/app/(public)/[locale]/layout.tsx new file mode 100644 index 0000000..a439757 --- /dev/null +++ b/apps/fronted/src/app/(public)/[locale]/layout.tsx @@ -0,0 +1,56 @@ +import { Metadata } from "next"; +import { notFound } from "next/navigation"; +import { NextIntlClientProvider } from "next-intl"; +import { + getMessages, + getTranslations, + setRequestLocale, +} from "next-intl/server"; +import { ReactNode } from "react"; +import Document from "@/components/Document"; +import { locales } from "@/i18n/config"; +import { Providers } from "@/app/providers"; + +type Props = { + children: ReactNode; + params: { locale: string }; +}; + +export function generateStaticParams() { + return locales.map((locale) => ({ locale })); +} + +export async function generateMetadata({ + params: { locale }, +}: Props): Promise { + const t = await getTranslations({ locale, namespace: "common" }); + + return { + title: t("title"), + }; +} + +export default async function LocaleLayout({ + children, + params: { locale }, +}: Props) { + // Enable static rendering + setRequestLocale(locale); + + // Ensure that the incoming locale is valid + if (!locales.includes(locale as any)) { + notFound(); + } + + // Providing all messages to the client + // side is the easiest way to get started + const messages = await getMessages(); + + return ( + + + {children} + + + ); +} diff --git a/apps/fronted/src/app/[locale]/page.tsx b/apps/fronted/src/app/(public)/[locale]/page.tsx similarity index 97% rename from apps/fronted/src/app/[locale]/page.tsx rename to apps/fronted/src/app/(public)/[locale]/page.tsx index 85b5570..bf82325 100644 --- a/apps/fronted/src/app/[locale]/page.tsx +++ b/apps/fronted/src/app/(public)/[locale]/page.tsx @@ -5,14 +5,18 @@ import { Button } from "@/components/ui/button"; import Header from "@/components/home/Header"; import FeaturesAnimation from "@/components/home/FeaturesAnimation"; import HeroAnimation from "@/components/home/HeroAnimation"; +import { setRequestLocale } from "next-intl/server"; -export default function LandingPage() { +type Props = { + params: { locale: string }; +}; + +export default function LandingPage({ params: { locale } }: Props) { const t = useTranslations("home"); - + setRequestLocale(locale); return (
-
diff --git a/apps/fronted/src/app/[locale]/layout.tsx b/apps/fronted/src/app/[locale]/layout.tsx deleted file mode 100644 index 106f458..0000000 --- a/apps/fronted/src/app/[locale]/layout.tsx +++ /dev/null @@ -1,27 +0,0 @@ -import { notFound } from "next/navigation"; -import { NextIntlClientProvider, useMessages } from "next-intl"; -import { locales } from "@/i18n/config"; - -export function generateStaticParams() { - return locales.map((locale) => ({ locale })); -} - -export default function LocaleLayout({ - children, - params: { locale } -}: { - children: React.ReactNode; - params: { locale: string }; -}) { - const messages = useMessages(); - - if (!locales.includes(locale as any)) { - notFound(); - } - - return ( - - {children} - - ); -} diff --git a/apps/fronted/src/app/dashboard/layout.tsx b/apps/fronted/src/app/app/dashboard/client.tsx similarity index 90% rename from apps/fronted/src/app/dashboard/layout.tsx rename to apps/fronted/src/app/app/dashboard/client.tsx index b33e923..bee97a0 100644 --- a/apps/fronted/src/app/dashboard/layout.tsx +++ b/apps/fronted/src/app/app/dashboard/client.tsx @@ -22,21 +22,23 @@ import { TooltipTrigger, } from "@/components/ui/tooltip"; import Logo from "@/components/shared/Logo"; - -const sidebarItems = [ - { - title: "简历", - url: "/dashboard/resumes", - icon: FileText, - }, - { - title: "设置", - url: "/dashboard/settings", - icon: Settings, - }, -]; +import { useTranslations } from "next-intl"; const DashboardLayout = ({ children }: { children: React.ReactNode }) => { + const t = useTranslations("dashboard"); + const sidebarItems = [ + { + title: t("sidebar.resumes"), + url: "/app/dashboard/resumes", + icon: FileText, + }, + { + title: t("sidebar.settings"), + url: "/app/dashboard/settings", + icon: Settings, + }, + ]; + const router = useRouter(); const pathname = usePathname(); const [open, setOpen] = useState(true); diff --git a/apps/fronted/src/app/app/dashboard/layout.tsx b/apps/fronted/src/app/app/dashboard/layout.tsx new file mode 100644 index 0000000..41943b7 --- /dev/null +++ b/apps/fronted/src/app/app/dashboard/layout.tsx @@ -0,0 +1,30 @@ +import { Metadata } from "next"; +import { NextIntlClientProvider } from "next-intl"; +import { getLocale, getMessages, getTranslations } from "next-intl/server"; +import { ReactNode } from "react"; +import Document from "@/components/Document"; +import Client from "./client"; +type Props = { + children: ReactNode; +}; +export async function generateMetadata({ + params: { locale }, +}: Props): Promise { + const t = await getTranslations({ locale, namespace: "common" }); + return { + title: t("title") + " - " + t("dashboard"), + }; +} +export default async function LocaleLayout({ children }: Props) { + const locale = await getLocale(); + + const messages = await getMessages(); + + return ( + + + {children} + + + ); +} diff --git a/apps/fronted/src/app/dashboard/page.tsx b/apps/fronted/src/app/app/dashboard/page.tsx similarity index 72% rename from apps/fronted/src/app/dashboard/page.tsx rename to apps/fronted/src/app/app/dashboard/page.tsx index f3469d9..ad1bb73 100644 --- a/apps/fronted/src/app/dashboard/page.tsx +++ b/apps/fronted/src/app/app/dashboard/page.tsx @@ -2,5 +2,5 @@ import { redirect } from "next/navigation"; export default function Dashboard() { - redirect("/dashboard/resumes"); + redirect("/app/dashboard/resumes"); } diff --git a/apps/fronted/src/app/dashboard/resumes/page.tsx b/apps/fronted/src/app/app/dashboard/resumes/page.tsx similarity index 92% rename from apps/fronted/src/app/dashboard/resumes/page.tsx rename to apps/fronted/src/app/app/dashboard/resumes/page.tsx index 1bda36b..1dba1cb 100644 --- a/apps/fronted/src/app/dashboard/resumes/page.tsx +++ b/apps/fronted/src/app/app/dashboard/resumes/page.tsx @@ -16,6 +16,7 @@ import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; import { getConfig, getFileHandle, verifyPermission } from "@/utils/fileSystem"; import { useResumeStore } from "@/store/useResumeStore"; import { cn } from "@/lib/utils"; +import { useTranslations } from "next-intl"; const ResumesList = () => { return ; @@ -32,6 +33,7 @@ const ResumeWorkbench = () => { } = useResumeStore(); const router = useRouter(); const [hasConfiguredFolder, setHasConfiguredFolder] = React.useState(false); + const t = useTranslations(); useEffect(() => { const syncResumesFromFiles = async () => { @@ -104,7 +106,9 @@ const ResumeWorkbench = () => { {hasConfiguredFolder ? ( - 已设置备份文件夹 + + {t("dashboard.resumes.synced")} + @@ -148,7 +152,7 @@ const ResumeWorkbench = () => { transition={{ duration: 0.3 }} >

- 我的简历 + {t("dashboard.resumes.myResume")}

@@ -188,8 +192,12 @@ const ResumeWorkbench = () => { > - 创建新简历 - 从头开始创建 + + {t("dashboard.resumes.newResume")} + + + {t("dashboard.resumes.newResumeDescription")} + @@ -242,10 +250,10 @@ const ResumeWorkbench = () => { onClick={(e) => { e.stopPropagation(); setActiveResume(id); - router.push(`/workbench/${id}`); + router.push(`/app/workbench/${id}`); }} > - 编辑 + {t("common.edit")} { deleteResume(resume); }} > - 删除 + {t("common.delete")}
diff --git a/apps/fronted/src/app/dashboard/settings/page.tsx b/apps/fronted/src/app/app/dashboard/settings/page.tsx similarity index 100% rename from apps/fronted/src/app/dashboard/settings/page.tsx rename to apps/fronted/src/app/app/dashboard/settings/page.tsx diff --git a/apps/fronted/src/app/app/page.tsx b/apps/fronted/src/app/app/page.tsx new file mode 100644 index 0000000..3a586da --- /dev/null +++ b/apps/fronted/src/app/app/page.tsx @@ -0,0 +1,34 @@ +import { Metadata } from "next"; +import { NextIntlClientProvider } from "next-intl"; +import { getLocale, getMessages, getTranslations } from "next-intl/server"; +import { ReactNode } from "react"; +import Document from "@/components/Document"; + +type Props = { + children: ReactNode; + params: { locale: string }; +}; + +export async function generateMetadata({ + params: { locale }, +}: Props): Promise { + const t = await getTranslations({ locale, namespace: "common" }); + return { + title: t("title"), + }; +} +export default async function LocaleLayout({ children }: Props) { + const locale = await getLocale(); + + // Providing all messages to the client + // side is the easiest way to get started + const messages = await getMessages(); + + return ( + + + {children} + + + ); +} diff --git a/apps/fronted/src/app/workbench/[id]/page.tsx b/apps/fronted/src/app/app/workbench/[id]/page.tsx similarity index 100% rename from apps/fronted/src/app/workbench/[id]/page.tsx rename to apps/fronted/src/app/app/workbench/[id]/page.tsx diff --git a/apps/fronted/src/app/app/workbench/layout.tsx b/apps/fronted/src/app/app/workbench/layout.tsx new file mode 100644 index 0000000..3d6b0cb --- /dev/null +++ b/apps/fronted/src/app/app/workbench/layout.tsx @@ -0,0 +1,29 @@ +import { Metadata } from "next"; +import { NextIntlClientProvider } from "next-intl"; +import { getLocale, getMessages, getTranslations } from "next-intl/server"; +import { ReactNode } from "react"; +import Document from "@/components/Document"; +type Props = { + children: ReactNode; +}; +export async function generateMetadata({ + params: { locale }, +}: Props): Promise { + const t = await getTranslations({ locale, namespace: "common" }); + return { + title: t("title") + " - " + t("dashboard"), + }; +} +export default async function LocaleLayout({ children }: Props) { + const locale = await getLocale(); + + const messages = await getMessages(); + + return ( + + + {children} + + + ); +} diff --git a/apps/fronted/src/app/layout.tsx b/apps/fronted/src/app/layout.tsx index f0fd4f2..64ee518 100644 --- a/apps/fronted/src/app/layout.tsx +++ b/apps/fronted/src/app/layout.tsx @@ -1,27 +1,10 @@ -import type { Metadata } from "next"; -import { Inter } from "next/font/google"; -import { Providers } from "./providers"; -import { Toaster } from "@/components/ui/sonner"; +import { ReactNode } from "react"; import "./globals.css"; -const inter = Inter({ subsets: ["latin"] }); - -export const metadata: Metadata = { - title: "魔法简历", - description: "极度自由的在线简历编辑器" +type Props = { + children: ReactNode; }; -export default function RootLayout({ - children -}: Readonly<{ - children: React.ReactNode; -}>) { - return ( - - - {children} - - - - ); +export default function RootLayout({ children }: Props) { + return children; } diff --git a/apps/fronted/src/components/Document.tsx b/apps/fronted/src/components/Document.tsx new file mode 100644 index 0000000..598ecf6 --- /dev/null +++ b/apps/fronted/src/components/Document.tsx @@ -0,0 +1,19 @@ +import { Inter } from "next/font/google"; +import { ReactNode } from "react"; + +const inter = Inter({ + subsets: ["latin"], +}); + +type Props = { + children: ReactNode; + locale: string; +}; + +export default function Document({ children, locale }: Props) { + return ( + + {children} + + ); +} diff --git a/apps/fronted/src/components/editor/EditorHeader.tsx b/apps/fronted/src/components/editor/EditorHeader.tsx index d3dbe3f..b36d9aa 100644 --- a/apps/fronted/src/components/editor/EditorHeader.tsx +++ b/apps/fronted/src/components/editor/EditorHeader.tsx @@ -16,6 +16,7 @@ import { } from "@/components/ui/hover-card"; import { AlertCircle } from "lucide-react"; import { Button } from "@/components/ui/button"; +import { useTranslations } from "next-intl"; interface EditorHeaderProps { isMobile?: boolean; @@ -28,7 +29,7 @@ export function EditorHeader({ isMobile }: EditorHeaderProps) { const themeConfig = getThemeConfig(); const { errors, selectError } = useGrammarCheck(); const router = useRouter(); - + const t = useTranslations(); const visibleSections = menuSections ?.filter((section) => section.enabled) .sort((a, b) => a.order - b.order); @@ -46,10 +47,10 @@ export function EditorHeader({ isMobile }: EditorHeaderProps) { whileHover={{ scale: 1.02 }} whileTap={{ scale: 0.98 }} onClick={() => { - router.push("/dashboard"); + router.push("/app/dashboard"); }} > - Magic Resume + {t("common.title")}
diff --git a/apps/fronted/src/components/home/Header.tsx b/apps/fronted/src/components/home/Header.tsx index 3344fda..2033b91 100644 --- a/apps/fronted/src/components/home/Header.tsx +++ b/apps/fronted/src/components/home/Header.tsx @@ -57,10 +57,10 @@ export default function Header() { initial={{ y: 0, opacity: 1 }} animate={{ y: isVisible ? 0 : -100, - opacity: isVisible ? 1 : 0 + opacity: isVisible ? 1 : 0, }} transition={{ - duration: 0.2 + duration: 0.2, }} >
@@ -79,7 +79,7 @@ export default function Header() {
- +