1.0.12 • Published 7 years ago

xliff-conv v1.0.12

Weekly downloads
23
License
BSD-2-Clause
Repository
github
Last release
7 years ago

Build Status Coverage Status npm Bower

xliff-conv

XLIFF to/from JSON converter for Polymer i18n-behavior

Features

  • Update bundle.*.json values with those from XLIFF
  • Generate XLIFF from bundles
  • Map todo operations in bundles onto XLIFF states
  • Update todo operations in bundles with XLIFF states
  • Concise and flexible expressions to customize conversion
  • Handy migration from xliff2bundlejson
  • UMD support

Install

For Node.js

    npm install --save-dev xliff-conv

Quick Tour with polymer-starter-kit-i18n

For Browsers

	bower install --save xliff-conv

Import

On Node.js

	var XliffConv = require('xliff-conv');

On Browsers

	<script src="path/to/bower_components/xliff-conv/xliff-conv.js"></script>

Examples

Import XLIFF task on gulp

Note: This task has to be processed before Leverage task with unbundle to pick up outputs of this task.

Input:

  • Next XLIFF files in source
  • Current bundle JSON files in source (as output templates)

Output:

  • Overwritten bundle JSON files in source
    var gulp = require('gulp');
    var JSONstringify = require('json-stringify-safe');
    var stripBom = require('strip-bom');
    var through = require('through2');
    var XliffConv = require('xliff-conv');

    // Import bundles.{lang}.xlf
    gulp.task('import-xliff', function () {
      var xliffPath = path.join('app', 'xliff');
      var xliffConv = new XliffConv();
      return gulp.src([
          'app/**/xliff/bundle.*.xlf'
        ])
        .pipe(through.obj(function (file, enc, callback) {
          var bundle, bundlePath;
          var base = path.basename(file.path, '.xlf').match(/^(.*)[.]([^.]*)$/);
          var xliff = String(file.contents);
          if (base) {
            try {
              bundlePath = path.join(file.base, 'locales', 'bundle.' + base[2] + '.json');
              bundle = JSON.parse(stripBom(fs.readFileSync(bundlePath, 'utf8')));
              xliffConv.parseXliff(xliff, { bundle: bundle }, function (output) {
                file.contents = new Buffer(JSONstringify(output, null, 2));
                file.path = bundlePath;
                callback(null, file);
              });
            }
            catch (ex) {
              callback(null, file);
            }
          }
          else {
            callback(null, file);
          }
        }))
        .pipe(gulp.dest('app'))
        .pipe($.size({
          title: 'import-xliff'
        }));
    });

Export XLIFF task on gulp

Note: If the todo items in JSON files are removed, the corresponding trans-units are treated as approved="yes" and state="translated".

Input:

  • Next bundles object in gulpfile.js

Output:

  • bundle.{lang}.xlf XLIFF in DEST_DIR/xliff
    var gulp = require('gulp');
    var through = require('through2');
    var XliffConv = require('xliff-conv');

    var bundles; // bundles object generated by preprocess and leverage tasks

    // Generate bundles.{lang}.xlf
    gulp.task('export-xliff', function (callback) {
      var DEST_DIR = 'dist';
      var srcLanguage = 'en';
      var xliffPath = path.join(DEST_DIR, 'xliff');
      var xliffConv = new XliffConv();
      var promises = [];
      try {
        fs.mkdirSync(xliffPath);
      }
      catch (e) {
      }
      for (var lang in bundles) {
        if (lang) {
          (function (destLanguage) {
            promises.push(new Promise(function (resolve, reject) {
              xliffConv.parseJSON(bundles, {
                srcLanguage: srcLanguage,
                destLanguage: destLanguage
              }, function (output) {
                fs.writeFile(path.join(xliffPath, 'bundle.' + destLanguage + '.xlf'), output, resolve);
              });
            }));
          })(lang);
        }
      }
      Promise.all(promises).then(function (outputs) {
        callback();
      });
    });

API

Constructor

var xliffConv = new XliffConv(options)

