Security News
pnpm 10.0.0 Blocks Lifecycle Scripts by Default
pnpm 10 blocks lifecycle scripts by default to improve security, addressing supply chain attack risks but sparking debate over compatibility and workflow changes.
gulp-i18n-leverage
Advanced tools
Merge changes in default JSON into localized JSON for i18n-behavior
Merge changes in default JSON into localized JSON for i18n-behavior
Project template available at polymer-starter-kit-i18n. On Github Pages (https://t2ym.github.io/polymer-starter-kit-i18n)
npm install --save-dev gulp-i18n-leverage
Build tasks from source to dist:
fs.writeFileSync()
bundle.json
from the bundles objectbundle.*.json
from the bundles objectSample to show default options:
var gulp = require('gulp');
var i18nLeverage = require('gulp-i18n-leverage');
gulp.task('leverage', function () {
return gulp.src([ 'app/**/locales/*.json' ]) // input localized JSON files in source
.pipe(i18nLeverage({
jsonSpace: 2, // default JSON format with 2 spaces
srcPath: 'app', // default path to source root
distPath: 'dist', // default path to dist root to fetch next default JSON files
finalize: false, // empty meta information if true
bundles: {} // default output bundles object is empty
}))
.pipe(gulp.dest('dist')); // path to output next localized JSON files
});
var gulp = require('gulp');
var i18nPreprocess = require('gulp-i18n-preprocess');
// Global object to store localizable attributes repository
var attributesRepository = {};
// Scan HTMLs and construct localizable attributes repository
gulp.task('scan', function () {
return gulp.src([ 'app/elements/**/*.html' ]) // input custom element HTMLs
.pipe(i18nPreprocess({
constructAttributesRepository: true, // construct attributes repository
attributesRepository: attributesRepository, // output object
srcPath: 'app', // path to source root
attributesRepositoryPath:
'bower_components/i18n-behavior/i18n-attr-repo.html', // path to i18n-attr-repo.html
dropHtml: true // drop HTMLs
}))
.pipe(gulp.dest('dist/elements')); // no outputs; dummy output path
});
var gulp = require('gulp');
var merge = require('merge-stream');
var i18nPreprocess = require('gulp-i18n-preprocess');
// Global object to store localizable attributes repository
var attributesRepository; // constructed attributes repository
// Other standard pipes such as crisper / minification / uglification are omitted for explanation
gulp.task('preprocess', function () {
var elements = gulp.src([ 'app/elements/**/*.html' ]) // input custom element HTMLs
.pipe(i18nPreprocess({
replacingText: true, // replace UI texts with {{annotations}}
jsonSpace: 2, // JSON format with 2 spaces
srcPath: 'app', // path to source root
attributesRepository: attributesRepository // input attributes repository
})))
.pipe(gulp.dest('dist/elements')); // output preprocessed HTMLs and default JSON files to dist
var html = gulp.src([ 'app/**/*.html', '!app/{elements,test}/**/*.html' ]) // non-custom-element HTMLs
.pipe(i18nPreprocess({
replacingText: true, // replace UI texts with {{annotations}}
jsonSpace: 2, // JSON format with 2 spaces
srcPath: 'app', // path to source root
force: true, // force processing even without direct i18n-behavior.html import
attributesRepository: attributesRepository // input attributes repository
}))
.pipe(gulp.dest('dist'));
return merge(elements, html)
.pipe($.size({title: 'copy'}));
});
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'
}));
});
var gulp = require('gulp');
var i18nLeverage = require('gulp-i18n-leverage');
var bundles = {};
gulp.task('leverage', function () {
return gulp.src([ 'app/**/locales/*.json' ]) // input localized JSON files in source
.pipe(i18nLeverage({
jsonSpace: 2, // JSON format with 2 spaces
srcPath: 'app', // path to source root
distPath: 'dist', // path to dist root to fetch next default JSON files
finalize: false, // keep meta information
bundles: bundles // output bundles object
}))
.pipe(gulp.dest('dist')); // path to output next localized JSON files
});
var gulp = require('gulp');
var i18nLeverage = require('gulp-i18n-leverage');
var through = require('through2'); // for unbundle
var stripBom = require('strip-bom'); // for unbundle
var bundles = {};
gulp.task('leverage', function () {
return gulp.src([ 'app/**/locales/*.json', '!app/**/locales/bundle.*.json' ]) // exclude bundles
// replace contents with unbundled ones
.pipe(through.obj(function (file, enc, callback) {
var bundle, base = path.basename(file.path, '.json').match(/^(.*)[.]([^.]*)$/);
if (base) {
try {
bundle = JSON.parse(stripBom(fs.readFileSync(path.join(file.base, 'locales', 'bundle.' + base[2] + '.json'), 'utf8')));
if (bundle[base[1]]) {
file.contents = new Buffer(JSONstringify(bundle[base[1]], null, 2));
}
}
catch (ex) {}
}
callback(null, file);
}))
.pipe(i18nLeverage({
jsonSpace: 2, // JSON format with 2 spaces
srcPath: 'app', // path to source root
distPath: 'dist', // path to dist root to fetch next default JSON files
finalize: false, // keep meta information
bundles: bundles // output bundles object
}))
.pipe(gulp.dest('dist')); // path to output next localized JSON files
});
var gulp = require('gulp');
var fs = require('fs');
var JSONstringify = require('json-stringify-safe');
var bundles; // constructed bundles
gulp.task('bundles', function (callback) {
var DEST_DIR = 'dist';
var localesPath = join.path(DEST_DIR, 'locales');
try {
fs.mkdirSync(localesPath);
}
catch (e) {}
for (var lang in bundles) {
bundles[lang].bundle = true;
if (lang) {
fs.writeFileSync(localesPath + '/bundle.' + lang + '.json',
JSONstringify(bundles[lang], null, 2));
}
else {
fs.writeFileSync(DEST_DIR + '/bundle.json',
JSONstringify(bundles[lang], null, 2));
}
}
callback();
});
todo
items in JSON files are removed, the corresponding trans-unit
s are treated as approved="yes"
and state="translated"
. 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();
});
});
Outputs are ready to commit in the repository
var gulp = require('gulp');
var merge = require('merge-stream');
var i18nPreprocess = require('gulp-i18n-preprocess');
// Only applicable to development builds; Skip it in production builds
gulp.task('feedback', function () {
// Copy from dist
var locales = gulp.src([
'dist/**/locales/*.json',
'dist/**/xliff/bundle.*.xlf', // Add this item if xliff import and export are enabled
//'!dist/locales/bundle.*.json' // Remove this item if translation is done in bundles
])
.pipe(gulp.dest('app'));
// Regenerate default JSON files
var elementDefault = gulp.src([ 'app/elements/**/*.html' ])
.pipe(i18nPreprocess({
replacingText: false,
jsonSpace: 2,
srcPath: 'app',
dropHtml: true,
attributesRepository: attributesRepository
}))
.pipe(gulp.dest('app/elements'));
// Regenerate default JSON files for non-custom-element HTMLs, i.e., i18n-dom-bind
var appDefault = gulp.src([ 'app/**/*.html', '!app/{elements,test}/**/*.html' ])
.pipe(i18nPreprocess({
replacingText: false,
jsonSpace: 2,
srcPath: 'app',
force: true,
dropHtml: true,
attributesRepository: attributesRepository
}))
.pipe(gulp.dest('app'));
return merge(locales, elementDefault, appDefault)
.pipe($.size({title: 'feedback'}));
});
polymer-build
library (highly experimental)polymer-build 0.4.0
, polymer-build
library is pre-release and subject to change.Polymer CLI 0.13.0
, the private API userTransformers
is deprecated and no longer available.package.json
and the dependent packages of the following gulpfile.js
npm init # if package.json is missing
npm install --save-dev gulp gulp-debug gulp-grep-contents \
gulp-i18n-add-locales gulp-i18n-leverage gulp-i18n-preprocess \
gulp-if gulp-ignore gulp-match gulp-merge gulp-size gulp-sort gulp-util \
json-stringify-safe strip-bom through2 xliff-conv polymer-build plylog merge-stream
gulp locales --targets="{space separated list of target locales}"
gulp default
- Build with polymer-build
library for gulp
'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);
});
}
i18nLeverage(options)
options
objectpolymer-starter-kit-i18n
npm install -g polymer-cli
npm install -g generator-polymer-init-i18n-starter-kit
mkdir i18n-starter-kit
cd i18n-starter-kit
polymer init i18n-starter-kit
# Add Locales
npm run build locales -- --targets="de es fr ja zh-Hans"
# Build
npm run build
# Translate XLIFF ./xliff/bundle.*.xlf
# Build and Merge Translation
npm run build
# App with Run-time I18N on http://localhost:8080
polymer serve
# App with Build-time I18N on http://localhost:8080
polymer serve build/bundled
lang
attribute of html
element from "en" to other locales such as "ja" <html lang="ja">
polymer-starter-kit-i18n/src/*.html
cd polymer-starter-kit-i18n
npm run build
git diff
FAQs
Merge changes in default JSON into localized JSON for i18n-behavior
We found that gulp-i18n-leverage demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
pnpm 10 blocks lifecycle scripts by default to improve security, addressing supply chain attack risks but sparking debate over compatibility and workflow changes.
Product
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.
Research
Security News
Socket researchers have discovered multiple malicious npm packages targeting Solana private keys, abusing Gmail to exfiltrate the data and drain Solana wallets.