XLIFF 1.2 files
The bilingual format almost every translation tool can read and write. It has been stable since 2008, which is why a file produced by one tool in 2011 still opens in another today — and why its quirks are worth knowing.
How a file is put together
One <xliff> root holds one or more
<file> elements, each with a header and a
body. The file element carries the language pair, the document the content came out of,
and what kind of content it is. Everything translatable lives in
<trans-unit> elements, optionally nested in
<group> elements that mirror the original
document's structure.
<file original="getting-started.dita" source-language="en"
target-language="fr" datatype="xml">
<header>
<tool tool-id="acme-segmenter" tool-name="Acme Segmenter" tool-version="2.0"/>
</header>
<body>
<trans-unit id="p1" translate="yes" approved="no">
<source>Install the desktop client before you connect a project.</source>
<target state="translated">Installez le client de bureau avant de connecter un projet.</target>
<note from="translator">"client" is the desktop app, not the customer.</note>
</trans-unit>
</body>
</file> id is meant to be unique in the document; in
practice exports with several
<file> elements restart the numbering, so
xliff.ai prefixes ids from the second file onwards and qualifies exact duplicates by
position. translate="no" makes the unit
read-only. approved="yes" means someone signed
it off. And <alt-trans> holds memory or
machine translation suggestions in its own source and target pair — a parser that does
not skip that subtree will show you a fuzzy match as if it were your translation.
Segmented and unsegmented units
In the simple case a unit has one source and one target, and the unit is the segment.
Tools that segment into sentences add a
<seg-source> element that mirrors the
source but splits it into
<mrk mtype="seg" mid="N"> markers, and the
target then carries markers with the same
mid values.
<trans-unit id="p2" translate="yes">
<source>Open the File menu. Select Import package.</source>
<seg-source><mrk mtype="seg" mid="1">Open the File menu.</mrk> <mrk mtype="seg" mid="2">Select Import package.</mrk></seg-source>
<target state="needs-review-translation"><mrk mtype="seg" mid="1">Ouvrez le menu Fichier.</mrk> <mrk mtype="seg" mid="2"></mrk></target>
</trans-unit>
When <seg-source> is present, the
mid values define the real segments and
<source> becomes a paragraph-level record
rather than a row. A mid with no counterpart in the target is an untranslated segment,
not a broken one; a marker in the target whose mid never appears in the seg-source is an
orphan, and xliff.ai warns rather than guessing which sentence it belongs to. Segmented
rows are identified by unit id plus mid, so the second sentence above is row
p2#2.
One consequence catches people out: the state attribute sits on
<target>, and a segmented unit has one
target element for all its sentences, so several segments share one state. That is a
limitation of the format, and one of the problems XLIFF 2.0 was designed to fix.
The state attribute
Ten values are defined. Most tools reduce them to three questions: has it been translated, has it been reviewed, is it finished.
| Value | What it means |
|---|---|
| new | Nobody has started this segment. Usually paired with an empty target. |
| needs-translation | There is content, but it is not a finished translation. |
| needs-adaptation | Work is needed beyond translation, such as a changed example or unit. |
| needs-l10n | Both translation and locale adaptation are needed. |
| needs-review-translation | Translated, waiting for a reviewer. |
| needs-review-adaptation | Adapted, waiting for review of the adaptation. |
| needs-review-l10n | Translated and adapted, waiting for review. |
| translated | A finished translation, not yet reviewed. |
| signed-off | Reviewed and approved. |
| final | Done in the producing tool's workflow. |
The attribute is optional: a target with content and no state is normal, and xliff.ai
reads it as a draft rather than inventing a value. A separate attribute,
state-qualifier, records where the content came
from (exact-match,
fuzzy-match,
mt-suggestion and friends) and is never rewritten
here. Changing a status writes
new,
needs-translation,
translated,
needs-review-translation or
signed-off. A state string the format does not
define — and vendors do invent them — stays as written unless you change that segment.
Inline tags, all ten of them
Inline tags stand in for markup from the original document: bold, a link, an image, a placeholder the developer substitutes at runtime. XLIFF 1.2 defines ten elements for this, and files in the wild use all of them.
-
<g>— A paired code that wraps translatable text. The native markup is implied by ctype rather than stored.Click <g id="1" ctype="bold">Save</g> to continue. -
<x/>— A standalone placeholder for a code that has no content of its own.Go to <x id="3" ctype="x-menu" equiv-text="Settings"/> and turn on developer mode. -
<bx/> and <ex/>— A begin/end pair written as two standalone elements and joined by rid. Used when the span cannot nest cleanly.Read <bx id="1" rid="1" ctype="link"/>the release notes<ex id="2" rid="1"/> first. -
<ph>— An opaque placeholder that carries the original code inside it, escaped.The logo <ph id="8" ctype="image"><img src="logo.svg"/></ph> appears in the header. -
<bpt> and <ept>— The paired version of ph: each half carries its own native markup, joined by rid.Press <bpt id="9" rid="2"><kbd></bpt>Ctrl+S<ept id="10" rid="2"></kbd></ept> to save. -
<it>— An isolated tag: one half of a pair whose partner sits in a different unit. pos says which half it is.<it id="11" pos="open" ctype="x-list"><ul class="formats"></it>Supported file formats -
<sub>— Translatable text held inside another code, such as an alt attribute inside a placeholder.<ph id="8" ctype="image"><img alt="<sub>Acme logo</sub>"/></ph> -
<mrk>— An annotation rather than a code. mtype="seg" marks a segment, "term" a term, "protected" untranslatable text; vendor values start with x-.Enter your <mrk mtype="term">API key</mrk> to continue.
Two attributes matter while you translate.
ctype says what formatting the code represents —
bold,
link,
image, or a vendor value starting with
x- — and it is what a tag chip usually shows you.
equiv-text gives a plain-text stand-in, so a
placeholder that renders as eighteen characters counts as eighteen in a length check
even though it is one chip on screen.
In xliff.ai each becomes one atomic chip. The opaque ones —
x, ph,
bpt, ept,
bx, ex,
it — keep their exact original bytes, nested
content and all. The wrappers g,
mrk and
sub become an opening chip and a closing chip
with your text between them. A chip is one caret step and one delete.
Why a round trip through another editor damages the file
Most browser-based XLIFF editors parse the XML into a document tree, apply your edits
and serialize the tree back out. The output is valid XML and a different file.
Attribute order changes. <x id="1"/>
becomes <x id="1"></x>, or the
reverse. A numeric entity turns into a literal character. CDATA collapses into escaped
text. Namespace prefixes get renumbered. Indentation is normalized, so the diff is the
entire file. Comments and processing instructions vanish. Elements in namespaces the
parser did not recognize are dropped, which is how a round trip loses match rates and
reviewer comments. None of that is a bug in the XML library — it is what serializers do.
It only becomes a problem when the file has to go back into the tool it came from.
What splicing bytes preserves
xliff.ai never serializes. It keeps the original bytes, records byte offsets while
tokenizing, and on export splices replacement ranges into those bytes in descending
order. Open a file, change nothing, export it, and the bytes are identical, byte-order
mark and line endings included — that is an automated test, not an aspiration. Change
three segments out of nine thousand and exactly three byte ranges differ, plus the
state attribute on their targets. A target element that did not exist is inserted after
</source>, indented to match its
neighbours. New text is escaped for
&,
< and
> only, so entities you did not touch stay as
the producing tool wrote them.
The same mechanism is why unknown things survive. A foreign namespace, a processing instruction, a vendor attribute nobody has documented: none of it has to be understood, because none of it is ever touched. Encodings work the same way — UTF-8 and UTF-16 in both byte orders, with or without a mark — and replacement text goes back in the file's own encoding.
Related formats
XLIFF 2.0 redesigns all of the above, with segments as real elements. SDLXLIFF and MQXLIFF are 1.2 with a vendor namespace bolted on, from Trados Studio and memoQ — everything here applies to them too.
Before you deliver: run the QA checks on an XLIFF file.