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
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