diff --git a/src/app/layout.tsx b/src/app/layout.tsx
new file mode 100644
index 00000000000..1680678e9ff
--- /dev/null
+++ b/src/app/layout.tsx
@@ -0,0 +1,36 @@
+import type { Metadata } from "next";
+import { Inter } from "next/font/google";
+import "./globals.css";
+
+import AntdGlobalProvider from "@/contexts/AntdGlobalProvider";
+import { AuthProvider } from "@/contexts/AuthContext";
+import { LanguageProvider } from "@/contexts/LanguageContext";
+import ReactQueryProvider from "@/contexts/ReactQueryProvider";
+
+const inter = Inter({ subsets: ["latin"] });
+
+export const metadata: Metadata = {
+ title: "LiteLLM Dashboard",
+ description: "LiteLLM Proxy Admin UI",
+ icons: { icon: "/get_favicon" },
+};
+
+export default function RootLayout({
+ children,
+}: Readonly<{
+ children: React.ReactNode;
+}>) {
+ return (
+
+
+
+
+
+ {children}
+
+
+
+
+
+ );
+}
\ No newline at end of file
diff --git a/src/components/LanguageSwitcher.tsx b/src/components/LanguageSwitcher.tsx
new file mode 100644
index 00000000000..893008e7423
--- /dev/null
+++ b/src/components/LanguageSwitcher.tsx
@@ -0,0 +1,37 @@
+"use client";
+
+import React from "react";
+import { Button } from "antd";
+import { useLanguage } from "@/contexts/LanguageContext";
+
+/**
+ * Inline language toggle used in navbar.
+ * Shows the opposite language (中文 ↔ English).
+ */
+export function LanguageSwitcher() {
+ const { locale, setLocale, t } = useLanguage();
+
+ const toggle = () => setLocale(locale === "en" ? "zh" : "en");
+
+ return (
+
+ );
+}
+
+/**
+ * Wraps children in LanguageProvider.
+ * This should be placed above ConfigProvider in the component tree.
+ */
+export function LanguageProviderWrapper({ children }: { children: React.ReactNode }) {
+ return {children};
+}
+
+export { LanguageProvider };
\ No newline at end of file
diff --git a/src/components/navbar.tsx b/src/components/navbar.tsx
new file mode 100644
index 00000000000..d153616ae37
--- /dev/null
+++ b/src/components/navbar.tsx
@@ -0,0 +1,167 @@
+import { useHealthReadinessDetails } from "@/app/(dashboard)/hooks/healthReadiness/useHealthReadinessDetails";
+import { useDisableBouncingIcon } from "@/app/(dashboard)/hooks/useDisableBouncingIcon";
+import { useDisableShowPrompts } from "@/app/(dashboard)/hooks/useDisableShowPrompts";
+import { useWorker } from "@/hooks/useWorker";
+import { getProxyBaseUrl } from "@/components/networking";
+import { useTheme } from "@/contexts/ThemeContext";
+import { clearTokenCookies } from "@/utils/cookieUtils";
+import { clearStoredReturnUrl } from "@/utils/returnUrlUtils";
+import useProxySettings from "@/app/(dashboard)/hooks/proxySettings/useProxySettings";
+import { DownOutlined, MenuFoldOutlined, MenuUnfoldOutlined } from "@ant-design/icons";
+import { Tag } from "antd";
+import Link from "next/link";
+import React from "react";
+import { BlogDropdown } from "./Navbar/BlogDropdown/BlogDropdown";
+import { CommunityEngagementButtons } from "./Navbar/CommunityEngagementButtons/CommunityEngagementButtons";
+import { NAV_PRODUCT_LINK_CLASS } from "./Navbar/navProductLinkClass";
+import { NotificationsBell } from "./Navbar/NotificationsBell/NotificationsBell";
+import UserDropdown from "./Navbar/UserDropdown/UserDropdown";
+import { LanguageSwitcher } from "./LanguageSwitcher";
+import WorkerDropdown from "./Navbar/WorkerDropdown/WorkerDropdown";
+
+interface NavbarProps {
+ accessToken: string | null;
+ isPublicPage: boolean;
+ sidebarCollapsed?: boolean;
+ onToggleSidebar?: () => void;
+}
+
+const Navbar: React.FC = ({
+ accessToken,
+ isPublicPage = false,
+ sidebarCollapsed = false,
+ onToggleSidebar,
+}) => {
+ const baseUrl = getProxyBaseUrl();
+ const proxySettings = useProxySettings(accessToken);
+ const { logoUrl } = useTheme();
+ const { data: healthData } = useHealthReadinessDetails(accessToken);
+ const version = healthData?.litellm_version;
+ const disableBouncingIcon = useDisableBouncingIcon();
+ const hideCommunityLinks = useDisableShowPrompts();
+ const { isControlPlane, selectedWorker } = useWorker();
+ const showWorkerSwitch = isControlPlane && selectedWorker !== null;
+
+ const imageUrl = logoUrl || `${baseUrl}/get_image`;
+
+ const handleLogout = () => {
+ clearTokenCookies();
+ localStorage.removeItem("litellm_selected_worker_id");
+ localStorage.removeItem("litellm_worker_url");
+ window.location.href = proxySettings.PROXY_LOGOUT_URL || "";
+ };
+
+ const handleWorkerSwitch = (workerId: string) => {
+ clearTokenCookies();
+ clearStoredReturnUrl();
+ localStorage.removeItem("litellm_selected_worker_id");
+ localStorage.removeItem("litellm_worker_url");
+ window.location.href = `/ui/login?worker=${encodeURIComponent(workerId)}`;
+ };
+
+ return (
+
+ );
+};
+
+export default Navbar;
\ No newline at end of file
diff --git a/src/contexts/LanguageContext.tsx b/src/contexts/LanguageContext.tsx
new file mode 100644
index 00000000000..05e10ca8529
--- /dev/null
+++ b/src/contexts/LanguageContext.tsx
@@ -0,0 +1,61 @@
+"use client";
+
+import React, { createContext, useCallback, useContext, useEffect, useState } from "react";
+import { ConfigProvider } from "antd";
+import enUS from "antd/locale/en_US";
+import zhCN from "antd/locale/zh_CN";
+import { Locale, DEFAULT_LOCALE, LOCALE_STORAGE_KEY, t as translate } from "@/i18n";
+
+interface LanguageContextValue {
+ locale: Locale;
+ setLocale: (l: Locale) => void;
+ t: (key: string) => string;
+}
+
+const LanguageContext = createContext({
+ locale: DEFAULT_LOCALE,
+ setLocale: () => {},
+ t: (key) => key,
+});
+
+export function useLanguage() {
+ return useContext(LanguageContext);
+}
+
+const antdLocales: Record = {
+ en: enUS,
+ zh: zhCN,
+};
+
+export function LanguageProvider({ children }: { children: React.ReactNode }) {
+ const [locale, setLocaleState] = useState(DEFAULT_LOCALE);
+
+ // Hydrate from localStorage on mount
+ useEffect(() => {
+ try {
+ const stored = localStorage.getItem(LOCALE_STORAGE_KEY);
+ if (stored === "zh" || stored === "en") {
+ setLocaleState(stored);
+ }
+ } catch {
+ // localStorage not available
+ }
+ }, []);
+
+ const setLocale = useCallback((l: Locale) => {
+ setLocaleState(l);
+ try {
+ localStorage.setItem(LOCALE_STORAGE_KEY, l);
+ } catch {
+ // ignore
+ }
+ }, []);
+
+ const t = useCallback((key: string) => translate(key, locale), [locale]);
+
+ return (
+
+ {children}
+
+ );
+}
\ No newline at end of file
diff --git a/src/i18n/index.ts b/src/i18n/index.ts
new file mode 100644
index 00000000000..6560cd85d77
--- /dev/null
+++ b/src/i18n/index.ts
@@ -0,0 +1,54 @@
+/**
+ * LiteLLM i18n - Lightweight translation system
+ *
+ * Supports English (default) and Chinese (中文).
+ * Uses React Context for state + localStorage for persistence.
+ * Zero external dependencies.
+ */
+
+import en from "./locales/en.json";
+import zh from "./locales/zh.json";
+
+export type Locale = "en" | "zh";
+export type TranslationMap = Record>;
+
+const translations: Record = { en, zh };
+
+/**
+ * Resolve a dotted key like "nav.dashboard" against the current locale.
+ */
+export function t(key: string, locale: Locale = "en"): string {
+ const map = translations[locale] || translations.en;
+ const parts = key.split(".");
+ let current: any = map;
+ for (const part of parts) {
+ if (current == null) break;
+ current = current[part];
+ }
+ if (typeof current === "string") return current;
+
+ // Fallback to English
+ let fallback: any = translations.en;
+ for (const part of parts) {
+ if (fallback == null) break;
+ fallback = fallback[part];
+ }
+ if (typeof fallback === "string") return fallback;
+
+ // Ultimate fallback: return the key itself
+ return key;
+}
+
+/** All supported locales (used by the switcher) */
+export const SUPPORTED_LOCALES: { value: Locale; label: string }[] = [
+ { value: "en", label: "English" },
+ { value: "zh", label: "中文" },
+];
+
+/** LocalStorage key */
+export const LOCALE_STORAGE_KEY = "litellm_locale";
+
+/** Default locale */
+export const DEFAULT_LOCALE: Locale = "en";
+
+export { translations };
\ No newline at end of file
diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json
new file mode 100644
index 00000000000..aef5deb5719
--- /dev/null
+++ b/src/i18n/locales/en.json
@@ -0,0 +1,130 @@
+{
+ "common": {
+ "save": "Save",
+ "cancel": "Cancel",
+ "delete": "Delete",
+ "edit": "Edit",
+ "create": "Create",
+ "search": "Search",
+ "refresh": "Refresh",
+ "loading": "Loading...",
+ "no_data": "No data",
+ "confirm": "Confirm",
+ "back": "Back",
+ "next": "Next",
+ "submit": "Submit",
+ "reset": "Reset",
+ "copy": "Copy",
+ "copied": "Copied!",
+ "close": "Close",
+ "export": "Export",
+ "import": "Import",
+ "more": "More",
+ "actions": "Actions",
+ "status": "Status",
+ "enabled": "Enabled",
+ "disabled": "Disabled",
+ "active": "Active",
+ "inactive": "Inactive",
+ "yes": "Yes",
+ "no": "No",
+ "on": "On",
+ "off": "Off",
+ "success": "Success",
+ "error": "Error",
+ "warning": "Warning",
+ "info": "Info"
+ },
+ "nav": {
+ "dashboard": "Dashboard",
+ "models": "Models & Endpoints",
+ "api_keys": "API Keys",
+ "users": "Users",
+ "teams": "Teams",
+ "logs": "Logs",
+ "usage": "Usage",
+ "spend": "Cost Tracking",
+ "settings": "Settings",
+ "admin": "Admin Panel",
+ "playground": "Playground",
+ "budgets": "Budgets",
+ "caching": "Caching",
+ "guardrails": "Guardrails",
+ "hooks": "Webhooks",
+ "model_hub": "Model Hub",
+ "router": "Router Settings",
+ "organizations": "Organizations",
+ "credentials": "Credentials",
+ "agents": "Agents",
+ "prompts": "Prompts",
+ "mcp": "MCP Servers",
+ "policies": "Policies",
+ "skills": "Skills",
+ "workflows": "Workflows",
+ "memory": "Memory",
+ "vector_stores": "Vector Stores",
+ "search_tools": "Search Tools",
+ "tag_management": "Tag Management",
+ "access_groups": "Access Groups",
+ "ui_theme": "UI Theme"
+ },
+ "models": {
+ "title": "Models & Endpoints",
+ "add_model": "Add Model",
+ "model_name": "Model Name",
+ "provider": "Provider",
+ "api_base": "API Base URL",
+ "api_key": "API Key",
+ "rpm": "RPM",
+ "tpm": "TPM",
+ "max_retries": "Max Retries",
+ "timeout": "Timeout (s)",
+ "custom_llm_provider": "Provider",
+ "litellm_params": "LiteLLM Parameters",
+ "model_info": "Model Info",
+ "deployment_name": "Deployment Name",
+ "no_models": "No models configured",
+ "delete_confirm": "Are you sure you want to delete this model?"
+ },
+ "keys": {
+ "title": "API Keys",
+ "create_key": "Create Key",
+ "key_name": "Key Name",
+ "key": "Key",
+ "models": "Models",
+ "max_budget": "Max Budget",
+ "budget_duration": "Budget Duration",
+ "expires_at": "Expires At",
+ "never": "Never",
+ "no_keys": "No API keys created",
+ "delete_confirm": "Are you sure you want to delete this key?"
+ },
+ "users": {
+ "title": "Users",
+ "create_user": "Create User",
+ "user_email": "Email",
+ "user_role": "Role",
+ "user_id": "User ID",
+ "no_users": "No users found"
+ },
+ "logs": {
+ "title": "Logs",
+ "request_id": "Request ID",
+ "model": "Model",
+ "status_code": "Status",
+ "tokens": "Tokens",
+ "cost": "Cost",
+ "duration": "Duration",
+ "timestamp": "Time"
+ },
+ "settings": {
+ "title": "Settings",
+ "general": "General",
+ "proxy": "Proxy Settings",
+ "alerting": "Alerting"
+ },
+ "language": {
+ "switch_to": "中文",
+ "label": "Language"
+ }
+}
\ No newline at end of file
diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json
new file mode 100644
index 00000000000..9026a8cd80b
--- /dev/null
+++ b/src/i18n/locales/zh.json
@@ -0,0 +1,130 @@
+{
+ "common": {
+ "save": "保存",
+ "cancel": "取消",
+ "delete": "删除",
+ "edit": "编辑",
+ "create": "创建",
+ "search": "搜索",
+ "refresh": "刷新",
+ "loading": "加载中...",
+ "no_data": "暂无数据",
+ "confirm": "确认",
+ "back": "返回",
+ "next": "下一步",
+ "submit": "提交",
+ "reset": "重置",
+ "copy": "复制",
+ "copied": "已复制!",
+ "close": "关闭",
+ "export": "导出",
+ "import": "导入",
+ "more": "更多",
+ "actions": "操作",
+ "status": "状态",
+ "enabled": "已启用",
+ "disabled": "已禁用",
+ "active": "活跃",
+ "inactive": "不活跃",
+ "yes": "是",
+ "no": "否",
+ "on": "开",
+ "off": "关",
+ "success": "成功",
+ "error": "错误",
+ "warning": "警告",
+ "info": "提示"
+ },
+ "nav": {
+ "dashboard": "仪表盘",
+ "models": "模型与端点",
+ "api_keys": "API 密钥",
+ "users": "用户管理",
+ "teams": "团队管理",
+ "logs": "日志",
+ "usage": "用量统计",
+ "spend": "费用追踪",
+ "settings": "设置",
+ "admin": "管理面板",
+ "playground": "调试沙盒",
+ "budgets": "预算管理",
+ "caching": "缓存设置",
+ "guardrails": "安全护栏",
+ "hooks": "Webhooks",
+ "model_hub": "模型中心",
+ "router": "路由设置",
+ "organizations": "组织管理",
+ "credentials": "凭证管理",
+ "agents": "智能代理",
+ "prompts": "提示词",
+ "mcp": "MCP 服务",
+ "policies": "策略管理",
+ "skills": "技能管理",
+ "workflows": "工作流",
+ "memory": "记忆管理",
+ "vector_stores": "向量存储",
+ "search_tools": "搜索工具",
+ "tag_management": "标签管理",
+ "access_groups": "访问组",
+ "ui_theme": "界面主题"
+ },
+ "models": {
+ "title": "模型与端点",
+ "add_model": "添加模型",
+ "model_name": "模型名称",
+ "provider": "提供商",
+ "api_base": "API 地址",
+ "api_key": "API 密钥",
+ "rpm": "每分钟请求数",
+ "tpm": "每分钟 Token 数",
+ "max_retries": "最大重试次数",
+ "timeout": "超时 (秒)",
+ "custom_llm_provider": "提供商",
+ "litellm_params": "LiteLLM 参数",
+ "model_info": "模型信息",
+ "deployment_name": "部署名称",
+ "no_models": "未配置模型",
+ "delete_confirm": "确定要删除此模型吗?"
+ },
+ "keys": {
+ "title": "API 密钥",
+ "create_key": "创建密钥",
+ "key_name": "密钥名称",
+ "key": "密钥",
+ "models": "模型",
+ "max_budget": "最大预算",
+ "budget_duration": "预算周期",
+ "expires_at": "过期时间",
+ "never": "永不过期",
+ "no_keys": "暂无 API 密钥",
+ "delete_confirm": "确定要删除此密钥吗?"
+ },
+ "users": {
+ "title": "用户管理",
+ "create_user": "创建用户",
+ "user_email": "邮箱",
+ "user_role": "角色",
+ "user_id": "用户 ID",
+ "no_users": "暂无用户"
+ },
+ "logs": {
+ "title": "日志",
+ "request_id": "请求 ID",
+ "model": "模型",
+ "status_code": "状态码",
+ "tokens": "Token 数",
+ "cost": "费用",
+ "duration": "耗时",
+ "timestamp": "时间"
+ },
+ "settings": {
+ "title": "设置",
+ "general": "常规设置",
+ "proxy": "代理设置",
+ "alerting": "告警设置"
+ },
+ "language": {
+ "switch_to": "English",
+ "label": "语言"
+ }
+}
\ No newline at end of file