From 7bf4de99972460f6b10d2af4e6b2da3dd485dafe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=90=B9=E9=9B=AA?= <2b@yorha.xyz> Date: Wed, 10 Jun 2026 12:35:26 +0900 Subject: [PATCH] feat(ui): i18n for legacy teams view --- .../src/components/OldTeams.tsx | 278 +++++++++--------- ui/litellm-dashboard/src/locales/en.json | 141 +++++++++ ui/litellm-dashboard/src/locales/zh-CN.json | 141 +++++++++ 3 files changed, 417 insertions(+), 143 deletions(-) diff --git a/ui/litellm-dashboard/src/components/OldTeams.tsx b/ui/litellm-dashboard/src/components/OldTeams.tsx index 4d045780c30..0262c7477a1 100644 --- a/ui/litellm-dashboard/src/components/OldTeams.tsx +++ b/ui/litellm-dashboard/src/components/OldTeams.tsx @@ -3,6 +3,7 @@ import AvailableTeamsPanel from "@/components/team/available_teams"; import TeamInfoView from "@/components/team/TeamInfo"; import TeamSSOSettings from "@/components/TeamSSOSettings"; import { isProxyAdminRole } from "@/utils/roles"; +import { useTranslation } from "react-i18next"; import { InfoCircleOutlined, PlusOutlined, TeamOutlined, ReloadOutlined } from "@ant-design/icons"; import { Accordion, AccordionBody, AccordionHeader, TextInput } from "@tremor/react"; import { @@ -176,6 +177,7 @@ const Teams: React.FC = ({ premiumUser = false, }) => { console.log(`organizations: ${JSON.stringify(organizations)}`); + const { t } = useTranslation(); const { data: organizationsData } = useOrganizations(); const [isLoading, setIsLoading] = useState(true); const [fetchError, setFetchError] = useState(null); @@ -224,7 +226,7 @@ const Teams: React.FC = ({ setTeams(response.teams ?? []); setTotalTeams(response.total ?? 0); } catch (err: any) { - setFetchError(err?.message || "Failed to fetch teams"); + setFetchError(err?.message || t("oldTeams.failedToFetchTeams")); } finally { setIsLoading(false); } @@ -407,9 +409,9 @@ const Teams: React.FC = ({ setIsTeamDeleting(true); await teamDeleteCall(accessToken, teamToDelete.team_id); await fetchTeamsV2(); - NotificationsManager.success("Team deleted successfully"); + NotificationsManager.success(t("oldTeams.teamDeletedSuccess")); } catch (error) { - NotificationsManager.fromBackend("Error deleting the team: " + error); + NotificationsManager.fromBackend(t("oldTeams.deleteTeamFailed", { error })); } finally { setIsTeamDeleting(false); setIsDeleteModalOpen(false); @@ -458,7 +460,7 @@ const Teams: React.FC = ({ throw new Error(`Team alias ${newTeamAlias} already exists, please pick another alias`); } - NotificationsManager.info("Creating Team"); + NotificationsManager.info(t("oldTeams.creatingTeam")); // Handle logging settings in metadata if (loggingSettings.length > 0) { @@ -578,7 +580,7 @@ const Teams: React.FC = ({ } await teamCreateCall(accessToken, formValues); - NotificationsManager.success("Team created"); + NotificationsManager.success(t("oldTeams.teamCreatedSuccess")); await fetchTeamsV2({ page: currentPage, size: pageSize, @@ -592,7 +594,7 @@ const Teams: React.FC = ({ } } catch (error) { console.error("Error creating the team:", error); - NotificationsManager.fromBackend("Error creating the team: " + error); + NotificationsManager.fromBackend(t("oldTeams.createTeamFailed", { error })); } }; @@ -680,7 +682,7 @@ const Teams: React.FC = ({ const teamColumns: ColumnsType = useMemo( () => [ { - title: "Team ID", + title: t("oldTeams.columns.teamId"), dataIndex: "team_id", key: "team_id", width: 170, @@ -700,7 +702,7 @@ const Teams: React.FC = ({ ), }, { - title: "Team Alias", + title: t("oldTeams.columns.teamAlias"), dataIndex: "team_alias", key: "team_alias", ellipsis: true, @@ -716,7 +718,7 @@ const Teams: React.FC = ({ ), }, { - title: "Organization", + title: t("oldTeams.columns.organization"), key: "organization", width: 160, ellipsis: true, @@ -732,7 +734,7 @@ const Teams: React.FC = ({ }, }, { - title: "Resources", + title: t("oldTeams.columns.resources"), key: "resources", width: 240, render: (_: unknown, record: Team) => { @@ -741,7 +743,7 @@ const Teams: React.FC = ({ const keyCount = perTeamInfo?.[record.team_id]?.keys_count ?? 0; return ( - + @@ -749,7 +751,7 @@ const Teams: React.FC = ({ - + @@ -757,7 +759,7 @@ const Teams: React.FC = ({ - + @@ -770,7 +772,7 @@ const Teams: React.FC = ({ }, }, { - title: "Spend / Budget", + title: t("oldTeams.columns.spendBudget"), key: "spend", width: 200, sorter: true, @@ -781,7 +783,7 @@ const Teams: React.FC = ({ const budgetStr = budgetVal != null ? `$${budgetVal.toLocaleString(undefined, { minimumFractionDigits: 2, maximumFractionDigits: 2 })}` - : "Unlimited"; + : t("oldTeams.unlimited"); const percent = budgetVal != null && budgetVal > 0 ? Math.min((spendVal / budgetVal) * 100, 100) : null; return ( @@ -806,7 +808,7 @@ const Teams: React.FC = ({ }, }, { - title: "Created", + title: t("oldTeams.columns.created"), dataIndex: "created_at", key: "created_at", width: 130, @@ -821,7 +823,7 @@ const Teams: React.FC = ({ ), }, { - title: "Actions", + title: t("common.actions"), key: "actions", width: 120, align: "right" as const, @@ -829,19 +831,19 @@ const Teams: React.FC = ({ { navigator.clipboard .writeText(record.team_id) - .then(() => message.success("Team ID copied")) - .catch(() => message.error("Failed to copy")); + .then(() => message.success(t("oldTeams.actions.teamIdCopied"))) + .catch(() => message.error(t("oldTeams.actions.failedToCopy"))); }} /> {userRole === "Admin" && ( <> { setSelectedTeamId(record.team_id); @@ -850,7 +852,7 @@ const Teams: React.FC = ({ /> handleDelete(record)} /> @@ -860,7 +862,7 @@ const Teams: React.FC = ({ ), }, ], - [userRole, perTeamInfo, organizationsData, organizations], + [userRole, perTeamInfo, organizationsData, organizations, t], ); const displayTeams = useMemo(() => teams ?? [], [teams]); @@ -878,13 +880,13 @@ const Teams: React.FC = ({ return ( - Failed to load teams + {t("oldTeams.failedToLoadTeams")} {fetchError} ); @@ -902,11 +904,11 @@ const Teams: React.FC = ({
- No teams yet + {t("oldTeams.noTeamsYet")}
- Create your first team to organize members and manage access to models. + {t("oldTeams.createFirstTeamHint")}
{canCreateOrManageTeams(userRole, userID, organizations) && ( @@ -917,7 +919,7 @@ const Teams: React.FC = ({ style={{ marginTop: 16 }} data-testid="create-team-button" > - Create Team + {t("oldTeams.createTeam")} )}
@@ -932,7 +934,7 @@ const Teams: React.FC = ({ const tabItems = [ { key: "your-teams", - label: "Your Teams", + label: t("oldTeams.tabs.yourTeams"), children: ( <> @@ -941,7 +943,7 @@ const Teams: React.FC = ({ } suffix={isSearching ? : null} - placeholder="Search teams by name or ID..." + placeholder={t("oldTeams.searchPlaceholder")} onChange={(e) => handleSearchChange(e.target.value)} allowClear style={{ maxWidth: 400 }} @@ -963,7 +965,7 @@ const Teams: React.FC = ({ fetchTeamsV2({ page, size }); }} size="small" - showTotal={(total) => `${total} teams`} + showTotal={(total) => t("oldTeams.totalTeams", { count: total })} showSizeChanger pageSizeOptions={["10", "20", "50"]} /> @@ -974,23 +976,23 @@ const Teams: React.FC = ({ { const deleteKeyCount = teamToDelete?.keys_count ?? teamToDelete?.keys?.length ?? 0; return deleteKeyCount === 0 ? undefined - : `Warning: This team has ${deleteKeyCount} keys associated with it. Deleting the team will also delete all associated keys. This action is irreversible.`; + : t("oldTeams.deleteModal.alertMessage", { count: deleteKeyCount }); })()} - message="Are you sure you want to delete this team and all its keys? This action cannot be undone." - resourceInformationTitle="Team Information" + message={t("oldTeams.deleteModal.message")} + resourceInformationTitle={t("oldTeams.deleteModal.resourceInformationTitle")} resourceInformation={[ - { label: "Team ID", value: teamToDelete?.team_id, code: true }, - { label: "Team Name", value: teamToDelete?.team_alias }, + { label: t("oldTeams.deleteModal.labelTeamId"), value: teamToDelete?.team_id, code: true }, + { label: t("oldTeams.deleteModal.labelTeamName"), value: teamToDelete?.team_alias }, { - label: "Keys", + label: t("oldTeams.deleteModal.labelKeys"), value: teamToDelete?.keys_count ?? teamToDelete?.keys?.length ?? 0, }, - { label: "Members", value: teamToDelete?.members_with_roles?.length }, + { label: t("oldTeams.deleteModal.labelMembers"), value: teamToDelete?.members_with_roles?.length }, ]} requiredConfirmation={teamToDelete?.team_alias} onCancel={cancelDelete} @@ -1002,14 +1004,14 @@ const Teams: React.FC = ({ }, { key: "available-teams", - label: "Available Teams", + label: t("oldTeams.tabs.availableTeams"), children: , }, ...(isProxyAdminRole(userRole || "") ? [ { key: "default-settings", - label: "Default Team Settings", + label: t("oldTeams.tabs.defaultTeamSettings"), children: , }, ] @@ -1052,9 +1054,9 @@ const Teams: React.FC = ({ <TeamOutlined style={{ marginRight: 8 }} /> - Teams + {t("oldTeams.pageTitle")} - Manage teams, members, and their access to models and budgets + {t("oldTeams.pageSubtitle")} {canCreateOrManageTeams(userRole, userID, organizations) && ( )}
@@ -1074,7 +1076,7 @@ const Teams: React.FC = ({ {canCreateOrManageTeams(userRole, userID, organizations) && ( = ({
<> @@ -1106,11 +1108,11 @@ const Teams: React.FC = ({ - Organization{" "} + {t("oldTeams.form.organization")}{" "} - Organizations can have multiple teams. Learn more about{" "} + {t("oldTeams.form.organizationTooltipPrefix")}{" "} = ({ }} onClick={(e) => e.stopPropagation()} > - user management hierarchy + {t("oldTeams.form.userManagementHierarchy")} } @@ -1138,24 +1140,22 @@ const Teams: React.FC = ({ ? [ { required: true, - message: "Please select an organization", + message: t("oldTeams.form.organizationRequired"), }, ] : [] } - help={ - isSingleOrg - ? "You can only create teams within this organization" - : isOrgAdmin - ? "required" - : "" - } + help={isSingleOrg ? t("oldTeams.form.singleOrgHelp") : isOrgAdmin ? t("common.required") : ""} > - daily - weekly - monthly + {t("oldTeams.form.budgetDaily")} + {t("oldTeams.form.budgetWeekly")} + {t("oldTeams.form.budgetMonthly")} - + - + @@ -1245,14 +1242,10 @@ const Teams: React.FC = ({ }} > - Additional Settings + {t("oldTeams.accordions.additionalSettings")} - + { e.target.value = e.target.value.trim(); @@ -1260,48 +1253,44 @@ const Teams: React.FC = ({ /> (value ? Number(value) : undefined)} - tooltip="This is the individual budget for a user in the team." + tooltip={t("oldTeams.form.teamMemberBudgetTooltip")} > - + - + = ({ JSON.parse(value); return Promise.resolve(); } catch (error) { - return Promise.reject(new Error("Please enter valid JSON")); + return Promise.reject(new Error(t("oldTeams.form.invalidJson"))); } }, }, @@ -1328,8 +1317,8 @@ const Teams: React.FC = ({ - Guardrails{" "} - + {t("oldTeams.form.guardrails")}{" "} + = ({ } name="guardrails" className="mt-8" - help="Select existing guardrails or enter new ones" + help={t("oldTeams.form.guardrailsHelp")} > ({ value: name, label: name, @@ -1412,45 +1401,49 @@ const Teams: React.FC = ({ - Access Groups{" "} - + {t("oldTeams.form.accessGroups")}{" "} + } name="access_group_ids" className="mt-8" - help="Select access groups to assign to this team" + help={t("oldTeams.form.accessGroupsHelp")} > - + - Allowed Vector Stores{" "} - + {t("oldTeams.form.allowedVectorStores")}{" "} + } name="allowed_vector_store_ids" className="mt-8" - help="Select vector stores this team can access. Leave empty for access to all vector stores" + help={t("oldTeams.form.allowedVectorStoresHelp")} > form.setFieldValue("allowed_vector_store_ids", values)} value={form.getFieldValue("allowed_vector_store_ids")} accessToken={accessToken || ""} - placeholder="Select vector stores (optional)" + placeholder={t("oldTeams.form.allowedVectorStoresPlaceholder")} /> - + = ({ onChange={(values: string[]) => form.setFieldValue("allowed_passthrough_routes", values)} value={form.getFieldValue("allowed_passthrough_routes")} accessToken={accessToken || ""} - placeholder="Select pass through routes (optional)" + placeholder={t("oldTeams.form.allowedPassThroughRoutesPlaceholder")} disabled={!premiumUser || !isProxyAdminRole(userRole || "")} /> @@ -1469,27 +1462,27 @@ const Teams: React.FC = ({ - MCP Settings + {t("oldTeams.accordions.mcpSettings")} - Allowed MCP Servers{" "} - + {t("oldTeams.form.allowedMcpServers")}{" "} + } name="allowed_mcp_servers_and_groups" className="mt-4" - help="Select MCP servers or access groups this team can access" + help={t("oldTeams.form.allowedMcpServersHelp")} > form.setFieldValue("allowed_mcp_servers_and_groups", val)} value={form.getFieldValue("allowed_mcp_servers_and_groups")} accessToken={accessToken || ""} - placeholder="Select MCP servers or access groups (optional)" + placeholder={t("oldTeams.form.allowedMcpServersPlaceholder")} /> @@ -1521,27 +1514,27 @@ const Teams: React.FC = ({ - Agent Settings + {t("oldTeams.accordions.agentSettings")} - Allowed Agents{" "} - + {t("oldTeams.form.allowedAgents")}{" "} + } name="allowed_agents_and_groups" className="mt-4" - help="Select agents or access groups this team can access" + help={t("oldTeams.form.allowedAgentsHelp")} > form.setFieldValue("allowed_agents_and_groups", val)} value={form.getFieldValue("allowed_agents_and_groups")} accessToken={accessToken || ""} - placeholder="Select agents or access groups (optional)" + placeholder={t("oldTeams.form.allowedAgentsPlaceholder")} /> @@ -1549,27 +1542,27 @@ const Teams: React.FC = ({ - Search Tool Settings + {t("oldTeams.accordions.searchToolSettings")} - Allowed Search Tools{" "} - + {t("oldTeams.form.allowedSearchTools")}{" "} + } name="object_permission_search_tools" className="mt-4" - help="Restrict which configured search tools keys on this team may call." + help={t("oldTeams.form.allowedSearchToolsHelp")} > form.setFieldValue("object_permission_search_tools", vals)} value={form.getFieldValue("object_permission_search_tools")} accessToken={accessToken || ""} - placeholder="Select search tools (optional, empty = all allowed)" + placeholder={t("oldTeams.form.allowedSearchToolsPlaceholder")} /> @@ -1577,7 +1570,7 @@ const Teams: React.FC = ({ - Logging Settings + {t("oldTeams.accordions.loggingSettings")}
@@ -1592,7 +1585,7 @@ const Teams: React.FC = ({ - Router Settings + {t("oldTeams.accordions.routerSettings")}
@@ -1611,13 +1604,12 @@ const Teams: React.FC = ({ - Model Aliases + {t("oldTeams.accordions.modelAliases")}
- Create custom aliases for models that can be used by team members in API calls. This allows you to - create shortcuts for specific models. + {t("oldTeams.form.modelAliasesDescription")} = ({
diff --git a/ui/litellm-dashboard/src/locales/en.json b/ui/litellm-dashboard/src/locales/en.json index e537fa4f64a..dd89a6c41ff 100644 --- a/ui/litellm-dashboard/src/locales/en.json +++ b/ui/litellm-dashboard/src/locales/en.json @@ -571,6 +571,147 @@ "helpIconAriaLabel": "Help information", "docsMenuLabel": "Docs" }, + "oldTeams": { + "failedToFetchTeams": "Failed to fetch teams", + "failedToLoadTeams": "Failed to load teams", + "teamDeletedSuccess": "Team deleted successfully", + "creatingTeam": "Creating Team", + "teamCreatedSuccess": "Team created", + "noTeamsYet": "No teams yet", + "createFirstTeamHint": "Create your first team to organize members and manage access to models.", + "createTeam": "Create Team", + "searchPlaceholder": "Search teams by name or ID...", + "totalTeams_one": "{{count}} team", + "totalTeams_other": "{{count}} teams", + "unlimited": "Unlimited", + "pageTitle": "Teams", + "pageSubtitle": "Manage teams, members, and their access to models and budgets", + "columns": { + "teamId": "Team ID", + "teamAlias": "Team Alias", + "organization": "Organization", + "resources": "Resources", + "spendBudget": "Spend / Budget", + "created": "Created" + }, + "resources": { + "members_one": "{{count}} Member", + "members_other": "{{count}} Members", + "models_one": "{{count}} Model", + "models_other": "{{count}} Models", + "keys_one": "{{count}} Key", + "keys_other": "{{count}} Keys" + }, + "actions": { + "copyTeamId": "Copy Team ID", + "teamIdCopied": "Team ID copied", + "failedToCopy": "Failed to copy", + "editTeam": "Edit team", + "deleteTeam": "Delete team" + }, + "tabs": { + "yourTeams": "Your Teams", + "availableTeams": "Available Teams", + "defaultTeamSettings": "Default Team Settings" + }, + "deleteModal": { + "title": "Delete Team?", + "alertMessage": "Warning: This team has {{count}} keys associated with it. Deleting the team will also delete all associated keys. This action is irreversible.", + "message": "Are you sure you want to delete this team and all its keys? This action cannot be undone.", + "resourceInformationTitle": "Team Information", + "labelTeamId": "Team ID", + "labelTeamName": "Team Name", + "labelKeys": "Keys", + "labelMembers": "Members" + }, + "form": { + "teamName": "Team Name", + "teamNameRequired": "Please input a team name", + "organization": "Organization", + "organizationTooltipPrefix": "Organizations can have multiple teams. Learn more about", + "userManagementHierarchy": "user management hierarchy", + "organizationRequired": "Please select an organization", + "singleOrgHelp": "You can only create teams within this organization", + "noOrganizationsAvailable": "No organizations available", + "searchOrSelectOrganization": "Search or select an Organization", + "selectOrgAdminHint": "Please select an organization to create a team for. You can only create teams within organizations where you are an admin.", + "models": "Models", + "modelsTooltip": "These are the models that your selected team has access to", + "modelsRequired": "Please select at least one model", + "maxBudget": "Max Budget (USD)", + "resetBudget": "Reset Budget", + "budgetDaily": "daily", + "budgetWeekly": "weekly", + "budgetMonthly": "monthly", + "tpmLimit": "Tokens per minute Limit (TPM)", + "rpmLimit": "Requests per minute Limit (RPM)", + "teamId": "Team ID", + "teamIdHelp": "ID of the team you want to create. If not provided, it will be generated automatically.", + "teamMemberBudget": "Team Member Budget (USD)", + "teamMemberBudgetTooltip": "This is the individual budget for a user in the team.", + "teamMemberKeyDuration": "Team Member Key Duration (eg: 1d, 1mo)", + "teamMemberKeyDurationTooltip": "Set a limit to the duration of a team member's key. Format: 30s (seconds), 30m (minutes), 30h (hours), 30d (days), 1mo (month)", + "teamMemberKeyDurationPlaceholder": "e.g., 30d", + "teamMemberRpmLimit": "Team Member RPM Limit", + "teamMemberRpmLimitTooltip": "The RPM (Requests Per Minute) limit for individual team members", + "teamMemberTpmLimit": "Team Member TPM Limit", + "teamMemberTpmLimitTooltip": "The TPM (Tokens Per Minute) limit for individual team members", + "metadata": "Metadata", + "metadataHelp": "Additional team metadata. Enter metadata as JSON object.", + "secretManagerSettings": "Secret Manager Settings", + "secretManagerSettingsHelp": "Enter secret manager configuration as a JSON object.", + "secretManagerSettingsPremiumHelp": "Premium feature - Upgrade to manage secret manager settings.", + "invalidJson": "Please enter valid JSON", + "guardrails": "Guardrails", + "guardrailsTooltip": "Setup your first guardrail", + "guardrailsHelp": "Select existing guardrails or enter new ones", + "guardrailsPlaceholder": "Select or enter guardrails", + "disableGlobalGuardrails": "Disable Global Guardrails", + "disableGlobalGuardrailsTooltip": "When enabled, this team will bypass any guardrails configured to run on every request (global guardrails)", + "disableGlobalGuardrailsHelp": "Bypass global guardrails for this team", + "disableGlobalGuardrailsPremium": "Premium feature - Upgrade to disable global guardrails by team", + "policies": "Policies", + "policiesTooltip": "Apply policies to this team to control guardrails and other settings", + "policiesHelp": "Select existing policies or enter new ones", + "policiesPlaceholder": "Select or enter policies", + "accessGroups": "Access Groups", + "accessGroupsTooltip": "Assign access groups to this team. Access groups control which models, MCP servers, and agents this team can use", + "accessGroupsHelp": "Select access groups to assign to this team", + "accessGroupsPlaceholder": "Select access groups (optional)", + "allowedVectorStores": "Allowed Vector Stores", + "allowedVectorStoresTooltip": "Select which vector stores this team can access by default. Leave empty for access to all vector stores", + "allowedVectorStoresHelp": "Select vector stores this team can access. Leave empty for access to all vector stores", + "allowedVectorStoresPlaceholder": "Select vector stores (optional)", + "allowedPassThroughRoutes": "Allowed Pass Through Routes", + "allowedPassThroughRoutesPremiumTooltip": "Premium feature - Upgrade to set allowed pass through routes", + "allowedPassThroughRoutesAdminTooltip": "Only proxy admins can set allowed pass through routes", + "allowedPassThroughRoutesPlaceholder": "Select pass through routes (optional)", + "allowedMcpServers": "Allowed MCP Servers", + "allowedMcpServersTooltip": "Select which MCP servers or access groups this team can access", + "allowedMcpServersHelp": "Select MCP servers or access groups this team can access", + "allowedMcpServersPlaceholder": "Select MCP servers or access groups (optional)", + "allowedAgents": "Allowed Agents", + "allowedAgentsTooltip": "Select which agents or access groups this team can access", + "allowedAgentsHelp": "Select agents or access groups this team can access", + "allowedAgentsPlaceholder": "Select agents or access groups (optional)", + "allowedSearchTools": "Allowed Search Tools", + "allowedSearchToolsTooltip": "Select which search tools this team can access. Leave empty to allow all search tools.", + "allowedSearchToolsHelp": "Restrict which configured search tools keys on this team may call.", + "allowedSearchToolsPlaceholder": "Select search tools (optional, empty = all allowed)", + "modelAliasesDescription": "Create custom aliases for models that can be used by team members in API calls. This allows you to create shortcuts for specific models." + }, + "accordions": { + "additionalSettings": "Additional Settings", + "mcpSettings": "MCP Settings", + "agentSettings": "Agent Settings", + "searchToolSettings": "Search Tool Settings", + "loggingSettings": "Logging Settings", + "routerSettings": "Router Settings", + "modelAliases": "Model Aliases" + }, + "deleteTeamFailed": "Error deleting the team: {{error}}", + "createTeamFailed": "Error creating the team: {{error}}" + }, "common": { "save": "Save", "cancel": "Cancel", diff --git a/ui/litellm-dashboard/src/locales/zh-CN.json b/ui/litellm-dashboard/src/locales/zh-CN.json index 59de5513a5c..80fac655fcb 100644 --- a/ui/litellm-dashboard/src/locales/zh-CN.json +++ b/ui/litellm-dashboard/src/locales/zh-CN.json @@ -571,6 +571,147 @@ "helpIconAriaLabel": "帮助信息", "docsMenuLabel": "文档" }, + "oldTeams": { + "failedToFetchTeams": "获取团队失败", + "failedToLoadTeams": "加载团队失败", + "teamDeletedSuccess": "团队已成功删除", + "creatingTeam": "正在创建团队", + "teamCreatedSuccess": "团队已创建", + "noTeamsYet": "暂无团队", + "createFirstTeamHint": "创建您的第一个团队,以便组织成员并管理对模型的访问权限。", + "createTeam": "创建团队", + "searchPlaceholder": "按名称或 ID 搜索团队...", + "totalTeams_one": "{{count}} 个团队", + "totalTeams_other": "{{count}} 个团队", + "unlimited": "无限制", + "pageTitle": "团队", + "pageSubtitle": "管理团队、成员及其对模型和预算的访问权限", + "columns": { + "teamId": "团队 ID", + "teamAlias": "团队别名", + "organization": "组织", + "resources": "资源", + "spendBudget": "花费 / 预算", + "created": "创建时间" + }, + "resources": { + "members_one": "{{count}} 位成员", + "members_other": "{{count}} 位成员", + "models_one": "{{count}} 个模型", + "models_other": "{{count}} 个模型", + "keys_one": "{{count}} 个密钥", + "keys_other": "{{count}} 个密钥" + }, + "actions": { + "copyTeamId": "复制团队 ID", + "teamIdCopied": "团队 ID 已复制", + "failedToCopy": "复制失败", + "editTeam": "编辑团队", + "deleteTeam": "删除团队" + }, + "tabs": { + "yourTeams": "我的团队", + "availableTeams": "可用团队", + "defaultTeamSettings": "默认团队设置" + }, + "deleteModal": { + "title": "删除团队?", + "alertMessage": "警告:此团队有 {{count}} 个关联密钥。删除团队将同时删除所有关联密钥,此操作不可逆。", + "message": "确定要删除此团队及其所有密钥吗?此操作无法撤销。", + "resourceInformationTitle": "团队信息", + "labelTeamId": "团队 ID", + "labelTeamName": "团队名称", + "labelKeys": "密钥", + "labelMembers": "成员" + }, + "form": { + "teamName": "团队名称", + "teamNameRequired": "请输入团队名称", + "organization": "组织", + "organizationTooltipPrefix": "组织可以有多个团队。了解更多关于", + "userManagementHierarchy": "用户管理层级", + "organizationRequired": "请选择一个组织", + "singleOrgHelp": "您只能在此组织内创建团队", + "noOrganizationsAvailable": "暂无可用组织", + "searchOrSelectOrganization": "搜索或选择组织", + "selectOrgAdminHint": "请选择要在其中创建团队的组织。您只能在您担任管理员的组织中创建团队。", + "models": "模型", + "modelsTooltip": "这些是您所选团队可以访问的模型", + "modelsRequired": "请至少选择一个模型", + "maxBudget": "最大预算(USD)", + "resetBudget": "重置预算", + "budgetDaily": "每天", + "budgetWeekly": "每周", + "budgetMonthly": "每月", + "tpmLimit": "每分钟 Token 限制(TPM)", + "rpmLimit": "每分钟请求限制(RPM)", + "teamId": "团队 ID", + "teamIdHelp": "您要创建的团队 ID。若不填写,将自动生成。", + "teamMemberBudget": "团队成员预算(USD)", + "teamMemberBudgetTooltip": "这是团队中单个用户的独立预算。", + "teamMemberKeyDuration": "团队成员密钥有效期(例如:1d、1mo)", + "teamMemberKeyDurationTooltip": "设置团队成员密钥的有效期限制。格式:30s(秒)、30m(分钟)、30h(小时)、30d(天)、1mo(月)", + "teamMemberKeyDurationPlaceholder": "例如:30d", + "teamMemberRpmLimit": "团队成员 RPM 限制", + "teamMemberRpmLimitTooltip": "单个团队成员的每分钟请求数(RPM)限制", + "teamMemberTpmLimit": "团队成员 TPM 限制", + "teamMemberTpmLimitTooltip": "单个团队成员的每分钟 Token 数(TPM)限制", + "metadata": "元数据", + "metadataHelp": "附加团队元数据。以 JSON 对象格式输入。", + "secretManagerSettings": "密钥管理器设置", + "secretManagerSettingsHelp": "以 JSON 对象格式输入密钥管理器配置。", + "secretManagerSettingsPremiumHelp": "高级功能 - 升级以管理密钥管理器设置。", + "invalidJson": "请输入有效的 JSON", + "guardrails": "护栏", + "guardrailsTooltip": "设置您的第一个护栏", + "guardrailsHelp": "选择现有护栏或输入新的护栏", + "guardrailsPlaceholder": "选择或输入护栏", + "disableGlobalGuardrails": "禁用全局护栏", + "disableGlobalGuardrailsTooltip": "启用后,此团队将绕过配置为在每次请求上运行的护栏(全局护栏)", + "disableGlobalGuardrailsHelp": "为此团队绕过全局护栏", + "disableGlobalGuardrailsPremium": "高级功能 - 升级以按团队禁用全局护栏", + "policies": "策略", + "policiesTooltip": "为此团队应用策略以控制护栏和其他设置", + "policiesHelp": "选择现有策略或输入新的策略", + "policiesPlaceholder": "选择或输入策略", + "accessGroups": "访问组", + "accessGroupsTooltip": "为此团队分配访问组。访问组控制该团队可以使用的模型、MCP 服务器和 Agent", + "accessGroupsHelp": "选择要分配给此团队的访问组", + "accessGroupsPlaceholder": "选择访问组(可选)", + "allowedVectorStores": "允许的向量存储", + "allowedVectorStoresTooltip": "选择此团队默认可以访问的向量存储。留空则允许访问所有向量存储", + "allowedVectorStoresHelp": "选择此团队可以访问的向量存储。留空则允许访问所有向量存储", + "allowedVectorStoresPlaceholder": "选择向量存储(可选)", + "allowedPassThroughRoutes": "允许的透传路由", + "allowedPassThroughRoutesPremiumTooltip": "高级功能 - 升级以设置允许的透传路由", + "allowedPassThroughRoutesAdminTooltip": "只有代理管理员可以设置允许的透传路由", + "allowedPassThroughRoutesPlaceholder": "选择透传路由(可选)", + "allowedMcpServers": "允许的 MCP 服务器", + "allowedMcpServersTooltip": "选择此团队可以访问的 MCP 服务器或访问组", + "allowedMcpServersHelp": "选择此团队可以访问的 MCP 服务器或访问组", + "allowedMcpServersPlaceholder": "选择 MCP 服务器或访问组(可选)", + "allowedAgents": "允许的 Agent", + "allowedAgentsTooltip": "选择此团队可以访问的 Agent 或访问组", + "allowedAgentsHelp": "选择此团队可以访问的 Agent 或访问组", + "allowedAgentsPlaceholder": "选择 Agent 或访问组(可选)", + "allowedSearchTools": "允许的搜索工具", + "allowedSearchToolsTooltip": "选择此团队可以访问的搜索工具。留空则允许使用所有搜索工具。", + "allowedSearchToolsHelp": "限制此团队上的密钥可以调用的已配置搜索工具。", + "allowedSearchToolsPlaceholder": "选择搜索工具(可选,留空 = 允许所有)", + "modelAliasesDescription": "为模型创建自定义别名,供团队成员在 API 调用中使用。这样您可以为特定模型创建快捷方式。" + }, + "accordions": { + "additionalSettings": "附加设置", + "mcpSettings": "MCP 设置", + "agentSettings": "Agent 设置", + "searchToolSettings": "搜索工具设置", + "loggingSettings": "日志设置", + "routerSettings": "路由设置", + "modelAliases": "模型别名" + }, + "deleteTeamFailed": "删除团队失败:{{error}}", + "createTeamFailed": "创建团队失败:{{error}}" + }, "common": { "save": "保存", "cancel": "取消",