1.0.12 • Published 7 years ago
xliff-conv v1.0.12
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-convQuick Tour with polymer-starter-kit-i18n
For Browsers
	bower install --save xliff-convImport
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
 
- Customize id and add a new attribute to 
      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
| restype | JSON type | Note | 
|---|---|---|
| x-json-string | string | Omitted | 
| x-json-boolean | boolean | "true" or "false" in value | 
| x-json-number | number | |
| x-json-object | object | Unused for now | 
| x-json-undefined | undefined | Empty string in value | 
Default Mapping of todo.op and XLIFF states
JSON -> XLIFF
| todo.op | XLIFF state | 
|---|---|
| add | new | 
| replace | needs-translation | 
| review | needs-review-translation | 
| N/A | translated | 
XLIFF -> JSON
| XLIFF state | approved | todo.op | Note | 
|---|---|---|---|
| new | no or N/A | add | |
| needs-translation | no or N/A | replace | |
| needs-adaptation | no or N/A | replace | |
| needs-l10n | no or N/A | replace | |
| N/A | no or N/A | replace | |
| needs-review-translation | no or N/A | review | |
| needs-review-adaptation | no or N/A | review | |
| needs-review-l10n | no or N/A | review | |
| translated | yes | N/A | Remove todo | 
| signed-off | yes | N/A | Remove todo | 
| final | yes | N/A | Remove todo | 
| N/A | yes | N/A | Remove todo | 
License
1.0.12
7 years ago
1.0.11
7 years ago
1.0.10
9 years ago
1.0.9
9 years ago
1.0.8
9 years ago
1.0.7
9 years ago
1.0.6
9 years ago
1.0.5
9 years ago
1.0.4
9 years ago
1.0.3
9 years ago
1.0.2
9 years ago
1.0.1
9 years ago
1.0.0
9 years ago
0.0.12
9 years ago
0.0.11
9 years ago
0.0.10
9 years ago
0.0.9
9 years ago
0.0.8
9 years ago
0.0.7
9 years ago
0.0.5
9 years ago
0.0.4
9 years ago
0.0.3
9 years ago
0.0.2
9 years ago
0.0.1
9 years ago