XLIFF to JSON

Turn an .xlf/.xliff localization file into clean JSON — source/target pairs keyed by unit id, a flat translation bundle, or a nested i18n tree. XLIFF 1.2 and 2.x are both read automatically, groups are walked, and inline placeholders survive. Runs entirely in your browser, no upload.

Try:
JSON

About this tool

XLIFF to JSON reads an XLIFF localization file — the .xlf / .xliff format that CAT tools, Angular, and most translation vendors exchange — and turns its translation units into JSON you can actually use: a source/target pair for every unit id, a flat bundle of just the translations, or a nested tree for an i18n library. Paste the file and the conversion runs locally in your browser; nothing is uploaded.

Both dialects are read by the same pass, so you never have to tell it which one you have:

Output shapes

Keys come from the unit id by default. You can key by the resname (1.2) / name (2.x) attribute or by the source text instead; both fall back to the id when a unit doesn't have one, so no unit is ever silently dropped. Turn on nesting to split each key on a separator — home.hero.title becomes { "home": { "hero": { "title": … } } }.

Placeholders are preserved

A naive text-only extraction quietly deletes inline markup, which is how an {{name}} interpolation disappears from a bundle and ships broken. By default each inline code element (<x/>, <ph>, <g>, <bpt>/<ept>, <pc>, <sc>/<ec>) is rendered as its equiv-text when the file supplies one, and as {id} otherwise — so an Angular messages.xlf round-trips its interpolations intact. Switch to Strip for plain prose, or Keep to hold on to the inline XML verbatim.

Untranslated units

Missing <target> and empty <target></target> mean the same thing here — no translation — because CAT tools disagree about which one to emit. Untranslated units are included by default with an empty target, so you can see what still needs work. Turn off "Include untranslated units" to get only finished strings, or turn on the source fallback to fill the gaps with the original text. The fallback is off by default on purpose: filling silently means untranslated English ships as if it were the translation.

What else it handles

Private and offline

Everything runs in your browser via WebAssembly. Your translation file never leaves your device — no upload, no account, no tracking. Output is deterministic pretty-printed JSON in document order, so re-running the same file gives a byte-identical result you can commit and diff.

FAQ

Does this translate my text?

No. It extracts what the file already contains. XLIFF stores the source string and, where a translator has filled one in, the target string; this tool reads both and reshapes them into JSON. Units with no target come out empty (or filled from the source, if you enable that option) — nothing is machine-translated.

Do I need to know whether my file is XLIFF 1.2 or 2.0?

No. The parser captures <source>/<target> when their parent is a <trans-unit> (1.2) or a <segment> (2.x), so both dialects are read by the same pass and a mixed or unlabelled file still works. Vendor files such as .sdlxliff and .mqxliff are XLIFF underneath, so their translation units parse fine; vendor-private namespaced extensions are not interpreted.

My file uses <group> tags — will that break the conversion?

No. Groups are legal in both versions and CAT tools emit them constantly, so they are walked transparently and the units inside come out like any others. Enable "Include notes, state, file and group" and each unit also reports the slash-joined path of the groups that enclose it.

Why did my {{name}} placeholder survive but the bold tags turn into {1}?

Inline elements are rendered from the best marker the file provides. Angular writes equiv-text="{{name}}" on its interpolation elements, so that exact text is restored. Paired formatting codes like <bpt id="1">/<ept id="1"> usually carry no equiv-text, so they fall back to {id}{1} in that case. Choose Strip if you want the markup gone entirely, or Keep to get the raw inline XML back.

Two units have the same id and one of them vanished. Why?

A JSON object cannot hold two members with the same key, so in the object shapes the last unit with a given id wins. This is legal in XLIFF — one document can contain several <file> elements that each define an id. Switch the output shape to Array of records to keep every unit, and turn on metadata to see which <file> each one came from.

Nesting failed with a "cannot nest key" error — what happened?

Nesting turns a.b into { "a": { "b": … } }, which is impossible if some other unit already claimed a as a plain string. A file containing both home and home.title hits exactly that collision. Either turn nesting off, or pick a separator that your ids don't otherwise use.

Developer & Automation Access

Run it from the terminal

Same engine as this page, headless — via the gizza CLI:

gizza tool xliff-to-json '<xliff version="1.2">
  <file original="app.ts" source-language="en" target-language="de">
    <body>
      <trans-unit id="home.title">
        <source>Welcome</source>
        <target>Willkommen</target>
      </trans-unit>
    </body>
  </file>
</xliff>'

New to the CLI? Get gizza →

Open it by URL

Pre-fill and auto-run this tool with query parameters — the names match the API/CLI:

https://gizza.ai/tools/xliff-to-json/?xliff=%3Cxliff%20version%3D%221.2%22%3E%0A%20%20%3Cfile%20original%3D%22app.ts%22%20source-language%3D%22en%22%20target-language%3D%22de%22%3E%0A%20%20%20%20%3Cbody%3E%0A%20%20%20%20%20%20%3Ctrans-unit%20id%3D%22home.title%22%3E%0A%20%20%20%20%20%20%20%20%3Csource%3EWelcome%3C%2Fsource%3E%0A%20%20%20%20%20%20%20%20%3Ctarget%3EWillkommen%3C%2Ftarget%3E%0A%20%20%20%20%20%20%3C%2Ftrans-unit%3E%0A%20%20%20%20%3C%2Fbody%3E%0A%20%20%3C%2Ffile%3E%0A%3C%2Fxliff%3E&output=pairs&key=id&inline_tags=placeholder&include_empty_targets=true&fallback_to_source=true&nested=true&separator=.&include_metadata=true

Machine-readable descriptor: tool.json — title + parameters JSON Schema for agents.