Inline tags are the markup that survives extraction. When a filter pulls a
sentence out of a DOCX, an HTML page or a resource file, the formatting and the
placeholders inside that sentence have to travel with it, and they cannot travel
as literal markup — a raw < in the text would break the XLIFF itself. So every
XLIFF version defines a set of elements that stand in for native code inside
source and target.
Getting them wrong is the most common way to produce a bilingual file that opens fine and fails on import.
The XLIFF 1.2 taxonomy
1.2 grew two competing inline models and kept both. That is the single biggest source of confusion in the format.
| Element | Represents | Notes |
|---|---|---|
g | A paired native code wrapping translatable text | Well-formed container; the text inside stays translatable |
x | A standalone native code with no content | Line break, image, field |
bx / ex | Begin and end of a pair, as two empty elements | Joined by rid; used when the pair cannot nest cleanly |
bpt / ept | Begin and end of a pair, carrying the native code inside | Joined by rid; the markup itself is escaped in the element content |
ph | A standalone code, carrying the native code inside | The x equivalent for the “native code” model |
it | An isolated half of a pair whose partner is in another unit | pos="open" or pos="close" |
sub | Translatable text embedded inside a native code | An alt or title attribute value; nests inside ph, bpt, ept, it |
mrk | An annotation over a span | mtype="seg", term, protected, or a vendor x-* value |
The difference between the two models is where the native markup lives. The abstract model hides it:
<source>Press <g id="1">Save</g> or <x id="2"/> to continue.</source>
The native-code model carries it:
<source>Press <bpt id="1"><b></bpt>Save<ept id="1"></b></ept> or
<ph id="2"><br/></ph> to continue.</source>
Both are valid. Which one you get depends entirely on the filter that produced the file, which is why a generic 1.2 reader has to implement all of it.
sub deserves a mention because it is routinely mishandled. It marks
translatable text inside a code — the alt text of an image, the title of a
link — so a segment can contain a tag which itself contains a second piece of
text that needs translating. Tools that treat every code as opaque simply lose
that text.
The XLIFF 2.0 taxonomy
2.0 replaced all of the above with one model, and moved the native markup out of the text stream entirely.
| Element | Represents | Notes |
|---|---|---|
pc | A paired code wrapping translatable text | Must be well-formed; dataRefStart / dataRefEnd point at originalData |
ph | A standalone code | dataRef points at originalData |
sc / ec | Start code and end code, as two empty elements | For overlapping or isolated codes; ec carries startRef |
mrk | An annotation over a well-formed span | type="term", translate, comment, generic, or a prefixed custom value |
sm / em | An annotation span that is not well-formed | em carries startRef; use when the annotation crosses code boundaries |
cp | A character that XML cannot represent literally | cp hex="0003" — control characters from the original resource |
The originalData store is the structural improvement:
<unit id="u1">
<originalData>
<data id="d1"><a href="/help"></data>
<data id="d2"></a></data>
</originalData>
<segment>
<source>See the <pc id="1" dataRefStart="d1" dataRefEnd="d2">help page</pc>.</source>
</segment>
</unit>
The markup appears once, at unit level. The content stream carries a reference.
A translator moving pc id="1" around the sentence cannot corrupt the href,
because the href is not in the sentence.
2.0 also attaches editing constraints to each code, and a conforming editor must honour them:
canDelete="no"— the tag may not be removed from the target.canReorder="no"— the tag may not move relative to its siblings.canCopy="no"— the tag may not be duplicated.canOverlap— whether this code may overlap another.
This editor reads canDelete and makes the chip undeletable in the grid when it
is no. A 1.2 file has no equivalent; every code there is nominally editable,
which is part of why 1.2 files break more often.
Pairing rules
The rules are simple and unforgiving.
In 1.2, bx/ex and bpt/ept are joined by rid. When rid is absent the
id serves as the link, which is why you see files where both halves of a pair
carry id="1". g needs no join because it is a container — the nesting is
the pairing. Overlap is legal in the flat models and impossible in g, which is
exactly why filters emit bpt/ept for HTML: overlapping spans are normal
there.
In 2.0, pc must nest well-formed and may not overlap unless canOverlap="yes".
Anything that cannot be expressed that way becomes sc/ec, with ec/@startRef
naming the id of its sc. When the partner is genuinely in a different unit —
a bold run that opens in one sentence and closes in the next — the survivor is
marked isolated="yes", the 2.0 equivalent of 1.2’s it.
The practical consequence for a translator: an unpaired end tag in the target is not a cosmetic problem. It is a file that will fail validation on import, or worse, produce output where the formatting runs to the end of the document.
Why an editor must treat tags as atomic
A tag chip is not text with a border around it. In this editor a tag’s identity is the exact original byte range it came from, kept verbatim, and export splices those same bytes back into the original file. The chip is selectable only as a whole; there is no caret position inside it.
That is a deliberate constraint, not a limitation:
- The markup inside a code is not translatable. An href, a
rsidattribute, a field code — none of it should ever be typed by a translator, and any interface that allows a caret inside it invites exactly that. - Ids and refs are a contract with the converter. The CAT tool that produced
the file maps
id="1"back to a specific run in the source document. Renumber it and the map breaks silently. - Partial edits produce valid XML with wrong meaning. Deleting one character
from an escaped
</b>yields a file that parses, passes a schema check, and renders a document where bold never ends.
The QA layer follows the same principle. The tag-mismatch check compares source
and target tag multisets by their exact original markup, not by the chip label,
because two chips can both display g and be different tags. It reports
missing, extra and reordered as three separate findings, since a translator
fixes each one with a different action, and it only evaluates order once the
multisets already agree — order is meaningless while tags are missing.
What tag soup does to leverage
Tag soup is what you get when the source document was formatted by hand instead of by styles. The usual producers:
- DOCX with revision-tracking residue, where
rsidattributes split one sentence into a dozen runs. - PDF-to-Word conversions, which set font and size on nearly every word.
- HTML from a WYSIWYG editor, with a
spancarrying inline CSS per phrase. - DTP files with character-level kerning or language overrides.
The result is a segment with fifteen tags around eight words. The cost is not mainly annoyance; it is leverage.
Translation memories store the tag structure alongside the text. Two segments with identical words but different tag counts or positions are not a 100% match — they return as 99% or fuzzy, which means a human has to open, check and confirm each one. On a repeat-heavy manual, that is the difference between a quote built on 70% leverage and one built on 40%, and the words themselves never changed. Tag soup also degrades concordance results, breaks alignment when you build a TM from legacy files, and forces MT engines to work around placeholders they cannot interpret.
The fix is upstream. Clean formatting in the source document — real paragraph and character styles, no direct formatting, no leftover tracked changes — before extraction. Most CAT tools also have filter settings that merge or ignore formatting-only codes; turning those on for a soupy project is usually the fastest available win.
What an editor must not do is clean tags on save. Normalizing a file’s inline markup to make it prettier destroys the vendor round trip, and the translator who did nothing wrong gets the rejection notice. This one leaves them alone.