{"record":{"id":"c40350f8d721dc98","repo":"Mintplex-Labs/anything-llm","slug":"warning-missing-trans-tags-in-translation-tagv","errorCode":null,"errorMessage":"Warning: Missing Trans tags in translation: ${tagValidation.missing.join(', ')}","messagePattern":"Warning: Missing Trans tags in translation: (.+?)","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"extras/translator/index.mjs","lineNumber":195,"sourceCode":"                model: Translator.modelTag,\n                messages: [{ role: 'user', content: prompt }],\n                temperature: 0.1,\n                stream: false,\n            }),\n        });\n        \n        if(!response.ok) throw new Error(`Failed to translate: ${response.statusText}`);\n        const data = await response.json();\n        let translatedText = this.cleanOutputText(data.message.content);\n        \n        // Restore Trans component tags first (order matters since tags may contain placeholders)\n        if (hasTags) {\n            translatedText = restoreTransTags(translatedText, tags);\n            \n            // Validate all tags were preserved\n            const tagValidation = validateTransTags(text, translatedText);\n            if (!tagValidation.valid) {\n                console.warn(`Warning: Missing Trans tags in translation: ${tagValidation.missing.join(', ')}`);\n                for (let i = 0; i < tags.length; i++) {\n                    if (!translatedText.includes(tags[i])) {\n                        console.warn(`  Tag ${tags[i]} was lost in translation`);\n                    }\n                }\n            }\n        }\n        \n        // Restore original placeholders\n        if (hasPlaceholders) {\n            translatedText = restorePlaceholders(translatedText, placeholders);\n            \n            // Validate all placeholders were preserved\n            const validation = validatePlaceholders(text, translatedText);\n            if (!validation.valid) {\n                console.warn(`Warning: Missing placeholders in translation: ${validation.missing.join(', ')}`);\n                // Attempt to fix by checking if tokens remain untranslated\n                for (let i = 0; i < placeholders.length; i++) {","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/Mintplex-Labs/anything-llm/blob/3aec848f2885144aa8f1e53b9731a04310d5d558/extras/translator/index.mjs#L177-L213","documentation":"The extras/translator tool protects react-i18next <Trans> markup by rewriting tags like <bold>/</bold> into opaque __TAG_n__ tokens before sending text to the LLM, then restoring them afterwards. After restore, validateTransTags() regex-matches tag occurrences in source vs translated text; if any tag from the source is absent, this summary warning lists every missing tag. The output string is still returned — the locale entry just renders without that formatting.","triggerScenarios":"Batch-translating locale files when the model deletes a __TAG_n__ token, translates it (\"__ETIKETT_0__\"), merges two tokens, or emits real markup like <bold> instead of the token — restore then can't reproduce the original tag and validateTransTags flags it. Strings with many/nested tags raise the odds.","commonSituations":"Running the translator on frontend locale JSON with a small local model (default translategemma:4b), long rich-text strings with several Trans tags, target languages with heavy reordering, or temperature/generation settings that make the model 'helpfully' rewrite tokens. The generated locale then drops bold/italic/link rendering for those keys.","solutions":["Re-run translation for the affected keys — nondeterministic loss often disappears on a second pass.","Use a stronger translation model than the default 4B one (or lower randomness) so token fidelity improves.","Manually re-insert the reported tags into the generated locale file before committing (the warning names exactly which tags are missing).","Reduce tag density per string in source strings when possible — fewer tokens, fewer chances to lose one."],"exampleFix":"// before: single-shot translate, lost tags ship silently\nconst out = await translator.translate(text, lang);\n\n// after: validate tags and retry, fall back to flagging\nlet out;\nfor (let attempt = 0; attempt < 3; attempt++) {\n  out = await translator.translate(text, lang);\n  const srcTags = text.match(/<\\/?[a-zA-Z][a-zA-Z0-9]*\\s*\\/?>/g) || [];\n  const ok = srcTags.every(t => out.includes(t));\n  if (ok) break;\n  if (attempt === 2) console.error(`tags still lost for key, manual fix needed`);\n}","handlingStrategy":"retry","validationCode":"const transTagsOf = (s) => s.match(/<\\/?[a-zA-Z][a-zA-Z0-9]*\\s*\\/?>/g) || [];\nconst missingTransTags = (source, translated) =>\n  transTagsOf(source).filter(t => !transTagsOf(translated).includes(t));\n\nconst ok = missingTransTags(sourceText, translatedText).length === 0;","typeGuard":null,"tryCatchPattern":"for (let attempt = 0; attempt < 3; attempt++) {\n  const out = await translator.translate(text, lang);\n  if (missingTransTags(text, out).length === 0) return out; // tags survived\n}\nthrow new Error(`Translation kept losing Trans tags for: ${text}`); // route to manual fix","preventionTips":["Run translations through a validation gate comparing tag sets (and ordering parity) before writing locale files to disk.","Use a model strong enough to copy opaque tokens verbatim; tiny models are the main cause of token loss.","Keep tag counts per string low and avoid nesting <Trans> markup more than two levels deep."],"tags":["i18n","translation","react-i18next","trans-component","llm"],"backgroundTag":"translation-markup-corruption","analyzedSha":"3aec848f2885144aa8f1e53b9731a04310d5d558","analyzedAt":"2026-08-18T10:02:21.017Z","contentChangedAt":"2026-08-18T10:02:21.017Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}