Mintplex-Labs/anything-llm · warning
Warning: Missing placeholders in translation: ${validation.m
Error message
Warning: Missing placeholders in translation: ${validation.missing.join(', ')} What it means
extras/translator rewrites ICU interpolation placeholders like {{count}} into opaque __PLACEHOLDER_n__ tokens before translation and restores them afterwards. validatePlaceholders() then compares the set of {{...}} occurrences in source vs translated text; when any placeholder from the source is missing, this summary warning lists it (e.g. '{{count}}'). The translation is still returned, but i18next interpolation for that key will render the literal translation or break at runtime.
Source
Thrown at extras/translator/index.mjs:211
const tagValidation = validateTransTags(text, translatedText);
if (!tagValidation.valid) {
console.warn(`Warning: Missing Trans tags in translation: ${tagValidation.missing.join(', ')}`);
for (let i = 0; i < tags.length; i++) {
if (!translatedText.includes(tags[i])) {
console.warn(` Tag ${tags[i]} was lost in translation`);
}
}
}
}
// Restore original placeholders
if (hasPlaceholders) {
translatedText = restorePlaceholders(translatedText, placeholders);
// Validate all placeholders were preserved
const validation = validatePlaceholders(text, translatedText);
if (!validation.valid) {
console.warn(`Warning: Missing placeholders in translation: ${validation.missing.join(', ')}`);
// Attempt to fix by checking if tokens remain untranslated
for (let i = 0; i < placeholders.length; i++) {
if (!translatedText.includes(placeholders[i])) {
console.warn(` Placeholder ${placeholders[i]} was lost in translation`);
}
}
}
}
return translatedText;
}
writeTranslations(langCode, translations) {
let langFilename = langCode.toLowerCase();
// Special cases
if(langCode === 'pt') langFilename = 'pt_BR';
if(langCode === 'zh-tw') langFilename = 'zh_TW';
if(langCode === 'vi') langFilename = 'vn';View on GitHub (pinned to 3aec848f28)
Solutions
- Re-run translation for the affected keys — placeholder loss is stochastic and often recovers on retry.
- Use a stronger/larger translation model and avoid creative sampling settings so tokens survive verbatim.
- Manually re-insert the listed {{placeholders}} into the generated locale file before merging (the warning names each missing one).
- Keep strings that contain placeholders short and avoid surrounding them with text the model is tempted to inflect.
- If a token like __PLACEHOLDER_0__ survives into the output verbatim, replace it with the original placeholder from the source string — that means the restore regex missed it.
Example fix
// before: translate once, ship even if {{count}} was dropped
translations[key] = await translator.translate(source[key], lang);
// after: validate placeholder set and retry, then flag for manual review
let out = await translator.translate(source[key], lang);
const ph = (s) => s.match(/\{\{[^}]+\}\}/g) || [];
for (let i = 0; i < 2 && ph(source[key]).some(p => !ph(out).includes(p)); i++) {
out = await translator.translate(source[key], lang);
}
translations[key] = out; Defensive patterns
Strategy: retry
Validate before calling
const placeholdersOf = (s) => s.match(/\{\{[^}]+\}\}/g) || [];
const missingPlaceholders = (source, translated) =>
placeholdersOf(source).filter(p => !placeholdersOf(translated).includes(p));
const ok = missingPlaceholders(sourceText, translatedText).length === 0; Try / catch
let result;
for (let attempt = 0; attempt < 3; attempt++) {
result = await translator.translate(text, lang);
if (missingPlaceholders(text, result).length === 0) break;
if (result.includes("__PLACEHOLDER_")) continue; // leaked token — restore may fix on retry
if (attempt === 2) throw new Error(`Placeholders lost in translation for: ${text}`);
}
return result; Prevention
- Post-validate every translated string: the {{...}} set must be identical (names and casing) to the source before writing the locale file.
- If a raw __PLACEHOLDER_n__ token survives into output, substitute the original placeholder by index instead of re-translating blind.
- For pluralization-heavy locales, prefer dedicated plural handling over free-text translation of '{{count}}' fragments.
When it happens
Trigger: The LLM deletes a __PLACEHOLDER_n__ token, translates or rewrites it ("__PLATZHALTER_0__"), changes case ({{Count}}), or converts it to native syntax the restore regex doesn't match — the source/translated {{...}} sets then differ and the warning fires.
Common situations: Batch-translating i18next locale files containing pluralization ({{count}}), usernames, or dates; small local translation models that 'localize' identifier-like tokens; target languages where the model reorders or rewrites around the placeholder. The shipped locale then shows raw tokens or drops the variable.
Related errors
- Placeholder ${placeholders[i]} was lost in translation
- Warning: Missing Trans tags in translation: ${tagValidation.
- Tag ${tags[i]} was lost in translation
- Unknown provider: ${config.provider}. Please use a valid pro
- An error occurred while downloading the model
AI-assisted analysis of Mintplex-Labs/anything-llm@3aec848f28 (2026-08-18).
Data as JSON: /api/errors/3cef2002550a0f4e.
Report an issue: GitHub.