Skip to content

Instantly share code, notes, and snippets.

@t2ym
Last active September 1, 2016 00:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save t2ym/c37990e422d4a19774ba1d749510c1b8 to your computer and use it in GitHub Desktop.
Save t2ym/c37990e422d4a19774ba1d749510c1b8 to your computer and use it in GitHub Desktop.
Integrate I18N transformers with Polymer CLI build process and gulp tasks with polymer-build library
'use strict';
var gulp = require('gulp');
var gutil = require('gulp-util');
var debug = require('gulp-debug');
var gulpif = require('gulp-if');
var gulpignore = require('gulp-ignore');
var gulpmatch = require('gulp-match');
var sort = require('gulp-sort');
var grepContents = require('gulp-grep-contents');
var size = require('gulp-size');
var merge = require('gulp-merge');
var through = require('through2');
var path = require('path');
var stripBom = require('strip-bom');
var JSONstringify = require('json-stringify-safe');
var i18nPreprocess = require('gulp-i18n-preprocess');
var i18nLeverage = require('gulp-i18n-leverage');
var XliffConv = require('xliff-conv');
var i18nAddLocales = require('gulp-i18n-add-locales');
const logging = require('plylog');
const mergeStream = require('merge-stream');
const isPolymerCLI = global._babelPolyfill;
// Global object to store localizable attributes repository
var attributesRepository = {};
// Bundles object
var prevBundles = {};
var bundles = {};
var title = 'I18N transform';
var tmpDir = '.tmp';
var xliffOptions = {};
// Scan HTMLs and construct localizable attributes repository
var scan = gulpif('*.html', i18nPreprocess({
constructAttributesRepository: true, // construct attributes repository
attributesRepository: attributesRepository, // output object
srcPath: '.', // path to source root
attributesRepositoryPath:
'bower_components/i18n-behavior/i18n-attr-repo.html', // path to i18n-attr-repo.html
dropHtml: false // do not drop HTMLs
}));
var basenameSort = sort({
comparator: function(file1, file2) {
var base1 = path.basename(file1.path).replace(/^bundle[.]/, ' bundle.');
var base2 = path.basename(file2.path).replace(/^bundle[.]/, ' bundle.');
return base1.localeCompare(base2);
}
});
var dropDefaultJSON = gulpignore([ 'src/**/*.json', '!**/locales/*.json' ]);
var preprocess = gulpif('*.html', i18nPreprocess({
replacingText: true, // replace UI texts with {{annotations}}
jsonSpace: 2, // JSON format with 2 spaces
srcPath: '.', // path to source root
attributesRepository: attributesRepository // input attributes repository
}));
var tmpJSON = gulpif([ 'src/**/*.json', '!src/**/locales/*' ], gulp.dest(tmpDir));
var unbundleFiles = [];
var importXliff = through.obj(function (file, enc, callback) {
// bundle files must come earlier
unbundleFiles.push(file);
callback();
}, function (callback) {
var match;
var file;
var bundleFileMap = {};
var xliffConv = new XliffConv(xliffOptions);
while (unbundleFiles.length > 0) {
file = unbundleFiles.shift();
if (path.basename(file.path).match(/^bundle[.]json$/)) {
prevBundles[''] = JSON.parse(stripBom(String(file.contents)));
bundleFileMap[''] = file;
}
else if (match = path.basename(file.path).match(/^bundle[.]([^.\/]*)[.]json$/)) {
prevBundles[match[1]] = JSON.parse(stripBom(String(file.contents)));
bundleFileMap[match[1]] = file;
}
else if (match = path.basename(file.path).match(/^bundle[.]([^.\/]*)[.]xlf$/)) {
xliffConv.parseXliff(String(file.contents), { bundle: prevBundles[match[1]] }, function (output) {
if (bundleFileMap[match[1]]) {
bundleFileMap[match[1]].contents = new Buffer(JSONstringify(output, null, 2));
}
});
}
else if (gulpmatch(file, '**/locales/*.json') &&
(match = path.basename(file.path, '.json').match(/^([^.]*)[.]([^.]*)/))) {
if (prevBundles[match[2]] && prevBundles[match[2]][match[1]]) {
file.contents = new Buffer(JSONstringify(prevBundles[match[2]][match[1]], null, 2));
}
}
this.push(file);
}
callback();
});
var leverage = gulpif([ 'src/**/locales/*.json', '!**/locales/bundle.*.json' ], i18nLeverage({
jsonSpace: 2, // JSON format with 2 spaces
srcPath: '', // path to source root
distPath: '/' + tmpDir, // path to dist root to fetch next default JSON files
bundles: bundles // output bundles object
}));
var bundleFiles = [];
var exportXliff = through.obj(function (file, enc, callback) {
bundleFiles.push(file);
callback();
}, function (callback) {
var file;
var cwd = bundleFiles[0].cwd;
var base = bundleFiles[0].base;
var xliffConv = new XliffConv(xliffOptions);
var srcLanguage = 'en';
var promises = [];
var self = this;
var lang;
while (bundleFiles.length > 0) {
file = bundleFiles.shift();
if (!gulpmatch(file, [ '**/bundle.json', '**/locales/bundle.*.json', '**/xliff/bundle.*.xlf' ])) {
this.push(file);
}
}
for (lang in bundles) {
bundles[lang].bundle = true;
this.push(new gutil.File({
cwd: cwd,
base: base,
path: lang ? path.join(cwd, 'locales', 'bundle.' + lang + '.json')
: path.join(cwd, 'bundle.json'),
contents: new Buffer(JSONstringify(bundles[lang], null, 2))
}));
}
for (lang in bundles) {
if (lang) {
(function (destLanguage) {
promises.push(new Promise(function (resolve, reject) {
xliffConv.parseJSON(bundles, {
srcLanguage: srcLanguage,
destLanguage: destLanguage
}, function (output) {
self.push(new gutil.File({
cwd: cwd,
base: base,
path: path.join(cwd, 'xliff', 'bundle.' + destLanguage + '.xlf'),
contents: new Buffer(output)
}));
resolve();
});
}));
})(lang);
}
}
Promise.all(promises).then(function (outputs) {
callback();
});
});
var feedback = gulpif([ '**/bundle.json', '**/locales/*.json', '**/src/**/*.json', '**/xliff/bundle.*.xlf' ], gulp.dest('.'));
var config = {
// list of target locales to add
locales: gutil.env.targets ? gutil.env.targets.split(/ /) : []
}
// Gulp task to add locales to I18N-ready elements and pages
// Usage: gulp locales --targets="{space separated list of target locales}"
gulp.task('locales', function() {
var elements = gulp.src([ 'src/**/*.html' ], { base: '.' })
.pipe(grepContents(/i18n-behavior.html/))
.pipe(grepContents(/<dom-module /));
var pages = gulp.src([ 'index.html' ], { base: '.' })
.pipe(grepContents(/is=['"]i18n-dom-bind['"]/));
return merge(elements, pages)
.pipe(i18nAddLocales(config.locales))
.pipe(gulp.dest('.'))
.pipe(debug({ title: 'Add locales:'}))
});
if (isPolymerCLI) {
module.exports = {
transformers: [
scan,
basenameSort,
dropDefaultJSON,
preprocess,
tmpJSON,
importXliff,
leverage,
exportXliff,
feedback,
debug({ title: title }),
size({ title: title })
]
};
}
else {
const polymer = require('polymer-build');
//const optimize = require('polymer-build/lib/optimize').optimize;
//const precache = require('polymer-build/lib/sw-precache');
const PolymerProject = polymer.PolymerProject;
const fork = polymer.forkStream;
const polymerConfig = require('./polymer.json');
//logging.setVerbose();
let project = new PolymerProject({
root: process.cwd(),
entrypoint: polymerConfig.entrypoint,
shell: polymerConfig.shell
});
gulp.task('default', () => {
// process source files in the project
let sources = project.sources()
.pipe(project.splitHtml())
// I18N processes
.pipe(scan)
.pipe(basenameSort)
.pipe(dropDefaultJSON)
.pipe(preprocess)
.pipe(tmpJSON)
.pipe(importXliff)
.pipe(leverage)
.pipe(exportXliff)
.pipe(feedback)
.pipe(debug({ title: title }))
.pipe(size({ title: title }))
// add compilers or optimizers here!
.pipe(project.rejoinHtml());
// process dependencies
let dependencies = project.dependencies()
.pipe(project.splitHtml())
// add compilers or optimizers here!
.pipe(project.rejoinHtml());
// merge the source and dependencies streams to we can analyze the project
let allFiles = mergeStream(sources, dependencies)
.pipe(project.analyze);
// fork the stream in case downstream transformers mutate the files
// this fork will vulcanize the project
let bundled = fork(allFiles)
.pipe(project.bundle)
// write to the bundled folder
.pipe(gulp.dest('build/bundled'));
let unbundled = fork(allFiles)
// write to the unbundled folder
.pipe(gulp.dest('build/unbundled'));
return mergeStream(bundled, unbundled);
});
}
@lordoftheflies
Copy link

lordoftheflies commented Aug 10, 2016

I trying to use your gist in my polymer-cli created application.
What I had done:

  1. Create a project with polymer init application.
  2. Add t2ym/i18n-behavior to my application through bower.
  3. Link in the index.html.
  4. Add BehaviorsStore.I18nBehavior to localizable elements and the shell component.
  5. Create a gulpfile.js same as this gist in the project base directory, install required modules through npm.

And when I enter the command: gulp --dev, I got the following output:

lordoftheflies@leviathan:~/Documents/mlm-portal$ gulp --dev
(node:5107) fs: re-evaluating native module sources is not supported. If you are using the graceful-fs module, please update it to a more recent version.
debug:   sourceGlobs: 
    /home/lordoftheflies/Documents/mlm-portal/index.html
    /home/lordoftheflies/Documents/mlm-portal/bower_components/mlm-elements/mlm-app.html
    /home/lordoftheflies/Documents/mlm-portal/src/**/*
    /home/lordoftheflies/Documents/mlm-portal/bower.json
debug:   root: /home/lordoftheflies/Documents/mlm-portal
debug:   shell: /home/lordoftheflies/Documents/mlm-portal/bower_components/mlm-elements/mlm-app.html
debug:   entrypoint: /home/lordoftheflies/Documents/mlm-portal/index.html
debug:   fragments: 
debug:   sources: /home/lordoftheflies/Documents/mlm-portal/src/**/*,/home/lordoftheflies/Documents/mlm-portal/bower.json
debug:   includeDependencies: 
[12:39:29] Using gulpfile ~/Documents/mlm-portal/gulpfile.js
[12:39:29] Starting 'default'...
debug:   sourceGlobs: 
    /home/lordoftheflies/Documents/mlm-portal/index.html
    /home/lordoftheflies/Documents/mlm-portal/bower_components/mlm-elements/mlm-app.html
    /home/lordoftheflies/Documents/mlm-portal/src/**/*
    /home/lordoftheflies/Documents/mlm-portal/bower.json
[12:39:29] 'default' errored after 9.05 ms
[12:39:29] TypeError: Cannot read property 'on' of undefined
    at PassThrough.Readable.pipe (/home/lordoftheflies/Documents/mlm-portal/node_modules/readable-stream/lib/_stream_readable.js:497:7)
    at Gulp.<anonymous> (/home/lordoftheflies/Documents/mlm-portal/gulpfile.js:221:59)
    at module.exports (/home/lordoftheflies/Documents/mlm-portal/node_modules/orchestrator/lib/runTask.js:34:7)
    at Gulp.Orchestrator._runTask (/home/lordoftheflies/Documents/mlm-portal/node_modules/orchestrator/index.js:273:3)
    at Gulp.Orchestrator._runStep (/home/lordoftheflies/Documents/mlm-portal/node_modules/orchestrator/index.js:214:10)
    at Gulp.Orchestrator.start (/home/lordoftheflies/Documents/mlm-portal/node_modules/orchestrator/index.js:134:8)
    at /usr/local/lib/node_modules/gulp/bin/gulp.js:129:20
    at _combinedTickCallback (internal/process/next_tick.js:67:7)
    at process._tickCallback (internal/process/next_tick.js:98:9)
    at Function.Module.runMain (module.js:577:11)
[12:39:29] I18N transform bower.json
[12:39:29] I18N transform index.html
[12:39:29] I18N transform index.html_script_0.js
[12:39:29] I18N transform bower_components/mlm-elements/mlm-app.html
[12:39:29] I18N transform bower_components/mlm-elements/mlm-app.html_script_0.js
[12:39:29] I18N transform src/my-icons.html
[12:39:29] I18N transform 6 items
[12:39:29] I18N transform all files 41.01 kB

Dear @t2ym could you help me to solve this issue?

@t2ym
Copy link
Author

t2ym commented Sep 1, 2016

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment