diff --git a/src/frontend/src/CustomNodes/GenericNode/components/NodeDialogComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/NodeDialogComponent/index.tsx index ffe6f313bf..1c3195961c 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/NodeDialogComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/NodeDialogComponent/index.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { useTranslation } from "react-i18next"; import { mutateTemplate } from "@/CustomNodes/helpers/mutate-template"; import type { handleOnNewValueType } from "@/CustomNodes/hooks/use-handle-new-value"; import { ParameterRenderComponent } from "@/components/core/parameterRenderComponent"; @@ -40,6 +41,7 @@ export const NodeDialog: React.FC = ({ name, nodeClass, }) => { + const { t } = useTranslation(); const [isLoading, setIsLoading] = useState(false); const [fieldValues, setFieldValues] = useState>({}); @@ -156,7 +158,7 @@ export const NodeDialog: React.FC = ({ "Knowledge Base"; setSuccessData({ - title: `Knowledge Base "${knowledgeBaseName}" created successfully!`, + title: t("success.knowledgeBaseNodeCreated", { name: knowledgeBaseName }), }); onCreated?.(knowledgeBaseName); diff --git a/src/frontend/src/CustomNodes/GenericNode/components/NodeLegacyComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/NodeLegacyComponent/index.tsx index 03c49e6bc1..4bf7fb4135 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/NodeLegacyComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/NodeLegacyComponent/index.tsx @@ -1,3 +1,4 @@ +import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; import useFlowStore from "@/stores/flowStore"; import { cn } from "@/utils/utils"; @@ -12,6 +13,7 @@ export default function NodeLegacyComponent({ replacement?: string[]; setDismissAll: (value: boolean) => void; }) { + const { t } = useTranslation(); const setFilterComponent = useFlowStore((state) => state.setFilterComponent); const setFilterType = useFlowStore((state) => state.setFilterType); const setFilterEdge = useFlowStore((state) => state.setFilterEdge); @@ -42,7 +44,7 @@ export default function NodeLegacyComponent({ e.stopPropagation(); setDismissAll(true); }} - aria-label="Dismiss warning bar" + aria-label={t("node.dismissWarning")} data-testid="dismiss-warning-bar" > Dismiss diff --git a/src/frontend/src/CustomNodes/GenericNode/components/NodeUpdateComponent/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/NodeUpdateComponent/index.tsx index 666d1f39b9..4eaf1e6e76 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/NodeUpdateComponent/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/NodeUpdateComponent/index.tsx @@ -1,3 +1,4 @@ +import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; import { cn } from "@/utils/utils"; @@ -20,6 +21,7 @@ export default function NodeUpdateComponent({ dismissed?: boolean; isRequired?: boolean; }) { + const { t } = useTranslation(); const showUpdateAction = !blocked; if (dismissed && isRequired) { @@ -88,7 +90,7 @@ export default function NodeUpdateComponent({ e.stopPropagation(); setDismissAll(true); }} - aria-label="Dismiss warning bar" + aria-label={t("node.dismissWarning")} data-testid="dismiss-warning-bar" > Dismiss diff --git a/src/frontend/src/CustomNodes/GenericNode/components/outputModal/index.tsx b/src/frontend/src/CustomNodes/GenericNode/components/outputModal/index.tsx index e77d0f288f..d13d218a01 100644 --- a/src/frontend/src/CustomNodes/GenericNode/components/outputModal/index.tsx +++ b/src/frontend/src/CustomNodes/GenericNode/components/outputModal/index.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { useTranslation } from "react-i18next"; import { ForwardedIconComponent } from "@/components/common/genericIconComponent"; import { Button } from "@/components/ui/button"; import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs"; @@ -15,6 +16,7 @@ export default function OutputModal({ open, setOpen, }): JSX.Element { + const { t } = useTranslation(); const [activeTab, setActiveTab] = useState<"Outputs" | "Logs">("Outputs"); const [isCopied, setIsCopied] = useState(false); const flowPool = useFlowStore((state) => state.flowPool); @@ -46,7 +48,7 @@ export default function OutputModal({ navigator.clipboard.writeText(content).then(() => { setIsCopied(true); - setSuccessData({ title: "Copied to clipboard" }); + setSuccessData({ title: t("success.outputCopied") }); setTimeout(() => { setIsCopied(false); }, 2000); diff --git a/src/frontend/src/CustomNodes/hooks/use-handle-new-value.ts b/src/frontend/src/CustomNodes/hooks/use-handle-new-value.ts index 0832380f95..df8c17fd36 100644 --- a/src/frontend/src/CustomNodes/hooks/use-handle-new-value.ts +++ b/src/frontend/src/CustomNodes/hooks/use-handle-new-value.ts @@ -1,6 +1,7 @@ import { useUpdateNodeInternals } from "@xyflow/react"; import { cloneDeep, debounce } from "lodash"; import { useCallback, useMemo, useRef } from "react"; +import { useTranslation } from "react-i18next"; import { DEBOUNCE_FIELD_LIST } from "@/constants/constants"; import { usePostTemplateValue } from "@/controllers/API/queries/nodes/use-post-template-value"; import { track } from "@/customization/utils/analytics"; @@ -49,6 +50,7 @@ const useHandleOnNewValue = ({ update: AllNodeType | ((oldState: AllNodeType) => AllNodeType), ) => void; }) => { + const { t } = useTranslation(); const takeSnapshot = useFlowsManagerStore((state) => state.takeSnapshot); const setNode = setNodeExternal ?? useFlowStore((state) => state.setNode); const updateNodeInternals = useUpdateNodeInternals(); @@ -104,14 +106,14 @@ const useHandleOnNewValue = ({ } if (!template) { - setErrorData({ title: "Template not found in the component" }); + setErrorData({ title: t("errors.templateNotFound") }); return; } const parameter = template[name]; if (!parameter) { - setErrorData({ title: "Parameter not found in the template" }); + setErrorData({ title: t("errors.parameterNotFound") }); return; } diff --git a/src/frontend/src/components/common/paginatorComponent/index.tsx b/src/frontend/src/components/common/paginatorComponent/index.tsx index 57a1be4bbf..227bf461ca 100644 --- a/src/frontend/src/components/common/paginatorComponent/index.tsx +++ b/src/frontend/src/components/common/paginatorComponent/index.tsx @@ -1,4 +1,5 @@ import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; import { PAGINATION_PAGE, PAGINATION_ROWS_COUNT, @@ -24,6 +25,7 @@ export default function PaginatorComponent({ pages, isComponent, }: PaginatorComponentType) { + const { t } = useTranslation(); const [size, setPageSize] = useState(pageSize); const [maxIndex, setMaxPageIndex] = useState( Math.ceil(totalRowsCount / pageSize), @@ -66,7 +68,7 @@ export default function PaginatorComponent({ direction="up" className="h-7 w-fit gap-1 border-none p-1 pl-1.5 text-mmd focus:border-none focus:ring-0 focus:!ring-offset-0" > - + {Array.from({ length: maxIndex }, (_, i) => i + 1).map((item) => ( diff --git a/src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx b/src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx index 1488637a14..e8c80e8385 100644 --- a/src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx +++ b/src/frontend/src/components/core/appHeaderComponent/components/FlowMenu/index.tsx @@ -75,7 +75,7 @@ export const MenuBar = memo((): JSX.Element => { const handleSave = () => { if (!onFlowPage) return; saveFlow().then(() => { - setSuccessData({ title: "Saved successfully" }); + setSuccessData({ title: t("flow.savedSuccessfully") }); }); }; diff --git a/src/frontend/src/components/core/assistantPanel/components/assistant-header.tsx b/src/frontend/src/components/core/assistantPanel/components/assistant-header.tsx index 0563d85766..fb1ee8089b 100644 --- a/src/frontend/src/components/core/assistantPanel/components/assistant-header.tsx +++ b/src/frontend/src/components/core/assistantPanel/components/assistant-header.tsx @@ -1,3 +1,4 @@ +import { useTranslation } from "react-i18next"; import ForwardedIconComponent from "@/components/common/genericIconComponent"; import ShadTooltip from "@/components/common/shadTooltipComponent"; import { Button } from "@/components/ui/button"; @@ -29,6 +30,7 @@ export function AssistantHeader({ onDeleteSession, isExpanded, }: AssistantHeaderProps) { + const { t } = useTranslation(); const isAtSessionLimit = sessions.length >= ASSISTANT_MAX_SESSIONS; const isNewSessionDisabled = !hasMessages || isAtSessionLimit; @@ -39,7 +41,7 @@ export function AssistantHeader({ - {isAtSessionLimit ? "Max sessions" : "New session"} + {isAtSessionLimit ? t("assistant.maxSessionsLabel") : t("assistant.newSession")} diff --git a/src/frontend/src/components/core/assistantPanel/components/assistant-input.tsx b/src/frontend/src/components/core/assistantPanel/components/assistant-input.tsx index 1e25ef44b6..cbe7e2a110 100644 --- a/src/frontend/src/components/core/assistantPanel/components/assistant-input.tsx +++ b/src/frontend/src/components/core/assistantPanel/components/assistant-input.tsx @@ -1,4 +1,5 @@ import { useEffect, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; import ForwardedIconComponent from "@/components/common/genericIconComponent"; import type { AgenticStepType } from "@/controllers/API/queries/agentic"; import { Button } from "@/components/ui/button"; @@ -71,6 +72,7 @@ export function AssistantInput({ draftMessage = "", onDraftChange, }: AssistantInputProps) { + const { t } = useTranslation(); const [message, setMessage] = useState(draftMessage); const [idlePlaceholder] = useState(getAssistantPlaceholder); @@ -176,7 +178,7 @@ export function AssistantInput({ isProcessing ? isPostGenerationStep ? "" - : "Working on it..." + : t("assistant.workingOnIt") : (placeholder ?? idlePlaceholder) } disabled={disabled || isProcessing} @@ -221,7 +223,7 @@ export function AssistantInput({ diff --git a/src/frontend/src/components/core/assistantPanel/components/assistant-loading-state.tsx b/src/frontend/src/components/core/assistantPanel/components/assistant-loading-state.tsx index 513dcd8fc2..025291d504 100644 --- a/src/frontend/src/components/core/assistantPanel/components/assistant-loading-state.tsx +++ b/src/frontend/src/components/core/assistantPanel/components/assistant-loading-state.tsx @@ -1,4 +1,5 @@ import { memo, useEffect, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; import { ArrowRight, Check, @@ -20,6 +21,7 @@ function AssistantLoadingStateComponent({ streamingContent, onValidationComplete, }: AssistantLoadingStateProps) { + const { t } = useTranslation(); const [codeOpen, setCodeOpen] = useState(true); const streamingRef = useRef(null); @@ -108,7 +110,7 @@ function AssistantLoadingStateComponent({ )} - Code + {t("assistant.code")} {codeOpen && (
@@ -126,7 +128,7 @@ function AssistantLoadingStateComponent({
             onClick={() => onValidationComplete?.()}
             className="mt-4 flex w-full items-center justify-center gap-2 rounded-md bg-accent-emerald-foreground/10 px-4 py-2.5 text-sm font-medium text-accent-emerald-foreground transition-colors hover:bg-accent-emerald-foreground/20"
           >
-            Continue
+            {t("assistant.continue")}
             
           
         )}
diff --git a/src/frontend/src/components/core/assistantPanel/components/assistant-no-models-state.tsx b/src/frontend/src/components/core/assistantPanel/components/assistant-no-models-state.tsx
index f2e6be3ea3..07bdbdaf8f 100644
--- a/src/frontend/src/components/core/assistantPanel/components/assistant-no-models-state.tsx
+++ b/src/frontend/src/components/core/assistantPanel/components/assistant-no-models-state.tsx
@@ -1,9 +1,11 @@
 import { useNavigate } from "react-router-dom";
+import { useTranslation } from "react-i18next";
 import langflowAssistantIcon from "@/assets/langflow_assistant.svg";
 import ForwardedIconComponent from "@/components/common/genericIconComponent";
 import { Button } from "@/components/ui/button";
 
 export function AssistantNoModelsState() {
+  const { t } = useTranslation();
   const navigate = useNavigate();
 
   const handleConfigureModels = () => {
@@ -20,11 +22,10 @@ export function AssistantNoModelsState() {
         />
       
       

- No Model Provider Configured + {t("assistant.noModelsConfigured")}

- To use the assistant, please configure at least one model provider in - your settings. + {t("assistant.noModelsDescription")}

); diff --git a/src/frontend/src/components/core/assistantPanel/components/session-history-dropdown.tsx b/src/frontend/src/components/core/assistantPanel/components/session-history-dropdown.tsx index 7ff4cd9ced..51da9033b3 100644 --- a/src/frontend/src/components/core/assistantPanel/components/session-history-dropdown.tsx +++ b/src/frontend/src/components/core/assistantPanel/components/session-history-dropdown.tsx @@ -2,6 +2,7 @@ * Dropdown showing saved assistant sessions with switch and delete actions. */ +import { useTranslation } from "react-i18next"; import ForwardedIconComponent from "@/components/common/genericIconComponent"; import ShadTooltip from "@/components/common/shadTooltipComponent"; import { Button } from "@/components/ui/button"; @@ -32,6 +33,7 @@ export function SessionHistoryDropdown({ onDeleteSession, isExpanded = false, }: SessionHistoryDropdownProps) { + const { t } = useTranslation(); const hasSessions = sessions.length > 0; // Expanded panel (has messages / height > min) โ†’ dropdown grows down; compact โ†’ grows up const dropAlign = isExpanded ? "start" : "end"; @@ -43,7 +45,7 @@ export function SessionHistoryDropdown({ variant="ghost" size="icon" className="h-8 w-8" - title="Session history" + title={t("assistant.sessionHistory")} data-testid="assistant-session-history" disabled={!hasSessions} > @@ -61,9 +63,9 @@ export function SessionHistoryDropdown({ className="z-[70] max-h-80 w-72 overflow-y-auto" > - Session History + {t("assistant.sessionHistoryLabel")}
@@ -78,7 +80,7 @@ export function SessionHistoryDropdown({ {!hasSessions ? (
- No previous sessions + {t("assistant.noPreviousSessions")}
) : ( sessions.map((entry) => { @@ -103,7 +105,7 @@ export function SessionHistoryDropdown({ {entry.firstUserMessage} - {entry.messageCount} msgs + {t("assistant.messageCounts", { count: entry.messageCount })} {" ยท "} {moment(entry.lastActiveAt).fromNow()} diff --git a/src/frontend/src/components/core/canvasControlsComponent/CanvasControls.tsx b/src/frontend/src/components/core/canvasControlsComponent/CanvasControls.tsx index 1a4f9367f6..5d2e9e623f 100644 --- a/src/frontend/src/components/core/canvasControlsComponent/CanvasControls.tsx +++ b/src/frontend/src/components/core/canvasControlsComponent/CanvasControls.tsx @@ -1,5 +1,6 @@ import { Panel, useStoreApi } from "@xyflow/react"; import { type ReactNode, useCallback, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; import { useShallow } from "zustand/react/shallow"; import langflowAssistantIcon from "@/assets/langflow_assistant.svg"; import ForwardedIconComponent from "@/components/common/genericIconComponent"; @@ -20,6 +21,7 @@ const CanvasControls = ({ selectedNode: AllNodeType | null; effectiveLocked?: boolean; }) => { + const { t } = useTranslation(); const reactFlowStoreApi = useStoreApi(); const isFlowLocked = useFlowStore( useShallow((state) => state.currentFlow?.locked), @@ -116,7 +118,7 @@ const CanvasControls = ({ size="icon" data-testid="canvas-add-note-button" className="group flex h-8 w-8 items-center justify-center rounded-md hover:bg-muted" - title="Add Sticky Note" + title={t("canvas.addStickyNote")} onClick={handleAddNote} > void; }): JSX.Element => { + const { t } = useTranslation(); const [isMaxLength, setIsMaxLength] = useState(false); const [isMaxDescriptionLength, setIsMaxDescriptionLength] = useState(false); const [isMinLength, setIsMinLength] = useState(false); @@ -92,18 +94,18 @@ export const EditFlowSettings: React.FC<
- Name{setName ? "" : ":"} + {t("flow.nameLabel")}{setName ? "" : ":"} {isMaxLength && ( - Character limit reached + {t("flow.characterLimitReached")} )} {isMinLength && ( - Minimum {minLength} character(s) required + {t("flow.minimumCharacters", { count: minLength })} )} {isInvalidName && ( - Flow name already exists + {t("flow.nameAlreadyExists")} )}
{setName ? ( @@ -114,7 +116,7 @@ export const EditFlowSettings: React.FC< type="text" name="name" value={name ?? ""} - placeholder="Flow name" + placeholder={t("flow.namePlaceholder")} id="name" maxLength={maxLength} minLength={minLength} @@ -131,22 +133,22 @@ export const EditFlowSettings: React.FC< )} - Please enter a name + {t("flow.pleaseEnterName")} !!(value && invalidNameList.includes(value))} className="field-invalid" > - Flow name already exists + {t("flow.nameAlreadyExists")}
- Description{setDescription ? "" : ":"} + {t("flow.descriptionLabel")}{setDescription ? "" : ":"} {isMaxDescriptionLength && ( - Character limit reached + {t("flow.characterLimitReached")} )}
{setDescription ? ( @@ -156,7 +158,7 @@ export const EditFlowSettings: React.FC< id="description" onChange={handleDescriptionChange} value={description!} - placeholder="Flow description" + placeholder={t("flow.descriptionPlaceholder")} data-testid="input-flow-description" className="mt-2 max-h-[250px] resize-none font-normal" rows={5} @@ -173,18 +175,18 @@ export const EditFlowSettings: React.FC< description === "" ? "font-light italic" : "", )} > - {description === "" ? "No description" : description} + {description === "" ? t("flow.noDescription") : description}
)} - Please enter a description + {t("flow.pleaseEnterDescription")}
- Lock Flow + {t("flow.lockFlow")}

- Lock your flow to prevent edits or accidental changes. + {t("flow.lockFlowDescription")}

diff --git a/src/frontend/src/components/core/flowSettingsComponent/index.tsx b/src/frontend/src/components/core/flowSettingsComponent/index.tsx index 24526bc5f7..4eb90ff2e8 100644 --- a/src/frontend/src/components/core/flowSettingsComponent/index.tsx +++ b/src/frontend/src/components/core/flowSettingsComponent/index.tsx @@ -1,6 +1,7 @@ import * as Form from "@radix-ui/react-form"; import { cloneDeep } from "lodash"; import { useEffect, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; import useSaveFlow from "@/hooks/flows/use-save-flow"; import useAlertStore from "@/stores/alertStore"; @@ -57,6 +58,7 @@ const FlowSettingsComponent = ({ close, open, }: FlowSettingsComponentProps): JSX.Element => { + const { t } = useTranslation(); const saveFlow = useSaveFlow(); const currentFlow = useFlowStore((state) => flowData ? undefined : state.currentFlow, @@ -89,7 +91,7 @@ const FlowSettingsComponent = ({ saveFlow(newFlow) ?.then(() => { setIsSaving(false); - setSuccessData({ title: "Changes saved successfully" }); + setSuccessData({ title: t("success.changesSaved") }); close(); }) .catch(() => { diff --git a/src/frontend/src/components/core/flowToolbarComponent/components/deploy-choice-dialog/deployment-phase.tsx b/src/frontend/src/components/core/flowToolbarComponent/components/deploy-choice-dialog/deployment-phase.tsx index d031273dc9..61d184276f 100644 --- a/src/frontend/src/components/core/flowToolbarComponent/components/deploy-choice-dialog/deployment-phase.tsx +++ b/src/frontend/src/components/core/flowToolbarComponent/components/deploy-choice-dialog/deployment-phase.tsx @@ -1,3 +1,4 @@ +import { useTranslation } from "react-i18next"; import ForwardedIconComponent from "@/components/common/genericIconComponent"; import { Button } from "@/components/ui/button"; import { @@ -41,10 +42,11 @@ export default function DeploymentPhaseContent({ onContinue, onCancel, }: DeploymentPhaseContentProps) { + const { t } = useTranslation(); return ( <> - Select Deployment + {t("deployments.selectDeployment")} {selectedProvider ? `Deployments on ${selectedProvider.name} for this flow.` @@ -91,7 +93,7 @@ export default function DeploymentPhaseContent({ htmlFor="deploy-new" className="flex flex-1 cursor-pointer flex-col gap-0.5" > - Create new deployment + {t("deployments.createNewDeployment")} {selectedProvider ? `New deployment on ${selectedProvider.name}` @@ -113,7 +115,7 @@ export default function DeploymentPhaseContent({ )}
diff --git a/src/frontend/src/components/core/flowToolbarComponent/components/deploy-choice-dialog/index.tsx b/src/frontend/src/components/core/flowToolbarComponent/components/deploy-choice-dialog/index.tsx index 688aa97ac3..155b49a25c 100644 --- a/src/frontend/src/components/core/flowToolbarComponent/components/deploy-choice-dialog/index.tsx +++ b/src/frontend/src/components/core/flowToolbarComponent/components/deploy-choice-dialog/index.tsx @@ -1,4 +1,5 @@ import { useCallback, useEffect, useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; import { Dialog, DialogContent } from "@/components/ui/dialog"; import { usePatchSnapshot } from "@/controllers/API/queries/deployments"; import { useGetDeploymentAttachments } from "@/controllers/API/queries/deployments/use-get-deployment-attachments"; @@ -56,6 +57,7 @@ export default function DeployChoiceDialog({ onUpdateComplete, onTestDeployment, }: DeployChoiceDialogProps) { + const { t } = useTranslation(); const [phase, setPhase] = useState("provider"); const [selectedProviderId, setSelectedProviderId] = useState(NEW_DEPLOYMENT_VALUE); @@ -148,7 +150,7 @@ export default function DeployChoiceDialog({ ); if (!nextReviewAttachment) { showError( - "Failed to load deployment details", + t("errors.failedToLoadDeployment"), "No provider snapshot was found for this flow on the selected deployment.", ); setPhase("deployments"); diff --git a/src/frontend/src/components/core/flowToolbarComponent/components/deploy-choice-dialog/provider-phase.tsx b/src/frontend/src/components/core/flowToolbarComponent/components/deploy-choice-dialog/provider-phase.tsx index d90217b8ad..ba9039cbe2 100644 --- a/src/frontend/src/components/core/flowToolbarComponent/components/deploy-choice-dialog/provider-phase.tsx +++ b/src/frontend/src/components/core/flowToolbarComponent/components/deploy-choice-dialog/provider-phase.tsx @@ -1,3 +1,4 @@ +import { useTranslation } from "react-i18next"; import { Button } from "@/components/ui/button"; import { DialogDescription, @@ -23,10 +24,11 @@ export default function ProviderPhaseContent({ onContinue, onCancel, }: ProviderPhaseContentProps) { + const { t } = useTranslation(); return ( <> - Select Provider + {t("deployments.selectProvider")} Choose a provider environment to deploy to, or create a new deployment from scratch. @@ -60,7 +62,7 @@ export default function ProviderPhaseContent({ - +
); diff --git a/src/frontend/src/components/core/flowToolbarComponent/components/deploy-choice-dialog/review-phase.tsx b/src/frontend/src/components/core/flowToolbarComponent/components/deploy-choice-dialog/review-phase.tsx index 79c5e65c94..ff52e4c56b 100644 --- a/src/frontend/src/components/core/flowToolbarComponent/components/deploy-choice-dialog/review-phase.tsx +++ b/src/frontend/src/components/core/flowToolbarComponent/components/deploy-choice-dialog/review-phase.tsx @@ -1,3 +1,4 @@ +import { useTranslation } from "react-i18next"; import ForwardedIconComponent from "@/components/common/genericIconComponent"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; @@ -34,12 +35,13 @@ export default function ReviewPhaseContent({ { enabled: !!attachment.flow_version_id }, ); + const { t } = useTranslation(); const currentVersionTag = currentVersion?.version_tag ?? "..."; return ( <> - Review Update + {t("deployments.reviewUpdate")} Review the version change before updating the deployment. @@ -55,7 +57,7 @@ export default function ReviewPhaseContent({
- Current + {t("deployments.current")} {isLoadingVersion ? (
- New + {t("deployments.new")} {newVersionTag}
@@ -87,7 +89,7 @@ export default function ReviewPhaseContent({ Back
diff --git a/src/frontend/src/components/core/flowToolbarComponent/components/deploy-dropdown.tsx b/src/frontend/src/components/core/flowToolbarComponent/components/deploy-dropdown.tsx index 1d6762c1a4..2623060542 100644 --- a/src/frontend/src/components/core/flowToolbarComponent/components/deploy-dropdown.tsx +++ b/src/frontend/src/components/core/flowToolbarComponent/components/deploy-dropdown.tsx @@ -78,8 +78,8 @@ export default function PublishDropdown({ setCurrentFlow(updatedFlow); } else { setErrorData({ - title: "Failed to save flow", - list: ["Flows variable undefined"], + title: t("errors.failedToSaveFlow"), + list: [t("errors.flowsVariableUndefined")], }); } }, @@ -87,7 +87,7 @@ export default function PublishDropdown({ const detail = e.response?.data?.detail || e.message || "Unknown error"; setErrorData({ - title: "Failed to save flow", + title: t("errors.failedToSaveFlow"), list: [detail], }); }, diff --git a/src/frontend/src/components/core/folderSidebarComponent/components/sideBarFolderButtons/components/get-started-progress.tsx b/src/frontend/src/components/core/folderSidebarComponent/components/sideBarFolderButtons/components/get-started-progress.tsx index 0376ca5ff6..0fdbef42e2 100644 --- a/src/frontend/src/components/core/folderSidebarComponent/components/sideBarFolderButtons/components/get-started-progress.tsx +++ b/src/frontend/src/components/core/folderSidebarComponent/components/sideBarFolderButtons/components/get-started-progress.tsx @@ -1,4 +1,5 @@ import { type FC, useEffect, useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; import { FaDiscord, FaGithub } from "react-icons/fa"; import IconComponent from "@/components/common/genericIconComponent"; import { Button } from "@/components/ui/button"; @@ -15,6 +16,7 @@ export const GetStartedProgress: FC<{ isDiscordJoined: boolean; handleDismissDialog: () => void; }> = ({ userData, isGithubStarred, isDiscordJoined, handleDismissDialog }) => { + const { t } = useTranslation(); const [isGithubStarredChild, setIsGithubStarredChild] = useState(isGithubStarred); const [isDiscordJoinedChild, setIsDiscordJoinedChild] = @@ -92,10 +94,10 @@ export const GetStartedProgress: FC<{ > {percentageGetStarted >= 100 ? ( <> - All Set ๐ŸŽ‰ + {t("sidebar.allSet")} ๐ŸŽ‰ ) : ( - "Get started" + t("sidebar.getStarted") )} @@ -202,7 +204,7 @@ export const GetStartedProgress: FC<{ isDiscordJoinedChild && "text-muted-foreground line-through", )} > - Join the community + {t("sidebar.joinCommunity")} @@ -229,7 +231,7 @@ export const GetStartedProgress: FC<{ /> - Create a flow + {t("sidebar.createFlow")} diff --git a/src/frontend/src/components/core/folderSidebarComponent/components/sideBarFolderButtons/components/mcp-server-notice.tsx b/src/frontend/src/components/core/folderSidebarComponent/components/sideBarFolderButtons/components/mcp-server-notice.tsx index c0533b8823..37d119e771 100644 --- a/src/frontend/src/components/core/folderSidebarComponent/components/sideBarFolderButtons/components/mcp-server-notice.tsx +++ b/src/frontend/src/components/core/folderSidebarComponent/components/sideBarFolderButtons/components/mcp-server-notice.tsx @@ -1,4 +1,5 @@ import type { FC } from "react"; +import { useTranslation } from "react-i18next"; import MCPLangflow from "@/assets/MCPLangflow.png"; import ForwardedIconComponent from "@/components/common/genericIconComponent"; import { Button } from "@/components/ui/button"; @@ -7,6 +8,7 @@ import { useCustomNavigate } from "@/customization/hooks/use-custom-navigate"; export const MCPServerNotice: FC<{ handleDismissDialog: () => void; }> = ({ handleDismissDialog }) => { + const { t } = useTranslation(); const navigate = useCustomNavigate(); return (
@@ -19,12 +21,12 @@ export const MCPServerNotice: FC<{
-
New
-
Projects as MCP Servers
+
{t("sidebar.mcpNewBadge")}
+
{t("sidebar.mcpProjectsTitle")}
MCP Notice Modal

- Expose flows as tools from clients like Cursor or Claude. + {t("sidebar.mcpExposeFlows")}

@@ -36,7 +38,7 @@ export const MCPServerNotice: FC<{ }} className="w-full" > - Go to Server + {t("sidebar.mcpGoToServer")}
diff --git a/src/frontend/src/components/core/jsonEditor/menuUtils.ts b/src/frontend/src/components/core/jsonEditor/menuUtils.ts index 0fba721e22..6750c13808 100644 --- a/src/frontend/src/components/core/jsonEditor/menuUtils.ts +++ b/src/frontend/src/components/core/jsonEditor/menuUtils.ts @@ -25,6 +25,13 @@ export const createCopyButton = ( getEditor: () => any, setSuccessData: (data: { title: string }) => void, setErrorData: (data: { title: string; list: string[] }) => void, + messages: { + copyFailed: string; + editorNotAvailable: string; + jsonCopied: string; + unableToCopy: string; + copyJson: string; + }, ): MenuItem => { return { type: "button" as const, @@ -32,8 +39,8 @@ export const createCopyButton = ( const editor = getEditor(); if (!editor) { setErrorData({ - title: "Copy Failed", - list: ["Editor not available"], + title: messages.copyFailed, + list: [messages.editorNotAvailable], }); return; } @@ -46,17 +53,17 @@ export const createCopyButton = ( navigator.clipboard .writeText(textContent) .then(() => { - setSuccessData({ title: "JSON copied to clipboard" }); + setSuccessData({ title: messages.jsonCopied }); }) .catch(() => { setErrorData({ - title: "Copy Failed", - list: ["Unable to copy to clipboard. Please copy manually."], + title: messages.copyFailed, + list: [messages.unableToCopy], }); }); }, icon: faCopy, - title: "Copy JSON to clipboard", + title: messages.copyJson, }; }; @@ -99,6 +106,14 @@ export const processTextModeItems = ( getEditor: () => any, setSuccessData: (data: { title: string }) => void, setErrorData: (data: { title: string; list: string[] }) => void, + messages: { + copyFailed: string; + editorNotAvailable: string; + jsonCopied: string; + unableToCopy: string; + copyJson: string; + outputCopied: string; + }, ): MenuItem[] => { let filteredItems = filterTextModeItems(items); @@ -107,10 +122,15 @@ export const processTextModeItems = ( getEditor, setSuccessData, setErrorData, + messages, ); filteredItems = addCopyButtonToItems(filteredItems, copyButton); } else { - filteredItems = enhanceExistingCopyButtons(filteredItems, setSuccessData); + filteredItems = enhanceExistingCopyButtons( + filteredItems, + setSuccessData, + messages.jsonCopied, + ); } return filteredItems; @@ -119,10 +139,7 @@ export const processTextModeItems = ( export const processTreeModeItems = ( items: MenuItem[], setSuccessData: (data: { title: string }) => void, + outputCopiedMsg: string, ): MenuItem[] => { - return enhanceExistingCopyButtons( - items, - setSuccessData, - "Copied to clipboard", - ); + return enhanceExistingCopyButtons(items, setSuccessData, outputCopiedMsg); }; diff --git a/src/frontend/src/components/core/jsonEditor/useMenuCustomization.ts b/src/frontend/src/components/core/jsonEditor/useMenuCustomization.ts index 4d5b748a17..71de7bf9e0 100644 --- a/src/frontend/src/components/core/jsonEditor/useMenuCustomization.ts +++ b/src/frontend/src/components/core/jsonEditor/useMenuCustomization.ts @@ -1,3 +1,4 @@ +import { useTranslation } from "react-i18next"; import type { MenuItem, Mode } from "vanilla-jsoneditor"; import { processTextModeItems, processTreeModeItems } from "./menuUtils"; @@ -5,6 +6,16 @@ export const useMenuCustomization = ( setSuccessData: (data: { title: string }) => void, setErrorData: (data: { title: string; list: string[] }) => void, ) => { + const { t } = useTranslation(); + const messages = { + copyFailed: t("misc.copyFailed"), + editorNotAvailable: t("misc.editorNotAvailable"), + jsonCopied: t("success.jsonCopied"), + unableToCopy: t("misc.unableToCopy"), + copyJson: t("misc.copyJson"), + outputCopied: t("success.outputCopied"), + }; + const customizeMenu = ( items: MenuItem[], context: { mode: Mode; modal: boolean; readOnly: boolean }, @@ -17,10 +28,15 @@ export const useMenuCustomization = ( getEditor, setSuccessData, setErrorData, + messages, ); case "tree": - return processTreeModeItems(items, setSuccessData); + return processTreeModeItems( + items, + setSuccessData, + messages.outputCopied, + ); default: // For all other modes, return items unchanged diff --git a/src/frontend/src/components/core/parameterRenderComponent/components/ToolsComponent/index.tsx b/src/frontend/src/components/core/parameterRenderComponent/components/ToolsComponent/index.tsx index dee919db41..9fab60a726 100644 --- a/src/frontend/src/components/core/parameterRenderComponent/components/ToolsComponent/index.tsx +++ b/src/frontend/src/components/core/parameterRenderComponent/components/ToolsComponent/index.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { useTranslation } from "react-i18next"; import ShadTooltip from "@/components/common/shadTooltipComponent"; import { ICON_STROKE_WIDTH } from "@/constants/constants"; import { ENABLE_MCP_COMPOSER } from "@/customization/feature-flags"; @@ -25,6 +26,7 @@ export default function ToolsComponent({ template, showParameter = true, }: InputProps): JSX.Element | null { + const { t } = useTranslation(); const [isModalOpen, setIsModalOpen] = useState(false); const actions = value ?.filter((action) => action.status === true) @@ -131,7 +133,7 @@ export default function ToolsComponent({ No actions added to this server ) diff --git a/src/frontend/src/components/core/parameterRenderComponent/components/accordionPromptComponent/index.tsx b/src/frontend/src/components/core/parameterRenderComponent/components/accordionPromptComponent/index.tsx index d126f8f743..b24ffabd2b 100644 --- a/src/frontend/src/components/core/parameterRenderComponent/components/accordionPromptComponent/index.tsx +++ b/src/frontend/src/components/core/parameterRenderComponent/components/accordionPromptComponent/index.tsx @@ -1,4 +1,5 @@ import { useEffect, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; import ForwardedIconComponent from "@/components/common/genericIconComponent"; import { Button } from "@/components/ui/button"; import { @@ -57,6 +58,7 @@ export default function AccordionPromptComponent({ showParameter = false, isDoubleBrackets = false, }: InputProps): JSX.Element { + const { t } = useTranslation(); const [isOpen, setIsOpen] = useState(true); const [internalValue, setInternalValue] = useState(value); const [isScrollable, setIsScrollable] = useState(false); @@ -564,7 +566,7 @@ export default function AccordionPromptComponent({ onClick={handleAddVariable} disabled={disabled || readonly} className="h-6 w-6 p-0 text-muted-foreground" - title="Add variable" + title={t("accordion.addVariable")} > {isDoubleBrackets ? "{{+}}" : "{+}"} @@ -632,7 +634,7 @@ export default function AccordionPromptComponent({ variant="ghost" size="sm" className="h-6 w-6 p-0 text-muted-foreground" - title="Fullscreen" + title={t("accordion.fullscreen")} data-testid={ isDoubleBrackets ? "button_open_mustache_prompt_modal" diff --git a/src/frontend/src/components/core/parameterRenderComponent/components/copyFieldAreaComponent/index.tsx b/src/frontend/src/components/core/parameterRenderComponent/components/copyFieldAreaComponent/index.tsx index e90ea0ed79..8f2b6f4426 100644 --- a/src/frontend/src/components/core/parameterRenderComponent/components/copyFieldAreaComponent/index.tsx +++ b/src/frontend/src/components/core/parameterRenderComponent/components/copyFieldAreaComponent/index.tsx @@ -1,4 +1,5 @@ import { useMemo, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; import { GRADIENT_CLASS_DISABLED } from "@/constants/constants"; import { customGetHostProtocol } from "@/customization/utils/custom-get-host-protocol"; import useAlertStore from "@/stores/alertStore"; @@ -61,6 +62,7 @@ export default function CopyFieldAreaComponent({ id = "", showParameter = true, }: InputProps): JSX.Element | null { + const { t } = useTranslation(); const inputRef = useRef(null); const [isFocused, setIsFocused] = useState(false); const [isCopied, setIsCopied] = useState(false); @@ -96,7 +98,7 @@ export default function CopyFieldAreaComponent({ navigator.clipboard.writeText(valueToRender); setSuccessData({ - title: "Endpoint URL copied", + title: t("success.endpointUrlCopied"), }); event?.stopPropagation(); diff --git a/src/frontend/src/components/core/parameterRenderComponent/components/floatComponent/index.tsx b/src/frontend/src/components/core/parameterRenderComponent/components/floatComponent/index.tsx index f0e5c9505e..35c457669c 100644 --- a/src/frontend/src/components/core/parameterRenderComponent/components/floatComponent/index.tsx +++ b/src/frontend/src/components/core/parameterRenderComponent/components/floatComponent/index.tsx @@ -7,6 +7,7 @@ import { } from "@chakra-ui/number-input"; import { MinusIcon, PlusIcon } from "lucide-react"; import { useEffect, useRef, useState } from "react"; +import { useTranslation } from "react-i18next"; import { cn } from "@/utils/utils"; import { handleKeyDown } from "../../../../../utils/reactflowUtils"; import type { FloatComponentType, InputProps } from "../../types"; @@ -20,6 +21,7 @@ export default function FloatComponent({ id = "", showParameter = true, }: InputProps): JSX.Element | null { + const { t } = useTranslation(); const step = rangeSpec?.step ?? 0.1; const min = rangeSpec?.min; const max = rangeSpec?.max; @@ -105,7 +107,7 @@ export default function FloatComponent({ onKeyDown={(event) => handleKeyDown(event, localValue, "")} onInput={handleInputChange} disabled={disabled} - placeholder={editNode ? "Float number" : "Type a float number"} + placeholder={editNode ? t("input.floatNumberShort") : t("input.floatNumberFull")} data-testid={id} ref={inputRef} onBlur={handleBlur} diff --git a/src/frontend/src/components/core/parameterRenderComponent/components/inputGlobalComponent/index.tsx b/src/frontend/src/components/core/parameterRenderComponent/components/inputGlobalComponent/index.tsx index 52ddee8429..5acebc0e6d 100644 --- a/src/frontend/src/components/core/parameterRenderComponent/components/inputGlobalComponent/index.tsx +++ b/src/frontend/src/components/core/parameterRenderComponent/components/inputGlobalComponent/index.tsx @@ -1,4 +1,5 @@ import { useEffect } from "react"; +import { useTranslation } from "react-i18next"; import { useGetGlobalVariables } from "@/controllers/API/queries/variables"; import GeneralDeleteConfirmationModal from "@/shared/components/delete-confirmation-modal"; import { looksLikeVariableName } from "../../../../../utils/reactflowUtils"; @@ -30,6 +31,7 @@ export default function InputGlobalComponent({ hasRefreshButton = false, showParameter = true, }: InputProps): JSX.Element | null { + const { t } = useTranslation(); const { data: globalVariables, isFetchedAfterMount: isGlobalVariablesFetchedAfterMount, @@ -128,7 +130,7 @@ export default function InputGlobalComponent({ className={cn("mr-2 h-4 w-4 text-primary")} aria-hidden="true" /> - Add New Variable + {t("input.addNewVariable")} ); diff --git a/src/frontend/src/components/core/parameterRenderComponent/components/keypairListComponent/index.tsx b/src/frontend/src/components/core/parameterRenderComponent/components/keypairListComponent/index.tsx index 7354b4523b..3291d670e4 100644 --- a/src/frontend/src/components/core/parameterRenderComponent/components/keypairListComponent/index.tsx +++ b/src/frontend/src/components/core/parameterRenderComponent/components/keypairListComponent/index.tsx @@ -1,5 +1,6 @@ import { cloneDeep } from "lodash"; import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; import { Input } from "@/components/ui/input"; import { ICON_STROKE_WIDTH } from "@/constants/constants"; import { @@ -19,6 +20,7 @@ const KeypairListComponent = ({ id, showParameter = true, }) => { + const { t } = useTranslation(); const getTestId = (prefix, index) => `${editNode ? "editNode" : ""}${prefix}${index}`; @@ -130,7 +132,7 @@ const KeypairListComponent = ({ type="text" value={key.trim()} className={getInputClassName(editNode, duplicateKey)} - placeholder="Type key..." + placeholder={t("input.typeKey")} onChange={(event) => handleChangeKey(event, index)} /> handleChangeValue(event, index)} />
diff --git a/src/frontend/src/components/core/parameterRenderComponent/components/mcpComponent/index.tsx b/src/frontend/src/components/core/parameterRenderComponent/components/mcpComponent/index.tsx index ea776aab39..2ee5ffcc1f 100644 --- a/src/frontend/src/components/core/parameterRenderComponent/components/mcpComponent/index.tsx +++ b/src/frontend/src/components/core/parameterRenderComponent/components/mcpComponent/index.tsx @@ -1,4 +1,5 @@ import { useEffect, useMemo, useState } from "react"; +import { useTranslation } from "react-i18next"; import { useAddMCPServer } from "@/controllers/API/queries/mcp/use-add-mcp-server"; import { useGetMCPServers } from "@/controllers/API/queries/mcp/use-get-mcp-servers"; import AddMcpServerModal from "@/modals/addMcpServerModal"; @@ -17,6 +18,7 @@ export default function McpComponent({ id = "", showParameter = true, }: InputProps): JSX.Element | null { + const { t } = useTranslation(); const [open, setOpen] = useState(false); const { data: mcpServers } = useGetMCPServers({ withCounts: true }); const { mutate: addMcpServer } = useAddMCPServer(); @@ -102,7 +104,7 @@ export default function McpComponent({ }, onError: (error) => { setErrorData({ - title: "Error adding MCP server", + title: t("errors.addMcpServer"), list: [error.message], }); }, @@ -197,7 +199,7 @@ export default function McpComponent({ onClick={handleAddButtonClick} data-testid="add-mcp-server-simple-button" > - Add MCP Server + {t("input.addMcpServer")} )} {options && ( diff --git a/src/frontend/src/components/core/parameterRenderComponent/components/multiselectComponent/index.tsx b/src/frontend/src/components/core/parameterRenderComponent/components/multiselectComponent/index.tsx index f9cd39304b..7425b34d4a 100644 --- a/src/frontend/src/components/core/parameterRenderComponent/components/multiselectComponent/index.tsx +++ b/src/frontend/src/components/core/parameterRenderComponent/components/multiselectComponent/index.tsx @@ -147,7 +147,7 @@ export default function MultiselectComponent({ onChange={(event) => { setSearchValue(event.target.value); }} - placeholder="Search options..." + placeholder={t("input.searchOptions")} className="flex h-9 w-full rounded-md bg-transparent py-3 text-sm outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed disabled:opacity-50" />
)}{" "}
- +
)} @@ -151,7 +153,7 @@ export function SessionMoreMenu({ name="Scroll" className="mr-2 h-4 w-4" /> - Message logs + {t("playgroundComponent.messageLogs")} )} @@ -163,7 +165,7 @@ export function SessionMoreMenu({ >
- Clear chat + {t("playgroundComponent.clearChat")}
)} @@ -178,7 +180,7 @@ export function SessionMoreMenu({ name="Trash2" className="mr-2 h-4 w-4" /> - {isDefaultSession ? "Clear session" : "Delete session"} + {isDefaultSession ? t("playgroundComponent.clearSession") : t("playgroundComponent.deleteSession")} )} diff --git a/src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/components/session-selector.tsx b/src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/components/session-selector.tsx index 2adec457fc..12bcb7f5ae 100644 --- a/src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/components/session-selector.tsx +++ b/src/frontend/src/components/core/playgroundComponent/chat-view/chat-header/components/session-selector.tsx @@ -1,4 +1,5 @@ import { useState } from "react"; +import { useTranslation } from "react-i18next"; import ForwardedIconComponent from "@/components/common/genericIconComponent"; import ShadTooltip from "@/components/common/shadTooltipComponent"; import { useUpdateSessionName } from "@/controllers/API/queries/messages/use-rename-session"; @@ -43,6 +44,7 @@ export function SessionSelector({ onToggleSelect, showCheckbox = false, }: SessionSelectorProps) { + const { t } = useTranslation(); const [isEditing, setIsEditing] = useState(false); const { mutate: updateSessionName } = useUpdateSessionName(); const setNewSessionCloseVoiceAssistant = useVoiceStore( @@ -174,7 +176,7 @@ export function SessionSelector({ sideOffset={4} contentClassName="z-[100] [&>div.p-1]:!h-auto [&>div.p-1]:!min-h-0" isVisible={true} - tooltipContent="More options" + tooltipContent={t("playgroundComponent.moreOptions")} tooltipSide="left" open={menuOpen} onOpenChange={onMenuOpenChange} diff --git a/src/frontend/src/components/core/playgroundComponent/chat-view/chat-input/components/input-wrapper.tsx b/src/frontend/src/components/core/playgroundComponent/chat-view/chat-input/components/input-wrapper.tsx index 5b2a38a0c2..748765e0db 100644 --- a/src/frontend/src/components/core/playgroundComponent/chat-view/chat-input/components/input-wrapper.tsx +++ b/src/frontend/src/components/core/playgroundComponent/chat-view/chat-input/components/input-wrapper.tsx @@ -1,3 +1,4 @@ +import { useTranslation } from "react-i18next"; import { ENABLE_FILES_ON_PLAYGROUND } from "@/customization/feature-flags"; import type { FilePreviewType } from "@/types/components"; import FilePreviewDisplay from "../../utils/file-preview-display"; @@ -44,6 +45,7 @@ const InputWrapper = ({ onStopRecording, isAudioSupported, }: InputWrapperProps) => { + const { t } = useTranslation(); const classNameFilePreview = `flex w-full items-center gap-2 py-2 overflow-auto`; const onClick = (e: React.MouseEvent) => { @@ -89,7 +91,7 @@ const InputWrapper = ({ onKeyDown={onKeyDown} role="button" tabIndex={0} - aria-label="Focus chat input" + aria-label={t("playgroundComponent.focusChatInput")} > {/* Text input area */}
diff --git a/src/frontend/src/components/core/playgroundComponent/chat-view/chat-messages/components/bot-message.tsx b/src/frontend/src/components/core/playgroundComponent/chat-view/chat-messages/components/bot-message.tsx index e47ede4c54..8573a19de3 100644 --- a/src/frontend/src/components/core/playgroundComponent/chat-view/chat-messages/components/bot-message.tsx +++ b/src/frontend/src/components/core/playgroundComponent/chat-view/chat-messages/components/bot-message.tsx @@ -1,4 +1,5 @@ import { memo, useState } from "react"; +import { useTranslation } from "react-i18next"; import LangflowLogo from "@/assets/LangflowLogo.svg?react"; import IconComponent, { ForwardedIconComponent, @@ -25,6 +26,7 @@ import { EditMessageButton } from "./message-options"; export const BotMessage = memo( ({ chat, lastMessage, updateChat, playgroundPage }: chatMessagePropsType) => { + const { t } = useTranslation(); const setErrorData = useAlertStore((state) => state.setErrorData); const [editMessage, setEditMessage] = useState(false); const isBuilding = useFlowStore((state) => state.isBuilding); @@ -65,7 +67,7 @@ export const BotMessage = memo( }, onError: () => { setErrorData({ - title: "Error updating messages.", + title: t("errors.updatingMessages"), }); }, }, @@ -96,7 +98,7 @@ export const BotMessage = memo( { onError: () => { setErrorData({ - title: "Error updating messages.", + title: t("errors.updatingMessages"), }); }, }, diff --git a/src/frontend/src/components/core/playgroundComponent/chat-view/chat-messages/components/user-message.tsx b/src/frontend/src/components/core/playgroundComponent/chat-view/chat-messages/components/user-message.tsx index 6000440711..7be160ed2a 100644 --- a/src/frontend/src/components/core/playgroundComponent/chat-view/chat-messages/components/user-message.tsx +++ b/src/frontend/src/components/core/playgroundComponent/chat-view/chat-messages/components/user-message.tsx @@ -54,7 +54,7 @@ export const UserMessage = memo( }, onError: () => { setErrorData({ - title: "Error updating messages.", + title: t("errors.updatingMessages"), }); }, }, @@ -88,7 +88,7 @@ export const UserMessage = memo( { onError: () => { setErrorData({ - title: "Error updating messages.", + title: t("errors.updatingMessages"), }); }, }, diff --git a/src/frontend/src/components/core/playgroundComponent/chat-view/utils/file-preview-display.tsx b/src/frontend/src/components/core/playgroundComponent/chat-view/utils/file-preview-display.tsx index 9d23449467..4cad8f29cc 100644 --- a/src/frontend/src/components/core/playgroundComponent/chat-view/utils/file-preview-display.tsx +++ b/src/frontend/src/components/core/playgroundComponent/chat-view/utils/file-preview-display.tsx @@ -1,4 +1,5 @@ import { useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; import { ForwardedIconComponent } from "@/components/common/genericIconComponent"; import Loading from "@/components/ui/loading"; import { cn } from "@/utils/utils"; @@ -56,6 +57,7 @@ export default function FilePreviewDisplay({ variant = "compact", className, }: FilePreviewDisplayProps) { + const { t } = useTranslation(); const [imageError, setImageError] = useState(false); const previewUrl = getFilePreviewUrl(file); const fileInfo = extractFileInfo(file); @@ -111,7 +113,7 @@ export default function FilePreviewDisplay({ onClick={onDelete} className="absolute -right-2 -top-2 flex h-5 w-5 items-center justify-center rounded-full bg-destructive text-destructive-foreground hover:bg-destructive/90" type="button" - aria-label="Delete file" + aria-label={t("playgroundComponent.deleteFile")} > diff --git a/src/frontend/src/components/core/playgroundComponent/hooks/use-session-manager.ts b/src/frontend/src/components/core/playgroundComponent/hooks/use-session-manager.ts index e674177d4b..7821d4cf39 100644 --- a/src/frontend/src/components/core/playgroundComponent/hooks/use-session-manager.ts +++ b/src/frontend/src/components/core/playgroundComponent/hooks/use-session-manager.ts @@ -1,4 +1,5 @@ import { useCallback, useEffect } from "react"; +import { useTranslation } from "react-i18next"; import { NEW_SESSION_NAME } from "@/constants/constants"; import { useDeleteSession } from "@/controllers/API/queries/messages/use-delete-sessions"; import { useBulkDeleteSessions } from "@/controllers/API/queries/messages/use-bulk-delete-sessions"; @@ -34,6 +35,7 @@ export function useSessionManager({ flowId }: UseSessionManagerProps) { const deleteSessionFromMessagesStore = useMessagesStore( (state) => state.deleteSession, ); + const { t } = useTranslation(); const setErrorData = useAlertStore((state) => state.setErrorData); const { data: dbSessionsResponse } = useGetSessionsFromFlowQuery({ @@ -46,8 +48,8 @@ export function useSessionManager({ flowId }: UseSessionManagerProps) { const { mutateAsync: updateSessionName } = useUpdateSessionName(); const notifyDeleteSessionError = useCallback(() => { - setErrorData({ title: "Error deleting session." }); - }, [setErrorData]); + setErrorData({ title: t("errors.deleteSession") }); + }, [setErrorData, t]); // Initialize store when flowId changes useEffect(() => { @@ -130,7 +132,7 @@ export function useSessionManager({ flowId }: UseSessionManagerProps) { }); renameSessionInStore(oldId, newId); } catch { - setErrorData({ title: "Error renaming session." }); + setErrorData({ title: t("errors.renamingSession") }); } }, [updateSessionName, renameSessionInStore, setErrorData], diff --git a/src/frontend/src/components/ui/sidebar.tsx b/src/frontend/src/components/ui/sidebar.tsx index 3e3cb0580f..9ca1660b9c 100644 --- a/src/frontend/src/components/ui/sidebar.tsx +++ b/src/frontend/src/components/ui/sidebar.tsx @@ -5,6 +5,7 @@ import { cva, type VariantProps } from "class-variance-authority"; import { PanelLeft } from "lucide-react"; import * as React from "react"; import { useHotkeys } from "react-hotkeys-hook"; +import { useTranslation } from "react-i18next"; import { useIsMobile } from "@/hooks/use-mobile"; import isWrappedWithClass from "../../pages/FlowPage/components/PageComponent/utils/is-wrapped-with-class"; import { useShortcutsStore } from "../../stores/shortcuts"; @@ -405,16 +406,17 @@ const SidebarRail = React.forwardRef< HTMLButtonElement, React.ComponentProps<"button"> >(({ className, ...props }, ref) => { + const { t } = useTranslation(); const { toggleSidebar } = useSidebar(); return (