From c5fd09d71d73a911c701381bcaa51a44acdb96ef Mon Sep 17 00:00:00 2001 From: Adolfo Reyna Date: Wed, 25 Feb 2026 18:41:46 -0500 Subject: [PATCH] feat: add post translation queue and background AI translation via API --- dbTools/post.js | 14 ++++++++++++++ routes/post.js | 40 ++++++++++++++++++++++++++++++++++++++++ utils/chatTranslation.js | 2 +- 3 files changed, 55 insertions(+), 1 deletion(-) diff --git a/dbTools/post.js b/dbTools/post.js index fd3f336..f12cbcb 100644 --- a/dbTools/post.js +++ b/dbTools/post.js @@ -38,6 +38,20 @@ postDB = (DB)=>{ }); } + DB.addTranslation = (postid, lang, translatedText) => { + if(!DB.ObjectID.isValid(postid)) return false; + const id = DB.ObjectID(postid); + let update = { + $set:{ + ["translations." + lang]: translatedText + } + } + return DB.postCols.updateOne({_id: id}, update).catch((err)=>{ + console.log(err); + return false; + }); + } + DB.newReaction = (postid, profileid, reaction) => { if(!DB.ObjectID.isValid(postid)) return false; const id = DB.ObjectID(postid); diff --git a/routes/post.js b/routes/post.js index 3aaa08a..903abe1 100644 --- a/routes/post.js +++ b/routes/post.js @@ -4,6 +4,7 @@ var router = express.Router(); const DB = require("./../mongoDB.js"); const Post = require("./../def/post.js"); const Notifications = require("./../notifications.js"); +const { translateText, normalizeLanguageCode } = require("../utils/chatTranslation.js"); DB.getDB.then((DB) => { @@ -481,6 +482,45 @@ DB.getDB.then((DB) => { }) }); + router.post("/translate", async (req, res) => { + let postid = req.body.postid; + let targetLang = normalizeLanguageCode(req.body.targetLang); + + // Return ack immediately + res.json({ status: "ok", message: "Translation queued" }); + + if (!postid || !targetLang) return; + + try { + // Get post + const posts = await DB.getPostsByTag('', null); // No good way to get one post by ID directly exposed? + // Let's use dbCols directly if needed or find it. Wait, how do we get a single post? + // I'll assume DB.getPost exists, let me check that later. Actually I will use DB.postCols directly. + const post = await DB.postCols.findOne({ _id: DB.ObjectID(postid) }); + if (!post || !post.content) return; + + // Strip inline tags and bible tags before translating to reduce token usage and confusion, + // or just translate the raw content and let the AI handle it? The chat translator prompt says: + // "You translate chat messages. Keep meaning, tone, emojis, names, and references. Return only the translated text." + // So it can handle tags. + + // To avoid huge translations or mostly-media posts + if (post.content.length > 1000) return; + + const translation = await translateText({ + text: post.content, + sourceLang: "auto", + targetLang: targetLang + }); + + if (translation && translation.translatedText) { + await DB.addTranslation(postid, targetLang, translation.translatedText); + } + } catch (error) { + console.error("Error in background post translation", error); + } + }); + /** * @swagger * /post/react: diff --git a/utils/chatTranslation.js b/utils/chatTranslation.js index bdcc4ac..6b9b261 100644 --- a/utils/chatTranslation.js +++ b/utils/chatTranslation.js @@ -54,7 +54,7 @@ const translateText = async ({ text, sourceLang, targetLang }) => { content: [ { type: "input_text", - text: "You translate chat messages. Keep meaning, tone, emojis, names, and references. Return only the translated text.", + text: "You translate chat messages and posts. Keep meaning, tone, emojis, names, and references. Do not translate structural tags starting with @ (e.g. @image:..., @youtube:..., @bible:...). Leave them exactly as they are or omit them if they do not fit the text flow. Return only the translated text.", }, ], },