diff --git a/ui/litellm-dashboard/src/components/Projects/ProjectDetailsPage.tsx b/ui/litellm-dashboard/src/components/Projects/ProjectDetailsPage.tsx
index 637771e2299..1cd9ce55252 100644
--- a/ui/litellm-dashboard/src/components/Projects/ProjectDetailsPage.tsx
+++ b/ui/litellm-dashboard/src/components/Projects/ProjectDetailsPage.tsx
@@ -19,6 +19,7 @@ import { LoadingOutlined } from "@ant-design/icons";
import { BarChart } from "@tremor/react";
import { ArrowLeftIcon, DollarSignIcon, EditIcon, KeyIcon, UsersIcon } from "lucide-react";
import { useMemo, useState } from "react";
+import { useTranslation } from "react-i18next";
import DefaultProxyAdminTag from "../common_components/DefaultProxyAdminTag";
import { EditProjectModal } from "./ProjectModals/EditProjectModal";
@@ -41,6 +42,7 @@ interface ProjectDetailProps {
}
export function ProjectDetail({ projectId, onBack }: ProjectDetailProps) {
+ const { t } = useTranslation();
const { data: project, isLoading } = useProjectDetails(projectId);
const { data: teamData } = useTeam(project?.team_id ?? undefined);
// teamInfoCall returns { team_id, team_info: {...}, keys, team_memberships }
@@ -86,7 +88,7 @@ export function ProjectDetail({ projectId, onBack }: ProjectDetailProps) {
}}
>
} onClick={onBack} type="text" style={{ marginBottom: 16 }} />
-
+
);
}
@@ -109,7 +111,9 @@ export function ProjectDetail({ projectId, onBack }: ProjectDetailProps) {
{project.project_alias ?? project.project_id}
- {project.blocked ? "Blocked" : "Active"}
+
+ {project.blocked ? t("projects.projectDetailsPage.statusBlocked") : t("common.active")}
+
ID: {project.project_id}
@@ -117,29 +121,29 @@ export function ProjectDetail({ projectId, onBack }: ProjectDetailProps) {
} onClick={() => setIsEditModalVisible(true)}>
- Edit Project
+ {t("projects.projectDetailsPage.editProject")}
{/* Project Details */}
-
- {project.description || "\u2014"}
-
+
+ {project.description || "\u2014"}
+
{new Date(project.created_at).toLocaleString()}
{project.created_by && (
- {"by"}
+ {t("projects.projectDetailsPage.by")}
)}
-
+
{new Date(project.updated_at).toLocaleString()}
{project.updated_by && (
- {"by"}
+ {t("projects.projectDetailsPage.by")}
)}
@@ -155,7 +159,7 @@ export function ProjectDetail({ projectId, onBack }: ProjectDetailProps) {
title={
- Budget
+ {t("projects.projectDetailsPage.budgetTitle")}
}
style={{ height: "100%" }}
@@ -166,13 +170,19 @@ export function ProjectDetail({ projectId, onBack }: ProjectDetailProps) {
${spend.toFixed(2)}
- {hasLimit ? `of $${maxBudget.toFixed(2)} budget` : "No budget limit"}
+
+ {hasLimit
+ ? t("projects.projectDetailsPage.budgetOf", { budget: maxBudget.toFixed(2) })
+ : t("projects.projectDetailsPage.noBudgetLimit")}
+
{hasLimit && (
- {(Math.round(spendPercent * 10) / 10).toFixed(1)}% utilized
+ {t("projects.projectDetailsPage.utilized", {
+ percent: (Math.round(spendPercent * 10) / 10).toFixed(1),
+ })}
)}
@@ -180,7 +190,7 @@ export function ProjectDetail({ projectId, onBack }: ProjectDetailProps) {
-
+
{modelSpendData.length > 0 ? (
) : (
-
+
)}
@@ -207,12 +217,15 @@ export function ProjectDetail({ projectId, onBack }: ProjectDetailProps) {
title={
- Keys
+ {t("projects.projectDetailsPage.keysTitle")}
}
style={{ height: "100%" }}
>
-
+
@@ -220,7 +233,7 @@ export function ProjectDetail({ projectId, onBack }: ProjectDetailProps) {
title={
- Team
+ {t("projects.projectDetailsPage.teamTitle")}
}
style={{ height: "100%" }}
@@ -252,7 +265,7 @@ export function ProjectDetail({ projectId, onBack }: ProjectDetailProps) {
{/* Models */}
- Models
+ {t("projects.projectDetailsPage.modelsLabel")}
{(teamInfo.models?.length ?? 0) > 0 ? (
@@ -263,7 +276,7 @@ export function ProjectDetail({ projectId, onBack }: ProjectDetailProps) {
))}
) : (
- All models
+ {t("projects.projectDetailsPage.allModels")}
)}
@@ -271,7 +284,7 @@ export function ProjectDetail({ projectId, onBack }: ProjectDetailProps) {
- Spend
+ {t("projects.projectDetailsPage.spendLabel")}
${teamSpend.toFixed(2)}
@@ -283,7 +296,7 @@ export function ProjectDetail({ projectId, onBack }: ProjectDetailProps) {
) : (
{" "}
- (Unlimited)
+ ({t("projects.projectDetailsPage.unlimited")})
)}
@@ -301,7 +314,7 @@ export function ProjectDetail({ projectId, onBack }: ProjectDetailProps) {
{/* Members */}
- Members
+ {t("projects.projectDetailsPage.membersLabel")}
{teamInfo.members_with_roles?.length ?? 0}
@@ -313,7 +326,10 @@ export function ProjectDetail({ projectId, onBack }: ProjectDetailProps) {
} size="small" />
) : (
-
+
)}
diff --git a/ui/litellm-dashboard/src/components/Projects/ProjectKeysSection.tsx b/ui/litellm-dashboard/src/components/Projects/ProjectKeysSection.tsx
index 9f60db596e8..e1bdb094b94 100644
--- a/ui/litellm-dashboard/src/components/Projects/ProjectKeysSection.tsx
+++ b/ui/litellm-dashboard/src/components/Projects/ProjectKeysSection.tsx
@@ -3,6 +3,7 @@ import { LoadingOutlined } from "@ant-design/icons";
import { Card, Flex, Input, Pagination, Spin } from "antd";
import { KeyIcon, SearchIcon } from "lucide-react";
import { useEffect, useState } from "react";
+import { useTranslation } from "react-i18next";
import { ProjectKeysTable } from "./ProjectKeysTable";
interface ProjectKeysSectionProps {
@@ -12,6 +13,7 @@ interface ProjectKeysSectionProps {
const PAGE_SIZE = 5;
export function ProjectKeysSection({ projectId }: ProjectKeysSectionProps) {
+ const { t } = useTranslation();
const [page, setPage] = useState(1);
const [keyAlias, setKeyAlias] = useState
("");
@@ -33,7 +35,7 @@ export function ProjectKeysSection({ projectId }: ProjectKeysSectionProps) {
title={
- Keys
+ {t("projects.projectKeysSection.title")}
}
style={{ height: "100%" }}
@@ -41,7 +43,7 @@ export function ProjectKeysSection({ projectId }: ProjectKeysSectionProps) {
}
- placeholder="Filter by key name..."
+ placeholder={t("projects.projectKeysSection.filterPlaceholder")}
style={{ maxWidth: 220 }}
value={keyAlias}
onChange={(e) => setKeyAlias(e.target.value)}
@@ -55,7 +57,7 @@ export function ProjectKeysSection({ projectId }: ProjectKeysSectionProps) {
onChange={setPage}
size="small"
showSizeChanger={false}
- showTotal={(total) => `${total} keys`}
+ showTotal={(total) => t("projects.projectKeysSection.totalKeys", { count: total })}
/>
= [
+const getProjectKeysTableColumns = (t: TFunction): ColumnsType => [
{
- title: "Key Name",
+ title: t("projects.projectKeysTable.colKeyName"),
dataIndex: "key_alias",
key: "key_alias",
render: (alias: string | null) => alias || "—",
},
{
- title: "Owner",
+ title: t("projects.projectKeysTable.colOwner"),
key: "owner",
render: (_: unknown, record: KeyResponse) => {
const email = record.user?.user_email ?? record.user_id ?? null;
@@ -30,20 +33,23 @@ const columns: ColumnsType = [
},
},
{
- title: "Created",
+ title: t("common.createdAt"),
dataIndex: "created_at",
key: "created_at",
render: (date: string) => (date ? new Date(date).toLocaleDateString() : "—"),
},
{
- title: "Last Active",
+ title: t("projects.projectKeysTable.colLastActive"),
dataIndex: "last_active",
key: "last_active",
- render: (date: string | null) => (date ? new Date(date).toLocaleDateString() : "Never"),
+ render: (date: string | null) => (date ? new Date(date).toLocaleDateString() : t("common.never")),
},
];
export function ProjectKeysTable({ keys, loading }: ProjectKeysTableProps) {
+ const { t } = useTranslation();
+ const columns = useMemo(() => getProjectKeysTableColumns(t), [t]);
+
return (
}}
+ locale={{
+ emptyText: (
+
+ ),
+ }}
/>
);
}
diff --git a/ui/litellm-dashboard/src/components/Projects/ProjectModals/CreateProjectModal.tsx b/ui/litellm-dashboard/src/components/Projects/ProjectModals/CreateProjectModal.tsx
index c7d1f54bd47..d53f4ba0c93 100644
--- a/ui/litellm-dashboard/src/components/Projects/ProjectModals/CreateProjectModal.tsx
+++ b/ui/litellm-dashboard/src/components/Projects/ProjectModals/CreateProjectModal.tsx
@@ -2,6 +2,7 @@ import { Modal, Form, Button, Typography } from "antd";
import { FolderAddOutlined } from "@ant-design/icons";
import MessageManager from "@/components/molecules/message_manager";
import { useCreateProject, ProjectCreateParams } from "@/app/(dashboard)/hooks/projects/useCreateProject";
+import { useTranslation } from "react-i18next";
import { ProjectBaseForm, ProjectFormValues } from "./ProjectBaseForm";
import { buildProjectApiParams } from "./projectFormUtils";
@@ -11,6 +12,7 @@ interface CreateProjectModalProps {
}
export function CreateProjectModal({ isOpen, onClose }: CreateProjectModalProps) {
+ const { t } = useTranslation();
const [form] = Form.useForm();
const createMutation = useCreateProject();
@@ -24,12 +26,12 @@ export function CreateProjectModal({ isOpen, onClose }: CreateProjectModalProps)
createMutation.mutate(params, {
onSuccess: () => {
- MessageManager.success("Project created successfully");
+ MessageManager.success(t("projects.createProjectModal.createSuccess"));
form.resetFields();
onClose();
},
onError: (error) => {
- MessageManager.error(error.message || "Failed to create project");
+ MessageManager.error(error.message || t("projects.createProjectModal.createFailed"));
},
});
} catch (error) {
@@ -46,7 +48,7 @@ export function CreateProjectModal({ isOpen, onClose }: CreateProjectModalProps)
- Create New Project
+ {t("projects.createProjectModal.title")}
}
open={isOpen}
@@ -55,7 +57,7 @@ export function CreateProjectModal({ isOpen, onClose }: CreateProjectModalProps)
destroyOnHidden
footer={[
,
,
]}
>
diff --git a/ui/litellm-dashboard/src/components/Projects/ProjectModals/EditProjectModal.tsx b/ui/litellm-dashboard/src/components/Projects/ProjectModals/EditProjectModal.tsx
index d8d9973d156..04396e3b8c4 100644
--- a/ui/litellm-dashboard/src/components/Projects/ProjectModals/EditProjectModal.tsx
+++ b/ui/litellm-dashboard/src/components/Projects/ProjectModals/EditProjectModal.tsx
@@ -4,6 +4,7 @@ import { SaveOutlined } from "@ant-design/icons";
import MessageManager from "@/components/molecules/message_manager";
import { ProjectResponse } from "@/app/(dashboard)/hooks/projects/useProjects";
import { useUpdateProject, ProjectUpdateParams } from "@/app/(dashboard)/hooks/projects/useUpdateProject";
+import { useTranslation } from "react-i18next";
import { ProjectBaseForm, ProjectFormValues } from "./ProjectBaseForm";
import { buildProjectApiParams } from "./projectFormUtils";
@@ -15,6 +16,7 @@ interface EditProjectModalProps {
}
export function EditProjectModal({ isOpen, project, onClose, onSuccess }: EditProjectModalProps) {
+ const { t } = useTranslation();
const [form] = Form.useForm();
const updateMutation = useUpdateProject();
@@ -72,12 +74,12 @@ export function EditProjectModal({ isOpen, project, onClose, onSuccess }: EditPr
{ projectId: project.project_id, params },
{
onSuccess: () => {
- MessageManager.success("Project updated successfully");
+ MessageManager.success(t("projects.editProjectModal.updateSuccess"));
onSuccess?.();
onClose();
},
onError: (error) => {
- MessageManager.error(error.message || "Failed to update project");
+ MessageManager.error(error.message || t("projects.editProjectModal.updateFailed"));
},
},
);
@@ -90,7 +92,7 @@ export function EditProjectModal({ isOpen, project, onClose, onSuccess }: EditPr
- Edit Project
+ {t("projects.editProjectModal.title")}
}
open={isOpen}
@@ -99,7 +101,7 @@ export function EditProjectModal({ isOpen, project, onClose, onSuccess }: EditPr
destroyOnHidden
footer={[
,
,
]}
>
diff --git a/ui/litellm-dashboard/src/components/Projects/ProjectModals/ProjectBaseForm.tsx b/ui/litellm-dashboard/src/components/Projects/ProjectModals/ProjectBaseForm.tsx
index ea0f31b56f0..1a30404a322 100644
--- a/ui/litellm-dashboard/src/components/Projects/ProjectModals/ProjectBaseForm.tsx
+++ b/ui/litellm-dashboard/src/components/Projects/ProjectModals/ProjectBaseForm.tsx
@@ -18,6 +18,7 @@ import {
import type { FormInstance } from "antd";
import { PlusOutlined, MinusCircleOutlined } from "@ant-design/icons";
import useAuthorized from "@/app/(dashboard)/hooks/useAuthorized";
+import { useTranslation } from "react-i18next";
import { useTeams } from "@/app/(dashboard)/hooks/teams/useTeams";
import { Team } from "../../key_team_helpers/key_list";
import { fetchTeamModels } from "../../organisms/create_key_button";
@@ -41,6 +42,7 @@ interface ProjectBaseFormProps {
}
export function ProjectBaseForm({ form }: ProjectBaseFormProps) {
+ const { t } = useTranslation();
const { accessToken, userId, userRole } = useAuthorized();
const { data: teams } = useTeams();
@@ -109,7 +111,7 @@ export function ProjectBaseForm({ form }: ProjectBaseFormProps) {
letterSpacing: "0.05em",
}}
>
- Basic Information
+ {t("projects.projectBaseForm.basicInfoTitle")}
@@ -117,17 +119,21 @@ export function ProjectBaseForm({ form }: ProjectBaseFormProps) {
-
+
-
+