-
Notifications
You must be signed in to change notification settings - Fork 137
feat: implementation of real-time translation engine for cross-lingual debates #181
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
feat: implementation of real-time translation engine for cross-lingual debates #181
Conversation
|
Caution Review failedAn error occurred during the review process. Please try again later. 📝 WalkthroughWalkthroughAdds multilingual real-time translation: initializes Gemini, provides TranslateContent service, exposes HTTP and WebSocket translation endpoints, expands WebSocket message schema, implements a full debate WebSocket flow with media capture/transcription/translation, and updates frontend chat UI to show/toggle translated vs original text. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant WS as WebSocket Server
participant Media as Media Capture
participant Trans as Transcription API
participant Gemini as Gemini API
Client->>WS: Join room
WS->>WS: Wait for second participant
WS->>Client: DEBATE_START
loop per section/turn
WS->>Client: TURN_START
Client->>Media: Send audio binary
Media->>Media: Save media file
WS->>Trans: POST audio file for transcription
Trans->>WS: Return transcribed text
alt translation requested (sender != target)
WS->>Gemini: TranslateContent(transcribed, source, target)
Gemini->>WS: Return translated text
WS->>Client: CHAT_MESSAGE (content=translated, originalText=transcribed, isTranslated=true)
else no translation
WS->>Client: CHAT_MESSAGE (content=transcribed, isTranslated=false)
end
WS->>Client: TURN_END
end
WS->>Client: DEBATE_END
WS->>WS: Close connections & expire room
Estimated code review effort🎯 4 (Complex) | ⏱️ ~75 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 18
🤖 Fix all issues with AI agents
In @backend/cmd/server/main.go:
- Around line 195-221: Create a single shared DTO package (e.g., package dto)
that defines TranslationRequest and TranslationResponse, remove the duplicate
type declarations from both places, and update code to import dto and use
dto.TranslationRequest/dto.TranslationResponse; keep only one TranslationHandler
implementation (choose the existing websocket.TranslationHandler or
main.TranslationHandler), delete the duplicate handler, and wire your routes to
call the single exported TranslationHandler function from its package. Ensure
package imports and references to services.TranslateContent remain correct after
moving types.
- Line 31: Remove the unused variable declaration apiKey :=
os.Getenv("GEMINI_API_KEY") and ensure the code uses the existing geminiKey
variable instead (geminiKey := os.Getenv("GEMINI_API_KEY")), so the environment
value is only read once and no redundant/unused apiKey symbol remains; update
any references to use geminiKey if necessary.
In @backend/services/gemini.go:
- Line 14: The package-level variable geminiClient is unexported but needed by
backend/services/translation.go; either export it by renaming geminiClient to
GeminiInstance and update all internal references (functions like
GenerateDefaultModelText, any other usages) to use GeminiInstance, or add a
small exported accessor wrapper (e.g., GenerateContent) in gemini.go that calls
the existing GenerateDefaultModelText and returns the generated text and error
so translation.go can call that instead; choose one approach and apply it
consistently across the package.
In @backend/services/translation.go:
- Around line 27-32: The code references gemini.GeminiInstance which doesn't
exist; update the usage to the actual global variable name geminiClient (or
alternatively export the instance from gemini.go as GeminiInstance and update
InitGemini accordingly). Specifically, replace references to
gemini.GeminiInstance in TranslateText (and any other uses) with geminiClient,
or modify gemini.go to declare and initialize an exported GeminiInstance symbol
and update InitGemini to set it so the existing calls compile.
- Line 7: Replace the wrong import and variable usage: change the import to
"arguehub/services/gemini" (matching module name) and update any uses of
gemini.GeminiInstance to the actual exported symbol from the gemini package
(open backend/services/gemini/gemini.go to find the exported constructor/type
name — e.g., GeminiClient, NewGeminiClient, or similar — and use that instead of
the nonexistent GeminiInstance).
In @backend/websocket/handler.go:
- Around line 403-407: The package-level variable rooms and its mutex roomMu in
handler.go conflict with another rooms declaration in websocket.go; rename the
handler.go variables (e.g., debateRooms and debateRoomMu or handlerRooms and
handlerRoomMu) and update every reference in handler.go (including types
referencing structs.Room) accordingly, or alternatively consolidate room
management by removing one variable and using the shared manager from
websocket.go (ensure types match: structs.Room vs Room) and update all usages to
the chosen symbol to fix the redeclaration compilation error.
- Around line 368-369: The file contains a duplicate "package websocket"
declaration (second occurrence around line 369) which breaks compilation; remove
the redundant package declaration so the file only has a single "package
websocket" at the top, and ensure any code following that duplicate is not part
of a stray package statement.
- Around line 577-594: The generateTranscript function lacks error handling and
a request timeout; fix by checking the json.Marshal error for payload creation,
use an http.Client with a reasonable Timeout instead of http.Post to avoid
hanging, check resp.StatusCode and return an error for non-2xx responses before
decoding, handle and return the error from json.NewDecoder(resp.Body).Decode
into the result struct, and propagate any errors back to the caller from
generateTranscript.
- Around line 551-561: The translation block currently calls
services.TranslateContent(transcript, "en", "es") which hardcodes source/target
languages and ignores user preferences; change it to obtain the user's preferred
target language (e.g., from the connection/session user object like
user.PreferredLanguage or session.Language) and, if available, pass that as the
target lang to services.TranslateContent (and avoid assuming the source is
"en"—either detect source via existing language-detection utilities or use a
user/source profile field if present); update references around translatedText,
finalMessage and isTranslated so translation only runs when a target preference
exists and falls back to the original transcript otherwise.
In @backend/websocket/translation_handler.go:
- Around line 10-19: TranslationRequest and TranslationResponse are duplicated
in translation_handler.go and main.go; extract those two structs into a shared
package (e.g., package dto or structs) and import that package from both
websocket/translation_handler.go and backend/cmd/server/main.go so both files
reference dto.TranslationRequest and dto.TranslationResponse (preserve JSON
tags), then either remove the unused Error field from dto.TranslationResponse or
ensure all error paths populate its Error string before sending responses from
both translation handler and server code; update imports and any code that
referenced the old local types accordingly.
- Around line 21-42: There are two problems: this TranslationHandler duplicates
the one in main.go (remove the duplicate and keep a single canonical
TranslationHandler) and the source language is hardcoded to "en"; modify the
request payload struct to accept a source_lang field (e.g., SourceLang) and use
that value when calling services.TranslateContent instead of the hardcoded "en"
(fall back to a sensible default if SourceLang is empty), and ensure only one
TranslationHandler implementation remains (update any imports/route
registrations to point to the retained handler).
In @frontend/src/components/Chatbox.tsx:
- Around line 165-173: Replace the hardcoded senderLang and targetLang in
Chatbox.tsx by deriving them from the current user's settings or props rather
than literals: get the language values the same way as DebateRoom.tsx uses
user?.language (or accept a language prop on the Chatbox component), then pass
those variables into the existing onSendMessage call
(onSendMessage(inputText.trim(), inputMode, senderLang, targetLang)). Update the
Chatbox props or hook to access the user/settings context and remove the
"en"/"es" literals so languages reflect the real user settings.
In @frontend/src/Pages/DebateRoom.tsx:
- Around line 491-502: The variable context is left empty but passed into
sendDebateMessage; populate it before the call by building a meaningful context
string (e.g., prefer an explicit debateData.context or debateData.background if
present, otherwise derive it from debateData.topic, debate rules/metadata and
recent state.messages—for example concatenate the last N messages or any
system/instructions messages into a single string). Update the block around
context and the sendDebateMessage invocation so context is assigned the
constructed string (using debateData and state variables) before calling
sendDebateMessage to ensure the bot receives the intended context.
- Around line 510-519: Replace the hardcoded fetch URL with a configurable API
base (e.g., use an imported API_BASE_URL or a Vite/CRA env var like
process.env.REACT_APP_API_BASE_URL or import.meta.env.VITE_API_BASE_URL) and
build the translate endpoint from that in the code that calls fetch (the
translatedResponse fetch using botText and targetLang); also check
translatedResponse.ok before calling translatedResponse.json(), handle non-OK
responses (log error and fall back to English), and only assign
data.translatedText to botText when the response is OK and contains that field.
🧹 Nitpick comments (4)
backend/structs/websocket.go (1)
11-22:IsTranslatedwithomitemptymay cause confusion.The
IsTranslatedfield (line 20) usesomitempty, which meansfalsevalues are omitted from JSON output. The frontend may need to distinguish between "not translated" (false) and "translation status unknown" (field absent). Consider removingomitemptyfrom this boolean field:-IsTranslated bool `json:"isTranslated,omitempty"` +IsTranslated bool `json:"isTranslated"`Otherwise, the struct expansion is clean and properly supports the translation feature.
backend/cmd/server/main.go (1)
22-22: Non-idiomatic separate import statement.The
godotenvimport on line 22 is outside the main import block (lines 3-19). Consolidate it with other imports:import ( "log" "os" "strconv" "arguehub/config" // ... other imports "github.com/gin-gonic/gin" + "github.com/joho/godotenv" ) - -import "github.com/joho/godotenv"backend/websocket/websocket.go (1)
478-499: Synchronous translation may introduce latency in the WebSocket handler.The translation call blocks the message processing loop. If the Gemini API is slow or times out, this could delay message delivery. Consider moving translation to a goroutine with a timeout and fallback to original content if translation takes too long.
Additionally, the current design translates to a single
TargetLanguageand broadcasts to all recipients. In multi-user rooms (with spectators), different recipients may prefer different languages. This is acceptable for 2-person PvP debates but may need revisiting for spectator support.♻️ Consider async translation with timeout
case "message": // Start translation asynchronously go func(msg Message, cl *Client) { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() if msg.SenderLanguage != "" && msg.TargetLanguage != "" && !strings.EqualFold(msg.SenderLanguage, msg.TargetLanguage) { translated, err := services.TranslateContentWithContext(ctx, msg.Content, msg.SenderLanguage, msg.TargetLanguage) if err == nil { msg.OriginalText = msg.Content msg.Content = translated msg.IsTranslated = true } } handleChatMessage(room, conn, msg, cl, roomID) }(message, client)backend/websocket/handler.go (1)
492-501: Busy-wait polling loop could be more efficient.The loop polls every second waiting for a second participant. While functional, this is inefficient. Consider using a channel or
sync.Condto signal when the room is ready.
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (9)
backend/cmd/server/main.gobackend/services/gemini.gobackend/services/translation.gobackend/structs/websocket.gobackend/websocket/handler.gobackend/websocket/translation_handler.gobackend/websocket/websocket.gofrontend/src/Pages/DebateRoom.tsxfrontend/src/components/Chatbox.tsx
🧰 Additional context used
🧬 Code graph analysis (6)
backend/structs/websocket.go (2)
backend/websocket/websocket.go (1)
Room(31-34)backend/routes/rooms.go (1)
Room(21-26)
frontend/src/Pages/DebateRoom.tsx (3)
frontend/src/services/vsbot.ts (1)
sendDebateMessage(83-101)backend/websocket/websocket.go (1)
Message(70-98)backend/models/debatevsbot.go (1)
Message(6-10)
backend/websocket/translation_handler.go (2)
backend/cmd/server/main.go (3)
TranslationHandler(204-221)TranslationRequest(195-198)TranslationResponse(200-202)backend/services/translation.go (1)
TranslateContent(10-45)
frontend/src/components/Chatbox.tsx (1)
backend/structs/websocket.go (1)
ChatMessage(48-51)
backend/websocket/websocket.go (1)
backend/services/translation.go (1)
TranslateContent(10-45)
backend/cmd/server/main.go (5)
backend/config/config.go (1)
LoadConfig(60-73)backend/services/gemini.go (1)
InitGemini(17-30)backend/services/debatevsbot.go (1)
InitDebateVsBotService(22-39)backend/websocket/translation_handler.go (3)
TranslationHandler(21-42)TranslationRequest(10-13)TranslationResponse(16-19)backend/services/translation.go (1)
TranslateContent(10-45)
🔇 Additional comments (5)
backend/services/gemini.go (2)
17-30: LGTM with minor observation.The initialization logic is sound. The empty API key case is handled gracefully—if the key is empty, the client creation will likely fail and return an error, which is propagated correctly. The calling code in
main.goalready logs a warning on failure.
37-44: Safety settings disable all content filtering.All harm categories are set to
HarmBlockThresholdBlockNone. While this may be intentional to allow debate content, it also means potentially harmful content won't be filtered. Verify this aligns with your content policy.frontend/src/components/Chatbox.tsx (1)
48-64: LGTM: Clean translation toggle component.The
MessageContentcomponent elegantly handles the translated/original text toggle. The early return for non-translated messages (line 51) is efficient, and the toggle button provides good UX.backend/websocket/websocket.go (2)
77-81: LGTM - Translation fields properly defined.The new translation-related fields are well-structured with appropriate JSON tags and
omitemptyto avoid sending empty values.
530-558: LGTM - Clean broadcast logic with translation fields.The function properly resets client state, sets a default timestamp, and includes all necessary translation metadata in the broadcast. The graceful handling of translation errors (falling back to original content) ensures chat reliability.
backend/cmd/server/main.go
Outdated
| } | ||
|
|
||
| // Now os.Getenv("GEMINI_API_KEY") will work! | ||
| apiKey := os.Getenv("GEMINI_API_KEY") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused variable apiKey.
apiKey is declared on line 31 but never used. It's redundantly redeclared as geminiKey on line 40. Remove the unused declaration:
- // Now os.Getenv("GEMINI_API_KEY") will work!
- apiKey := os.Getenv("GEMINI_API_KEY")Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In @backend/cmd/server/main.go at line 31, Remove the unused variable
declaration apiKey := os.Getenv("GEMINI_API_KEY") and ensure the code uses the
existing geminiKey variable instead (geminiKey := os.Getenv("GEMINI_API_KEY")),
so the environment value is only read once and no redundant/unused apiKey symbol
remains; update any references to use geminiKey if necessary.
backend/cmd/server/main.go
Outdated
| type TranslationRequest struct { | ||
| Text string `json:"text"` | ||
| TargetLang string `json:"target_lang"` | ||
| } | ||
|
|
||
| type TranslationResponse struct { | ||
| TranslatedText string `json:"translatedText"` | ||
| } | ||
|
|
||
| func TranslationHandler(c *gin.Context) { | ||
| var req TranslationRequest | ||
| if err := c.ShouldBindJSON(&req); err != nil { | ||
| c.JSON(400, gin.H{"error": "Invalid request"}) | ||
| return | ||
| } | ||
|
|
||
| // This calls the TranslateContent function we created in services/gemini.go | ||
| translated, err := services.TranslateContent(req.Text, "en", req.TargetLang) | ||
| if err != nil { | ||
| c.JSON(500, gin.H{"error": "Translation failed"}) | ||
| return | ||
| } | ||
|
|
||
| c.JSON(200, TranslationResponse{ | ||
| TranslatedText: translated, | ||
| }) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Duplicate translation types and handler.
TranslationRequest, TranslationResponse, and TranslationHandler are duplicated in backend/websocket/translation_handler.go. This creates maintenance burden and risk of divergence. Consolidate to a single location:
- Keep types in a shared package (e.g.,
structsordto) - Keep handler in one place (either
main.goorwebsocket/translation_handler.go) - Import and use from the chosen location
🤖 Prompt for AI Agents
In @backend/cmd/server/main.go around lines 195 - 221, Create a single shared
DTO package (e.g., package dto) that defines TranslationRequest and
TranslationResponse, remove the duplicate type declarations from both places,
and update code to import dto and use
dto.TranslationRequest/dto.TranslationResponse; keep only one TranslationHandler
implementation (choose the existing websocket.TranslationHandler or
main.TranslationHandler), delete the duplicate handler, and wire your routes to
call the single exported TranslationHandler function from its package. Ensure
package imports and references to services.TranslateContent remain correct after
moving types.
backend/services/translation.go
Outdated
| "context" | ||
| "fmt" | ||
| "strings" | ||
| "github.com/debateai/DebateAI/backend/services/gemini" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the actual module path used in go.mod
echo "=== go.mod module declaration ==="
head -5 go.mod
echo -e "\n=== Import patterns in backend/services ==="
find backend/services -name "*.go" -type f -exec grep -l "^import" {} \; | head -10 | while read file; do
echo "--- $file ---"
grep -A 20 "^import" "$file" | head -25
doneRepository: AOSSIE-Org/DebateAI
Length of output: 5314
🏁 Script executed:
#!/bin/bash
# View translation.go to see the actual imports
echo "=== backend/services/translation.go ==="
head -30 backend/services/translation.go
echo -e "\n=== Search for github.com/debateai/DebateAI references ==="
rg "github\.com/debateai/DebateAI" --type go
echo -e "\n=== Check if go.mod exists in different locations ==="
find . -name "go.mod" -type f 2>/dev/null | head -5Repository: AOSSIE-Org/DebateAI
Length of output: 1314
🏁 Script executed:
#!/bin/bash
# Check the module path in backend/go.mod
echo "=== backend/go.mod module declaration ==="
head -5 backend/go.mod
echo -e "\n=== Check what's exported from gemini.go ==="
grep -n "^var\|^const\|^func" backend/services/gemini.go | head -20
echo -e "\n=== Search for GeminiInstance reference ==="
rg "GeminiInstance" backend/Repository: AOSSIE-Org/DebateAI
Length of output: 889
Import path and variable reference are incorrect.
The import path "github.com/debateai/DebateAI/backend/services/gemini" is inconsistent with the module declaration in backend/go.mod, which uses arguehub. All other service files in the codebase correctly use arguehub/... imports (e.g., arguehub/db, arguehub/models).
Additionally, the code references gemini.GeminiInstance, but gemini.go exports geminiClient (lowercase, unexported), not GeminiInstance. This will cause a compile-time error.
The import should be "arguehub/services/gemini", and the variable reference needs to be corrected to access the actual exported symbols from the gemini package.
🤖 Prompt for AI Agents
In @backend/services/translation.go at line 7, Replace the wrong import and
variable usage: change the import to "arguehub/services/gemini" (matching module
name) and update any uses of gemini.GeminiInstance to the actual exported symbol
from the gemini package (open backend/services/gemini/gemini.go to find the
exported constructor/type name — e.g., GeminiClient, NewGeminiClient, or similar
— and use that instead of the nonexistent GeminiInstance).
| func TranslationHandler(c *gin.Context) { | ||
| var req TranslationRequest | ||
|
|
||
| // 1. Bind the incoming JSON to our struct | ||
| if err := c.ShouldBindJSON(&req); err != nil { | ||
| c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid request payload"}) | ||
| return | ||
| } | ||
|
|
||
| // 2. Call the Gemini translation service | ||
| // We assume source is "en" as the bot generates English by default | ||
| translated, err := services.TranslateContent(req.Text, "en", req.TargetLang) | ||
| if err != nil { | ||
| c.JSON(http.StatusInternalServerError, gin.H{"error": "Translation service failed"}) | ||
| return | ||
| } | ||
|
|
||
| // 3. Return the result to the React app | ||
| c.JSON(http.StatusOK, TranslationResponse{ | ||
| TranslatedText: translated, | ||
| }) | ||
| } No newline at end of file |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Duplicate handler and hardcoded source language.
This handler duplicates TranslationHandler in main.go (lines 204-221). Only one should be kept to avoid confusion and ensure consistent behavior.
The source language is hardcoded to "en" (line 32). For PvP scenarios where users may write in different languages, consider accepting source_lang in the request:
♻️ Suggested change
type TranslationRequest struct {
Text string `json:"text"`
+ SourceLang string `json:"source_lang,omitempty"`
TargetLang string `json:"target_lang"`
}
func TranslationHandler(c *gin.Context) {
// ...
- translated, err := services.TranslateContent(req.Text, "en", req.TargetLang)
+ sourceLang := req.SourceLang
+ if sourceLang == "" {
+ sourceLang = "en" // Default to English for bot responses
+ }
+ translated, err := services.TranslateContent(req.Text, sourceLang, req.TargetLang)Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In @backend/websocket/translation_handler.go around lines 21 - 42, There are two
problems: this TranslationHandler duplicates the one in main.go (remove the
duplicate and keep a single canonical TranslationHandler) and the source
language is hardcoded to "en"; modify the request payload struct to accept a
source_lang field (e.g., SourceLang) and use that value when calling
services.TranslateContent instead of the hardcoded "en" (fall back to a sensible
default if SourceLang is empty), and ensure only one TranslationHandler
implementation remains (update any imports/route registrations to point to the
retained handler).
| // 1. Get the languages. | ||
| // In a real app, these should come from your settings state or props. | ||
| // For now, we use "en" and "es" as placeholders. | ||
| const senderLang = "en"; | ||
| const targetLang = "es"; | ||
|
|
||
| if (isRecognizing) { | ||
| stopRecognition(); | ||
| } | ||
| }; | ||
| // 2. Pass the new arguments to onSendMessage | ||
| // This matches the update we need to make in your WebSocket handler | ||
| onSendMessage(inputText.trim(), inputMode, senderLang, targetLang); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Hardcoded language placeholders need replacement.
The comment acknowledges that senderLang and targetLang are placeholders. These should be derived from user settings (similar to how DebateRoom.tsx uses user?.language). Consider passing these as props or accessing them from a shared user/settings context.
♻️ Suggested approach
+// Add to Chatbox props:
+userLanguage?: string;
+opponentLanguage?: string;
const handleSendMessage = () => {
if (!inputText.trim() || disabled || !isMyTurn) return;
- const senderLang = "en";
- const targetLang = "es";
+ const senderLang = userLanguage || "en";
+ const targetLang = opponentLanguage || "en";Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In @frontend/src/components/Chatbox.tsx around lines 165 - 173, Replace the
hardcoded senderLang and targetLang in Chatbox.tsx by deriving them from the
current user's settings or props rather than literals: get the language values
the same way as DebateRoom.tsx uses user?.language (or accept a language prop on
the Chatbox component), then pass those variables into the existing
onSendMessage call (onSendMessage(inputText.trim(), inputMode, senderLang,
targetLang)). Update the Chatbox props or hook to access the user/settings
context and remove the "en"/"es" literals so languages reflect the real user
settings.
|
Hello, @bhavik-mangla , @Zahnentferner, @varunchitre15, @saisankargochhayat, @heychirag. |
Overview
Communication shouldn't have a language barrier. This PR introduces a robust, real-time translation layer that allows users to participate in debates using their preferred language,regardless of whether they are facing a human opponent in a WebSocket room or training against our AI bots.
By leveraging Google’s Gemini AI, we now translate incoming and outgoing arguments on the fly, ensuring that the core of the debate, the logic and the rhetoric remains intact across different languages.
Core Enhancements
The Translation Engine (backend/services)
Developed a centralized TranslateContent service that interfaces with Gemini.
Implemented smart caching/initialization to ensure the API is only called when a language mismatch is detected.
Seamless PvP Experience (backend/websocket)
Integrated the translation logic directly into the WebSocket broadcast cycle.
The Result: If Player A speaks English and Player B speaks Spanish, the system translates the payload mid-transit, allowing both users to see the argument in their native tongue instantly.
Bot Mode Intelligence (backend/main.go & frontend)
Exposed a new /api/translate REST endpoint to bridge the Gap for the Bot Room.
Updated the DebateRoom logic to intercept bot-generated English responses and translate them before they hit the state, providing a localized experience for solo practice.
Technical Implementation Details
issue(s)
closes #171
How to Test
Closing Note
This feature significantly increases the accessibility of ArgueHub, opening the doors for a global user base to practice their debating skills without language constraints.
Summary by CodeRabbit
✏️ Tip: You can customize this high-level summary in your review settings.