Giter Club home page Giter Club logo

xliff's Introduction

CI travis npm

Download

The source is available for download from GitHub. Alternatively, you can install using npm:

npm install --save xliff

You can then import or require() xliff as normal:

import xliff from 'xliff'
// or
const xliff = require('xliff')

xliff.xliff2js(xml, (err, res) => {})

Or you can directly import or require() its functions:

import xliff2js from 'xliff/xliff2js'
// or
const xliff2js = require('xliff/cjs/xliff2js')

Usage

XLIFF 2.0
const xliff = `<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="en-US" trgLang="de-CH">
  <file id="namespace1">
    <unit id="key1">
      <segment>
        <source>Hello</source>
        <target>Hallo</target>
      </segment>
    </unit>
    <unit id="key2">
      <segment>
        <source>An application to manipulate and process XLIFF documents</source>
        <target>Eine Applikation um XLIFF Dokumente zu manipulieren und verarbeiten</target>
      </segment>
    </unit>
    <unit id="key.nested">
      <segment>
        <source>XLIFF Data Manager</source>
        <target>XLIFF Daten Manager</target>
      </segment>
    </unit>
    <group id="group">
      <unit id="groupUnit">
        <segment>
          <source>Group</source>
          <target>Gruppe</target>
        </segment>
      </unit>
    </group>
  </file>
</xliff>`

const js = {
  "resources": {
    "namespace1": {
      "key1": {
        "source": "Hello",
        "target": "Hallo"
      },
      "key2": {
        "source": "An application to manipulate and process XLIFF documents",
        "target": "Eine Applikation um XLIFF Dokumente zu manipulieren und verarbeiten"
      },
      "key.nested": {
        "source": "XLIFF Data Manager",
        "target": "XLIFF Daten Manager"
      },
      "group": {
        "groupUnits":{
          "groupUnit": {
            "source": "Group",
            "target": "Gruppe"
          }
        }
      }
    }
  },
  "sourceLanguage": "en-US",
  "targetLanguage": "de-CH"
}

import xliff2js from 'xliff/xliff2js'
xliff2js(xliff, (err, res) => {
  // res is like js
})
// or without callback
const res = await xliff2js(xliff)
// res is like js

import js2xliff from 'xliff/js2xliff'
js2xliff(js, (err, res) => {
  // res is like xliff
})
// or without callback
const res = await js2xliff(js)
// res is like xliff

import targetOfjs from 'xliff/targetOfjs'
const res = targetOfjs(js)
// res is:
// {
//   "key1": "Hallo",
//   "key2": "Eine Applikation um XLIFF Dokumente zu manipulieren und verarbeiten",
//   "key.nested": "XLIFF Daten Manager",
//   "group": {
//     "groupUnit": "Gruppe"
//   }
// }

import sourceOfjs from 'xliff/sourceOfjs'
const res = sourceOfjs(js)
// res is:
// {
//   "key1": "Hello",
//   "key2": "An application to manipulate and process XLIFF documents",
//   "key.nested": "XLIFF Data Manager",
//   "group": {
//     "groupUnit": "Group"
//   }
// }

import createjs from 'xliff/createjs'
createjs(
  js.sourceLanguage,
  js.targetLanguage,
  {
    "key1": "Hello",
    "key2": "An application to manipulate and process XLIFF documents",
    "key.nested": "XLIFF Data Manager"
  },
  {
    "key1": "Hallo",
    "key2": "Eine Applikation um XLIFF Dokumente zu manipulieren und verarbeiten",
    "key.nested": "XLIFF Daten Manager"
  },
  'namespace1',
  (err, res) => {
  // res is like js
  }
  // you can specify notes with this param (ntKeys)
  // ,{
  //    "key1": "custom note for key1",
  //    "key.nested": "another note for nested key"
  // }
)
// or without callback
//const res = await createjs(...


