From 99952a393ff332cff01ba4e8f073d6986d530d2e Mon Sep 17 00:00:00 2001 From: ManishMadan2882 Date: Tue, 28 May 2024 20:50:07 +0530 Subject: [PATCH] feat(i18n): modals, Hero, Nav --- frontend/src/Hero.tsx | 55 ++++------ frontend/src/Navigation.tsx | 13 ++- frontend/src/components/SourceDropdown.tsx | 6 +- frontend/src/conversation/Conversation.tsx | 6 +- frontend/src/locale/en.json | 122 +++++++++++++++------ frontend/src/locale/es.json | 66 ++++++++++- frontend/src/modals/DeleteConvModal.tsx | 8 +- frontend/src/settings/APIKeys.tsx | 26 +++-- frontend/src/settings/General.tsx | 11 +- frontend/src/upload/Upload.tsx | 45 ++++---- 10 files changed, 237 insertions(+), 121 deletions(-) diff --git a/frontend/src/Hero.tsx b/frontend/src/Hero.tsx index cd6d4397..a58327e5 100644 --- a/frontend/src/Hero.tsx +++ b/frontend/src/Hero.tsx @@ -1,28 +1,15 @@ import DocsGPT3 from './assets/cute_docsgpt3.svg'; -const demos: { header: string; query: string }[] = [ - { - header: 'Learn about DocsGPT', - query: 'What is DocsGPT?', - }, - { - header: 'Summarise documentation', - query: 'Summarise current context', - }, - { - header: 'Write Code', - query: 'Write code for api request to /api/answer', - }, - { - header: 'Learning Assistance', - query: 'Write potential questions for context', - }, -]; - +import { useTranslation } from 'react-i18next'; export default function Hero({ handleQuestion, }: { handleQuestion: (question: string) => void; }) { + const { t } = useTranslation(); + const demos = t('demo', { returnObjects: true }) as Array<{ + header: string; + query: string; + }>; return (
- {demos.map((demo) => ( - <> - - - ))} + {demos?.map( + (demo: { header: string; query: string }) => + demo.header && + demo.query && ( + <> + + + ), + )}
); diff --git a/frontend/src/Navigation.tsx b/frontend/src/Navigation.tsx index 9c5df178..76e09608 100644 --- a/frontend/src/Navigation.tsx +++ b/frontend/src/Navigation.tsx @@ -39,7 +39,7 @@ import SelectDocsModal from './preferences/SelectDocsModal'; import ConversationTile from './conversation/ConversationTile'; import { useDarkTheme } from './hooks'; import SourceDropdown from './components/SourceDropdown'; - +import { useTranslation } from 'react-i18next'; interface NavigationProps { navOpen: boolean; setNavOpen: React.Dispatch>; @@ -70,6 +70,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) { const { isMobile } = useMediaQuery(); const [isDarkTheme] = useDarkTheme(); const [isDocsListOpen, setIsDocsListOpen] = useState(false); + const { t } = useTranslation(); const isApiKeySet = useSelector(selectApiKeyStatus); const [apiKeyModalState, setApiKeyModalState] = @@ -265,14 +266,14 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) { className="opacity-80 group-hover:opacity-100" />

- New Chat + {t('newChat')}

{conversations && conversations.length > 0 ? (
-

Chats

+

{t('chats')}

{conversations?.map((conversation) => ( @@ -310,7 +311,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) { onClick={() => setUploadModalState('ACTIVE')} >
-

Source Docs

+

{t('sourceDocs')}

- Settings + {t('settings.label')}

@@ -345,7 +346,7 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) { alt="icon" className="ml-2 w-5 filter dark:invert" /> -

About

+

{t('about')}

diff --git a/frontend/src/conversation/Conversation.tsx b/frontend/src/conversation/Conversation.tsx index 218e8532..cf4949e9 100644 --- a/frontend/src/conversation/Conversation.tsx +++ b/frontend/src/conversation/Conversation.tsx @@ -17,6 +17,7 @@ import Spinner from './../assets/spinner.svg'; import SpinnerDark from './../assets/spinner-dark.svg'; import { FEEDBACK, Query } from './conversationModels'; import { sendFeedback } from './conversationApi'; +import { useTranslation } from 'react-i18next'; import ArrowDown from './../assets/arrow-down.svg'; export default function Conversation() { const queries = useSelector(selectQueries); @@ -28,6 +29,7 @@ export default function Conversation() { const [hasScrolledToLast, setHasScrolledToLast] = useState(true); const fetchStream = useRef(null); const [eventInterrupt, setEventInterrupt] = useState(false); + const { t } = useTranslation(); const handleUserInterruption = () => { if (!eventInterrupt && status === 'loading') setEventInterrupt(true); @@ -177,7 +179,7 @@ export default function Conversation() { id="inputbox" ref={inputRef} tabIndex={1} - placeholder="Type your message here..." + placeholder={t('inputPlaceholder')} contentEditable onPaste={handlePaste} className={`max-h-24 min-h-[3.8rem] w-full overflow-y-auto overflow-x-hidden whitespace-pre-wrap rounded-full bg-white py-2 pl-4 pr-9 text-base leading-10 opacity-100 focus:outline-none dark:bg-raisin-black dark:text-bright-gray`} @@ -212,7 +214,7 @@ export default function Conversation() { )}

- DocsGPT uses GenAI, please review critial information using sources. + {t('tagline')}

diff --git a/frontend/src/locale/en.json b/frontend/src/locale/en.json index 22a51a2c..6d4fe8e2 100644 --- a/frontend/src/locale/en.json +++ b/frontend/src/locale/en.json @@ -1,39 +1,99 @@ { "language": "English", - "chat":"Chat", - "newChat":"New Chat", - "myPlan":"My Plan", - "about":"About", - "inputPlaceholder":"Type your message here...", - "tagline":"DocsGPT uses GenAI, please review critial information using sources.", - "sourceDocs":"Source Docs", - "settings":{ - "label":"Settings", - "general":{ - "label":"General", - "selectTheme":"Select Theme", - "light":"Light", - "dark":"Dark", - "selectLanguage":"Select Language", - "chunks":"Chunks processed per query", - "prompt":"Active Prompt", - "deleteAllLabel":"Delete all Conversation", - "deleteAllBtn":"Delete all", - "addNew":"Add New" + "chat": "Chat", + "newChat": "New Chat", + "myPlan": "My Plan", + "about": "About", + "inputPlaceholder": "Type your message here...", + "tagline": "DocsGPT uses GenAI, please review critial information using sources.", + "sourceDocs": "Source Docs", + "none":"None", + "demo":[ + { + "header": "Learn about DocsGPT", + "query": "What is DocsGPT?" }, - "documents":{ - "label":"Documents", - "name":"Document Name", - "date":"Vector Date", - "type":"Type", - "tokenUsage":"Token Usage" + { + "header": "Summarise documentation", + "query": "Summarise current context" }, - "apiKeys":{ - "label":"API Keys", + { + "header": "Write Code", + "query": "Write code for api request to /api/answer" + }, + { + "header": "Learning Assistance", + "query": "Write potential questions for context" + } + ], + "settings": { + "label": "Settings", + "general": { + "label": "General", + "selectTheme": "Select Theme", + "light": "Light", + "dark": "Dark", + "selectLanguage": "Select Language", + "chunks": "Chunks processed per query", + "prompt": "Active Prompt", + "deleteAllLabel": "Delete all Conversation", + "deleteAllBtn": "Delete all", + "addNew": "Add New" + }, + "documents": { + "label": "Documents", + "name": "Document Name", + "date": "Vector Date", + "type": "Type", + "tokenUsage": "Token Usage" + }, + "apiKeys": { + "label": "API Keys", + "name": "Name", + "key": "API Key", + "sourceDoc": "Source Document", + "createNew": "Create New" + } + }, + "modals":{ + "uploadDoc":{ + "label":"Upload New Documentation", + "file":"From File", + "remote":"Remote", "name":"Name", - "key":"API Key", - "sourceDoc":"Source Document", - "createNew":"Create New" + "choose":"Choose Files", + "info":"Please upload .pdf, .txt, .rst, .docx, .md, .zip limited to 25mb", + "uploadedFiles":"Uploaded Files", + "cancel":"Cancel", + "train":"Train", + "link":"Link", + "urlLink":"URL Link", + "reddit":{ + "id":"Client ID", + "secret":"Client Secret", + "agent":"User agent", + "searchQueries":"Search queries", + "numberOfPosts":"Number of posts" + } + }, + "createAPIKey":{ + "label":"Create New API Key", + "apiKeyName":"API Key Name", + "chunks":"Chunks processed per query", + "prompt":"Select active prompt", + "sourceDoc":"Source document", + "create":"Create" + }, + "saveKey":{ + "note":"Please save your Key", + "disclaimer":"This is the only time your key will be shown.", + "copy":"Copy", + "copied":"Copied", + "confirm":"I saved the Key" + }, + "deleteConv":{ + "confirm":"Are you sure you want to delete all the conversations?", + "delete":"Delete" } } } diff --git a/frontend/src/locale/es.json b/frontend/src/locale/es.json index 3d03bc39..f643b1d7 100644 --- a/frontend/src/locale/es.json +++ b/frontend/src/locale/es.json @@ -7,13 +7,32 @@ "inputPlaceholder": "Escribe tu mensaje aquí...", "tagline": "DocsGPT utiliza GenAI, por favor revisa información crítica utilizando fuentes.", "sourceDocs": "Documentos Fuente", + "none": "Nada", + "demo": [ + { + "header": "Aprende sobre DocsGPT", + "query": "¿Qué es DocsGPT?" + }, + { + "header": "Resumir documentación", + "query": "Resumir contexto actual" + }, + { + "header": "Escribir Código", + "query": "Escribir código para solicitud de API a /api/answer" + }, + { + "header": "Asistencia de Aprendizaje", + "query": "Escribe posibles preguntas para el contexto" + } + ], "settings": { "label": "Configuración", "general": { - "label":"General", + "label": "General", "selectTheme": "Seleccionar Tema", - "light":"de luz", - "dark":"oscura", + "light": "de luz", + "dark": "oscura", "selectLanguage": "Seleccionar Idioma", "chunks": "Trozos procesados por consulta", "prompt": "Prompt Activo", @@ -22,18 +41,55 @@ "addNew": "Agregar Nuevo" }, "documents": { - "label":"Documentos", + "label": "Documentos", "name": "Nombre del Documento", "date": "Fecha Vector", "type": "Tipo", "tokenUsage": "Uso de Tokens" }, "apiKeys": { - "label":"Claves API", + "label": "Claves API", "name": "Nombre", "key": "Clave de API", "sourceDoc": "Documento Fuente", "createNew": "Crear Nuevo" } + }, + "modals": { + "uploadDoc": { + "label": "Subir Nueva Documentación", + "file": "Desde Archivo", + "remote": "Remota", + "name": "Nombre", + "choose": "Seleccionar Archivos", + "info": "Por favor, suba archivos .pdf, .txt, .rst, .docx, .md, .zip limitados a 25 MB", + "uploadedFiles": "Archivos Subidos", + "cancel": "Cancelar", + "train": "Entrenar", + "link": "Enlace", + "urlLink": "Enlace URL", + "reddit": { + "id": "ID de Cliente", + "secret": "Secreto de Cliente", + "agent": "Agente de Usuario", + "searchQueries": "Consultas de Búsqueda", + "numberOfPosts": "Número de publicaciones" + } + }, + "createAPIKey": { + "label": "Crear Nueva Clave de API", + "apiKeyName": "Nombre de la Clave de API", + "chunks": "Fragmentos procesados por consulta", + "prompt": "Seleccione el prompt activo", + "sourceDoc": "Documento Fuente", + "create": "Crear" + }, + "saveKey": { + "note": "Por favor, guarde su Clave", + "disclaimer": "Esta es la única vez que se mostrará su clave.", + "copy": "Copiar", + "copied": "Copiado", + "confirm": "He guardado la Clave" + } } } diff --git a/frontend/src/modals/DeleteConvModal.tsx b/frontend/src/modals/DeleteConvModal.tsx index 4371fc14..c2fc5e38 100644 --- a/frontend/src/modals/DeleteConvModal.tsx +++ b/frontend/src/modals/DeleteConvModal.tsx @@ -3,7 +3,7 @@ import { useDispatch } from 'react-redux'; import { ActiveState } from '../models/misc'; import { useMediaQuery, useOutsideAlerter } from '../hooks'; import ConfirmationModal from './ConfirmationModal'; - +import { useTranslation } from 'react-i18next'; import { Action } from '@reduxjs/toolkit'; export default function DeleteConvModal({ @@ -18,7 +18,7 @@ export default function DeleteConvModal({ const modalRef = React.useRef(null); const dispatch = useDispatch(); const { isMobile } = useMediaQuery(); - + const { t } = useTranslation(); useOutsideAlerter( modalRef, () => { @@ -40,10 +40,10 @@ export default function DeleteConvModal({ return ( diff --git a/frontend/src/settings/APIKeys.tsx b/frontend/src/settings/APIKeys.tsx index 54a54dd6..8264af08 100644 --- a/frontend/src/settings/APIKeys.tsx +++ b/frontend/src/settings/APIKeys.tsx @@ -221,7 +221,7 @@ const CreateAPIKeyModal: React.FC = ({ }; }) : []; - + const { t } = useTranslation(); return (
@@ -230,12 +230,12 @@ const CreateAPIKeyModal: React.FC = ({
- Create New API Key + {t('modals.createAPIKey.label')}
- API Key Name + {t('modals.createAPIKey.apiKeyName')} = ({
setSourcePath(selection) @@ -260,7 +260,7 @@ const CreateAPIKeyModal: React.FC = ({ setPrompt(value) } @@ -269,7 +269,7 @@ const CreateAPIKeyModal: React.FC = ({

- Chunks processed per query + {t('modals.createAPIKey.chunks')}

= ({ } className="float-right mt-4 rounded-full bg-purple-30 px-5 py-2 text-sm text-white hover:bg-[#6F3FD1] disabled:opacity-50" > - Create + {t('modals.createAPIKey.create')}
@@ -301,6 +301,7 @@ const CreateAPIKeyModal: React.FC = ({ const SaveAPIKeyModal: React.FC = ({ apiKey, close }) => { const [isCopied, setIsCopied] = React.useState(false); + const { t } = useTranslation(); const handleCopyKey = () => { navigator.clipboard.writeText(apiKey); setIsCopied(true); @@ -311,9 +312,12 @@ const SaveAPIKeyModal: React.FC = ({ apiKey, close }) => { -

Please save your Key

+

+ {' '} + {t('modals.saveKey.note')} +

- This is the only time your key will be shown. + {t('modals.saveKey.disclaimer')}

@@ -324,14 +328,14 @@ const SaveAPIKeyModal: React.FC = ({ apiKey, close }) => { className="my-1 h-10 w-20 rounded-full border border-solid border-purple-30 p-2 text-sm text-purple-30 hover:bg-purple-30 hover:text-white" onClick={handleCopyKey} > - {isCopied ? 'Copied' : 'Copy'} + {isCopied ? t('modals.saveKey.copied') : t('modals.saveKey.copy')}
diff --git a/frontend/src/settings/General.tsx b/frontend/src/settings/General.tsx index 470b6d3a..82693aa1 100644 --- a/frontend/src/settings/General.tsx +++ b/frontend/src/settings/General.tsx @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React from 'react'; import { useSelector, useDispatch } from 'react-redux'; import Prompts from './Prompts'; import { useDarkTheme } from '../hooks'; @@ -19,7 +19,7 @@ const General: React.FC = () => { t, i18n: { changeLanguage, language }, } = useTranslation(); - const themes = [t('settings.general.light'), t('settings.general.dark')]; + const themes = ['Light', 'Dark']; const languageOptions = [ { @@ -38,7 +38,7 @@ const General: React.FC = () => { const selectedChunks = useSelector(selectChunks); const [isDarkTheme, toggleTheme] = useDarkTheme(); const [selectedTheme, setSelectedTheme] = React.useState( - isDarkTheme ? t('settings.general.dark') : t('settings.general.light'), + isDarkTheme ? 'Dark' : 'Light', ); const dispatch = useDispatch(); const locale = localStorage.getItem('docsgpt-locale'); @@ -46,10 +46,7 @@ const General: React.FC = () => { locale ? languageOptions.find((option) => option.value === locale) : 'en', ); const selectedPrompt = useSelector(selectPrompt); - useEffect(() => { - console.log(selectedLanguage); - console.log(language); - }, [selectedLanguage]); + React.useEffect(() => { const fetchPrompts = async () => { try { diff --git a/frontend/src/upload/Upload.tsx b/frontend/src/upload/Upload.tsx index 605b4fae..6437accd 100644 --- a/frontend/src/upload/Upload.tsx +++ b/frontend/src/upload/Upload.tsx @@ -6,7 +6,7 @@ import { ActiveState } from '../models/misc'; import { getDocs } from '../preferences/preferenceApi'; import { setSourceDocs } from '../preferences/preferenceSlice'; import Dropdown from '../components/Dropdown'; - +import { useTranslation } from 'react-i18next'; export default function Upload({ modalState, setModalState, @@ -24,6 +24,7 @@ export default function Upload({ search_queries: [''], number_posts: 10, }); + const { t } = useTranslation(); const urlOptions: { label: string; value: string }[] = [ { label: 'Crawler', value: 'crawler' }, // { label: 'Sitemap', value: 'sitemap' }, @@ -238,7 +239,7 @@ export default function Upload({ view = ( <>

- Upload New Documentation + {t('modals.uploadDoc.label')}

{activeTab === 'file' && ( @@ -272,21 +273,21 @@ export default function Upload({ >
- Name + {t('modals.uploadDoc.name')}
- Choose Files + {t('modals.uploadDoc.choose')}

- Please upload .pdf, .txt, .rst, .docx, .md, .zip limited to 25mb + {t('modals.uploadDoc.info')}

- Uploaded Files + {t('modals.uploadDoc.uploadedFiles')}

{files.map((file) => (

@@ -294,7 +295,9 @@ export default function Upload({

))} {files.length === 0 && ( -

None

+

+ {t('none')} +

)}
@@ -313,7 +316,7 @@ export default function Upload({ {urlType.label !== 'Reddit' ? ( <>
- Name + {t('modals.uploadDoc.name')}
- Link + {t('modals.uploadDoc.link')}
@@ -349,7 +352,7 @@ export default function Upload({ >
- Client ID + {t('modals.uploadDoc.reddit.id')}
- Client secret + {t('modals.uploadDoc.reddit.secret')}
- User agent + {t('modals.uploadDoc.reddit.agent')}
- Search queries + {t('modals.uploadDoc.reddit.searchQueries')}
- Number of posts + {t('modals.uploadDoc.reddit.numberOfPosts')}
@@ -422,14 +425,14 @@ export default function Upload({ activeTab === 'file' } > - Train + {t('modals.uploadDoc.train')} ) : ( )}