Translate endpoint: numbered-marker protocol instead of JSON arrays - multi-line swipe strings with embedded quotes broke JSON.parse and poisoned whole batches

This commit is contained in:
martbost
2026-08-21 15:46:47 -05:00
parent e22f483f99
commit 7f527989b6
+10 -5
View File
@@ -391,17 +391,22 @@ async function handleTranslate(req,res){
method:'POST',signal:ctrl.signal, method:'POST',signal:ctrl.signal,
headers:{'Authorization':`Bearer ${apiKey}`,'Content-Type':'application/json','HTTP-Referer':'https://rmcircle.team','X-Title':'RM Circle Translate'}, headers:{'Authorization':`Bearer ${apiKey}`,'Content-Type':'application/json','HTTP-Referer':'https://rmcircle.team','X-Title':'RM Circle Translate'},
body:JSON.stringify({model:OPENROUTER_MODEL,max_tokens:6000,temperature:0,messages:[ body:JSON.stringify({model:OPENROUTER_MODEL,max_tokens:6000,temperature:0,messages:[
{role:'system',content:`You translate website UI strings from English to ${TR_LANG_NAMES[tl]}. Reply with ONLY a JSON array of the translated strings, same order and length as the input array. Keep these words untranslated wherever they appear: Scintilla, Ascensus, Fabrica, Culmen, Apex, Fastigium, Vertex, Corona, POL, Polygon, RM Circle, MoonPay, MetaMask, Trust Wallet. Keep numbers, #ids, emoji and punctuation intact. Natural, friendly tone.`}, {role:'system',content:`You translate website UI strings from English to ${TR_LANG_NAMES[tl]}. The input is numbered items, each preceded by a line @@N@@. Reply with the SAME @@N@@ marker lines, each followed by that item's translation (multi-line items keep their line breaks). Output nothing except markers and translations — no preamble, no code fences. Keep these words untranslated wherever they appear: Scintilla, Ascensus, Fabrica, Culmen, Apex, Fastigium, Vertex, Corona, POL, Polygon, RM Circle, MoonPay, MetaMask, Trust Wallet. Keep numbers, #ids, emoji and punctuation intact. Natural, friendly tone.`},
{role:'user',content:JSON.stringify(miss.map(i=>texts[i]))} {role:'user',content:miss.map((i,j)=>`@@${j+1}@@
${texts[i]}`).join('
')}
]}) ]})
}); });
clearTimeout(timer); clearTimeout(timer);
if(r.ok){ if(r.ok){
const d=await r.json(); const d=await r.json();
let reply=d&&d.choices&&d.choices[0]&&d.choices[0].message&&d.choices[0].message.content||''; let reply=d&&d.choices&&d.choices[0]&&d.choices[0].message&&d.choices[0].message.content||'';
reply=reply.replace(/^```(?:json)?\s*/,'').replace(/```\s*$/,'').trim(); reply=reply.replace(/^```[a-z]*\s*/,'').replace(/```\s*$/,'').trim();
const arr=JSON.parse(reply); const parts=reply.split(/^@@(\d+)@@\s*$/m);
if(Array.isArray(arr)&&arr.length===miss.length){ // parts: [lead, "1", text1, "2", text2, ...]
const arr=[];
for(let k=1;k+1<parts.length;k+=2){ arr[Number(parts[k])-1]=String(parts[k+1]).trim(); }
if(arr.length===miss.length&&arr.every(x=>typeof x==='string'&&x.length)){
miss.forEach((idx,j)=>{ const tr=String(arr[j]).slice(0,600); out[idx]=tr; cache[keyOf(texts[idx])]=tr; }); miss.forEach((idx,j)=>{ const tr=String(arr[j]).slice(0,600); out[idx]=tr; cache[keyOf(texts[idx])]=tr; });
trDirty=true; done=true; trDirty=true; done=true;
} }