- Implement runtime i18n using MutationObserver for automatic DOM translation
- Add language switcher dropdown in dashboard header (EN/VI/ZH)
- Support 3 languages: English (default), Tiếng Việt, 简体中文
- Add translation files: vi.json (197 entries), zh-CN.json (513 entries, cleaned)
- Translate dashboard UI: sidebar menu, header, settings, MITM page
- Use cookie-based locale persistence with /api/locale endpoint
- Zero component changes required - translations applied at runtime
- Fix Header flicker on route change with key={pathname}
Co-authored-by: eachann <each1024@qq.com>
Based on PR #247 from decolua/9router with runtime approach
Made-with: Cursor
45 lines
1.4 KiB
JavaScript
45 lines
1.4 KiB
JavaScript
"use client";
|
|
|
|
import { useState } from "react";
|
|
import { usePathname } from "next/navigation";
|
|
import Sidebar from "../Sidebar";
|
|
import Header from "../Header";
|
|
|
|
export default function DashboardLayout({ children }) {
|
|
const [sidebarOpen, setSidebarOpen] = useState(false);
|
|
const pathname = usePathname();
|
|
|
|
return (
|
|
<div className="flex h-screen w-full overflow-hidden bg-bg">
|
|
{/* Mobile sidebar overlay */}
|
|
{sidebarOpen && (
|
|
<div
|
|
className="fixed inset-0 z-40 bg-black/20 lg:hidden"
|
|
onClick={() => setSidebarOpen(false)}
|
|
/>
|
|
)}
|
|
|
|
{/* Sidebar - Desktop */}
|
|
<div className="hidden lg:flex">
|
|
<Sidebar />
|
|
</div>
|
|
|
|
{/* Sidebar - Mobile */}
|
|
<div
|
|
className={`fixed inset-y-0 left-0 z-50 transform lg:hidden transition-transform duration-300 ease-in-out ${
|
|
sidebarOpen ? "translate-x-0" : "-translate-x-full"
|
|
}`}
|
|
>
|
|
<Sidebar onClose={() => setSidebarOpen(false)} />
|
|
</div>
|
|
|
|
{/* Main content */}
|
|
<main className="flex flex-col flex-1 h-full min-w-0 relative transition-colors duration-300">
|
|
<Header key={pathname} onMenuClick={() => setSidebarOpen(true)} />
|
|
<div className="flex-1 overflow-y-auto custom-scrollbar p-6 lg:p-10">
|
|
<div className="max-w-7xl mx-auto">{children}</div>
|
|
</div>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|