Summary
When importing an XLIFF file via the Locale Sync plugin, translations for formattedText elements display raw HTML tags as visible text on the page (e.g. <p dir="auto">Datenschutzerklärung</p> instead of just Datenschutzerklärung). This only affects non-default locales — the default locale renders correctly.
Steps to Reproduce
- Create a Framer project with a non-default locale (e.g. German)
- Add some
formattedText content (headings, paragraphs)
- Use "Translate All" to generate translations — they display correctly
- Export the XLIFF using Locale Sync
- Edit a few translations in the exported XLIFF file (change the text inside
<target> elements)
- Re-import the edited XLIFF via Locale Sync
- Preview the non-default locale
Expected: Translations display as normal text.
Actual: Translations show literal <p dir="auto">...</p> tags as visible text on the page.
Screenshots
Before import (correct):
Datenschutzerklärung
After import (broken):
<p dir="auto">Datenschutzerklärung</p>
Root Cause
The bug is in xliff.ts, specifically in createValuesBySourceFromXliff().
On import, the plugin reads the target value using:
const targetValue = target.textContent
textContent strips all HTML markup from the parsed XML node. For formattedText sources, the XLIFF target contains XML-escaped HTML like <p dir="auto">Text</p>. The XML parser decodes the entities and creates actual DOM elements. textContent then returns only the text content without the HTML structure.
When this plain text is passed to framer.setLocalizationData(), Framer receives a plain string for a formattedText slot. It wraps it in <p dir="auto"> internally, but as a literal string rather than parsed HTML — so the tags render visibly.
This also means that any rich text formatting in translations (bold, links, line breaks) is silently stripped on every import, even if the XLIFF file contains the correct HTML structure.
Suggested Fix
The import function should differentiate between formattedText and other source types. For formattedText, it should preserve the HTML structure:
// In createValuesBySourceFromXliff(), around line 151:
const type = unit.querySelector('note[category="type"]')?.textContent
let targetValue: string
if (type === 'formattedText') {
// Preserve HTML structure for formatted text
targetValue = target.innerHTML
} else {
targetValue = target.textContent ?? ""
}
This ensures that formattedText translations retain their <p>, <a>, <strong>, <br> etc. tags, while string and other types continue to work as plain text.
Environment
- Framer Web (latest)
- Locale Sync plugin (latest from Marketplace)
- XLIFF 2.0 format
- Tested with German (de) as non-default locale, English as default
Summary
When importing an XLIFF file via the Locale Sync plugin, translations for
formattedTextelements display raw HTML tags as visible text on the page (e.g.<p dir="auto">Datenschutzerklärung</p>instead of justDatenschutzerklärung). This only affects non-default locales — the default locale renders correctly.Steps to Reproduce
formattedTextcontent (headings, paragraphs)<target>elements)Expected: Translations display as normal text.
Actual: Translations show literal
<p dir="auto">...</p>tags as visible text on the page.Screenshots
Before import (correct):
After import (broken):
Root Cause
The bug is in
xliff.ts, specifically increateValuesBySourceFromXliff().On import, the plugin reads the target value using:
textContentstrips all HTML markup from the parsed XML node. ForformattedTextsources, the XLIFF target contains XML-escaped HTML like<p dir="auto">Text</p>. The XML parser decodes the entities and creates actual DOM elements.textContentthen returns only the text content without the HTML structure.When this plain text is passed to
framer.setLocalizationData(), Framer receives a plain string for aformattedTextslot. It wraps it in<p dir="auto">internally, but as a literal string rather than parsed HTML — so the tags render visibly.This also means that any rich text formatting in translations (bold, links, line breaks) is silently stripped on every import, even if the XLIFF file contains the correct HTML structure.
Suggested Fix
The import function should differentiate between
formattedTextand other source types. ForformattedText, it should preserve the HTML structure:This ensures that
formattedTexttranslations retain their<p>,<a>,<strong>,<br>etc. tags, whilestringand other types continue to work as plain text.Environment