diff --git a/application/api/user/attachments/routes.py b/application/api/user/attachments/routes.py
index 87af6a7e..cd67f1ba 100644
--- a/application/api/user/attachments/routes.py
+++ b/application/api/user/attachments/routes.py
@@ -130,11 +130,15 @@ class TextToSpeech(Resource):
@api.expect(tts_model)
@api.doc(description="Synthesize audio speech from text")
def post(self):
+ from application.utils import clean_text_for_tts
+
data = request.get_json()
text = data["text"]
+ cleaned_text = clean_text_for_tts(text)
+
try:
tts_instance = TTSCreator.create_tts(settings.TTS_PROVIDER)
- audio_base64, detected_language = tts_instance.text_to_speech(text)
+ audio_base64, detected_language = tts_instance.text_to_speech(cleaned_text)
return make_response(
jsonify(
{
diff --git a/application/utils.py b/application/utils.py
index 528cbac5..432fca5f 100644
--- a/application/utils.py
+++ b/application/utils.py
@@ -187,3 +187,44 @@ def generate_image_url(image_path):
else:
base_url = getattr(settings, "API_URL", "http://localhost:7091")
return f"{base_url}/api/images/{image_path}"
+
+
+def clean_text_for_tts(text: str) -> str:
+ """
+ clean text for Text-to-Speech processing.
+ """
+ # Handle code blocks and links
+ text = re.sub(r'```mermaid[\s\S]*?```', ' flowchart, ', text) ## ```mermaid...```
+ text = re.sub(r'```[\s\S]*?```', ' code block, ', text) ## ```code```
+ text = re.sub(r'\[([^\]]+)\]\([^\)]+\)', r'\1', text) ## [text](url)
+ text = re.sub(r'!\[([^\]]*)\]\([^\)]+\)', '', text) ## 
+
+ # Remove markdown formatting
+ text = re.sub(r'`([^`]+)`', r'\1', text) ## `code`
+ text = re.sub(r'\{([^}]*)\}', r' \1 ', text) ## {text}
+ text = re.sub(r'[{}]', ' ', text) ## unmatched {}
+ text = re.sub(r'\[([^\]]+)\]', r' \1 ', text) ## [text]
+ text = re.sub(r'[\[\]]', ' ', text) ## unmatched []
+ text = re.sub(r'(\*\*|__)(.*?)\1', r'\2', text) ## **bold** __bold__
+ text = re.sub(r'(\*|_)(.*?)\1', r'\2', text) ## *italic* _italic_
+ text = re.sub(r'^#{1,6}\s+', '', text, flags=re.MULTILINE) ## # headers
+ text = re.sub(r'^>\s+', '', text, flags=re.MULTILINE) ## > blockquotes
+ text = re.sub(r'^[\s]*[-\*\+]\s+', '', text, flags=re.MULTILINE) ## - * + lists
+ text = re.sub(r'^[\s]*\d+\.\s+', '', text, flags=re.MULTILINE) ## 1. numbered lists
+ text = re.sub(r'^[\*\-_]{3,}\s*$', '', text, flags=re.MULTILINE) ## --- *** ___ rules
+ text = re.sub(r'<[^>]*>', '', text) ## tags
+
+ #Remove non-ASCII (emojis, special Unicode)
+ text = re.sub(r'[^\x20-\x7E\n\r\t]', '', text)
+
+ #Replace special sequences
+ text = re.sub(r'-->', ', ', text) ## -->
+ text = re.sub(r'<--', ', ', text) ## <--
+ text = re.sub(r'=>', ', ', text) ## =>
+ text = re.sub(r'::', ' ', text) ## ::
+
+ #Normalize whitespace
+ text = re.sub(r'\s+', ' ', text)
+ text = text.strip()
+
+ return text
diff --git a/frontend/src/Navigation.tsx b/frontend/src/Navigation.tsx
index b8c714bc..439267c3 100644
--- a/frontend/src/Navigation.tsx
+++ b/frontend/src/Navigation.tsx
@@ -411,7 +411,9 @@ export default function Navigation({ navOpen, setNavOpen }: NavigationProps) {
{recentAgents?.length > 0 ? (
-
Agents
+
+ {t('navigation.agents')}
+
diff --git a/frontend/src/PageNotFound.tsx b/frontend/src/PageNotFound.tsx
index 2129d0ee..36c106a3 100644
--- a/frontend/src/PageNotFound.tsx
+++ b/frontend/src/PageNotFound.tsx
@@ -1,13 +1,16 @@
import { Link } from 'react-router-dom';
+import { useTranslation } from 'react-i18next';
export default function PageNotFound() {
+ const { t } = useTranslation();
+
return (
-
404
-
The page you are looking for does not exist.
+
{t('pageNotFound.title')}
+
{t('pageNotFound.message')}
diff --git a/frontend/src/agents/AgentLogs.tsx b/frontend/src/agents/AgentLogs.tsx
index 979a701e..62478329 100644
--- a/frontend/src/agents/AgentLogs.tsx
+++ b/frontend/src/agents/AgentLogs.tsx
@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
import { useSelector } from 'react-redux';
import { useNavigate, useParams } from 'react-router-dom';
@@ -11,6 +12,7 @@ import Logs from '../settings/Logs';
import { Agent } from './types';
export default function AgentLogs() {
+ const { t } = useTranslation();
const navigate = useNavigate();
const { agentId } = useParams();
const token = useSelector(selectToken);
@@ -45,12 +47,12 @@ export default function AgentLogs() {
- Back to all agents
+ {t('agents.backToAll')}
- Agent Logs
+ {t('agents.logs.title')}
@@ -59,9 +61,10 @@ export default function AgentLogs() {
{agent.name}
{agent.last_used_at
- ? 'Last used at ' +
+ ? t('agents.logs.lastUsedAt') +
+ ' ' +
new Date(agent.last_used_at).toLocaleString()
- : 'No usage history'}
+ : t('agents.logs.noUsageHistory')}
)}
@@ -79,7 +82,9 @@ export default function AgentLogs() {
) : (
- agent &&
+ agent && (
+
+ )
)}
);
diff --git a/frontend/src/agents/AgentPreview.tsx b/frontend/src/agents/AgentPreview.tsx
index a242e52a..cc8f04dc 100644
--- a/frontend/src/agents/AgentPreview.tsx
+++ b/frontend/src/agents/AgentPreview.tsx
@@ -1,4 +1,5 @@
import { useCallback, useEffect, useRef, useState } from 'react';
+import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import MessageInput from '../components/MessageInput';
@@ -17,6 +18,7 @@ import { selectSelectedAgent } from '../preferences/preferenceSlice';
import { AppDispatch } from '../store';
export default function AgentPreview() {
+ const { t } = useTranslation();
const dispatch = useDispatch();
const queries = useSelector(selectPreviewQueries);
@@ -130,8 +132,7 @@ export default function AgentPreview() {
/>
- This is a preview of the agent. You can publish it to start using it
- in conversations.
+ {t('agents.preview.testMessage')}
diff --git a/frontend/src/agents/AgentsList.tsx b/frontend/src/agents/AgentsList.tsx
index 04957443..bdccb0f1 100644
--- a/frontend/src/agents/AgentsList.tsx
+++ b/frontend/src/agents/AgentsList.tsx
@@ -1,4 +1,5 @@
import { useEffect, useState } from 'react';
+import { useTranslation } from 'react-i18next';
import { useDispatch, useSelector } from 'react-redux';
import { useNavigate } from 'react-router-dom';
@@ -17,6 +18,7 @@ import { agentSectionsConfig } from './agents.config';
import { Agent } from './types';
export default function AgentsList() {
+ const { t } = useTranslation();
const dispatch = useDispatch();
const token = useSelector(selectToken);
const selectedAgent = useSelector(selectSelectedAgent);
@@ -33,11 +35,10 @@ export default function AgentsList() {
return (
- Agents
+ {t('agents.title')}
- Discover and create custom versions of DocsGPT that combine
- instructions, extra knowledge, and any combination of skills
+ {t('agents.description')}