Preserving Formats and Placeholders in Translation API Payloads
Preserving formats and placeholders in translation API payloads: protecting variables, markup, and ICU syntax from corruption, and the validation steps that catch breakage before it ships.

When translation API calls silently corrupt an HTML tag, swallow a {variable} placeholder, or reorder ICU message tokens, the damage rarely surfaces during development. It surfaces in production, as broken UI layouts, crashed mobile apps, or garbled email templates that reach real customers. The core challenge is straightforward: machine translation engines are optimized to transform natural language, not to respect the structured markup and code tokens embedded within it. Every major translation API handles this differently, and misconfiguration is the norm rather than the exception. This guide walks through the concrete payload structures, API-specific parameters, and verification strategies you need to keep format and placeholder integrity intact when translating at scale.
If you're already dealing with broken markup in your localization pipeline, talk to our API integration team to see how Ollang enforces format safety across providers.
How Translation APIs Handle HTML and Markdown Tags
Ollang's integration layer normalizes tag handling differences across providers so you can apply a consistent payload pattern and verification flow.
DeepL: tag_handling, non_splitting_tags, and splitting_tags
DeepL provides the most granular tag-handling controls of any major translation API. The tag_handling parameter accepts "xml" or "html" and tells the engine to parse and protect markup rather than treating it as translatable text.
Once tag_handling is set to "html", DeepL automatically identifies most standard HTML elements. However, the real power lies in two additional parameters:
- non_splitting_tags, A comma-separated list of tag names that should not cause sentence segmentation. For example, <em> or <strong> tags mid-sentence should not split the surrounding text into separate translation units.
- splitting_tags, Tags that should force a sentence boundary, such as <br> or custom block-level elements.
A well-configured request looks like this:
{
"text": ["<p>Hello <em>{userName}</em>, your order <span translate=\"no\">#{{orderId}}</span> is ready.</p>"],
"target_lang": "DE",
"tag_handling": "html",
"non_splitting_tags": "em,strong,span",
"ignore_tags": "span"
}
The ignore_tags parameter tells DeepL to leave the content inside those tags untouched, critical for protecting placeholders wrapped in <span translate="no">. Without non_splitting_tags, DeepL may segment mid-sentence around inline elements, producing fragmented translations that lose grammatical coherence.
Google Cloud Translation: format=html and mime types
Google Cloud Translation's v2 API uses a format parameter that accepts "text" or "html". When set to "html", the API parses HTML entities and tags, preserving them in the output. The v3 (Advanced) API uses mime_type instead:
{
"contents": ["<p>Welcome back, <b>{displayName}</b>.</p>"],
"targetLanguageCode": "fr",
"mimeType": "text/html"
}
Setting mimeType to "text/html" tells the engine to treat angle-bracket content as markup rather than literal text. Without this, tags may be translated, escaped, or stripped entirely. In HTML mode, Google generally preserves non-translatable spans indicated by translate="no"; confirm this behavior with your content and language pairs. Google does not expose splitting or non-splitting tag controls. If you need fine-grained segmentation behavior, pre-segment your content before sending it to the API.
Amazon Translate: HTML content handling
Amazon Translate can detect and preserve HTML tags in well-formed input, but relying solely on auto-detection is risky. Amazon's documentation recommends sending valid, well-formed HTML; malformed tags can cause unpredictable behavior, including content being dropped from the output.
Amazon Translate does not consistently honor translate="no". A safer approach is to replace placeholders with XML-style custom tags before sending the request, then restore them afterward, for example, replacing {orderId} with <x id="orderId"/> during pre-processing.
Azure Translator: document types and textType
Azure's Translator API provides a textType parameter that accepts "plain" or "html". When set to "html", Azure preserves HTML structure and respects common non-translation patterns such as translate="no" and class="notranslate":
[
{
"Text": "<div>Your balance is <span class=\"notranslate\">${{amount}}</span>.</div>"
}
]
Azure also supports document translation endpoints that accept entire files (DOCX, PPTX, HTML, XLIFF) and preserve their native formatting. For API-level text translation, the textType parameter is the critical switch, omitting it defaults to "plain", which causes all tags to be treated as translatable text.
If you’re deciding whether to implement provider-specific HTML protection yourself or use a unified pre/post-processing layer, see how Ollang standardizes tag handling across DeepL, Google, Amazon, and Azure.
Protecting ICU MessageFormat Tokens
ICU MessageFormat is the standard for parameterized strings in internationalized applications. A typical ICU string looks like this:
{count, plural, =0 {No items} one {# item} other {# items}} in your cart.
Translation APIs have no built-in understanding of ICU syntax. If sent as plain text, engines will frequently translate keywords like plural, one, and other, or restructure the nested braces in ways that break parsing at runtime.
Pre-processing strategies
The safest approach is to extract ICU tokens before translation and reinsert them afterward:
- Parse the ICU message into an AST using a library such as @formatjs/icu-messageformat-parser.
- Replace each non-translatable token with a numbered placeholder (for example, <x1/>, <x2/>).
- Send the simplified string to the translation API with tag handling enabled.
- Map the placeholders back to the original ICU tokens in the response.
For simpler ICU patterns (just named variables like {userName}), wrapping them in <span translate="no">{userName}</span> and using HTML mode is often sufficient. But for nested plural, select, and selectordinal blocks, full pre-processing is the only reliable method.
Common failure modes
| Failure | Cause | Result |
|---|---|---|
| Translated keywords | plural rendered as pluriel in French | ICU parser throws at runtime |
| Reordered braces | Engine restructures nested {} | Mismatched brace count, crash |
| Escaped hash | # converted to # in HTML mode | Literal # displayed instead of count |
| Split segments | Engine breaks message across segments | Partial ICU blocks in each segment |
Always validate ICU output with a parser before committing translations to your resource files.
XLIFF Round-Tripping Without Data Loss
XLIFF (XML Localization Interchange File Format) is the industry standard for exchanging translation data between tools and systems. The specification, maintained by OASIS, defines inline elements like <x/>, <bx/>, <ex/>, <g>, and <ph> specifically to represent non-translatable code within translatable segments.
Preserving inline elements
When round-tripping XLIFF through a translation API, the critical requirement is that every inline element present in the source <seg-source> or <source> must appear in the target with the same id attributes and in a linguistically valid order. A source segment like:
<source>Click <g id="1">here</g> to view <x id="2"/> results.</source>
Must produce a target where <g id="1"> wraps the translated equivalent of "here" and <x id="2"/> remains present. If you send the inner text to a translation API, you need to either:
- Send the full XML segment with tag_handling set to XML (DeepL) or mimeType set to text/html (Google, with adaptation).
- Extract the text between tags, translate it, and reconstruct the XLIFF programmatically.
The second approach is more reliable because it gives you full control over element preservation, but it requires a proper XLIFF parser, not regex.
XLIFF 2.0 vs 1.2
XLIFF 2.0 replaced many inline elements with a unified <pc> (paired code) and <ph> (placeholder) model, which simplifies round-tripping. If you're starting a new integration, prefer XLIFF 2.0. If you're working with legacy 1.2 files, be aware that <bpt>/<ept> paired tags require careful tracking to avoid orphaned opening or closing elements in the translated output.
Ready to see Ollang in action?
Talk to our team about your localization goals and see how the Ollang platform fits your workflow.
Concrete Payload Patterns for Placeholder Protection
Ollang provides API-layer token mapping, placeholder protection, and pre/post-processing that automate these patterns across providers.
Using translate="no" attributes
The simplest cross-API approach for protecting placeholders in HTML-mode requests:
{
"text": "Your verification code is <span translate=\"no\">{{code}}</span>. It expires in <span translate=\"no\">{minutes}</span> minutes.",
"target_lang": "JA",
"tag_handling": "html"
}
This works with DeepL (when ignore_tags includes span or when translate="no" is respected), Google (frequently in HTML mode; verify with your content), and Azure (via translate="no" or class="notranslate"). For Amazon, test thoroughly, support is less consistent.
Using XML placeholder tags
For APIs with XML tag handling, replace each placeholder with a self-closing XML tag before sending:
import re
def protect_placeholders(text):
counter = [0]
mapping = {}
def replace(match):
counter[0] += 1
tag = f'<x id="{counter[0]}"/>'
mapping[tag] = match.group(0)
return tag
protected = re.sub(r'\{\{?\w+\}?\}', replace, text)
return protected, mapping
After receiving the translated response, reverse the mapping to restore original placeholders. This approach is API-agnostic and provides the highest reliability across providers.
API-specific protected tokens
- DeepL: Use ignore_tags to leave content untouched inside specified elements.
- Azure: Use textType=html and mark spans with translate="no" or class="notranslate".
- Google and Amazon: Prefer explicit XML-like placeholder tags and reinsertion for complex tokens, especially when behavior around translate="no" is uncertain in your specific content.
Verification: Ensuring Placeholder Parity After Translation
Translating content is only half the job. Without automated verification, placeholder corruption will reach production.
Count and order checks
After every API response, run these checks programmatically:
- Placeholder count, Extract all placeholders from both source and target using the same regex or parser. The counts must match exactly.
- Placeholder identity, Every placeholder token in the source must appear in the target. No additions, no omissions.
- Nesting integrity, For paired tags (<b>...</b>, <g id="1">...</g>), verify that every opening tag has a matching close and that nesting order is valid.
function validatePlaceholders(source, target) {
const pattern = /\{\{?\w+\}?\}|<x\s+id="[^"]+"\s*\/?>|<\/?\w+[^>]*>/g;
const srcTokens = source.match(pattern) || [];
const tgtTokens = target.match(pattern) || [];
const srcSorted = [...srcTokens].sort();
const tgtSorted = [...tgtTokens].sort();
return JSON.stringify(srcSorted) === JSON.stringify(tgtSorted);
}
Escaping strategies
HTML entities in translated output can silently break placeholders. Watch for:
- { becoming { or {
- < inside placeholders being escaped to <
- Quotes inside attribute values being double-escaped
Normalize entities before running parity checks, and ensure your rendering pipeline handles the encoding your API returns.
Language-specific punctuation concerns
Some languages introduce punctuation that can interfere with placeholders:
- French adds a non-breaking space before colons, semicolons, and exclamation marks. If a placeholder sits adjacent to these characters, the inserted space can break token parsing.
- Japanese and Chinese may omit spaces around placeholders that English relies on for word boundaries.
- Arabic and Hebrew RTL markers can reorder placeholder positions visually, even if the underlying string is correct.
Test translations in your highest-risk languages, not just one or two European targets.
If you want to see how Ollang automates these verification steps across multiple API providers, schedule a walkthrough with our team.
Segmentation Pitfalls and Non-Translatable Attributes
How segmentation breaks structured content
Translation APIs segment input text into sentence-level units before translating. This is usually invisible, until it breaks your markup. Common failures include:
- A sentence boundary detected inside an HTML tag's attribute value, splitting the tag across two segments.
- An ICU message block split at a period inside a plural branch, producing two incomplete ICU fragments.
- Markdown link syntax [text](https://ollang.com/book-a-demo) split at the ]( boundary.
To avoid segmentation issues, send the smallest meaningful unit to the API. If your source content is a full HTML document, extract individual translatable strings and translate them separately rather than sending the entire DOM.
Attributes that must not be translated
Not all attributes are translatable. A common mistake is sending HTML with alt, title, and placeholder attributes marked for translation while also exposing href, src, class, id, and data-* attributes to the engine. In HTML mode, most APIs will leave attribute values alone, but not all, and not always.
Explicitly mark non-translatable attributes by wrapping their parent elements with translate="no" or by extracting attribute values into separate API calls where you control which ones are sent for translation.
Verification Checklist
Use this checklist before promoting any translation API integration to production:
| Check | Method | Pass Criteria |
|---|---|---|
| Tag handling parameter set | Code review | Every API call includes tag_handling, mimeType, or textType as appropriate |
| Placeholder count parity | Automated test | Source and target contain identical placeholder tokens |
| Nesting validity | XML/HTML parser | No orphaned or misnested tags in output |
| ICU parse test | @formatjs/icu-messageformat-parser or equivalent | Translated ICU strings parse without error |
| Entity normalization | Unit test | { and { treated as equivalent before comparison |
| RTL/LTR marker check | Regex scan | No directional markers inserted inside placeholder tokens |
| Non-translatable attributes | Code review | href, src, id, class, data-* values unchanged |
| Language-specific punctuation | Sample translations in FR, JA, AR, ZH | Placeholders adjacent to punctuation still parse correctly |
| XLIFF round-trip | Diff source vs re-imported target | All inline element id attributes preserved, no elements added or removed |
| Escaping consistency | End-to-end render test | Translated strings render correctly in target UI framework |
FAQ
What is the safest way to protect placeholders across multiple translation APIs?
Wrap each placeholder in a <span translate="no"> tag and send the content in HTML mode. This approach is supported by most HTML-capable translation APIs (with provider-specific options such as ignore_tags or native handling of non-translation markers). For providers that do not reliably honor HTML non-translation attributes, replace placeholders with XML-style self-closing tags like <x id="1"/> and restore them after translation. Ollang's integration enforces these protections and centralizes the pre/post-processing so you can apply a single, reliable strategy across providers.
How do I prevent ICU MessageFormat strings from breaking during translation?
Never send raw ICU MessageFormat strings to a translation API as plain text. At minimum, wrap variable references like {userName} in non-translatable spans. For complex patterns involving plural, select, or selectordinal, parse the ICU string into an AST, extract only the translatable text segments, translate those individually, and reassemble the full ICU string programmatically. Always validate the output by parsing it with an ICU library before storing it.
Can XLIFF files be round-tripped through translation APIs without losing inline codes?
Yes, but it requires careful handling. Extract the text content from XLIFF <source> elements, preserve inline elements (<x/>, <g>, <ph>, <pc>) as protected tags during translation, and reconstruct the <target> elements with the translated text and original inline elements. Do not send raw XLIFF XML to a translation API endpoint designed for plain text or HTML, the API will not understand XLIFF-specific elements and may corrupt or remove them. Use a proper XLIFF parser for extraction and reconstruction.
How do I automate placeholder verification in a CI/CD pipeline?
Add a post-translation validation step that extracts all placeholder tokens from both source and target strings using a consistent regex or parser, then asserts count and identity parity. Integrate this as a test in your CI pipeline so that any translation job that introduces placeholder mismatches fails the build. For XLIFF workflows, diff the inline element IDs between source and target. For ICU strings, run the translated output through an ICU parser and fail on any parse error. These checks add minimal latency and catch the majority of format corruption before it reaches production.
Ready to see Ollang in action?
Talk to our team about your localization goals and see how the Ollang platform fits your workflow.
Get Started with Format-Safe Translation at Scale
Placeholder corruption and markup breakage are preventable problems, but only with the right configuration, verification, and provider-aware logic in your pipeline. Ollang's API integration layer handles tag protection, placeholder validation, and format preservation across translation providers so your engineering team can ship localized products without debugging broken markup.
Published on July 30, 2026