XLIFF

XLIFF, the translation interchange format

XLIFF is how the translation industry passes work around: an OASIS standard since 2002, spoken by every serious CAT tool, emitted by Angular and exported by Xcode. Each translation unit pairs the source text with its target and a note, and that pairing is what makes agency round trips work.

This page covers the 1.2 and 2.0 anatomies, inline markup, how plurals are represented in a format that predates plural support, and where each toolchain puts the files.

translations.fr.xlf
<?xml version="1.0" encoding="UTF-8"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
  <file source-language="en" target-language="fr" datatype="plaintext" original="messages">
    <body>
      <trans-unit id="checkout.title">
        <source>Order summary</source>
        <target>Récapitulatif de commande</target>
        <note>Title of the checkout screen</note>
      </trans-unit>
    </body>
  </file>
</xliff>
One translation unit: source, target and the note a translator actually reads.
Download a sample: messages.fr.xlf

Cheatsheet

XLIFF anatomy in five rows

You seeWhat it means
<trans-unit id="…"> One translatable unit in 1.2, at any nesting depth. The id becomes the key.
<unit> with <segment> The 2.0 spelling of the same thing, wrapped in a mandatory segment element.
<source>, <target>, <note> Original, translation and context, imported together. An empty target is respected as deliberately empty, not skipped.
<x/>, <ph>, <g> Inline placeholder markup contributes its text in place, so a unit containing tags still imports as a complete string.
<group restype="x-gettext-plurals"> The OASIS convention for plurals in 1.2: one unit per CLDR form, grouped so they stay one plural.

Untranslated keys, three spellings

An empty target is an explicit request to translate; an omitted unit hides the work; a source copy pre-fills it.

exported empty
<trans-unit id="checkout.confirm">
  <source>Confirm purchase</source>
  <target></target>
</trans-unit>
left out
<!-- unit not exported -->
source fallback
<trans-unit id="checkout.confirm">
  <source>Confirm purchase</source>
  <target>Confirm purchase</target>
</trans-unit>

Conventions

Where XLIFF shows up

ToolchainShape
Angular ng extract-i18n emits messages.xlf; translations live in messages.fr.xlf
Xcode Export Localizations produces .xcloc bundles carrying XLIFF
Symfony translations/messages.fr.xlf
CAT tools Plain .xlf exchanges cleanly; vendor dialects like SDLXLIFF are their own formats

Typical flow: export XLIFF from locamorph for an agency, let them work in their CAT tool of choice, and import the handback; source, target and notes line up automatically.

The same unit in 1.2 and 2.0

Version 1.2 has the widest tool support; 2.0 wraps content in segments.

XLIFF 1.2
<trans-unit id="checkout.title">
  <source>Order summary</source>
  <target>Récapitulatif de commande</target>
  <note>Title of the checkout screen</note>
</trans-unit>
XLIFF 2.0
<unit id="checkout.title">
  <segment>
    <source>Order summary</source>
    <target>Récapitulatif de commande</target>
  </segment>
</unit>

The 1.2 spec also defines target states such as new, needs-translation, translated, signed-off and final, which agencies use to track progress through a handback.

Plurals

Plurals in XLIFF

XLIFF 1.2 has no plural element, so the OASIS representation guide’s convention applies: a group holding one unit per form, suffixed with the CLDR category so partial translations keep their identity.

messages.fr.xlf
<group restype="x-gettext-plurals" resname="cart.items">
  <trans-unit id="cart.items[one]" resname="cart.items">
    <source>%d item</source>
    <target state="translated">%d article</target>
  </trans-unit>
  <trans-unit id="cart.items[other]" resname="cart.items">
    <source>%d items</source>
    <target state="translated">%d articles</target>
  </trans-unit>
</group>
en · two units
<group restype="x-gettext-plurals" resname="cart.items">
  <trans-unit id="cart.items[one]">
    <source>%d item</source>
    <target>%d article</target>
  </trans-unit>
  <trans-unit id="cart.items[other]">
    <source>%d items</source>
    <target>%d articles</target>
  </trans-unit>
</group>
pl · four units
<group restype="x-gettext-plurals" resname="cart.items">
  <trans-unit id="cart.items[one]">…</trans-unit>
  <trans-unit id="cart.items[few]">…</trans-unit>
  <trans-unit id="cart.items[many]">…</trans-unit>
  <trans-unit id="cart.items[other]">…</trans-unit>
</group>

One trans-unit per CLDR category, bracketed so partial translations keep their identity. Polish grows to four units, Arabic to six.

See the same plural in every format locamorph exports.

In locamorph

How locamorph handles XLIFF

versions

1.2 and 2.0 in, 1.2 out

trans-unit files at any nesting depth and 2.0 unit/segment files both import. Exports use 1.2, the dialect with the widest tool support.

fidelity

Text is never guessed at

A target of 2024 stays the string it was, and notes stay attached to their unit through import, edit and export.

language

Locales from the file

source-language and target-language attributes identify the languages, so an agency handback lands in the right locale on its own.

Convert

Convert XLIFF to and from other formats

XLIFF is the bridge format, so most conversions start or end here.

XLIFF to JSON

The agency handback into a web app.

  1. Import the .xlf; languages come from its attributes.
  2. Source, target and notes land on the right keys.
  3. Export as JSON for your framework.

Anything to XLIFF

Preparing an agency handoff from whatever you have.

  1. Import JSON, YAML, strings.xml, ARB or .properties.
  2. Notes become <note> elements translators see.
  3. Export XLIFF 1.2 per target language.

XLIFF to strings.xml

Translated units delivered straight into the Android build.

  1. Import the handback.
  2. Plural groups become CLDR forms.
  3. Export strings.xml per locale with escaping applied.

Bring the agency file

Import an XLIFF handback on the free plan and watch source, target and notes land on the right keys in the right language.