options object

  • date: Date, default: new Date() - date attribute value for XLIFF
  • xliffStates: Object, default: XliffConv.xliffStates.default - todo.op to XLIFF state mapping table
  • patterns: Object, default: XliffConv.patterns - A set of named regular expressions for pattern matching
  • logger: Function, default: console.log - information logger
  • warnLogger: Function, default: console.warn - warning logger
  • errorLogger: Function, default: console.error - error logger

XliffConv.xliffStates object - predefined mapping tables for options.xliffStates

  XliffConv.xliffStates = {
    // All state-less unapproved strings are regarded as needs-translation
    'default': {
      'add'    : [ 'new' ],
      'replace': [ 'needs-translation', 'needs-adaptation', 'needs-l10n', '' ],
      'review' : [ 'needs-review-translation', 'needs-review-adaptation', 'needs-review-l10n' ],
      'default': [ 'translated', 'signed-off', 'final', '[approved]' ]
    },
    // Aannotations {{name}} and tags <tag-name> are regarded as translated
    'annotationsAsTranslated': {
      'add'    : [ 'new' ],
      'replace': [ 'needs-translation', 'needs-adaptation', 'needs-l10n', '' ],
      'review' : [ 'needs-review-translation', 'needs-review-adaptation', 'needs-review-l10n' ],
      'default': [ 'translated', 'signed-off', 'final', '[approved]', '[source~=annotationsAndTags]' ]
    },
    // Newly added annotations {{name}} and tags <tag-name> are regarded as translated
    'newAnnotationsAsTranslated': {
      'add'    : [ 'new' ],
      'replace': [ 'needs-translation', 'needs-adaptation', 'needs-l10n', '' ],
      'review' : [ 'needs-review-translation', 'needs-review-adaptation', 'needs-review-l10n' ],
      'default': [ 'translated', 'signed-off', 'final', '[approved]', '[state==new&&source~=annotationsAndTags]' ]
    },
    // Newly added annotations {{name}} and tags <tag-name> are regarded as translated only at export
    'newAnnotationsAsTranslatedAtExport': {
      'add'    : [ 'new' ],
      'replace': [ 'needs-translation', 'needs-adaptation', 'needs-l10n', '' ],
      'review' : [ 'needs-review-translation', 'needs-review-adaptation', 'needs-review-l10n' ],
      'default': [ 'translated', 'signed-off', 'final', '[approved]', '[export&&state==new&&source~=annotationsAndTags]' ]
    },
    // Annotations {{name}} and tags <tag-name> are skipped in translation by translate=no
    'annotationsAsNoTranslate': {
      'add'    : [ 'new' ],
      'replace': [ 'needs-translation', 'needs-adaptation', 'needs-l10n', '' ],
      'review' : [ 'needs-review-translation', 'needs-review-adaptation', 'needs-review-l10n' ],
      'default': [ 'translated', 'signed-off', 'final', '[source~=annotationsAndTags&&translate:=no&&state:=final]', '[approved]' ],
    },
    /* === State Mapping Tables for migration from xliff2bundlejson === */
    // All state-less strings are regarded as approved=yes
    'approveAll': {
      'add'    : [ 'new' ],
      'replace': [ 'needs-translation', 'needs-adaptation', 'needs-l10n' ],
      'review' : [ 'needs-review-translation', 'needs-review-adaptation', 'needs-review-l10n' ],
      'default': [ 'translated', 'signed-off', 'final', '' ]
    },
    // State-less translated strings need review
    'reviewTranslated': {
      'add'    : [ 'new' ],
      'replace': [ 'needs-translation', 'needs-adaptation', 'needs-l10n', '[!state&&!approved&&source==target]', '' ],
      'review' : [ 'needs-review-translation', 'needs-review-adaptation', 'needs-review-l10n', '[!state&&!approved&&source!=target]' ],
      'default': [ 'translated', 'signed-off', 'final', '[approved]' ]
    },
    // State-less translated strings are regarded as approved=yes
    'approveTranslated': {
      'add'    : [ 'new' ],
      'replace': [ 'needs-translation', 'needs-adaptation', 'needs-l10n', '[!state&&!approved&&source==target]', '' ],
      'review' : [ 'needs-review-translation', 'needs-review-adaptation', 'needs-review-l10n' ],
      'default': [ 'translated', 'signed-off', 'final', '[!state&&!approved&&source!=target]', '[approved]' ]
    }
    /*
      Expression format:
        [condition1&&condition2&&...&&effect1&&effect2&&...]
          - expression is true when all the conditions are true
          - optional effects are processed if the expression is true

      Operators for conditions:
        parameter
          - true if parameter is non-null
        !parameter
          - true if parameter is undefined, null, or ''
        parameter1==parameter2
          - true if parameter1 is equal to parameter2
        parameter1!=parameter2
          - true if parameter1 is not equal to parameter2
        parameter~=pattern
          - true if parameter matches the regular expression options.patterns.pattern
          - if options.patterns.pattern is undefined, pattern is treated as the matching string
        tag.attribute~=pattern
          - true if attribute value of tag matched the regular expression options.patterns.pattern
          - if options.patterns.pattern is undefined, pattern is treated as the matching string

      Operators for effects:
        tag.attribute:=value
          - assign attribute of tag with the string value
        attribute:=value
          - assign predefined alias attribute with the string value
        tag:=value
          - assign textContent of tag with the string value

      Predefined parameters: Undefined parameters are treated as strings for matching
        state
          - state attribute of target
        id
          - id attribute of trans-unit
        component
          - component name in id
        restype
          - restype attribute of trans-unit. 'x-json-string' for strings
        source
          - text content of source tag
        target
          - text content of target tag
        approved
          - true if approved attribute of trans-unit is 'yes'
        import
          - true on XLIFF import (parseXliff); false on XLIFF export (parseJSON)
        export
          - true on XLIFF export (parseJSON); false on XLIFF import (parseXliff)

      Predefined tags:
        file
          - file tag
        trans-unit
          - trans-unit tag
        source
          - source tag
        target
          - target tag

      Predefined alias attributes:
        translate
          - alias for trans-unit.translate
        approved
          - alias for trans-unit.approved
        state
          - alias for target.state
     */
  };

