mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-04 02:31:27 +00:00
feat(ui): i18n for survey prompts and modals
This commit is contained in:
parent
fca081d574
commit
f6bf079554
7 changed files with 194 additions and 62 deletions
|
|
@ -1,6 +1,7 @@
|
|||
import React from "react";
|
||||
import { X, Code, ExternalLink } from "lucide-react";
|
||||
import { Button } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface ClaudeCodeModalProps {
|
||||
isOpen: boolean;
|
||||
|
|
@ -11,6 +12,8 @@ interface ClaudeCodeModalProps {
|
|||
const GOOGLE_FORM_URL = "https://forms.gle/LZeJQ3XytBakckYa9";
|
||||
|
||||
export function ClaudeCodeModal({ isOpen, onClose, onComplete }: ClaudeCodeModalProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
const handleOpenForm = () => {
|
||||
|
|
@ -29,7 +32,9 @@ export function ClaudeCodeModal({ isOpen, onClose, onComplete }: ClaudeCodeModal
|
|||
<div className="px-6 py-4 border-b border-gray-100 flex items-center justify-between bg-gray-50/50">
|
||||
<div className="flex items-center gap-2 text-purple-600">
|
||||
<Code className="h-5 w-5" />
|
||||
<span className="font-semibold text-sm tracking-wide uppercase">Claude Code Feedback</span>
|
||||
<span className="font-semibold text-sm tracking-wide uppercase">
|
||||
{t("survey.claudeCodeModal.headerLabel")}
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
|
|
@ -41,12 +46,9 @@ export function ClaudeCodeModal({ isOpen, onClose, onComplete }: ClaudeCodeModal
|
|||
|
||||
{/* Content */}
|
||||
<div className="p-8">
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-4">Help us improve your experience</h2>
|
||||
<p className="text-gray-600 mb-6">
|
||||
We'd love to hear about your experience using LiteLLM with Claude Code. Your feedback helps us improve
|
||||
the product for everyone.
|
||||
</p>
|
||||
<p className="text-sm text-gray-500 mb-6">This brief survey takes about 2-3 minutes to complete.</p>
|
||||
<h2 className="text-2xl font-bold text-gray-900 mb-4">{t("survey.claudeCodeModal.title")}</h2>
|
||||
<p className="text-gray-600 mb-6">{t("survey.claudeCodeModal.description")}</p>
|
||||
<p className="text-sm text-gray-500 mb-6">{t("survey.claudeCodeModal.durationNote")}</p>
|
||||
|
||||
<Button
|
||||
type="primary"
|
||||
|
|
@ -56,7 +58,7 @@ export function ClaudeCodeModal({ isOpen, onClose, onComplete }: ClaudeCodeModal
|
|||
icon={<ExternalLink className="h-4 w-4" />}
|
||||
style={{ backgroundColor: "#7c3aed", borderColor: "#7c3aed" }}
|
||||
>
|
||||
Open Feedback Form
|
||||
{t("survey.claudeCodeModal.openFormButton")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from "react";
|
||||
import { Code } from "lucide-react";
|
||||
import { NudgePrompt } from "./NudgePrompt";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface ClaudeCodePromptProps {
|
||||
onOpen: () => void;
|
||||
|
|
@ -9,14 +10,16 @@ interface ClaudeCodePromptProps {
|
|||
}
|
||||
|
||||
export function ClaudeCodePrompt({ onOpen, onDismiss, isVisible }: ClaudeCodePromptProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<NudgePrompt
|
||||
onOpen={onOpen}
|
||||
onDismiss={onDismiss}
|
||||
isVisible={isVisible}
|
||||
title="Claude Code Feedback"
|
||||
description="Help us improve your Claude Code experience with LiteLLM! Share your feedback in 4 quick questions."
|
||||
buttonText="Share feedback"
|
||||
title={t("survey.claudeCodePrompt.title")}
|
||||
description={t("survey.claudeCodePrompt.description")}
|
||||
buttonText={t("survey.claudeCodePrompt.buttonText")}
|
||||
icon={Code}
|
||||
accentColor="#7c3aed"
|
||||
buttonStyle={{ backgroundColor: "#7c3aed", borderColor: "#7c3aed" }}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { X, LucideIcon, Check } from "lucide-react";
|
|||
import { Button } from "antd";
|
||||
import { useDisableShowPrompts } from "@/app/(dashboard)/hooks/useDisableShowPrompts";
|
||||
import { setLocalStorageItem, emitLocalStorageChange } from "@/utils/localStorageUtils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface NudgePromptProps {
|
||||
onOpen: () => void;
|
||||
|
|
@ -30,6 +31,7 @@ export function NudgePrompt({
|
|||
accentColor,
|
||||
buttonStyle,
|
||||
}: NudgePromptProps) {
|
||||
const { t } = useTranslation();
|
||||
const disableShowPrompts = useDisableShowPrompts();
|
||||
const [progress, setProgress] = useState(100);
|
||||
const [showConfirmation, setShowConfirmation] = useState(false);
|
||||
|
|
@ -86,9 +88,7 @@ export function NudgePrompt({
|
|||
<Check className="h-5 w-5 text-green-600" />
|
||||
</div>
|
||||
<div className="flex-1">
|
||||
<p className="text-sm text-gray-700 font-medium">
|
||||
Got it, we will not ask again. Reactivate this at any time in the User Menu.
|
||||
</p>
|
||||
<p className="text-sm text-gray-700 font-medium">{t("survey.nudgePrompt.gotIt")}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -134,7 +134,7 @@ export function NudgePrompt({
|
|||
{buttonText}
|
||||
</Button>
|
||||
<Button variant="outlined" danger block onClick={handleDontAskAgain} className="text-xs">
|
||||
Don't ask me again
|
||||
{t("survey.nudgePrompt.dontAskAgain")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import React, { useState } from "react";
|
||||
import React, { useState, useMemo } from "react";
|
||||
import { X, MessageSquare, ArrowRight, ArrowLeft } from "lucide-react";
|
||||
import { Button, Input, Radio, Space, Progress, Checkbox } from "antd";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { TFunction } from "i18next";
|
||||
|
||||
interface SurveyModalProps {
|
||||
isOpen: boolean;
|
||||
|
|
@ -8,33 +10,37 @@ interface SurveyModalProps {
|
|||
onComplete: () => void;
|
||||
}
|
||||
|
||||
const REASONS_OPTIONS = [
|
||||
type ReasonOption = {
|
||||
id: string;
|
||||
label: string;
|
||||
description: string;
|
||||
};
|
||||
|
||||
const getReasonsOptions = (t: TFunction): ReasonOption[] => [
|
||||
{
|
||||
id: "oss_adoption",
|
||||
label: "OSS Adoption",
|
||||
description: "Stars, contributors, forks, community support",
|
||||
label: t("survey.surveyModal.reasonOssLabel"),
|
||||
description: t("survey.surveyModal.reasonOssDesc"),
|
||||
},
|
||||
{
|
||||
id: "ai_integration",
|
||||
label: "AI Integration",
|
||||
description:
|
||||
"LiteLLM had the logging/guardrail integration we needed - Langfuse, OTEL, S3 logging, Azure Content Safety guardrails",
|
||||
label: t("survey.surveyModal.reasonAiIntegrationLabel"),
|
||||
description: t("survey.surveyModal.reasonAiIntegrationDesc"),
|
||||
},
|
||||
{
|
||||
id: "unified_api",
|
||||
label: "Unified API",
|
||||
description: "LiteLLM had the best OpenAI-compatible API across providers - OpenAI, Anthropic, Gemini, etc.",
|
||||
label: t("survey.surveyModal.reasonUnifiedApiLabel"),
|
||||
description: t("survey.surveyModal.reasonUnifiedApiDesc"),
|
||||
},
|
||||
{
|
||||
id: "breadth_of_models",
|
||||
label: "Breadth of Models/Providers",
|
||||
description:
|
||||
"LiteLLM had the provider + endpoint combinations we needed - /ocr endpoint with Mistral OCR, /batches endppint with Bedrock API, etc.",
|
||||
label: t("survey.surveyModal.reasonBreadthLabel"),
|
||||
description: t("survey.surveyModal.reasonBreadthDesc"),
|
||||
},
|
||||
{
|
||||
id: "other",
|
||||
label: "Other",
|
||||
description: "Something else not listed above",
|
||||
label: t("survey.surveyModal.reasonOtherLabel"),
|
||||
description: t("survey.surveyModal.reasonOtherDesc"),
|
||||
},
|
||||
];
|
||||
|
||||
|
|
@ -48,6 +54,7 @@ type SurveyData = {
|
|||
};
|
||||
|
||||
export function SurveyModal({ isOpen, onClose, onComplete }: SurveyModalProps) {
|
||||
const { t } = useTranslation();
|
||||
const [step, setStep] = useState(1);
|
||||
const [data, setData] = useState<SurveyData>({
|
||||
usingAtCompany: null,
|
||||
|
|
@ -59,6 +66,8 @@ export function SurveyModal({ isOpen, onClose, onComplete }: SurveyModalProps) {
|
|||
});
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const reasonsOptions = useMemo(() => getReasonsOptions(t), [t]);
|
||||
|
||||
// Steps: 1=company?, 2=company name (conditional), 3=when, 4=why, 5=email
|
||||
// If not at company: skip step 2, so total is 4
|
||||
// If at company: total is 5
|
||||
|
|
@ -169,15 +178,20 @@ export function SurveyModal({ isOpen, onClose, onComplete }: SurveyModalProps) {
|
|||
return step;
|
||||
};
|
||||
|
||||
const startDateOptions = [
|
||||
{ value: "Less than a month ago", label: t("survey.surveyModal.lessThanMonth") },
|
||||
{ value: "1-3 months ago", label: t("survey.surveyModal.oneToThreeMonths") },
|
||||
{ value: "3-6 months ago", label: t("survey.surveyModal.threeToSixMonths") },
|
||||
{ value: "More than 6 months ago", label: t("survey.surveyModal.moreThanSixMonths") },
|
||||
];
|
||||
|
||||
const renderStepContent = () => {
|
||||
// Step 1: Using at company?
|
||||
if (step === 1) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900">Are you using LiteLLM at your company?</h2>
|
||||
<p className="text-gray-500">
|
||||
Help us understand how our product is being used in professional environments.
|
||||
</p>
|
||||
<h2 className="text-2xl font-bold text-gray-900">{t("survey.surveyModal.step1Title")}</h2>
|
||||
<p className="text-gray-500">{t("survey.surveyModal.step1Description")}</p>
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 gap-4 pt-4">
|
||||
<button
|
||||
onClick={() => updateData("usingAtCompany", true)}
|
||||
|
|
@ -187,8 +201,8 @@ export function SurveyModal({ isOpen, onClose, onComplete }: SurveyModalProps) {
|
|||
: "border-gray-200 hover:border-blue-300 hover:bg-gray-50"
|
||||
}`}
|
||||
>
|
||||
<span className="block text-lg font-semibold text-gray-900 mb-1">Yes</span>
|
||||
<span className="text-sm text-gray-500">We use it for work</span>
|
||||
<span className="block text-lg font-semibold text-gray-900 mb-1">{t("common.yes")}</span>
|
||||
<span className="text-sm text-gray-500">{t("survey.surveyModal.yesSubLabel")}</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => updateData("usingAtCompany", false)}
|
||||
|
|
@ -198,8 +212,8 @@ export function SurveyModal({ isOpen, onClose, onComplete }: SurveyModalProps) {
|
|||
: "border-gray-200 hover:border-blue-300 hover:bg-gray-50"
|
||||
}`}
|
||||
>
|
||||
<span className="block text-lg font-semibold text-gray-900 mb-1">No</span>
|
||||
<span className="text-sm text-gray-500">Personal project / Hobby</span>
|
||||
<span className="block text-lg font-semibold text-gray-900 mb-1">{t("common.no")}</span>
|
||||
<span className="text-sm text-gray-500">{t("survey.surveyModal.noSubLabel")}</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -210,11 +224,11 @@ export function SurveyModal({ isOpen, onClose, onComplete }: SurveyModalProps) {
|
|||
if (step === 2 && data.usingAtCompany === true) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900">What company are you using LiteLLM at?</h2>
|
||||
<p className="text-gray-500">This helps us understand our user base better.</p>
|
||||
<h2 className="text-2xl font-bold text-gray-900">{t("survey.surveyModal.step2Title")}</h2>
|
||||
<p className="text-gray-500">{t("survey.surveyModal.step2Description")}</p>
|
||||
<Input
|
||||
size="large"
|
||||
placeholder="Enter your company name"
|
||||
placeholder={t("survey.surveyModal.companyPlaceholder")}
|
||||
value={data.companyName}
|
||||
onChange={(e) => updateData("companyName", e.target.value)}
|
||||
autoFocus
|
||||
|
|
@ -227,23 +241,23 @@ export function SurveyModal({ isOpen, onClose, onComplete }: SurveyModalProps) {
|
|||
if (step === 3) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900">When did you start using LiteLLM?</h2>
|
||||
<h2 className="text-2xl font-bold text-gray-900">{t("survey.surveyModal.step3Title")}</h2>
|
||||
<Radio.Group
|
||||
value={data.startDate}
|
||||
onChange={(e) => updateData("startDate", e.target.value)}
|
||||
className="w-full"
|
||||
>
|
||||
<Space direction="vertical" className="w-full">
|
||||
{["Less than a month ago", "1-3 months ago", "3-6 months ago", "More than 6 months ago"].map((option) => (
|
||||
{startDateOptions.map((option) => (
|
||||
<label
|
||||
key={option}
|
||||
key={option.value}
|
||||
className={`flex items-center p-4 rounded-lg border cursor-pointer transition-all w-full ${
|
||||
data.startDate === option
|
||||
data.startDate === option.value
|
||||
? "border-blue-600 bg-blue-50 ring-1 ring-blue-600"
|
||||
: "border-gray-200 hover:bg-gray-50"
|
||||
}`}
|
||||
>
|
||||
<Radio value={option}>{option}</Radio>
|
||||
<Radio value={option.value}>{option.label}</Radio>
|
||||
</label>
|
||||
))}
|
||||
</Space>
|
||||
|
|
@ -256,10 +270,10 @@ export function SurveyModal({ isOpen, onClose, onComplete }: SurveyModalProps) {
|
|||
if (step === 4) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900">Why did you pick LiteLLM over other AI Gateways?</h2>
|
||||
<p className="text-gray-500">Select all that apply.</p>
|
||||
<h2 className="text-2xl font-bold text-gray-900">{t("survey.surveyModal.step4Title")}</h2>
|
||||
<p className="text-gray-500">{t("survey.surveyModal.step4Description")}</p>
|
||||
<div className="space-y-3">
|
||||
{REASONS_OPTIONS.map((option) => {
|
||||
{reasonsOptions.map((option) => {
|
||||
const isSelected = data.reasons.includes(option.id);
|
||||
return (
|
||||
<div key={option.id}>
|
||||
|
|
@ -289,7 +303,7 @@ export function SurveyModal({ isOpen, onClose, onComplete }: SurveyModalProps) {
|
|||
{option.id === "other" && isSelected && (
|
||||
<Input
|
||||
className="mt-2 ml-7"
|
||||
placeholder="Please specify..."
|
||||
placeholder={t("survey.surveyModal.otherPlaceholder")}
|
||||
value={data.otherReason}
|
||||
onChange={(e) => updateData("otherReason", e.target.value)}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
|
|
@ -308,19 +322,17 @@ export function SurveyModal({ isOpen, onClose, onComplete }: SurveyModalProps) {
|
|||
if (step === 5) {
|
||||
return (
|
||||
<div className="space-y-6">
|
||||
<h2 className="text-2xl font-bold text-gray-900">Want to share more?</h2>
|
||||
<p className="text-gray-500">
|
||||
Leave your email and we may reach out to learn more about your experience. This is completely optional.
|
||||
</p>
|
||||
<h2 className="text-2xl font-bold text-gray-900">{t("survey.surveyModal.step5Title")}</h2>
|
||||
<p className="text-gray-500">{t("survey.surveyModal.step5Description")}</p>
|
||||
<Input
|
||||
size="large"
|
||||
type="email"
|
||||
placeholder="your@email.com (optional)"
|
||||
placeholder={t("survey.surveyModal.emailPlaceholder")}
|
||||
value={data.email}
|
||||
onChange={(e) => updateData("email", e.target.value)}
|
||||
autoFocus
|
||||
/>
|
||||
<p className="text-xs text-gray-400">We will only use this to follow up on your feedback. No spam, ever.</p>
|
||||
<p className="text-xs text-gray-400">{t("survey.surveyModal.emailNote")}</p>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -341,7 +353,7 @@ export function SurveyModal({ isOpen, onClose, onComplete }: SurveyModalProps) {
|
|||
<div className="px-6 py-4 border-b border-gray-100 flex items-center justify-between bg-gray-50/50">
|
||||
<div className="flex items-center gap-2 text-blue-600">
|
||||
<MessageSquare className="h-5 w-5" />
|
||||
<span className="font-semibold text-sm tracking-wide uppercase">Quick Feedback</span>
|
||||
<span className="font-semibold text-sm tracking-wide uppercase">{t("survey.surveyModal.headerLabel")}</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
|
|
@ -365,12 +377,12 @@ export function SurveyModal({ isOpen, onClose, onComplete }: SurveyModalProps) {
|
|||
{/* Footer */}
|
||||
<div className="px-6 py-4 bg-gray-50 border-t border-gray-200 flex items-center justify-between">
|
||||
<div className="text-sm text-gray-500 font-medium">
|
||||
Step {getStepNumber()} of {totalSteps}
|
||||
{t("survey.surveyModal.stepIndicator", { current: getStepNumber(), total: totalSteps })}
|
||||
</div>
|
||||
<div className="flex gap-3">
|
||||
{step > 1 && (
|
||||
<Button onClick={handleBack} disabled={isSubmitting} icon={<ArrowLeft className="h-4 w-4" />}>
|
||||
Back
|
||||
{t("common.back")}
|
||||
</Button>
|
||||
)}
|
||||
<Button
|
||||
|
|
@ -380,7 +392,7 @@ export function SurveyModal({ isOpen, onClose, onComplete }: SurveyModalProps) {
|
|||
loading={isSubmitting}
|
||||
className="min-w-[100px]"
|
||||
>
|
||||
{isLastStep ? "Submit" : "Next"}
|
||||
{isLastStep ? t("common.submit") : t("common.next")}
|
||||
{!isLastStep && <ArrowRight className="ml-2 h-4 w-4" />}
|
||||
</Button>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import React from "react";
|
||||
import { MessageSquare } from "lucide-react";
|
||||
import { NudgePrompt } from "./NudgePrompt";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
interface SurveyPromptProps {
|
||||
onOpen: () => void;
|
||||
|
|
@ -9,14 +10,16 @@ interface SurveyPromptProps {
|
|||
}
|
||||
|
||||
export function SurveyPrompt({ onOpen, onDismiss, isVisible }: SurveyPromptProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
return (
|
||||
<NudgePrompt
|
||||
onOpen={onOpen}
|
||||
onDismiss={onDismiss}
|
||||
isVisible={isVisible}
|
||||
title="Quick feedback"
|
||||
description="Help us improve LiteLLM! Share your experience in 5 quick questions."
|
||||
buttonText="Share feedback"
|
||||
title={t("survey.surveyPrompt.title")}
|
||||
description={t("survey.surveyPrompt.description")}
|
||||
buttonText={t("survey.surveyPrompt.buttonText")}
|
||||
icon={MessageSquare}
|
||||
accentColor="#3b82f6"
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -7329,5 +7329,61 @@
|
|||
"updateSuccess": "router settings updated successfully",
|
||||
"saveChanges": "Save Changes"
|
||||
}
|
||||
},
|
||||
"survey": {
|
||||
"claudeCodeModal": {
|
||||
"headerLabel": "Claude Code Feedback",
|
||||
"title": "Help us improve your experience",
|
||||
"description": "We'd love to hear about your experience using LiteLLM with Claude Code. Your feedback helps us improve the product for everyone.",
|
||||
"durationNote": "This brief survey takes about 2-3 minutes to complete.",
|
||||
"openFormButton": "Open Feedback Form"
|
||||
},
|
||||
"claudeCodePrompt": {
|
||||
"title": "Claude Code Feedback",
|
||||
"description": "Help us improve your Claude Code experience with LiteLLM! Share your feedback in 4 quick questions.",
|
||||
"buttonText": "Share feedback"
|
||||
},
|
||||
"nudgePrompt": {
|
||||
"dontAskAgain": "Don't ask me again",
|
||||
"gotIt": "Got it, we will not ask again. Reactivate this at any time in the User Menu."
|
||||
},
|
||||
"surveyModal": {
|
||||
"headerLabel": "Quick Feedback",
|
||||
"stepIndicator": "Step {{current}} of {{total}}",
|
||||
"step1Title": "Are you using LiteLLM at your company?",
|
||||
"step1Description": "Help us understand how our product is being used in professional environments.",
|
||||
"yesSubLabel": "We use it for work",
|
||||
"noSubLabel": "Personal project / Hobby",
|
||||
"step2Title": "What company are you using LiteLLM at?",
|
||||
"step2Description": "This helps us understand our user base better.",
|
||||
"companyPlaceholder": "Enter your company name",
|
||||
"step3Title": "When did you start using LiteLLM?",
|
||||
"lessThanMonth": "Less than a month ago",
|
||||
"oneToThreeMonths": "1-3 months ago",
|
||||
"threeToSixMonths": "3-6 months ago",
|
||||
"moreThanSixMonths": "More than 6 months ago",
|
||||
"step4Title": "Why did you pick LiteLLM over other AI Gateways?",
|
||||
"step4Description": "Select all that apply.",
|
||||
"otherPlaceholder": "Please specify...",
|
||||
"reasonOssLabel": "OSS Adoption",
|
||||
"reasonOssDesc": "Stars, contributors, forks, community support",
|
||||
"reasonAiIntegrationLabel": "AI Integration",
|
||||
"reasonAiIntegrationDesc": "LiteLLM had the logging/guardrail integration we needed - Langfuse, OTEL, S3 logging, Azure Content Safety guardrails",
|
||||
"reasonUnifiedApiLabel": "Unified API",
|
||||
"reasonUnifiedApiDesc": "LiteLLM had the best OpenAI-compatible API across providers - OpenAI, Anthropic, Gemini, etc.",
|
||||
"reasonBreadthLabel": "Breadth of Models/Providers",
|
||||
"reasonBreadthDesc": "LiteLLM had the provider + endpoint combinations we needed - /ocr endpoint with Mistral OCR, /batches endppint with Bedrock API, etc.",
|
||||
"reasonOtherLabel": "Other",
|
||||
"reasonOtherDesc": "Something else not listed above",
|
||||
"step5Title": "Want to share more?",
|
||||
"step5Description": "Leave your email and we may reach out to learn more about your experience. This is completely optional.",
|
||||
"emailPlaceholder": "your@email.com (optional)",
|
||||
"emailNote": "We will only use this to follow up on your feedback. No spam, ever."
|
||||
},
|
||||
"surveyPrompt": {
|
||||
"title": "Quick feedback",
|
||||
"description": "Help us improve LiteLLM! Share your experience in 5 quick questions.",
|
||||
"buttonText": "Share feedback"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7329,5 +7329,61 @@
|
|||
"updateSuccess": "路由器设置已成功更新",
|
||||
"saveChanges": "保存更改"
|
||||
}
|
||||
},
|
||||
"survey": {
|
||||
"claudeCodeModal": {
|
||||
"headerLabel": "Claude Code 反馈",
|
||||
"title": "帮助我们改善您的体验",
|
||||
"description": "我们很乐意了解您使用 LiteLLM 与 Claude Code 的体验。您的反馈有助于我们为所有人改进产品。",
|
||||
"durationNote": "这份简短的调查大约需要 2-3 分钟完成。",
|
||||
"openFormButton": "打开反馈表单"
|
||||
},
|
||||
"claudeCodePrompt": {
|
||||
"title": "Claude Code 反馈",
|
||||
"description": "帮助我们改善您在 LiteLLM 上的 Claude Code 体验!通过 4 个简短问题分享您的反馈。",
|
||||
"buttonText": "分享反馈"
|
||||
},
|
||||
"nudgePrompt": {
|
||||
"dontAskAgain": "不再询问",
|
||||
"gotIt": "好的,我们不再询问。您可以随时在用户菜单中重新激活此功能。"
|
||||
},
|
||||
"surveyModal": {
|
||||
"headerLabel": "快速反馈",
|
||||
"stepIndicator": "第 {{current}} 步,共 {{total}} 步",
|
||||
"step1Title": "您是否在公司中使用 LiteLLM?",
|
||||
"step1Description": "帮助我们了解我们的产品在专业环境中的使用情况。",
|
||||
"yesSubLabel": "我们将其用于工作",
|
||||
"noSubLabel": "个人项目 / 兴趣爱好",
|
||||
"step2Title": "您在哪家公司使用 LiteLLM?",
|
||||
"step2Description": "这有助于我们更好地了解我们的用户群。",
|
||||
"companyPlaceholder": "请输入您的公司名称",
|
||||
"step3Title": "您何时开始使用 LiteLLM?",
|
||||
"lessThanMonth": "不到一个月前",
|
||||
"oneToThreeMonths": "1-3 个月前",
|
||||
"threeToSixMonths": "3-6 个月前",
|
||||
"moreThanSixMonths": "超过 6 个月前",
|
||||
"step4Title": "您为何选择 LiteLLM 而非其他 AI 网关?",
|
||||
"step4Description": "请选择所有适用项。",
|
||||
"otherPlaceholder": "请详细说明...",
|
||||
"reasonOssLabel": "OSS 采用",
|
||||
"reasonOssDesc": "Star 数、贡献者、Fork 数、社区支持",
|
||||
"reasonAiIntegrationLabel": "AI 集成",
|
||||
"reasonAiIntegrationDesc": "LiteLLM 拥有我们所需的日志/护栏集成 - Langfuse、OTEL、S3 日志、Azure Content Safety 护栏",
|
||||
"reasonUnifiedApiLabel": "统一 API",
|
||||
"reasonUnifiedApiDesc": "LiteLLM 在各提供商中拥有最佳的 OpenAI 兼容 API - OpenAI、Anthropic、Gemini 等",
|
||||
"reasonBreadthLabel": "模型/提供商广度",
|
||||
"reasonBreadthDesc": "LiteLLM 拥有我们所需的提供商 + 端点组合 - 使用 Mistral OCR 的 /ocr 端点、使用 Bedrock API 的 /batches 端点等",
|
||||
"reasonOtherLabel": "其他",
|
||||
"reasonOtherDesc": "上面未列出的其他原因",
|
||||
"step5Title": "想分享更多吗?",
|
||||
"step5Description": "留下您的邮箱,我们可能会联系您了解更多关于您体验的信息。这完全是可选的。",
|
||||
"emailPlaceholder": "your@email.com(可选)",
|
||||
"emailNote": "我们只会用此邮箱跟进您的反馈。绝不发送垃圾邮件。"
|
||||
},
|
||||
"surveyPrompt": {
|
||||
"title": "快速反馈",
|
||||
"description": "帮助我们改进 LiteLLM!通过 5 个简短问题分享您的使用体验。",
|
||||
"buttonText": "分享反馈"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue