Main menu

Importing XLIFF files into Loco

The XLIFF XML format is used by many translation applications, and with some variation between implementations.

When importing XLIFF files you may have to set some specific options for your platform. Here are a few examples of how Loco will deal with variations in the format.

XLIFF 1.2

This is a pretty standard bi-lingual XLIFF file. It describes English as the "source", French as the "target", and gives two translations for a single unit called "greeting".

<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file source-language="en" target-language="fr" datatype="plaintext" original="words.txt">
        <body>
            <trans-unit id="1" resname="greeting">
                <source>Hello</source>
                <target>Bonjour</target>
            </trans-unit>
        </body>
    </file>
</xliff>

Importing this into Loco will map the <trans-unit> element to a single translatable asset. The asset ID will take the value of the resname attribute. The id attribute in this example may have meaning to the software that generated the file, but Loco will ignore it in favour of the resname attribute.

To import both the English and the French translations you will have to run the import process twice, selecting the required target language each time. If you're importing files via the API, then make two requests specifying locale=en and locale=fr respectively.

XLIFF 2.0

Loco will also parse XLIFF 2.0. The following is semantically equivalent to the example above:

<?xml version="1.0"?>
<xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en" trgLang="fr">
    <file original="words.txt">
        <unit id="1" name="greeting">
            <segment>
                <source>Hello</source>
                <target>Bonjour</target>
            </segment>
        </unit>
    </file>
</xliff>

XLIFF 2.0 has been an ISO standard since November 2017, but most software localization platforms still use the 1.2 specification.

Additional translations

In addition to the single <target> element shown above, XLIFF 1.2 supports additional translations inside the <alt-trans> element:

<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file source-language="en" target-language="fr" datatype="plaintext" original="more-words.txt">
        <body>
            <trans-unit id="1" resname="greeting">
                <source>Hello</source>
                <target>Bonjour</target>
                <alt-trans>
                    <target xml:lang="it-IT">Ciao</target> 
                    <target xml:lang="es-ES">Hola</target> 
                </alt-trans>
            </trans-unit>
        </body>
    </file>
</xliff>

Provided the language exists in your project, you can select any of these "alternative" targets to import into Loco. Loco will extract any of these languages by matching its xml:lang attribute, but you will still have to run the import for each language you want to import.

Xcode

The following example was exported from Xcode 9.2. It contains only a source language indexed by id attributes.

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
  <file original="en.lproj/Localizable.strings" source-language="en" datatype="plaintext">
    <header>
      <tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="9.2" build-num="9C40b"/>
    </header>
    <body>
      <trans-unit id="greeting">
        <source>Hello</source>
      </trans-unit>
    </body>
  </file>
</xliff>

Importing this into Loco will create an asset called "greeting". The id attribute is used for the asset ID in this case, because the preferred resname attribute is not present.

The options for importing from Xcode into Loco are the same as the generic example above. Loco will fall back to the id attributes automatically.

Symfony

Versions of Symfony have exported XLIFF files in slightly different ways over the years. The various Symfony documentation pages show slightly different examples, but the following is how v3.4 of the Translation component would dump an XLIFF catalogue for the default locale.

<?xml version="1.0"?>
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
    <file source-language="en" target-language="en" datatype="plaintext" original="file.ext">
        <header>
          <tool tool-id="symfony" tool-name="Symfony"/>
        </header>
        <body>
            <trans-unit id="LCa0a2j" resname="title.post_list">
                <source>title.post_list</source>
                <target>Post List</target>
            </trans-unit>
        </body>
    </file>
</xliff>

The options for importing from Symfony into Loco are the same as the examples above, but there is one critical difference: The <source> element holds a duplicate of the "translation key" and not really the source text at all. (Compare this to the Xcode example).

Loco observes the resname attribute and will ignore the <source> element completely here. It is still able to extract the English text, because the file's target-language attribute is set to "en".

If you get unexpected results when importing XLIFF files, check the target-language attribute matches the project locale you're importing.

Read more about Symfony support in Loco.

IDs vs source text

Most file formats index translations by key/value pairs. For example: {"greeting":"Hello"} or "Hello"="Bonjour". The key could be either a generic ID or the full source text, However, XLIFF is different as it can define up to three different keys for each translation. Consider the following snippet:

<trans-unit id="foo" resname="greeting">
    <source>Hello</source>
    <target>Bonjour</target>
</trans-unit>

The translation in this example is "Bonjour" but the key could be "foo", "greeting" or "Hello" depending on what you were doing with this file. See above for how differently platforms use the XLIFF format.

It's import to understand how Loco's key mapping options affect the behaviour of XLIFF imports. The logic is as follows:

  • "greeting" will always be taken as the Asset ID because it's a valid identifier.
  • "foo" will be ignored because the resname attribute takes priority when it's present.
  • "Hello" will be used as the default language source text, if you specified index=text.
  • "Bonjour" will be imported as the French translation if you specified locale=fr.

In summary:

  • If your <source> elements contain generic keys, import English with ?index=id&locale=en.
  • If your <source> elements contain source text, map English to French with ?index=text&locale=fr.