XliffConv.patterns object - predefined named regular expressions for options.patterns

  XliffConv.patterns = {
    'annotationsAndTags': /^({{[^{} ]*}}|\[\[[^\[\] ]*\]\]|<[-a-zA-Z]{1,}>)$/,
    'annotations': /^({{[^{} ]*}}|\[\[[^\[\] ]*\]\])$/,
    'numbers': /^[0-9.]{1,}$/,
    'tags': /^<[-a-zA-Z]{1,}>$/
  };

xliffConv.parseXliff(xliff, options, callback) method

  • xliff: String, XLIFF as a string
  • options: Object, options.bundle as target bundle JSON object
  • callback: Function, callback(output) with output JSON object

xliffConv.parseJSON(bundles, options, callback) method

  • bundles: Object, bundles object
  • options.srcLanguage: String, default: 'en' - <file source-language> attribute
  • options.destLanguage: String, default: 'fr' - <file target-language> attribute
  • options.xmlSpace: String, default: 'default' - <file xml:space> attribute
  • options.dataType: String, default: 'plaintext' - <file datatype> attribute
  • options.original: String, default: 'messages' - <file original> attribute
  • options.productName: String, default: 'messages' - <file product-name> attribute
  • options.xmlHeader: String, default:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xliff PUBLIC "-//XLIFF//DTD XLIFF//EN" "http://www.oasis-open.org/committees/xliff/documents/xliff.dtd">
  • options.xliffTemplate: String, default:
<xliff version="1.0">
  <file xml:space="[options.xmlSpace]"
      source-language="[options.srcLanguage]"
      target-language="[options.destLanguage]"
      datatype="[options.dataType]"
      original="[options.original]"
      date="[this.date.toISOString().replace(/[.][0-9]*Z$/, 'Z')]"
      product-name="[options.productName]">
    <header>
      <tool tool-id="xliff-conv" tool-name="xliff-conv" tool-version="[toolVersion]"/>
    </header>
    <body>
    </body>
  </file>
