From b94df9929729e38825b1cceb58b5c882e43c0ca6 Mon Sep 17 00:00:00 2001 From: corsacca Date: Mon, 7 Sep 2026 16:56:39 +0100 Subject: [PATCH] restore source edge whitespace on translated fragments MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Formatting splits a sentence across text nodes mid-phrase, and no model reliably reproduces a fragment's leading or trailing whitespace — a dropped edge space glues two words together once the fragments are concatenated back into the document. The source fragment now decides the edges. --- server/utils/translate.ts | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/server/utils/translate.ts b/server/utils/translate.ts index a76efccb..344d8480 100644 --- a/server/utils/translate.ts +++ b/server/utils/translate.ts @@ -26,6 +26,16 @@ export async function translateText( return translated[0] ?? '' } +// Restore the source fragment's leading/trailing whitespace on a translation. +// No model reliably reproduces edge whitespace, and formatting splits sentences +// mid-phrase, so a dropped edge space glues two words together in the rendered +// output. The source fragment is authoritative for the edges. +function restoreEdgeWhitespace(source: string, translated: string): string { + const leading = source.match(/^\s*/)![0] + const trailing = source.match(/\s*$/)![0] + return leading + translated.trim() + trailing +} + export async function translateTexts( texts: string[], targetLanguage: string, @@ -49,7 +59,9 @@ export async function translateTexts( const translated = await openrouterTranslateTexts(translatable, targetLanguage, sourceLanguage) const out = [...texts] - indexes.forEach((target, i) => { out[target] = translated[i]! }) + indexes.forEach((target, i) => { + out[target] = restoreEdgeWhitespace(texts[target]!, translated[i]!) + }) return out }