import createxliff from 'xliff/createxliff'
createxliff(
  js.sourceLanguage,
  js.targetLanguage,
  {
    "key1": "Hello",
    "key2": "An application to manipulate and process XLIFF documents",
    "key.nested": "XLIFF Data Manager"
  },
  {
    "key1": "Hallo",
    "key2": "Eine Applikation um XLIFF Dokumente zu manipulieren und verarbeiten",
    "key.nested": "XLIFF Daten Manager"
  },
  'namespace1',
  (err, res) => {
  // res is like xliff  
  }  
  // you can specify notes with this param (ntKeys)
  // ,{
  //    "key1": "custom note for key1",
  //    "key.nested": "another note for nested key"
  // }
)
// or without callback
//const res = await createxliff(...
XLIFF 1.2
  const xliff = `<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd" version="1.2" srcLang="en-US" trgLang="de-CH">
    <file original="namespace1">
      <body>
        <trans-unit id="key1">
          <source>Hello</source>
          <target>Hallo</target>
        </trans-unit>
        <trans-unit id="key2">
          <source>An application to manipulate and process XLIFF documents</source>
          <target>Eine Applikation um XLIFF Dokumente zu manipulieren und verarbeiten</target>
        </trans-unit>
        <trans-unit id="key.nested">
          <source>XLIFF Data Manager</source>
          <target>XLIFF Daten Manager</target>
        </trans-unit>
        <group id="group">
          <trans-unit id="groupUnit">
            <source>Group</source>
            <target>Gruppe</target>
          </trans-unit>
        </group>
      </body>
    </file>
  </xliff>`

  const js = {
    "resources": {
      "namespace1": {
        "key1": {
          "source": "Hello",
          "target": "Hallo"
        },
        "key2": {
          "source": "An application to manipulate and process XLIFF documents",
          "target": "Eine Applikation um XLIFF Dokumente zu manipulieren und verarbeiten"
        },
        "key.nested": {
          "source": "XLIFF Data Manager",
          "target": "XLIFF Daten Manager"
        },
        "group": {
          "groupUnits":{
            "groupUnit": {
              "source": "Group",
              "target": "Gruppe"
            }
          }
        }
      }
    },
    "sourceLanguage": "en-US",
    "targetLanguage": "de-CH"
  }

  import xliff12ToJs from 'xliff/xliff12ToJs'
  xliff12ToJs(xliff, (err, res) => {
    // res is like js
  })
  // or without callback
  //const res = await xliff12ToJs(...

  import jsToXliff12 from 'xliff/jsToXliff12'
  jsToXliff12(js, (err, res) => {
    // res is like xliff
  })
  // or without callback
  //const res = await jsToXliff12(...

  import createxliff12 from 'xliff/createxliff12'
  createxliff12(
    js.sourceLanguage,
    js.targetLanguage,
    {
      "key1": "Hello",
      "key2": "An application to manipulate and process XLIFF documents",
      "key.nested": "XLIFF Data Manager"
    },
    {
      "key1": "Hallo",
      "key2": "Eine Applikation um XLIFF Dokumente zu manipulieren und verarbeiten",
      "key.nested": "XLIFF Daten Manager"
    },
    'namespace1',
    (err, res) => {
    // res is like xliff
    }
    // you can specify notes with this param (ntKeys)
    // ,{
    //    "key1": "custom note for key1",
    //    "key.nested": "another note for nested key"
    // }
  )
  // or without callback
  //const res = await createxliff12(...

Using Inline Elements

XLIFF 1.2 and 2.x support the use of a set of XML elements within source and target declarations. In general these "inline" tags exist to specify special elements within translation strings. For example, in XLIFF 1.2 the <ph>..</ph> element is used to define a "placeholder" such as for a variable value that is substituted at runtime, e.g.:

  • String: "Hello there, {fullName}"
  • XLIFF 1.2: <source>Hello there, <ph>{fullName}</ph></source>

In the standard case described previously, the source and target values are string instances. A source or target value can also be defined as an Array instance.

// Simple value:
"source": "Hello there"
// Value with multiple child elements:
"source": ["Hello ", "there"]

