diff --git a/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Confluence/index.jsx b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Confluence/index.jsx index 5f63812c869..2a9084fda97 100644 --- a/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Confluence/index.jsx +++ b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Confluence/index.jsx @@ -1,10 +1,12 @@ import { useState } from "react"; +import { useTranslation } from "react-i18next"; import System from "@/models/system"; import showToast from "@/utils/toast"; import { Warning } from "@phosphor-icons/react"; import { Tooltip } from "react-tooltip"; export default function ConfluenceOptions() { + const { t } = useTranslation(); const [loading, setLoading] = useState(false); const [accessType, setAccessType] = useState("username"); @@ -61,12 +63,11 @@ export default function ConfluenceOptions() {

- Determine if your Confluence instance is hosted on Atlassian - cloud or self-hosted. + {t("connectors.confluence.deployment_type_explained")}

- This is the spaces key of your confluence instance that will - be used. Usually begins with ~ + {t("connectors.confluence.space_key_explained")}

- Your Confluence authentication type, eg: username and access - token / personal access token. + {t("connectors.confluence.auth_type_explained")}

- Access token for authentication. + {t("connectors.confluence.token_desc")}

- Your Confluence personal access token. + {t("connectors.confluence.pat_token_explained")}

{loading && (

- Once complete, all pages will be available for embedding into - workspaces. + {t("connectors.confluence.task_explained")}

)} diff --git a/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Github/index.jsx b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Github/index.jsx index 8bcc00b9ded..e773d165873 100644 --- a/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Github/index.jsx +++ b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Github/index.jsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from "react"; import System from "@/models/system"; +import { useTranslation } from "react-i18next"; import showToast from "@/utils/toast"; import pluralize from "pluralize"; import { TagsInput } from "react-tag-input-component"; @@ -8,6 +9,7 @@ import { Tooltip } from "react-tooltip"; const DEFAULT_BRANCHES = ["main", "master"]; export default function GithubOptions() { + const { t } = useTranslation(); const [loading, setLoading] = useState(false); const [repo, setRepo] = useState(null); const [accessToken, setAccessToken] = useState(null); @@ -68,10 +70,10 @@ export default function GithubOptions() {

- Url of the GitHub repo you wish to collect. + {t("connectors.github.URL_explained")}

- Access Token to prevent rate limiting. + {t("connectors.github.token_explained")}

- List in .gitignore format to ignore specific files during - collection. Press enter after each entry you want to save. + {t("connectors.github.git_ignore")}

{loading && (

- Once complete, all files will be available for embedding into - workspaces in the document picker. + {t("connectors.github.task_explained")}

)}
@@ -166,6 +170,7 @@ export default function GithubOptions() { } function GitHubBranchSelection({ repo, accessToken }) { + const { t } = useTranslation(); const [allBranches, setAllBranches] = useState(DEFAULT_BRANCHES); const [loading, setLoading] = useState(true); @@ -194,7 +199,7 @@ function GitHubBranchSelection({ repo, accessToken }) {

- Branch you wish to collect files from. + {t("connectors.github.branch")}

@@ -215,7 +220,7 @@ function GitHubBranchSelection({ repo, accessToken }) {

- Branch you wish to collect files from. + {t("connectors.github.branch_explained")}

- Access Token to prevent rate limiting. + {t("connectors.gitlab.token_description")}

Settings

{" "}

- Select additional entities to fetch from the GitLab API. + {t("connectors.gitlab.token_description")}

@@ -132,7 +136,7 @@ export default function GitlabOptions() { />
- Fetch Issues as Documents + {t("connectors.gitlab.fetch_issues")}
@@ -146,11 +150,12 @@ export default function GitlabOptions() {

- List in .gitignore format to ignore specific files during - collection. Press enter after each entry you want to save. + {t("connectors.gitlab.git_ignore")}

{loading && (

- Once complete, all files will be available for embedding into - workspaces in the document picker. + {t("connectors.gitlab.task_explained")}

)}
@@ -190,6 +194,7 @@ export default function GitlabOptions() { } function GitLabBranchSelection({ repo, accessToken }) { + const { t } = useTranslation(); const [allBranches, setAllBranches] = useState(DEFAULT_BRANCHES); const [loading, setLoading] = useState(true); @@ -216,9 +221,11 @@ function GitLabBranchSelection({ repo, accessToken }) { return (
- +

- Branch you wish to collect files from. + {t("connectors.gitlab.branch_explained")}

@@ -239,7 +246,7 @@ function GitLabBranchSelection({ repo, accessToken }) {

- Branch you wish to collect files from. + {t("connectors.gitlab.branch_explained")}

- +

- This is the number of child-links that the worker should - follow from the origin URL. + {t("connectors.website-depth.depth_explained")}

- Maximum number of links to scrape. + {t("connectors.website-depth.max_pages_explained")}

{loading && (

- Once complete, all scraped pages will be available for embedding - into workspaces in the document picker. + {t("connectors.website-depth.task_explained")}

)}
diff --git a/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Youtube/index.jsx b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Youtube/index.jsx index 3f162ac131c..5226e58984e 100644 --- a/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Youtube/index.jsx +++ b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/Connectors/Youtube/index.jsx @@ -1,8 +1,10 @@ import React, { useState } from "react"; import System from "@/models/system"; import showToast from "@/utils/toast"; +import { useTranslation } from "react-i18next"; export default function YoutubeOptions() { + const { t } = useTranslation(); const [loading, setLoading] = useState(false); const handleSubmit = async (e) => { @@ -50,10 +52,20 @@ export default function YoutubeOptions() {

- URL of the YouTube video you wish to transcribe. + {t("connectors.youtube.URL_explained_start")} + e.stopPropagation()} + > + {t("connectors.youtube.URL_explained_link")} + + {t("connectors.youtube.URL_explained_end")}

{loading && (

- Once complete, the transcription will be available for embedding - into workspaces in the document picker. + {t("connectors.youtube.task_explained")}

)}
diff --git a/frontend/src/components/Modals/ManageWorkspace/DataConnectors/index.jsx b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/index.jsx index fdbef0751f4..82560b433d2 100644 --- a/frontend/src/components/Modals/ManageWorkspace/DataConnectors/index.jsx +++ b/frontend/src/components/Modals/ManageWorkspace/DataConnectors/index.jsx @@ -1,5 +1,6 @@ import ConnectorImages from "@/components/DataConnectorOption/media"; import { MagnifyingGlass } from "@phosphor-icons/react"; +import { useTranslation } from "react-i18next"; import GithubOptions from "./Connectors/Github"; import GitlabOptions from "./Connectors/Gitlab"; import YoutubeOptions from "./Connectors/Youtube"; @@ -8,45 +9,44 @@ import { useState } from "react"; import ConnectorOption from "./ConnectorOption"; import WebsiteDepthOptions from "./Connectors/WebsiteDepth"; -export const DATA_CONNECTORS = { +export const getDataConnectors = (t) => ({ github: { - name: "GitHub Repo", + name: t("connectors.github.name"), image: ConnectorImages.github, - description: - "Import an entire public or private GitHub repository in a single click.", + description: t("connectors.github.description"), options: , }, gitlab: { - name: "GitLab Repo", + name: t("connectors.gitlab.name"), image: ConnectorImages.gitlab, - description: - "Import an entire public or private GitLab repository in a single click.", + description: t("connectors.gitlab.description"), options: , }, "youtube-transcript": { - name: "YouTube Transcript", + name: t("connectors.youtube.name"), image: ConnectorImages.youtube, - description: - "Import the transcription of an entire YouTube video from a link.", + description: t("connectors.youtube.description"), options: , }, "website-depth": { - name: "Bulk Link Scraper", + name: t("connectors.website-depth.name"), image: ConnectorImages.websiteDepth, - description: "Scrape a website and its sub-links up to a certain depth.", + description: t("connectors.website-depth.description"), options: , }, confluence: { - name: "Confluence", + name: t("connectors.confluence.name"), image: ConnectorImages.confluence, - description: "Import an entire Confluence page in a single click.", + description: t("connectors.confluence.description"), options: , }, -}; +}); export default function DataConnectors() { + const { t } = useTranslation(); const [selectedConnector, setSelectedConnector] = useState("github"); const [searchQuery, setSearchQuery] = useState(""); + const DATA_CONNECTORS = getDataConnectors(t); const filteredConnectors = Object.keys(DATA_CONNECTORS).filter((slug) => DATA_CONNECTORS[slug].name.toLowerCase().includes(searchQuery.toLowerCase()) @@ -63,7 +63,7 @@ export default function DataConnectors() { /> - No data connectors found. + {t("connectors.no-connectors")} )} diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/index.jsx b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/index.jsx index d29e893578b..2115fb20c2b 100644 --- a/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/index.jsx +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/Directory/index.jsx @@ -1,6 +1,7 @@ import UploadFile from "../UploadFile"; import PreLoader from "@/components/Preloader"; import { memo, useEffect, useState } from "react"; +import { useTranslation } from "react-i18next"; import FolderRow from "./FolderRow"; import System from "@/models/system"; import { MagnifyingGlass, Plus, Trash } from "@phosphor-icons/react"; @@ -30,6 +31,7 @@ function Directory({ setLoadingMessage, loadingMessage, }) { + const { t } = useTranslation(); const [amountSelected, setAmountSelected] = useState(0); const [showFolderSelection, setShowFolderSelection] = useState(false); const [searchTerm, setSearchTerm] = useState(""); @@ -50,11 +52,7 @@ function Directory({ const deleteFiles = async (event) => { event.stopPropagation(); - if ( - !window.confirm( - "Are you sure you want to delete these files and folders?\nThis will remove the files from the system and remove them from any existing workspaces automatically.\nThis action is not reversible." - ) - ) { + if (!window.confirm(t("connectors.directory.delete-confirmation"))) { return false; } @@ -83,7 +81,10 @@ function Directory({ setLoading(true); setLoadingMessage( - `Removing ${toRemove.length} documents and ${foldersToRemove.length} folders. Please wait.` + t("connectors.directory.removing-message", { + count: toRemove.length, + folderCount: foldersToRemove.length, + }) ); await System.deleteDocuments(toRemove); for (const folderName of foldersToRemove) { @@ -166,7 +167,10 @@ function Directory({ // show info if some files were not moved due to being embedded showToast(message, "info"); } else { - showToast(`Successfully moved ${toMove.length} documents.`, "success"); + showToast( + t("connectors.directory.move-success", { count: toMove.length }), + "success" + ); } await fetchKeys(true); setSelectedItems({}); @@ -194,11 +198,13 @@ function Directory({
-

My Documents

+

+ {t("connectors.directory.my-documents")} +

@@ -218,7 +224,7 @@ function Directory({ className="text-theme-text-primary light:text-[#0ba5ec]" />
- New Folder + {t("connectors.directory.new-folder")}
@@ -257,7 +263,7 @@ function Directory({ ) : (

- No Documents + {t("connectors.directory.no-documents")}

)} @@ -272,7 +278,7 @@ function Directory({ onMouseLeave={() => setHighlightWorkspace(false)} className="border-none text-sm font-semibold bg-white light:bg-[#E0F2FE] h-[30px] px-2.5 rounded-lg hover:bg-neutral-800/80 hover:text-white light:text-[#026AA2] light:hover:bg-[#026AA2] light:hover:text-white" > - Move to Workspace + {t("connectors.directory.move-workspace")}
) : files.length === 0 ? (
- Click to upload or drag and drop + {t("connectors.upload.click-upload")}
- supports text files, csv's, spreadsheets, audio files, and more! + {t("connectors.upload.file-types")}
) : ( @@ -130,7 +131,7 @@ export default function UploadFile({ )}
- or submit a link + {t("connectors.upload.or-submit-link")}
- These files will be uploaded to the document processor running on this - AnythingLLM instance. These files are not sent or shared with a third - party. + {t("connectors.upload.privacy-notice")}
); diff --git a/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/index.jsx b/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/index.jsx index 18a73de8ff9..4a30b86b078 100644 --- a/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/index.jsx +++ b/frontend/src/components/Modals/ManageWorkspace/Documents/WorkspaceDirectory/index.jsx @@ -10,6 +10,7 @@ import { Link } from "react-router-dom"; import Workspace from "@/models/workspace"; import { Tooltip } from "react-tooltip"; import { safeJsonParse } from "@/utils/request"; +import { useTranslation } from "react-i18next"; function WorkspaceDirectory({ workspace, @@ -25,6 +26,7 @@ function WorkspaceDirectory({ embeddingCosts, movedItems, }) { + const { t } = useTranslation(); const [selectedItems, setSelectedItems] = useState({}); const toggleSelection = (item) => { @@ -182,7 +184,7 @@ function WorkspaceDirectory({ ) : (

- No Documents + {t("connectors.directory.no_docs")}

)} @@ -201,14 +203,14 @@ function WorkspaceDirectory({ (sum, folder) => sum + folder.items.length, 0 ) - ? "Deselect All" - : "Select All"} + ? t("connectors.directory.deselect_all") + : t("connectors.directory.select_all")}
@@ -229,7 +231,7 @@ function WorkspaceDirectory({ }`}

@@ -237,7 +239,7 @@ function WorkspaceDirectory({ onClick={(e) => handleSaveChanges(e)} className="border border-slate-200 px-5 py-2.5 rounded-lg text-white text-sm items-center flex gap-x-2 hover:bg-slate-200 hover:text-slate-800 focus:ring-gray-800" > - Save and Embed + {t("connectors.directory.save_embed")} )} @@ -250,6 +252,7 @@ function WorkspaceDirectory({ } const PinAlert = memo(() => { + const { t } = useTranslation(); const [showAlert, setShowAlert] = useState(false); function dismissAlert() { setShowAlert(false); @@ -277,26 +280,27 @@ const PinAlert = memo(() => { weight="regular" />

- What is document pinning? + {t("connectors.pinning.what_pinning")}

- When you pin a document in AnythingLLM we will inject the - entire content of the document into your prompt window for your - LLM to fully comprehend. +

- This works best with large-context models or small files - that are critical to its knowledge-base. -

-

- If you are not getting the answers you desire from AnythingLLM by - default then pinning is a great way to get higher quality answers - in a click. +

+

{t("connectors.pinning.pin_explained_block3")}

@@ -304,7 +308,7 @@ const PinAlert = memo(() => { onClick={dismissAlert} className="transition-all duration-300 bg-white text-black hover:opacity-60 px-4 py-2 rounded-lg text-sm" > - Okay, got it + {t("connectors.pinning.accept")}
@@ -313,6 +317,7 @@ const PinAlert = memo(() => { }); const DocumentWatchAlert = memo(() => { + const { t } = useTranslation(); const [showAlert, setShowAlert] = useState(false); function dismissAlert() { setShowAlert(false); @@ -340,31 +345,29 @@ const DocumentWatchAlert = memo(() => { weight="regular" />

- What does watching a document do? + {t("connectors.pinning.what_watching")}

- When you watch a document in AnythingLLM we will{" "} - automatically sync your document content from it's original - source on regular intervals. This will automatically update the - content in every workspace where this file is managed. -

-

- This feature currently supports online-based content and will not - be available for manually uploaded documents. +

+

{t("connectors.watching.watch_explained_block2")}

- You can manage what documents are watched from the{" "} + {t("connectors.watching.watch_explained_block3_start")} - File manager - {" "} - admin view. + {t("connectors.watching.watch_explained_block3_link")} + + {t("connectors.watching.watch_explained_block3_end")}

@@ -373,7 +376,7 @@ const DocumentWatchAlert = memo(() => { onClick={dismissAlert} className="transition-all duration-300 bg-white text-black hover:opacity-60 px-4 py-2 rounded-lg text-sm" > - Okay, got it + {t("connectors.watching.accept")} diff --git a/frontend/src/components/Modals/ManageWorkspace/index.jsx b/frontend/src/components/Modals/ManageWorkspace/index.jsx index 33c9fa8cbf2..cf6186bb4c3 100644 --- a/frontend/src/components/Modals/ManageWorkspace/index.jsx +++ b/frontend/src/components/Modals/ManageWorkspace/index.jsx @@ -1,5 +1,6 @@ import React, { useState, useEffect, memo } from "react"; import { X } from "@phosphor-icons/react"; +import { useTranslation } from "react-i18next"; import { useParams } from "react-router-dom"; import Workspace from "../../../models/workspace"; import System from "../../../models/system"; @@ -11,6 +12,7 @@ import ModalWrapper from "@/components/ModalWrapper"; const noop = () => {}; const ManageWorkspace = ({ hideModal = noop, providedSlug = null }) => { + const { t } = useTranslation(); const { slug } = useParams(); const { user } = useUser(); const [workspace, setWorkspace] = useState(null); @@ -42,7 +44,7 @@ const ManageWorkspace = ({ hideModal = noop, providedSlug = null }) => {

- Editing "{workspace.name}" + {t("connectors.manage.editing")} "{workspace.name}"

@@ -70,7 +71,7 @@ const ManageWorkspace = ({ hideModal = noop, providedSlug = null }) => { type="button" className="transition-all duration-300 bg-white text-black hover:opacity-60 px-4 py-2 rounded-lg text-sm" > - Dismiss + {t("connectors.manage.dismiss")} @@ -114,6 +115,7 @@ const ManageWorkspace = ({ hideModal = noop, providedSlug = null }) => { export default memo(ManageWorkspace); const ModalTabSwitcher = ({ selectedTab, setSelectedTab }) => { + const { t } = useTranslation(); return (
@@ -125,7 +127,7 @@ const ModalTabSwitcher = ({ selectedTab, setSelectedTab }) => { : "text-white/20 font-medium hover:text-white light:bg-white light:text-[#535862] light:hover:bg-[#E0F2FE]" }`} > - Documents + {t("connectors.manage.documents")}
diff --git a/frontend/src/components/UserMenu/AccountModal/index.jsx b/frontend/src/components/UserMenu/AccountModal/index.jsx index a77c5145756..583da1f90d8 100644 --- a/frontend/src/components/UserMenu/AccountModal/index.jsx +++ b/frontend/src/components/UserMenu/AccountModal/index.jsx @@ -6,6 +6,7 @@ import showToast from "@/utils/toast"; import { Plus, X } from "@phosphor-icons/react"; import ModalWrapper from "@/components/ModalWrapper"; import { useTheme } from "@/hooks/useTheme"; +import { useTranslation } from "react-i18next"; export default function AccountModal({ user, hideModal }) { const { pfp, setPfp } = usePfp(); @@ -61,14 +62,14 @@ export default function AccountModal({ user, hideModal }) { showToast(`Failed to update user: ${error}`, "error"); } }; - + const { t } = useTranslation(); return (

- Edit Account + {t("profile_settings.edit_account")}

)}
@@ -129,7 +130,7 @@ export default function AccountModal({ user, hideModal }) { htmlFor="username" className="block mb-2 text-sm font-medium text-theme-text-primary" > - Username + {t("profile_settings.username")}

- Username must be only contain lowercase letters, numbers, - underscores, and hyphens with no spaces + {t("profile_settings.username_description")}

@@ -151,7 +151,7 @@ export default function AccountModal({ user, hideModal }) { htmlFor="password" className="block mb-2 text-sm font-medium text-white" > - New Password + {t("profile_settings.new_password")}

- Password must be at least 8 characters long + {t("profile_settings.passwort_description")}

@@ -189,13 +189,13 @@ export default function AccountModal({ user, hideModal }) { type="button" className="transition-all duration-300 text-white hover:bg-zinc-700 px-4 py-2 rounded-lg text-sm" > - Cancel + {t("profile_settings.cancel")}
@@ -212,14 +212,14 @@ function LanguagePreference() { getLanguageName, changeLanguage, } = useLanguageOptions(); - + const { t } = useTranslation(); return (

- Welcome to your new workspace. + {t("chat_window.welcome")}

{!user || user.role !== "default" ? (

- To get started either{" "} + {t("chat_window.get_started")} - upload a document + {t("chat_window.upload")} - or send a chat. + {t("chat_window.or")}{" "} + {t("chat_window.send_chat")}

) : (

- To get started send a chat. + {t("chat_window.get_started_default")}{" "} + {t("chat_window.send_chat")}

)} setShowAgents(!showing)} className={`flex justify-center items-center cursor-pointer ${ showing ? "!opacity-100" : "" diff --git a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/AttachItem/index.jsx b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/AttachItem/index.jsx index fcdee57f9a5..81a402d2c82 100644 --- a/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/AttachItem/index.jsx +++ b/frontend/src/components/WorkspaceChat/ChatContainer/PromptInput/AttachItem/index.jsx @@ -1,12 +1,14 @@ import useUser from "@/hooks/useUser"; import { PaperclipHorizontal } from "@phosphor-icons/react"; import { Tooltip } from "react-tooltip"; +import { useTranslation } from "react-i18next"; /** * This is a simple proxy component that clicks on the DnD file uploader for the user. * @returns */ export default function AttachItem() { + const { t } = useTranslation(); const { user } = useUser(); if (!!user && user.role === "default") return null; @@ -15,8 +17,8 @@ export default function AttachItem() {