diff --git a/ui/litellm-dashboard/src/components/EntityUsageExport/EntityUsageExportModal.tsx b/ui/litellm-dashboard/src/components/EntityUsageExport/EntityUsageExportModal.tsx index 4c934bbf1d3..62964f3b116 100644 --- a/ui/litellm-dashboard/src/components/EntityUsageExport/EntityUsageExportModal.tsx +++ b/ui/litellm-dashboard/src/components/EntityUsageExport/EntityUsageExportModal.tsx @@ -2,6 +2,7 @@ import { useTeams } from "@/app/(dashboard)/hooks/teams/useTeams"; import { createTeamAliasMap } from "@/utils/teamUtils"; import { Button, Modal, Skeleton } from "antd"; import React, { useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; import NotificationsManager from "../molecules/notifications_manager"; import ExportFormatSelector from "./ExportFormatSelector"; import ExportSummary from "./ExportSummary"; @@ -18,13 +19,14 @@ const EntityUsageExportModal: React.FC = ({ selectedFilters, customTitle, }) => { + const { t } = useTranslation(); const [exportFormat, setExportFormat] = useState("csv"); const [exportScope, setExportScope] = useState("daily"); const [isExporting, setIsExporting] = useState(false); const { data: teams, isLoading: isLoadingTeams } = useTeams(); const entityLabel = entityType.charAt(0).toUpperCase() + entityType.slice(1); - const modalTitle = customTitle || `Export ${entityLabel} Usage`; + const modalTitle = customTitle || t("usageExport.entityUsageExportModal.exportTitle", { entityLabel }); // Cache team alias map using useMemo const teamAliasMap = useMemo(() => createTeamAliasMap(teams), [teams]); @@ -34,15 +36,15 @@ const EntityUsageExportModal: React.FC = ({ try { if (formatToUse === "csv") { handleExportCSV(spendData, exportScope, entityLabel, entityType, teamAliasMap); - NotificationsManager.success(`${entityLabel} usage data exported successfully as CSV`); + NotificationsManager.success(t("usageExport.entityUsageExportModal.exportSuccessCsv", { entityLabel })); } else { handleExportJSON(spendData, exportScope, entityLabel, entityType, dateRange, selectedFilters, teamAliasMap); - NotificationsManager.success(`${entityLabel} usage data exported successfully as JSON`); + NotificationsManager.success(t("usageExport.entityUsageExportModal.exportSuccessJson", { entityLabel })); } onClose(); } catch (error) { console.error("Error exporting data:", error); - NotificationsManager.fromBackend("Failed to export data"); + NotificationsManager.fromBackend(t("usageExport.entityUsageExportModal.exportFailed")); } finally { setIsExporting(false); } @@ -74,7 +76,7 @@ const EntityUsageExportModal: React.FC = ({ ) : (
)} diff --git a/ui/litellm-dashboard/src/components/EntityUsageExport/ExportFormatSelector.tsx b/ui/litellm-dashboard/src/components/EntityUsageExport/ExportFormatSelector.tsx index 2adc4a0eca3..2cf92e3ef19 100644 --- a/ui/litellm-dashboard/src/components/EntityUsageExport/ExportFormatSelector.tsx +++ b/ui/litellm-dashboard/src/components/EntityUsageExport/ExportFormatSelector.tsx @@ -1,4 +1,6 @@ -import React from "react"; +import React, { useMemo } from "react"; +import { useTranslation } from "react-i18next"; +import type { TFunction } from "i18next"; import { Select } from "antd"; import type { ExportFormat } from "./types"; @@ -7,25 +9,27 @@ interface ExportFormatSelectorProps { onChange: (value: ExportFormat) => void; } +const getFormatOptions = (t: TFunction) => [ + { + value: "csv", + label: t("usageExport.exportFormatSelector.optionCsv"), + }, + { + value: "json", + label: t("usageExport.exportFormatSelector.optionJson"), + }, +]; + const ExportFormatSelector: React.FC = ({ value, onChange }) => { + const { t } = useTranslation(); + const formatOptions = useMemo(() => getFormatOptions(t), [t]); + return (
- -
); }; diff --git a/ui/litellm-dashboard/src/components/EntityUsageExport/ExportSummary.tsx b/ui/litellm-dashboard/src/components/EntityUsageExport/ExportSummary.tsx index bec65db9309..aa364f85d38 100644 --- a/ui/litellm-dashboard/src/components/EntityUsageExport/ExportSummary.tsx +++ b/ui/litellm-dashboard/src/components/EntityUsageExport/ExportSummary.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { useTranslation } from "react-i18next"; import type { DateRangePickerValue } from "@tremor/react"; interface ExportSummaryProps { @@ -7,10 +8,13 @@ interface ExportSummaryProps { } const ExportSummary: React.FC = ({ dateRange, selectedFilters }) => { + const { t } = useTranslation(); + return (
{dateRange.from?.toLocaleDateString()} - {dateRange.to?.toLocaleDateString()} - {selectedFilters.length > 0 && ` · ${selectedFilters.length} filter${selectedFilters.length > 1 ? "s" : ""}`} + {selectedFilters.length > 0 && + ` · ${t("usageExport.exportSummary.filterCount", { count: selectedFilters.length })}`}
); }; diff --git a/ui/litellm-dashboard/src/components/EntityUsageExport/ExportTypeSelector.tsx b/ui/litellm-dashboard/src/components/EntityUsageExport/ExportTypeSelector.tsx index 6bc85948608..b7845577457 100644 --- a/ui/litellm-dashboard/src/components/EntityUsageExport/ExportTypeSelector.tsx +++ b/ui/litellm-dashboard/src/components/EntityUsageExport/ExportTypeSelector.tsx @@ -1,4 +1,5 @@ import React from "react"; +import { useTranslation } from "react-i18next"; import { Radio } from "antd"; import type { ExportScope, EntityType } from "./types"; @@ -9,32 +10,48 @@ interface ExportTypeSelectorProps { } const ExportTypeSelector: React.FC = ({ value, onChange, entityType }) => { + const { t } = useTranslation(); + return (
- + onChange(e.target.value)} className="w-full">
diff --git a/ui/litellm-dashboard/src/components/EntityUsageExport/UsageExportHeader.tsx b/ui/litellm-dashboard/src/components/EntityUsageExport/UsageExportHeader.tsx index ee7857a1544..b45bd3e103e 100644 --- a/ui/litellm-dashboard/src/components/EntityUsageExport/UsageExportHeader.tsx +++ b/ui/litellm-dashboard/src/components/EntityUsageExport/UsageExportHeader.tsx @@ -2,6 +2,7 @@ import type { DateRangePickerValue } from "@tremor/react"; import { Button, Text } from "@tremor/react"; import { Select } from "antd"; import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; import EntityUsageExportModal from "./EntityUsageExportModal"; import type { EntitySpendData, EntityType } from "./types"; import type { Team } from "@/components/key_team_helpers/key_list"; @@ -38,6 +39,7 @@ const UsageExportHeader: React.FC = ({ compactLayout = false, teams = [], }) => { + const { t } = useTranslation(); const [isExportModalOpen, setIsExportModalOpen] = useState(false); // Determine grid layout based on what's visible @@ -92,7 +94,7 @@ const UsageExportHeader: React.FC = ({ )} > - Export Data + {t("usageExport.usageExportHeader.exportData")}
diff --git a/ui/litellm-dashboard/src/locales/en.json b/ui/litellm-dashboard/src/locales/en.json index e1cd79fb39f..ad934fe9e6a 100644 --- a/ui/litellm-dashboard/src/locales/en.json +++ b/ui/litellm-dashboard/src/locales/en.json @@ -7123,5 +7123,36 @@ "timezoneLabel": "Timezone", "timezoneTooltip": "Timezone for date handling (defaults to UTC if not provided)" } + }, + "usageExport": { + "entityUsageExportModal": { + "exportTitle": "Export {{entityLabel}} Usage", + "exportSuccessCsv": "{{entityLabel}} usage data exported successfully as CSV", + "exportSuccessJson": "{{entityLabel}} usage data exported successfully as JSON", + "exportFailed": "Failed to export data", + "exporting": "Exporting...", + "exportFormat": "Export {{format}}" + }, + "exportFormatSelector": { + "label": "Format", + "optionCsv": "CSV (Excel, Google Sheets)", + "optionJson": "JSON (includes metadata)" + }, + "exportSummary": { + "filterCount_one": "{{count}} filter", + "filterCount_other": "{{count}} filters" + }, + "exportTypeSelector": { + "label": "Export type", + "dailyTitle": "Day-by-day breakdown by {{entityType}}", + "dailyDesc": "Daily metrics for each {{entityType}}", + "dailyWithKeysTitle": "Day-by-day breakdown by {{entityType}} and key", + "dailyWithKeysDesc": "Daily metrics for each {{entityType}}, split by API key", + "dailyWithModelsTitle": "Day-by-day by {{entityType}} and model", + "dailyWithModelsDesc": "Daily metrics split by model" + }, + "usageExportHeader": { + "exportData": "Export Data" + } } } diff --git a/ui/litellm-dashboard/src/locales/zh-CN.json b/ui/litellm-dashboard/src/locales/zh-CN.json index 431f27d0a4a..f95565266fd 100644 --- a/ui/litellm-dashboard/src/locales/zh-CN.json +++ b/ui/litellm-dashboard/src/locales/zh-CN.json @@ -7123,5 +7123,36 @@ "timezoneLabel": "时区", "timezoneTooltip": "日期处理时区(未提供时默认为 UTC)" } + }, + "usageExport": { + "entityUsageExportModal": { + "exportTitle": "导出 {{entityLabel}} 用量", + "exportSuccessCsv": "{{entityLabel}} 用量数据已成功导出为 CSV", + "exportSuccessJson": "{{entityLabel}} 用量数据已成功导出为 JSON", + "exportFailed": "导出数据失败", + "exporting": "导出中...", + "exportFormat": "导出 {{format}}" + }, + "exportFormatSelector": { + "label": "格式", + "optionCsv": "CSV(Excel、Google Sheets)", + "optionJson": "JSON(包含元数据)" + }, + "exportSummary": { + "filterCount_one": "{{count}} 个筛选器", + "filterCount_other": "{{count}} 个筛选器" + }, + "exportTypeSelector": { + "label": "导出类型", + "dailyTitle": "按 {{entityType}} 逐日明细", + "dailyDesc": "每个 {{entityType}} 的每日指标", + "dailyWithKeysTitle": "按 {{entityType}} 和密钥逐日明细", + "dailyWithKeysDesc": "每个 {{entityType}} 的每日指标,按 API 密钥拆分", + "dailyWithModelsTitle": "按 {{entityType}} 和模型逐日明细", + "dailyWithModelsDesc": "按模型拆分的每日指标" + }, + "usageExportHeader": { + "exportData": "导出数据" + } } }