(Note that in this example there's no benefit from splitting the string into two strings wrapped in an array.)

When the source and target values are Array instances, the elements of the Array contain strings (representing plain text) or objects (representing XLIFF inline elements). The structure for those objects is described next.

Inline element object structure

An object representing an inline element has the following structure:

{
  [<Element Type>]: {
    "id": "<Value>",
    "contents": "<Element Contents>",
    "<Other Property 1>": "<Other Property 1 Value>",
    ...
    "<Other Property N>": "<Other Property N Value>"
  }
}

The parts are:

  • <Element Type>: A string (used as a property name) indicating the element type.
  • id property: The value of the XLIFF element's id attribute
  • contents property: The contents of the XLIFF element, if supported. This value can be a string or array and is treated like the source/target values.
  • All other properties: Map directly to attributes of the XLIFF element tag

Here's a real-world example:

{
  "Span": {
    "id": "dataType",
    "contents": "{dataType}",
    "ctype": "x-python-brace-param"
  }
}

This maps to the following XLIFF inline element structure:

<ph id="dataType" ctype="x-python-brace-param">{dataType}</ph>

Full inline element example

The following code shows a full object structure for one type of XLIFF inline element (Generic span), and the corresponding XLIFF 1.2 and XLIFF 2.0 that it produces. For other examples of different element types, see the inline element test fixtures

Strings
key1:
source: "Hello {name}"
target: "Hallo {name}"

key2:
source: "An application to manipulate and process {doctype} documents."
target: "Eine Applikation um {doctype} Dokumente zu manipulieren und verarbeiten"
JSON
{
  "resources": {
    "namespace1": {
      "key1": {
        "source": [
          "Hello ",
          {
            "GenericSpan": {
              "id": "name",
              "ctype": "x-python-brace-param",
              "contents": "{name}"
            }
          }
        ],
        "target": [
          "Hallo ",
          {
            "GenericSpan": {
              "id": "name",
              "ctype": "x-python-brace-param",
              "contents": "{name}"
            }
          }
        ]
      },
      "key2": {
        "source": [
          "An application to manipulate and process ",
          {
            "GenericSpan": {
              "id": "doctype",
              "ctype": "x-python-brace-param",
              "contents": "{doctype}"
            }
          },
          " documents"
        ],
        "target": [
          "Eine Applikation um ",
          {
            "GenericSpan": {
              "id": "doctype",
              "ctype": "x-python-brace-param",
              "contents": "{doctype}"
            }
          },
          " Dokumente zu manipulieren und verarbeiten"
        ]
      }
    }
  },
  "sourceLanguage": "en-US",
  "targetLanguage": "de-CH"
}
XLIFF 1.2
<xliff xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd" xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
  <file original="namespace1" datatype="plaintext" source-language="en-US" target-language="de-CH">
    <body>
      <trans-unit id="key1">
        <source>Hello 
          <g id="name" ctype="x-python-brace-param">{name}</g>
        </source>
        <target>Hallo 
          <g id="name" ctype="x-python-brace-param">{name}</g>
        </target>
      </trans-unit>
      <trans-unit id="key2">
        <source>An application to manipulate and process 
          <g id="doctype" ctype="x-python-brace-param">{doctype}</g> documents
        </source>
        <target>Eine Applikation um 
          <g id="doctype" ctype="x-python-brace-param">{doctype}</g> Dokumente zu manipulieren und verarbeiten
        </target>
      </trans-unit>
    </body>
  </file>
</xliff>
XLIFF 2.0
<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="en-US" trgLang="de-CH">
  <file id="namespace1">
    <unit id="key1">
      <segment>
        <source>Hello 
          <pc id="name" ctype="x-python-brace-param">{name}</pc>
        </source>
        <target>Hallo 
          <pc id="name" ctype="x-python-brace-param">{name}</pc>
        </target>
      </segment>
    </unit>
    <unit id="key2">
      <segment>
        <source>An application to manipulate and process 
          <pc id="doctype" ctype="x-python-brace-param">{doctype}</pc> documents
        </source>
        <target>Eine Applikation um 
          <pc id="doctype" ctype="x-python-brace-param">{doctype}</pc> Dokumente zu manipulieren und verarbeiten
        </target>
      </segment>
    </unit>
  </file>
</xliff>

Supported inline element types

XLIFF 1.2 and XLIFF 2.x define different sets of inline elements. However, the underlying semantics of many of the elements are the same and they can be mapped to each other. The supported element types are:

Element type Use Case Representation (1.2) Representation (2.0)
Generic
Standalone Standalone code <x/> <ph/>
GenericSpan Well-formed spanning code <g></g> <pc></pc>
GenericSpanStart Start marker of spanning code <bx/> <sc/>
GenericSpanEnd End marker of spanning code <ex/> <ec/>
Native code (same as generic for 2.0)
Span Well-formed spanning code <ph></ph> <pc></pc>
SpanStart Start marker of spanning code <bpt></bpt> <sc/>
SpanEnd End marker of spanning code <ept></ept> <ec/>

Note that there are additional inline elements defined in the XLIFF specifications that are not supported by this library, and are not listed here.

These types are defined as constants in inline-elements/ElementTypes.js

Although both XLIFF versions define Generic element types, only XLIFF 1.2 defines Native element types. This library uses a "superset" approach to allow for compatibility between its data model and the different XLIFF versions. For example, an object representation of an XLIFF value that includes a Span (Native spanning code) is converted to a <pc>..</pc> element in XLIFF 2.0, even though XLIFF 2.0 doesn't technically support Native elements.

The rules for mapping between element types are as follows:

JS -> XLIFF 1.2 Elements are written as their corresponding types

JS -> XLIFF 2.0 Elements are written as their corresponding types. Native/generic types are mapped to the same XLIFF element type

XLIFF 1.2 -> JS Elements are read as their corresponding types

XLIFF 2.0 -> JS Elements are read as their corresponding (non-generic) types

As a result, you should be able to have "roundtrip" support for converting between JavaScript and XLIFF. The only exception is if an XLIFF 1.2 value is converted to JavaScript, then to XLIFF 2, then back to JavaScript, then to XLIFF 1.2. In that case the Native inline elements will be converted to XLIFF 1.2 Generic elements.

Helpers for creating inline element objects

If you need to create your own inline element objects to construct a source or target array, you can use the makeInlineElement() function.

For example, suppose you have this string:

"Hello {name}"

You want to use it as a source value containing two parts -- the string "Hello " and a Generic Span element containing the placeholder variable "{name}", so that the end result (in XLIFF 1.2) should look like this:

<source>Hello 
  <g id="name" ctype="x-python-brace-param">{name}</g>
</source>

You can create this structure using the makeInlineElement() function with the following code:

// import or require makeInlineElements and ElementTypes
// signature: makeInlineElement(type, id, attributes, contents)
var attributesObj = { ctype: 'x-python-brace-param' }
var inlineElementObj = makeInlineElement(ElementTypes.GenericSpan, 'name', attributesObj, '{name}')

var source = [ 'Hello ', inlineElementObj ]

Additional attributes example

It is possible to pass additionalAttributes to your js file. These will be added to the <trans-unit> element in xliff:

const js = {
  "resources": {
    "namespace1": {
      "key1": {
        "source": "Hello",
        "target": "Hallo",
        "additionalAttributes": {
          "translate": "no",
          "approved": "yes"
        }
      }
    }
  }
}

Of course, this also works the other way around:

const xliff = `<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="en-US" trgLang="de-CH">
  <file id="namespace1">
    <unit id="key1" translate="no" approved="yes">
      <segment>
        <source>Hello</source>
        <target>Hallo</target>
      </segment>
    </unit>
  </file>
</xliff>`

xliff's People

Contributors

adrai avatar adriat360 avatar alexandernoskov avatar armonge avatar birkestroem avatar borodiliz avatar cietho avatar dhoko avatar gjchoi avatar h-h-h-h avatar jamuhl avatar k4zh avatar marmarosi avatar mxs17 avatar noelmugnier avatar probertson avatar robilol avatar samataro avatar servel333 avatar spencerfdavis avatar villelahdenvuo avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

xliff's Issues

TypeError: Cannot read properties of undefined (reading 'id')

๐Ÿ› Bug Report

The following error occurs on seamingly valid XLIFF files (using https://dev.maxprograms.com/Validation/ to validate).

To Reproduce

Xliff contents:

<?xml version="1.0" encoding="utf-8" ?>
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:jms="urn:jms:translation" version="1.2">
    <file date="2020-02-11T14:49:15Z" source-language="fr" target-language="es" datatype="plaintext" original="not.available">
        <header>
            <tool tool-id="JMSTranslationBundle" tool-name="JMSTranslationBundle" tool-version="1.1.0-DEV"/>
            <note>The source node in most cases contains the sample message as written by the developer. If it looks like a dot-delimitted string such as "form.label.firstname", then the developer has not provided a default message.</note>
        </header>
        <body>
            <trans-unit id="a1" resname="item1">
                <source>item1</source>
                <target>Transaltion 1</target>
            </trans-unit>
            <trans-unit id="a2" resname="item2">
                <source>item2</source>
                <target>Translation 2</target>
            </trans-unit>
        </body>
    </file>
</xliff>

import xliff from 'xliff'

const fileEs = fs.readFileSync(`${ROOT_DIR}/src/models/refs/test.xlf`, 'utf-8')

;(async () => {
    try {
        const result = await xliff.xliff2js(fileEs)
        console.log(result)
            
    } catch (e: any) {
        // general error here
        console.log(e)
    }
})()

Error:

ts-node src/models/refs/xliff.ts
TypeError: Cannot read properties of undefined (reading 'id')

Expected behavior

A JSON valid output (or a readable error).

Your Environment

  • Node v14/16, MacOS
  • Typescript

Add seamless conversion between XLIFF and i18next JSON

๐Ÿš€ Feature Proposal

I would like to convert back and forth between i18next JSON and XLIFF, including the appropriate representation of placeholders, plurals, etc.

Currently, you convert the XLIFF back and forth to an XLIFF-like JSON structure:

{
  "resources": {
    "namespace1": {
      "key1": {
        "source": [
          "Hello ",
          {
            "GenericSpan": {
              "id": "name",
              "ctype": "x-python-brace-param",
              "contents": "{name}"
            }
          }
        ],
        "target": [
          "Hallo ",
          {
            "GenericSpan": {
              "id": "name",
              "ctype": "x-python-brace-param",
              "contents": "{name}"
            }
          }
        ]
      },
      // ...
    }
  },
  "sourceLanguage": "en-US",
  "targetLanguage": "de-CH"
}

However, the format I want to use is the i18next JSON format, like this:

{
  "namespace1.key1": "Hello {{name}}"
}

I want the placeholder to be automatically converted to/from the XLIFF GenericSpan.

Motivation

I want to send XLIFF files off to my translation service, and use i18next to power my app.

Example

Something along the lines of xliff/createxliff and xliff/createjs, but which interact using the i18next format instead of the current XLIFF-like JSON format.

Got a TypeError after converting xliff12 to json with empty groups

๐Ÿ› Bug Report

Got a TypeError after converting xliff12 to json with empty groups

To Reproduce

  1. ะกonvert xliff12 with empty group to json
  2. Got TypeError:
TypeError: Cannot read property 'filter' of undefined
    at /.../node_modules/xliff/cjs/xliff12ToJs.js:67:35
    at Array.reduce (<anonymous>)
    at createUnits (/...node_modules/xliff/cjs/xliff12ToJs.js:65:24)
    at /.../node_modules/xliff/cjs/xliff12ToJs.js:57:28
    at Array.reduce (<anonymous>)
    at xliff12ToJsClb (/.../node_modules/xliff/cjs/xliff12ToJs.js:48:41)
    at /.../node_modules/xliff/cjs/xliff12ToJs.js:121:14
    at new Promise (<anonymous>)
    at Object.xliff12ToJs (/.../node_modules/xliff/cjs/xliff12ToJs.js:120:12)
    at readXlf (/.../src/locale/scripts/failure_example.js:15:24)

Artifact:

<xliff xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd" xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
   <file original="namespace1" datatype="plaintext" source-language="en-US" target-language="ru-RU">
        <body>
            <trans-unit id="standalone">
                <source>Source</source>
                <target>Target</target>
            </trans-unit>
            <group id="example-empty-group"></group>
        </body>
    </file>
</xliff>

Code to reproduce:

const xliff = require('xliff');

async function readXlf() {
    const rawXliff = `<xliff xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd" xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
        <file original="namespace1" datatype="plaintext" source-language="en-US" target-language="ru-RU">
            <body>
                <trans-unit id="standalone">
                    <source>Source</source>
                    <target>Target</target>
                </trans-unit>
                <group id="example-empty-group"></group>
            </body>
            </file>
        </xliff>`;
    return await xliff.xliff12ToJs(rawXliff);
}


readXlf();

Expected behavior

Get correct error or ignore empty groups

Your Environment

  • runtime version: i.e. node v10.19.0
  • xliff version: 6.0.1
  • os: Linux Manjaro

xliff.jsToXliff12 unexpected parse result with [object Object]

๐Ÿ› Bug Report

xliff.jsToXliff12 incorrect parsing with source child > [object Object]

To Reproduce

steps

  1. convert xliff to js with xliff.xliff12ToJs command ( this is ok )
  2. convert js (result step1) to xliff again with xliff.jsToXliff12 ( this is unexpected )

original value

      <trans-unit id="ngb.timepicker.AM" datatype="html">
        <source><x id="INTERPOLATION" equiv-text="Step: number;"/></source>
        <context-group purpose="location">
          <context context-type="sourcefile">node_modules/@ng-bootstrap/ng-bootstrap/src/timepicker/timepicker.ts</context>
          <context context-type="linenumber">116,123</context>
        </context-group>
      </trans-unit>

xliff.xliff12ToJs

"ngb.timepicker.AM": {
      "source": {
        "Standalone": {
          "id": "INTERPOLATION",
          "equiv-text": "Step: number;"
        }
      },
      "additionalAttributes": {
        "datatype": "html"
      }
    }

xliff.jsToXliff12

      <trans-unit id="ngb.timepicker.AM" datatype="html">
        <source>[object Object]</source>
      </trans-unit>

Expected behavior

I expect the result as source with <x ..> tag not [object Object]

      <trans-unit id="ngb.timepicker.AM" datatype="html">
        <source><x id="INTERPOLATION" equiv-text="Step: number;"/></source>
      </trans-unit>

Your Environment

  • runtime version: i.e. node v15
  • "xliff": "^5.5.3"
  • os: Mac

targetOfjs does not convert groups

๐Ÿ› Bug Report

Function targetOfjs does not convert groups.

To Reproduce

Using your sample Xliff 2.0 data:

const file = path.resolve(__dirname, './sample.xlf');
const xliff = fs.readFileSync(file).toString();
const js = await xliff2js(xliff);
const target = targetOfjs(js);
console.log(target);

The output is:

{
  key1: 'Hallo',
  key2: 'Eine Applikation um XLIFF Dokumente zu manipulieren und verarbeiten',
  'key.nested': 'XLIFF Daten Manager',
  group: undefined
}

Expected behavior

I would expect the following output:

{
  key1: 'Hallo',
  key2: 'Eine Applikation um XLIFF Dokumente zu manipulieren und verarbeiten',
  'key.nested': 'XLIFF Daten Manager',
  group: {
    groupUnit: 'Gruppe'
  }
}

Your Environment

  • runtime version: node v16.13.0
  • module version: >=5.7.2
  • os: Windows

package.json: missing git url

The package.json descriptor doesn't have the repository git url.

  "repository": {
    "type": "git",
    "url": "git+https://github.com/locize/xliff.git"
  }

Place <note> inside <notes> section of the <unit>

Hello!

I am trying to generate xliffs for Memsource. Everything works well except for <note> blocks. If I supply something like:

{
  "resources": {
    "units": {
      "unit-id": {
        "source": "Some source text",
        "target": "",
        "note": "Some note"
      }
    }
  },
  "sourceLanguage": "en",
  "targetLanguage": "ru"
}

into js2xliff, I will get:

<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="en" trgLang="ru">
  <file id="units">
    <unit id="unit-id">
      <segment>
        <source>Some source text</source>
        <target></target>
        <note>Some note</note>
      </segment>
    </unit>
  </file>
</xliff>

while Memsource (and, I believe, the xliff spec too) expects <notes>...</notes> section on the unit level:

<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="en" trgLang="ru">
  <file id="units">
    <unit id="unit-id">
      <notes>
        <note>Some note</note>
      </notes>
      <segment>
        <source>Some source text</source>
        <target></target>
      </segment>
    </unit>
  </file>
</xliff>

Is it possible to generate these <notes>...</notes> sections with the current version of xliff library? If not, can we implement this feature?

'State' attribute missing from converted xliff files

๐Ÿ› Bug Report

After converting from xliff to json and back, the xliff file is missing certain attributes, such as state on <segment> nodes. state in particular is important for other translation software when working with the xliff files.

To Reproduce

We have a complex multi-site app using Angular, so we are using this library to merge and edit the xliff files that Angular generates initially. An example use case would be to read an xliff file into JS, remove certain <unit> nodes, then write the contents to a different xliff file, but a barebones case is:

var fs = require("fs");
var util = require("util");
var xliff = require("./modules/xliff");
var readFile = util.promisify(fs.readFile);

processMessageFile("./src/locale/base-messages/messages.es-MX.xlf");

async function processMessageFile(filePath) {

  //Read xlf file and convert to JS
  var fileContent = await readFile(filePath, "utf8");
  var jsContent = await xliff.xliff2js(fileContent);

  //Normally we'd manipulate jsContent here, but it doesn't matter

  //Convert back to xlf and replace file contents
  var newXlf = await xliff.js2xliff(jsContent);
  await writeFile(filePath, newXlf);
}

async function writeFile(path, content) {
  fs.writeFile(path, content, "utf8", function (err) {
    if (err) {
      return console.log(err);
    }
  });
}

Below is an example message file with one translated message and one non-translated message.

Before:

<?xml version="1.0" encoding="UTF-8"?>
<xliff version="2.0" xmlns="urn:oasis:names:tc:xliff:document:2.0" srcLang="en-US" trgLang="es-MX">
  <file id="ngi18n" original="ng.template">
    <unit id="core.pageTitles.account">
      <notes>
        <note category="location">src/app/account/account-routing.module.ts:13</note>
      </notes>
      <segment state="translated">
        <source>My Account</source>
        <target>Mi cuenta</target>
      </segment>
    </unit>
    <unit id="core.pageTitles.account.reservations">
      <notes>
        <note category="location">src/app/account/account-routing.module.ts:19</note>
      </notes>
      <segment state="initial">
        <source>Reservations</source>
        <target>Reservations</target>
      </segment>
    </unit>
  </file>
</xliff>

After:

<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="en-US" trgLang="es-MX">
  <file id="ngi18n">
    <unit id="core.pageTitles.account">
      <notes>
        <note>src/app/account/account-routing.module.ts:13</note>
      </notes>
      <segment>
        <source>My Account</source>
        <target>Mi cuenta</target>
      </segment>
    </unit>
    <unit id="core.pageTitles.account.reservations">
      <notes>
        <note>src/app/account/account-routing.module.ts:19</note>
      </notes>
      <segment>
        <source>Reservations</source>
        <target>Reservations</target>
      </segment>
    </unit>
  </file>
</xliff>

You can see that afterwards there is no way to tell which ones are already translated.

Expected behavior

The only thing I really care about at the moment is that <segment> nodes should maintain their state attribute. This is important so that translators working with these files (using a translation tool like Poedit, BabelEdit, etc) know which messages have already been translated and which need attention. We don't want to redo our whole manual translation process each time the file is regenerated or merged to.

Other than that, I'm assuming that, ideally, no attributes or other nodes would be removed. But I don't think this is actually causing me any issues the moment. For example, I noticed that:

  • <?xml version="1.0" encoding="UTF-8"?> is removed.
  • The original attribute on <file> is removed.
  • The category attribute on <note> is removed. I think this may be useful for certain tools that can link translations to their location in the Angular code, but I'm not sure.

Your Environment

  • node: v16.13.1
  • xliff: tested with v5.7.2 and 6.0.0
  • os: Windows 10

Could not find a declaration file for module 'xliff'.

๐Ÿ‘‰ Please follow one of these issue templates ๐Ÿ‘ˆ

You have already researched for similar issues? Yes

It's not uncommon that somebody already opened an issue or in best case it's already fixed but not merged. That's the reason why you should search at first before submitting a new one.
Search also on StackOverflow or similar.

Are you sure this is an issue with locize?

Yes
import xliff from 'xliff'
import xliff2js from 'xliff/xliff2js'

I can't do anyone of these, can anyone help me on this? is this still working? Appreciate your help
Issues should only be posted in this repository after you have been able to reproduce them and confirm that they are a bug or incorrect/missing information in the docs.

Maybe your issue is more related to i18next.com, if so please open an issue here.

Weird XLIFF parsing issue

I'm trying to parse an XLIFF 1.2 file using the xliff12ToJs function, but for some reason it cuts out the last line of a multiline html translation if the ending target tag is on the same line. Here is a minimal repro: https://runkit.com/embed/6ahusa56f2r5

If I add a linebreak before the </target> it parses correctly.

Adding support for metadata module

๐Ÿš€ Feature Proposal

Adding support for metadata module

Motivation

Metadata comes handy for saving customized data/attributes that doesn't belong to other tags

Example

 <mda:metadata>
   <mda:metaGroup category="document_xml_attribute">
     <mda:meta type="version">3</mda:meta>
     <mda:meta type="phase">draft</mda:meta>
   </mda:metaGroup>
 </mda:metadata>

Xliff 1.2 doesn't use srcLang and trgLang

According to Xliff 1.2 specs the attributes specifying the source and target language are on each file element and are called source-language and target-language. The change to the attributes srcLang and trgLang was made with Xliff 2.0.

Now if you try to parse an Xliff 1.2 like for example exported by Xcode you will have undefined values for source and target language.

Incorrect handling of <ignorable>...</ignorable> sections placed by CAT tools after segments

Hello!

The problem arises when there are trailing whitespaces in the source document segments. When importing, SDL Studio separates them from the text and moves into separate structures. E.g., if the source segment is "Hello ", the exported translated xliff will look like:

   <unit id="key1">
      <segment>
        <source>Hello</source>
        <target>Hi</target>
      </segment>
      <ignorable>
        <source xml:space="preserve"> </source>
        <target xml:space="preserve"> </target>
      </ignorable>
    </unit>

createUnit function of xliff2js saves the last child of the element. When it is <ignorable> rather than <segment>, the resulting source and target are empty as they are copied from <ignorable>. I believe this can be fixed by adding a check for element name.

function createUnit (unit, initValues) {
  // source, target, note
  return unit.elements.reduce((unit, segment) => {
  if (segment.name === 'segment') {   // <------- check if it is a segment and not an ignore section
      segment.elements.forEach((element) => {
        switch (element.name) {
          case 'source':
          case 'target':
          case 'note':
            unit[element.name] = extractValue(element.elements, ElementTypes2)
            break
        }
      })
    }

    return unit
  }, JSON.parse(JSON.stringify(initValues)))
}

xliff 1.2 generated by WPML (Wordpress plugin) not parsed

XLIFF like the below, generated by WPML is not parsed correctly. The system expects

<segment>

elements in

<unit>

elements...

And the XLIFF 1.2 from WPML looks different:

<?xml version="1.0" encoding="utf-8" standalone="no"?><xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
<file original="1842-43fa4a29d62b66b56d7b5c5e055bbad5" source-language="nl" target-language="en" datatype="plaintext">
<header><reference><external-file href="http://www.domain.nl/somepage/"/></reference></header>
<body>
  <trans-unit id="key2">
    <source>hallo</source>
    <target>hallo</target>
  </trans-unit>
</body>
</file>
</xliff>

XLIFF to JSON doesn't ignore comments

Hello,

I have some comments in my XLIFF files and i got an error with the function cause of them.

node_modules/xliff/xliff12ToJs.js:49

const key = transUnit.attributes.id;
TypeError: Cannot read property 'id' of undefined

Indeed the function try to get the ID on a comment type element :

console.log(transUnit)

...
{ type: 'element',
  name: 'trans-unit',
  attributes: { id: 'seo_anchor_city' },
  elements:
   [ { type: 'element', name: 'source', elements: [Array] },
     { type: 'element', name: 'target', elements: [Array] } ] }

{ type: 'comment', comment: '            comment ' }

I think you should probably add a check to get the ID attribute only on element type and ignore comment type

The `jsToXliff12` lib creates xliff with wrong format in case of falsy `source`

๐Ÿ› Bug Report

The jsToXliff12 lib creates xliff with wrong format in case of falsy source

To Reproduce

Below is an example, what is important is to provide a resource with a falsy source, e.g. an empty string

const resources = {
    namespace: {
        field: { source: "", target: "translation" }
    }
};
const xliffDescriptor = {
    resources,
    sourceLanguage: "en",
    targetLanguage: "fr"
};
const xliffFile = await xliff.jsToXliff12(xliffDescriptor);

xliffFile contains something like below, with no source tag, which is not valid against the specification, image

  <file original="namespace" datatype="plaintext" source-language="en" target-language="fr">
    <body>
      <trans-unit id="field">
        <target>translation</target>
      </trans-unit>
    </body>
  </file>

Expected behavior

The expected result should include something like below

  <file original="namespace" datatype="plaintext" source-language="en" target-language="fr">
    <body>
      <trans-unit id="field">
        <source></source>
        <target>translation</target>
      </trans-unit>
    </body>
  </file>

Your Environment

  • runtime version: node v20
  • os: Mac, Windows, Linux
  • any other relevant information

Support for `additionalAttributes` on target

๐Ÿš€ Feature Proposal

There's a way to use define inline elements for target via the JS API, but I don't find a way to define additional attributes on such.

Motivation

For instance, defining the translation state on a target, http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html#target

Example

AFAIS, source and target could be either a string or an array (inline elements). Allowing a structured object could define attributes e.g.

target: {
  content: "Hallo",
  additionalAttributes: {
    state: "new",
  }
}

xliff 1.2 does not support inline elements like <g>, </x>, </bx>.

Supposedly for following Xliff

<?xml version="1.0" encoding="UTF-8"?><xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:okp="okapi-framework:xliff-extensions" xmlns:its="http://www.w3.org/2005/11/its" xmlns:itsxlf="http://www.w3.org/ns/its-xliff/" its:version="2.0" version="1.2">
<file original="AppBody-Sample-English.docx" datatype="x-text/plain" source-language="en">
<body>
<trans-unit xml:space="preserve" id="3" resname="tu3-p2">
<source xml:lang="en">This is a sample text. The <g id="1">description</g> must disclose the invention in a manner sufficiently clear and complete for it to be carried out by a person skilled in the art.</source>
<target xml:lang="fr">This is a sample text. The <g id="1">description</g> must disclose the invention in a manner sufficiently clear and complete for it to be carried out by a person skilled in the art.</target>
</trans-unit>
</body>
</file>
</xliff>

the parsed source i get is

This is a sample text. The must disclose the invention in a manner sufficiently clear and complete for it to be carried out by a person skilled in the art.

If you notice text is missing description.

Tested on xliff 1.2 only not on xliff 2.

Convert from json to XLIFF with CDATA

๐Ÿš€ Feature Proposal

Allow to produce XLIFF 2 or XLIFF 1.1 with all translations inside a CDATA.

Motivation

We have a bunch of react-i18next json strings with embed HTML that we want to convert to xliff js2Xliff function escapes the html tags.

We would like to generate an XLIFF file without all those tags escaped but wrapped inside a CDATA

Example

{
  "string_to_translate" : "<strong>Careful!</strong> You are going to delete the following task.",
}

Desired output

<xliff xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd" xmlns="urn:oasis:names:tc:xliff:document:1.2" version="1.2">
  <file original="situm" datatype="plaintext" source-language="en" target-language="en">
    <body>
      <trans-unit id="1" resname="string_to_translate">
        <source><![CDATA[strong>Careful!</strong> You are going to delete the following task.]]></source>
        <target><![CDATA[strong>Careful!</strong> You are going to delete the following task.]]></target>

Add a LICENSE file

๐Ÿš€ Feature Proposal

Hi, thanks for a parser!
Would you accept a PR with a default MIT License file, so the GitHub repository would correctly recognize it?

Motivation

Making sure that dependency licenses are from the accepted range is a requirement in many companies.

Example

Here is how GitHub would present it after the License would be added:
image

xliff12ToJs() looses spaces between inline elements

๐Ÿ› Bug Report

I have a translation unit with just a space between two inline elements. When parsing this with xliff12ToJs the space is lost and I end up with an array of only the two inline elements.

To Reproduce

This is the snipped which I am using (created using Angulars extract-i18n.

<trans-unit id="3036695099328175425" datatype="html">
  <source><x id="PH" equiv-text="..."/> <x id="PH_1" equiv-text="..."/></source>
  <target state="translated"><x id="PH" equiv-text="..."/> <x id="PH_1" equiv-text="..."/></target>
</trans-unit>

Expected behavior

The resulting array should contain 3 elements, the two inline elements and the space-string in between.

Convert JSON to XLIFF without translating

Hello-
I didn't see a way to simple take a JSON file and covert it to XLIFF. Is there a way?

It looks like the tool is for converting and translating at the same time.

I have many JSON files, I want to convert them to XLIFF.

I will then send the XLIFF files to my localization team.

Thanks

Placeholder elements not inline in XLIFF v1.2

๐Ÿ› Bug Report

When using Standalone inline elements to produce placeholder tags, like <x></x>, these placeholders are moved to next line in both source and target elements.

To Reproduce

{
  source: [
    "Request ",
    {
      Standalone: {
        id: "0",
        contents: "0",
      },
    },
    " is no longer active.",
  ],
  target: [
    "Request ",
    {
      Standalone: {
        id: "0",
        contents: "0",
      },
    },
    " is no longer active.",
  ],
}

gets exported into:

<trans-unit id="error516">
  <source>Request 
    <x id="0">0</x> is no longer active.
  </source>
  <target>Request 
    <x id="0">0</x> is no longer active.
  </target>
</trans-unit>

In translation editor, eg POEDIT, this looks like this:
image

Expected behavior

<trans-unit id="error516">
  <source>Request <x id="0">0</x> is no longer active.
  </source>
  <target>Request <x id="0">0</x> is no longer active.
  </target>
</trans-unit>

xliff to JSON functions should return

A number of function already support returning their results instead of passing it to callbacks. However, the xliff to json functions - xliff12ToJs and xliffToJs - do not. I cannot see any async operations and would suggest to add the same pattern for these.

  if (cb) cb(null, result);
  return result;

XLIFF to JS conversion error { Error: Text data outside of root node.

Hi @adrai ,

I am able to create successfully XLIFF files using xliff/js2xliff and it worked as expected.
now i wanted to convert same XLIFF to js using xliff/xliff2js . but i am getting below error

yarn import

code
`xliff2js('./1537854025565.xml', (err, res) => {
if (err) {
console.log(err)
}
console.log(res)
})

`
Please note the XLIFF generated is valid .
Should i read XLIFF first to string and then pass it on xliff2js fuction ?

CDATA Sections

๐Ÿ› Bug Report

Thanks for this project!

I'm using XILF files generated with Symfony translator, lot of them, includes CDATA Sections as specified here. Is CDATA supported?

Not working (empty string given):

      <trans-unit id="ALVJf0m" resname="general_validateSubmit">
        <source>general_validateSubmit</source>
        <target><![CDATA[Validate & Submit]]></target>
      </trans-unit>

Working:

      <trans-unit id="ALVJf0m" resname="general_validateSubmit">
        <source>general_validateSubmit</source>
        <target>Validate &amp; Submit</target>
      </trans-unit>

To Reproduce

Based on this

  getTranslation(langPath: string) {
    return this.http
      .get(`${TranslocoHttpLoader.getAssetsPath()}/i18n/myfile.${langPath}.xlf`, {
        responseType: 'text' as 'text',
      })
      .pipe(mergeMap((translation) => xliff(translation).then((toJSON) => toTranslationFormat(toJSON))));
  }
}

function toTranslationFormat(json): Translation {
  const obj = json.resources['file.ext'];
  return Object.keys(obj).reduce((acc, key) => {
    const { source } = obj[key];
    acc[source] = obj[key].target; //EMPTY STRING GIVEN HERE FOR CDATA TRANSLATIONS
    return acc;
  }, {});
}

Expected behavior

Non empty strings for CDATA translations

Your Environment

xliff 5.1.0 and:

Angular CLI: 10.0.2
Node: 12.16.3
OS: darwin x64

Angular: 10.0.3
... animations, common, compiler, compiler-cli, core, elements
... forms, language-service, platform-browser
... platform-browser-dynamic, router
Ivy Workspace: <error>

Package                           Version
-----------------------------------------------------------
@angular-devkit/architect         0.901.11
@angular-devkit/build-angular     0.1000.2
@angular-devkit/build-optimizer   0.901.11
@angular-devkit/build-webpack     0.901.11
@angular-devkit/core              9.1.11
@angular-devkit/schematics        10.0.2
@angular/cli                      10.0.2
@angular/google-maps              10.0.1
@ngtools/webpack                  9.1.11
@schematics/angular               10.0.2
@schematics/update                0.1000.2
rxjs                              6.6.0
typescript                        3.9.6
webpack                           4.42.0

Thanks in advance!

When there are XML comments in the XLIFF document, xliff2js throws an error

๐Ÿ› Bug Report

Comments cause an error in createUnits while iterating over elements.

To Reproduce

Reproduction: https://runkit.com/nullvoxpopuli/xliff-library-can-t-handle-comments

const xliff = require('xliff');
let xml = `<xliff xmlns="urn:oasis:names:tc:xliff:document:2.0" version="2.0" srcLang="en-us" trgLang="en-us">
  <file id="file/path/wooo">
    <!-- comment a -->
    <unit id="foo"><segment><target>woosh</target></segment></unit>
  </file>
</xliff>`;

xliff.xliff2js(xml, (err, res) => {})

image

Expected behavior

No error

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.