From 4882456643061d13c9aae6a5f20b5b37274047d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B9=E9=9B=AA?= <2b@yorha.xyz> Date: Thu, 11 Jun 2026 02:42:35 +0900 Subject: [PATCH] feat(ui): i18n for team batch 1 --- .../src/components/team/EditMembership.tsx | 41 ++++++++---- .../src/components/team/LoggingSettings.tsx | 66 +++++++++++------- .../src/components/team/MyUserTab.tsx | 48 +++++++------ ui/litellm-dashboard/src/locales/en.json | 67 +++++++++++++++++++ ui/litellm-dashboard/src/locales/zh-CN.json | 67 +++++++++++++++++++ 5 files changed, 232 insertions(+), 57 deletions(-) diff --git a/ui/litellm-dashboard/src/components/team/EditMembership.tsx b/ui/litellm-dashboard/src/components/team/EditMembership.tsx index 16e4ec58dd0..ca39e452f4c 100644 --- a/ui/litellm-dashboard/src/components/team/EditMembership.tsx +++ b/ui/litellm-dashboard/src/components/team/EditMembership.tsx @@ -1,6 +1,7 @@ import { Text, TextInput } from "@tremor/react"; import { Button as AntButton, Form, Modal, Select } from "antd"; import React, { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; import NumericalInput from "../shared/numerical_input"; import BudgetDurationDropdown from "../common_components/budget_duration_dropdown"; @@ -48,6 +49,7 @@ const MemberModal = ({ mode, config, }: MemberModalProps) => { + const { t } = useTranslation(); const [form] = Form.useForm(); const [isSubmitting, setIsSubmitting] = useState(false); @@ -135,7 +137,7 @@ const MemberModal = ({ step={field.step || 1} min={field.min || 0} style={{ width: "100%" }} - placeholder={field.placeholder || "Enter a numerical value"} + placeholder={field.placeholder || t("teamPage.editMembership.numericalPlaceholder")} /> ); case "select": @@ -152,7 +154,7 @@ const MemberModal = ({ return ( {mode === "edit" && initialData @@ -237,10 +244,16 @@ const MemberModal = ({
- Cancel + {t("common.cancel")} - {mode === "add" ? (isSubmitting ? "Adding..." : "Add Member") : isSubmitting ? "Saving..." : "Save Changes"} + {mode === "add" + ? isSubmitting + ? t("teamPage.editMembership.addingButton") + : t("teamPage.editMembership.addMemberButton") + : isSubmitting + ? t("teamPage.editMembership.savingButton") + : t("teamPage.editMembership.saveChangesButton")}
diff --git a/ui/litellm-dashboard/src/components/team/LoggingSettings.tsx b/ui/litellm-dashboard/src/components/team/LoggingSettings.tsx index 56d2e23a90c..99aa68fbf6d 100644 --- a/ui/litellm-dashboard/src/components/team/LoggingSettings.tsx +++ b/ui/litellm-dashboard/src/components/team/LoggingSettings.tsx @@ -5,6 +5,7 @@ import { Select, Tooltip, Divider } from "antd"; import { InfoCircleOutlined } from "@ant-design/icons"; import { Button, Card, TextInput } from "@tremor/react"; import { PlusIcon, TrashIcon, CogIcon, BanIcon } from "@heroicons/react/outline"; +import { useTranslation } from "react-i18next"; import { callbackInfo, callback_map, mapDisplayToInternalNames } from "../callback_info_helpers"; import NumericalInput from "../shared/numerical_input"; @@ -29,6 +30,8 @@ const LoggingSettings: React.FC = ({ disabledCallbacks = [], onDisabledCallbacksChange, }) => { + const { t } = useTranslation(); + // Get callbacks that support team and key logging const supportedCallbacks = Object.entries(callbackInfo) .filter(([_, info]) => info.supports_key_team_logging) @@ -110,7 +113,9 @@ const LoggingSettings: React.FC = ({
- Integration Parameters + + {t("teamPage.loggingSettings.integrationParametersTitle")} +
{Object.entries(dynamicParams).map(([paramName, paramType]) => ( @@ -122,16 +127,18 @@ const LoggingSettings: React.FC = ({ {paramType === "password" && ( - Sensitive + {t("teamPage.loggingSettings.sensitiveBadge")} )} {paramType === "number" && ( - Number + {t("teamPage.loggingSettings.numberBadge")} )} - {paramType === "number" && Value must be between 0 and 1} + {paramType === "number" && ( + {t("teamPage.loggingSettings.numberRangeHint")} + )} {paramType === "number" ? ( = ({
- Disabled Callbacks - + + {t("teamPage.loggingSettings.disabledCallbacksTitle")} + +
- + -
- Select callbacks that should be disabled for this key. These callbacks will not receive any logging data. -
+
{t("teamPage.loggingSettings.disabledCallbacksHelp")}
@@ -222,8 +231,10 @@ const LoggingSettings: React.FC = ({
- Logging Integrations - + + {t("teamPage.loggingSettings.loggingIntegrationsTitle")} + +
@@ -235,7 +246,7 @@ const LoggingSettings: React.FC = ({ className="hover:border-blue-400 hover:text-blue-500" type="button" > - Add Integration + {t("teamPage.loggingSettings.addIntegrationButton")}
@@ -256,7 +267,10 @@ const LoggingSettings: React.FC = ({
{logoUrl && {callbackDisplayName}} - {callbackDisplayName || "New Integration"} Configuration + + {callbackDisplayName || t("teamPage.loggingSettings.newIntegrationFallback")}{" "} + {t("teamPage.loggingSettings.configurationSuffix")} +
- + updateLoggingConfig(index, "callback_type", value)} @@ -326,19 +344,19 @@ const LoggingSettings: React.FC = ({ @@ -355,8 +373,8 @@ const LoggingSettings: React.FC = ({ {value.length === 0 && (
-
No logging integrations configured
-
Click "Add Integration" to configure logging for this team
+
{t("teamPage.loggingSettings.emptyTitle")}
+
{t("teamPage.loggingSettings.emptySubtitle")}
)}
diff --git a/ui/litellm-dashboard/src/components/team/MyUserTab.tsx b/ui/litellm-dashboard/src/components/team/MyUserTab.tsx index e215f687df4..c98c28994a2 100644 --- a/ui/litellm-dashboard/src/components/team/MyUserTab.tsx +++ b/ui/litellm-dashboard/src/components/team/MyUserTab.tsx @@ -3,6 +3,7 @@ import { formatNumberWithCommas } from "@/utils/dataUtils"; import { InfoCircleOutlined } from "@ant-design/icons"; import { Card, Col, Row, Space, Tag, Tooltip, Typography } from "antd"; import React from "react"; +import { useTranslation } from "react-i18next"; import { useMyTeamMember } from "./useMyTeamMember"; interface MyUserTabProps { @@ -23,18 +24,19 @@ const formatNumber = (value: number | null | undefined, digits = 4): string => { return formatNumberWithCommas(value, digits); }; -const formatRateLimit = (value: number | null | undefined): string => { - if (value === null || value === undefined) return "Unlimited"; +const formatRateLimit = (value: number | null | undefined, unlimitedLabel: string): string => { + if (value === null || value === undefined) return unlimitedLabel; return formatNumberWithCommas(value, 0); }; export default function MyUserTab({ teamId }: MyUserTabProps) { + const { t } = useTranslation(); const { data, isLoading, error } = useMyTeamMember(teamId); if (isLoading) { return ( - Loading your membership info… + {t("teamPage.myUserTab.loadingText")} ); } @@ -43,7 +45,7 @@ export default function MyUserTab({ teamId }: MyUserTabProps) { return ( - {error instanceof Error ? error.message : "Failed to load your membership info for this team."} + {error instanceof Error ? error.message : t("teamPage.myUserTab.errorFallback")} ); @@ -52,9 +54,7 @@ export default function MyUserTab({ teamId }: MyUserTabProps) { if (!data) { return ( - - No membership info available for the current user in this team. - + {t("teamPage.myUserTab.noDataText")} ); } @@ -68,12 +68,14 @@ export default function MyUserTab({ teamId }: MyUserTabProps) { const budgetReset = formatBudgetReset(budgetTable?.budget_reset_at); const allowedModels = budgetTable?.allowed_models ?? null; + const unlimitedLabel = t("teamPage.myUserTab.unlimited"); + return ( - User + {t("teamPage.myUserTab.userLabel")}
{data.user_email || data.user_id}
@@ -82,7 +84,7 @@ export default function MyUserTab({ teamId }: MyUserTabProps) { - Team Role + {t("teamPage.myUserTab.teamRoleLabel")}
{data.role || "user"}
@@ -94,20 +96,24 @@ export default function MyUserTab({ teamId }: MyUserTabProps) { {labelWithTooltip( - "Current Cycle Spend (USD)", - "Spend for the current budget cycle. Resets to $0 when the budget window rolls over.", + t("teamPage.myUserTab.currentCycleSpendLabel"), + t("teamPage.myUserTab.currentCycleSpendTooltip"), )}
${formatNumber(spend, 4)} - of {maxBudget === null ? "Unlimited" : `$${formatNumber(maxBudget, 4)}`} + {t("teamPage.myUserTab.ofBudget", { + budget: maxBudget === null ? unlimitedLabel : `$${formatNumber(maxBudget, 4)}`, + })}
{budgetReset && (
- Resets {budgetReset} + + {t("teamPage.myUserTab.resetsAt", { when: budgetReset })} +
)}
@@ -115,18 +121,22 @@ export default function MyUserTab({ teamId }: MyUserTabProps) { - {labelWithTooltip("Rate Limits", "Your per-member rate limits within this team.")} + {labelWithTooltip(t("teamPage.myUserTab.rateLimitsLabel"), t("teamPage.myUserTab.rateLimitsTooltip"))}
- TPM: {formatRateLimit(tpmLimit)} + + {t("teamPage.myUserTab.tpmValue", { value: formatRateLimit(tpmLimit, unlimitedLabel) })} +
- RPM: {formatRateLimit(rpmLimit)} + + {t("teamPage.myUserTab.rpmValue", { value: formatRateLimit(rpmLimit, unlimitedLabel) })} +
- {labelWithTooltip("Total Spend (USD)", "Cumulative spend across all budget cycles within this team.")} + {labelWithTooltip(t("teamPage.myUserTab.totalSpendLabel"), t("teamPage.myUserTab.totalSpendTooltip"))}
${formatNumber(totalSpend, 4)} @@ -137,7 +147,7 @@ export default function MyUserTab({ teamId }: MyUserTabProps) { - {labelWithTooltip("Model Scope", "Models you can access within this team.")} + {labelWithTooltip(t("teamPage.myUserTab.modelScopeLabel"), t("teamPage.myUserTab.modelScopeTooltip"))}
{allowedModels && allowedModels.length > 0 ? ( @@ -146,7 +156,7 @@ export default function MyUserTab({ teamId }: MyUserTabProps) { ))} ) : ( - All Team Models + {t("teamPage.myUserTab.allTeamModels")} )}
diff --git a/ui/litellm-dashboard/src/locales/en.json b/ui/litellm-dashboard/src/locales/en.json index e80c21cb42d..b3ca4343f37 100644 --- a/ui/litellm-dashboard/src/locales/en.json +++ b/ui/litellm-dashboard/src/locales/en.json @@ -6181,5 +6181,72 @@ "descriptionPlaceholder": "Describe what this agent does...", "credentialFieldRequired": "Please enter {{label}}" } + }, + "teamPage": { + "editMembership": { + "emailLabel": "Email", + "emailPlaceholder": "user@example.com", + "emailInvalid": "Please enter a valid email!", + "orDivider": "OR", + "userIdLabel": "User ID", + "userIdPlaceholder": "user_123", + "roleLabel": "Role", + "roleCurrent": "(Current: {{role}})", + "roleRequired": "Please select a role!", + "numericalPlaceholder": "Enter a numerical value", + "selectOptionsPlaceholder": "Select options", + "addMemberTitle": "Add Member", + "editMemberTitle": "Edit Member", + "addingButton": "Adding...", + "addMemberButton": "Add Member", + "savingButton": "Saving...", + "saveChangesButton": "Save Changes" + }, + "loggingSettings": { + "disabledCallbacksTitle": "Disabled Callbacks", + "disabledCallbacksTooltip": "Select callbacks to disable for this key. Disabled callbacks will not receive any logging data.", + "disabledCallbacksLabel": "Disabled Callbacks", + "disabledCallbacksPlaceholder": "Select callbacks to disable", + "disabledCallbacksHelp": "Select callbacks that should be disabled for this key. These callbacks will not receive any logging data.", + "loggingIntegrationsTitle": "Logging Integrations", + "loggingIntegrationsTooltip": "Configure callback logging integrations for this team.", + "addIntegrationButton": "Add Integration", + "integrationParametersTitle": "Integration Parameters", + "sensitiveBadge": "Sensitive", + "numberBadge": "Number", + "numberRangeHint": "Value must be between 0 and 1", + "newIntegrationFallback": "New Integration", + "configurationSuffix": "Configuration", + "removeButton": "Remove", + "integrationTypeLabel": "Integration Type", + "selectIntegrationPlaceholder": "Select integration", + "eventTypeLabel": "Event Type", + "successOnly": "Success Only", + "failureOnly": "Failure Only", + "successAndFailure": "Success & Failure", + "emptyTitle": "No logging integrations configured", + "emptySubtitle": "Click \"Add Integration\" to configure logging for this team" + }, + "myUserTab": { + "loadingText": "Loading your membership info…", + "errorFallback": "Failed to load your membership info for this team.", + "noDataText": "No membership info available for the current user in this team.", + "userLabel": "User", + "teamRoleLabel": "Team Role", + "currentCycleSpendLabel": "Current Cycle Spend (USD)", + "currentCycleSpendTooltip": "Spend for the current budget cycle. Resets to $0 when the budget window rolls over.", + "ofBudget": "of {{budget}}", + "unlimited": "Unlimited", + "resetsAt": "Resets {{when}}", + "rateLimitsLabel": "Rate Limits", + "rateLimitsTooltip": "Your per-member rate limits within this team.", + "tpmValue": "TPM: {{value}}", + "rpmValue": "RPM: {{value}}", + "totalSpendLabel": "Total Spend (USD)", + "totalSpendTooltip": "Cumulative spend across all budget cycles within this team.", + "modelScopeLabel": "Model Scope", + "modelScopeTooltip": "Models you can access within this team.", + "allTeamModels": "All Team Models" + } } } diff --git a/ui/litellm-dashboard/src/locales/zh-CN.json b/ui/litellm-dashboard/src/locales/zh-CN.json index 4e6f9c724de..b3b31c1e8dc 100644 --- a/ui/litellm-dashboard/src/locales/zh-CN.json +++ b/ui/litellm-dashboard/src/locales/zh-CN.json @@ -6181,5 +6181,72 @@ "descriptionPlaceholder": "描述该 Agent 的功能...", "credentialFieldRequired": "请输入{{label}}" } + }, + "teamPage": { + "editMembership": { + "emailLabel": "邮箱", + "emailPlaceholder": "user@example.com", + "emailInvalid": "请输入有效的邮箱地址!", + "orDivider": "或", + "userIdLabel": "用户 ID", + "userIdPlaceholder": "user_123", + "roleLabel": "角色", + "roleCurrent": "(当前:{{role}})", + "roleRequired": "请选择角色!", + "numericalPlaceholder": "请输入数值", + "selectOptionsPlaceholder": "请选择选项", + "addMemberTitle": "添加成员", + "editMemberTitle": "编辑成员", + "addingButton": "添加中...", + "addMemberButton": "添加成员", + "savingButton": "保存中...", + "saveChangesButton": "保存更改" + }, + "loggingSettings": { + "disabledCallbacksTitle": "已禁用的回调", + "disabledCallbacksTooltip": "选择要为此密钥禁用的回调。已禁用的回调不会收到任何日志数据。", + "disabledCallbacksLabel": "已禁用的回调", + "disabledCallbacksPlaceholder": "选择要禁用的回调", + "disabledCallbacksHelp": "选择应为此密钥禁用的回调。这些回调不会收到任何日志数据。", + "loggingIntegrationsTitle": "日志集成", + "loggingIntegrationsTooltip": "为此团队配置回调日志集成。", + "addIntegrationButton": "添加集成", + "integrationParametersTitle": "集成参数", + "sensitiveBadge": "敏感", + "numberBadge": "数值", + "numberRangeHint": "值必须在 0 到 1 之间", + "newIntegrationFallback": "新集成", + "configurationSuffix": "配置", + "removeButton": "移除", + "integrationTypeLabel": "集成类型", + "selectIntegrationPlaceholder": "选择集成", + "eventTypeLabel": "事件类型", + "successOnly": "仅成功", + "failureOnly": "仅失败", + "successAndFailure": "成功与失败", + "emptyTitle": "未配置日志集成", + "emptySubtitle": "点击「添加集成」为此团队配置日志" + }, + "myUserTab": { + "loadingText": "正在加载您的成员信息…", + "errorFallback": "加载您在此团队的成员信息失败。", + "noDataText": "当前用户在此团队中没有可用的成员信息。", + "userLabel": "用户", + "teamRoleLabel": "团队角色", + "currentCycleSpendLabel": "当前周期花费(USD)", + "currentCycleSpendTooltip": "当前预算周期的花费。预算窗口滚动时将重置为 $0。", + "ofBudget": "共 {{budget}}", + "unlimited": "不限", + "resetsAt": "重置于 {{when}}", + "rateLimitsLabel": "速率限制", + "rateLimitsTooltip": "您在此团队中的每成员速率限制。", + "tpmValue": "TPM:{{value}}", + "rpmValue": "RPM:{{value}}", + "totalSpendLabel": "累计花费(USD)", + "totalSpendTooltip": "此团队所有预算周期的累计花费。", + "modelScopeLabel": "模型范围", + "modelScopeTooltip": "您在此团队中可访问的模型。", + "allTeamModels": "所有团队模型" + } } }