</xliff>
  • options.transUnitTemplate: String, default:
      <trans-unit>
        <source></source>
        <target></target>
      </trans-unit>
  • options.addNewAttr: Object, default: undefined
    • Customize id and add a new attribute to <trans-unit> with the original id value
    • labelArrayWithUniqueId is an Object mapping a new attribute value for each id
      xliffConv.parseJSON(bundles, {
        srcLanguage: srcLanguage,
        destLanguage: destLanguage,
        addNewAttr: {
          newAttrName: labelMapWithUniqueId
        }
      }, function (output) {
        fs.writeFile(path.join(xliffPath, 'bundle.' + destLanguage + '.xlf'), output, resolve);
      });
      // example labelMapWithUniqueId Object
      labelMapWithUniqueId =
        {
          // id: attribute value
          "Factory_audit_address": "ckv7ymf07ahqog4lur12bwobg1z3dsxzkqkdwxan",
          "alert_info_when_update_config_preferences": "ybsqyempsolypcf4poq1wdxxl8c04oam03ei27bc",
          "application_title": "rj7rtcdbefchcbrq9itw6sewjifd2v3c5dn99969",
          "back": "48gtruuew3ndd7pnj26lttt0kbgnlv2iyhtti99v",
          "barcode_section": "i2d0t2y11b5zlrlhbn5it8qkbxbp7ub0bdgxy7tr",
          "cancel_title": "bbzgu18z7wl6thj0eh9p83nlcrz4znyfox4khjuq",
          "client_initial_2_letter": "ilttwryn5jccb4wnhfu3nq9z72ds21m2ho7fnsgs"
        }
      <!-- example trans-unit -->
      <!-- without options.addNewAttr -->
      <trans-unit id="Factory_audit_address" approved="yes">
        <source>Address</source>
        <target state="translated">Adresse</target>
      </trans-unit>
      <!-- with options.addNewAttr = { resname: labelMapWithUniqueId } above -->
      <trans-unit id="ckv7ymf07ahqog4lur12bwobg1z3dsxzkqkdwxan" resname="Factory_audit_address" approved="yes">
        <source>Address</source>
        <target state="translated">Adresse</target>
      </trans-unit>
  • callback: Function, callback(output) with output XLIFF as a string

Notes:

  • With options.xliffTemplate, all the attribute values within the template are NOT replaced. It is the caller's responsibility to set appropriate values to the attributes.
  • With options.transUnitTemplate, XliffConv does NOT recognize <note> tags in the template in importing XLIFF and discards the contents.

Custom XLIFF restype attributes

restypeJSON typeNote
x-json-stringstringOmitted
x-json-booleanboolean"true" or "false" in value
x-json-numbernumber
x-json-objectobjectUnused for now
x-json-undefinedundefinedEmpty string in value

Default Mapping of todo.op and XLIFF states

JSON -> XLIFF

todo.opXLIFF state
addnew
replaceneeds-translation
reviewneeds-review-translation
N/Atranslated

XLIFF -> JSON

XLIFF stateapprovedtodo.opNote
newno or N/Aadd
needs-translationno or N/Areplace
needs-adaptationno or N/Areplace
needs-l10nno or N/Areplace
N/Ano or N/Areplace
needs-review-translationno or N/Areview
needs-review-adaptationno or N/Areview
needs-review-l10nno or N/Areview
translatedyesN/ARemove todo
signed-offyesN/ARemove todo
finalyesN/ARemove todo
N/AyesN/ARemove todo

License

BSD-2-Clause

1.0.12

7 years ago

1.0.11

8 years ago

1.0.10

10 years ago

1.0.9

10 years ago

1.0.8

10 years ago

1.0.7

10 years ago

1.0.6

10 years ago

1.0.5

10 years ago

1.0.4

10 years ago

1.0.3

10 years ago

1.0.2

10 years ago

1.0.1

10 years ago

1.0.0

10 years ago

0.0.12

10 years ago

0.0.11

10 years ago

0.0.10

10 years ago

0.0.9

10 years ago

0.0.8

10 years ago

0.0.7

10 years ago

0.0.5

10 years ago

0.0.4

10 years ago

0.0.3

10 years ago

0.0.2

10 years ago

0.0.1

10 years ago