{"id":17965348,"url":"https://github.com/t2ym/gulp-i18n-leverage","last_synced_at":"2026-05-13T20:33:22.018Z","repository":{"id":57257833,"uuid":"50919040","full_name":"t2ym/gulp-i18n-leverage","owner":"t2ym","description":"Merge changes in default JSON into localized JSON for Polymer I18N with i18n-behavior","archived":false,"fork":false,"pushed_at":"2017-07-03T06:30:22.000Z","size":75,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-08-16T23:53:00.910Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/t2ym.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-02-02T12:28:19.000Z","updated_at":"2016-02-04T09:59:15.000Z","dependencies_parsed_at":"2022-08-25T19:12:36.909Z","dependency_job_id":null,"html_url":"https://github.com/t2ym/gulp-i18n-leverage","commit_stats":null,"previous_names":[],"tags_count":41,"template":false,"template_full_name":null,"purl":"pkg:github/t2ym/gulp-i18n-leverage","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t2ym%2Fgulp-i18n-leverage","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t2ym%2Fgulp-i18n-leverage/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t2ym%2Fgulp-i18n-leverage/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t2ym%2Fgulp-i18n-leverage/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/t2ym","download_url":"https://codeload.github.com/t2ym/gulp-i18n-leverage/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/t2ym%2Fgulp-i18n-leverage/sbom","scorecard":{"id":864740,"data":{"date":"2025-08-11","repo":{"name":"github.com/t2ym/gulp-i18n-leverage","commit":"4b3c29ff68c87a3423a6a96c7c167b630dc68c01"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":9,"reason":"license file detected","details":["Info: project has a license file: LICENSE.md:0","Warn: project license file does not contain an FSF or OSI license."],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}}]},"last_synced_at":"2025-08-24T02:22:18.577Z","repository_id":57257833,"created_at":"2025-08-24T02:22:18.577Z","updated_at":"2025-08-24T02:22:18.577Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32999349,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-13T13:14:54.681Z","status":"ssl_error","status_checked_at":"2026-05-13T13:14:51.610Z","response_time":115,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-29T12:41:48.489Z","updated_at":"2026-05-13T20:33:22.001Z","avatar_url":"https://github.com/t2ym.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![Build Status](https://travis-ci.org/t2ym/gulp-i18n-leverage.svg?branch=master)](https://travis-ci.org/t2ym/gulp-i18n-leverage)\n[![Coverage Status](https://coveralls.io/repos/github/t2ym/gulp-i18n-leverage/badge.svg?branch=master)](https://coveralls.io/github/t2ym/gulp-i18n-leverage?branch=master)\n[![npm](https://img.shields.io/npm/v/gulp-i18n-leverage.svg)](https://www.npmjs.com/package/gulp-i18n-leverage)\n\n# gulp-i18n-leverage\n\nMerge changes in default JSON into localized JSON for [i18n-behavior](https://github.com/t2ym/i18n-behavior)\n\nProject template available at [polymer-starter-kit-i18n](https://github.com/t2ym/polymer-starter-kit-i18n). On Github Pages (https://t2ym.github.io/polymer-starter-kit-i18n)\n\n## Features\n\n- Merge changes in next default JSON from current default JSON into next localized JSON with the help of [rfc6902](https://github.com/chbrown/rfc6902)\n- Put meta information on merged changes in meta property in JSON for ease of translation\n- Output bundles object to merge all the UI texts into JSON per locale\n- Export the merged localized JSON as files\n\n## Install\n\n```\n    npm install --save-dev gulp-i18n-leverage\n```\n\n[Quick Tour](#quick-tour)\n\n## Workflow\n\nBuild tasks from source to dist:\n\n### 1. Scan task with [gulp-i18n-preprocess](https://github.com/t2ym/gulp-i18n-preprocess)\n\n### 2. Preprocess task with [gulp-i18n-preprocess](https://github.com/t2ym/gulp-i18n-preprocess)\n\n### 3. (Optional) Import XLIFF task with [xliff-conv](https://github.com/t2ym/xliff-conv)\n\n### 4. Leverage task \n\n  - Update localized JSON files by merging differences in default JSON from the previous build\n  - Put them in dist\n  - Merge all the UI texts into bundles object\n\n### 5. Bundles task with `fs.writeFileSync()`\n\n  - Generate default bundled JSON file `bundle.json` from the bundles object\n  - Generate per-locale bundled JSON files `bundle.*.json` from the bundles object\n  - Put them in dist\n\n### 6. (Optional) Export XLIFF task with [xliff-conv](https://github.com/t2ym/xliff-conv)\n\n### 7. Feedback task with [gulp-i18n-preprocess](https://github.com/t2ym/gulp-i18n-preprocess)\n\n  - Update default and localized JSON files in source to commit them later by a developer or a build system\n\n## Usage\n\n### Default options\n\nSample to show default options:\n\n```javascript\n    var gulp = require('gulp');\n    var i18nLeverage = require('gulp-i18n-leverage');\n\n    gulp.task('leverage', function () {\n      return gulp.src([ 'app/**/locales/*.json' ]) // input localized JSON files in source\n        .pipe(i18nLeverage({\n          jsonSpace: 2, // default JSON format with 2 spaces\n          srcPath: 'app', // default path to source root\n          distPath: 'dist', // default path to dist root to fetch next default JSON files\n          finalize: false, // empty meta information if true\n          bundles: {} // default output bundles object is empty\n        }))\n        .pipe(gulp.dest('dist')); // path to output next localized JSON files\n    });\n```\n\n### Scan task\n\n#### Note: Target HTMLs must import [i18n-behavior.html](https://github.com/t2ym/i18n-behavior) directly.\n\n#### Input: \n  - Custom element HTMLs in source\n\n#### Output: \n  - attributesRepository object in gulpfile.js\n\n```javascript\n    var gulp = require('gulp');\n    var i18nPreprocess = require('gulp-i18n-preprocess');\n\n    // Global object to store localizable attributes repository\n    var attributesRepository = {};\n\n    // Scan HTMLs and construct localizable attributes repository\n    gulp.task('scan', function () {\n      return gulp.src([ 'app/elements/**/*.html' ]) // input custom element HTMLs\n        .pipe(i18nPreprocess({\n          constructAttributesRepository: true, // construct attributes repository\n          attributesRepository: attributesRepository, // output object\n          srcPath: 'app', // path to source root\n          attributesRepositoryPath: \n            'bower_components/i18n-behavior/i18n-attr-repo.html', // path to i18n-attr-repo.html\n          dropHtml: true // drop HTMLs\n        })) \n        .pipe(gulp.dest('dist/elements')); // no outputs; dummy output path\n    });\n```\n\n### Preprocess task\n\n#### Note: Target custom element HTMLs must import [i18n-behavior.html](https://github.com/t2ym/i18n-behavior) directly.\n\n#### Input: \n  - Custom element HTMLs\n  - Non-custom-element HTMLs in source\n\n#### Output: \n  - Preprocessed HTMLs and default JSON files in dist\n\n```javascript\n    var gulp = require('gulp');\n    var merge = require('merge-stream');\n    var i18nPreprocess = require('gulp-i18n-preprocess');\n\n    // Global object to store localizable attributes repository\n    var attributesRepository; // constructed attributes repository\n\n    // Other standard pipes such as crisper / minification / uglification are omitted for explanation\n    gulp.task('preprocess', function () {\n      var elements = gulp.src([ 'app/elements/**/*.html' ]) // input custom element HTMLs\n        .pipe(i18nPreprocess({\n          replacingText: true, // replace UI texts with {{annotations}}\n          jsonSpace: 2, // JSON format with 2 spaces\n          srcPath: 'app', // path to source root\n          attributesRepository: attributesRepository // input attributes repository\n        })))\n        .pipe(gulp.dest('dist/elements')); // output preprocessed HTMLs and default JSON files to dist\n\n      var html = gulp.src([ 'app/**/*.html', '!app/{elements,test}/**/*.html' ]) // non-custom-element HTMLs\n        .pipe(i18nPreprocess({\n          replacingText: true, // replace UI texts with {{annotations}}\n          jsonSpace: 2, // JSON format with 2 spaces\n          srcPath: 'app', // path to source root\n          force: true, // force processing even without direct i18n-behavior.html import\n          attributesRepository: attributesRepository // input attributes repository\n         }))\n        .pipe(gulp.dest('dist'));\n\n      return merge(elements, html)\n        .pipe($.size({title: 'copy'}));\n    });\n```\n\n### Import XLIFF task with [xliff-conv](https://github.com/t2ym/xliff-conv)\n\n#### Note: This task has to be processed before [Leverage task with unbundle](#leverage-task-with-unbundle) to pick up outputs of this task.\n\n#### Input:\n  - Next XLIFF files in source\n  - Current bundle JSON files in source (as output templates)\n\n#### Output:\n  - Overwritten bundle JSON files in source\n\n```javascript\n    var gulp = require('gulp');\n    var JSONstringify = require('json-stringify-safe');\n    var stripBom = require('strip-bom');\n    var through = require('through2');\n    var XliffConv = require('xliff-conv');\n\n    // Import bundles.{lang}.xlf\n    gulp.task('import-xliff', function () {\n      var xliffPath = path.join('app', 'xliff');\n      var xliffConv = new XliffConv();\n      return gulp.src([\n          'app/**/xliff/bundle.*.xlf'\n        ])\n        .pipe(through.obj(function (file, enc, callback) {\n          var bundle, bundlePath;\n          var base = path.basename(file.path, '.xlf').match(/^(.*)[.]([^.]*)$/);\n          var xliff = String(file.contents);\n          if (base) {\n            try {\n              bundlePath = path.join(file.base, 'locales', 'bundle.' + base[2] + '.json');\n              bundle = JSON.parse(stripBom(fs.readFileSync(bundlePath, 'utf8')));\n              xliffConv.parseXliff(xliff, { bundle: bundle }, function (output) {\n                file.contents = new Buffer(JSONstringify(output, null, 2));\n                file.path = bundlePath;\n                callback(null, file);\n              });\n            }\n            catch (ex) {\n              callback(null, file);\n            }\n          }\n          else {\n            callback(null, file);\n          }\n        }))\n        .pipe(gulp.dest('app'))\n        .pipe($.size({\n          title: 'import-xliff'\n        }));\n    });\n```\n\n### Leverage task\n\n#### Input:\n  - Current localized JSON files in source\n  - Current default JSON files in source\n  - Next default JSON files in dist\n\n#### Output:\n  - Next localized JSON files in dist\n  - Bundles object in gulpfile.js\n\n```javascript\n    var gulp = require('gulp');\n    var i18nLeverage = require('gulp-i18n-leverage');\n\n    var bundles = {};\n\n    gulp.task('leverage', function () {\n      return gulp.src([ 'app/**/locales/*.json' ]) // input localized JSON files in source\n        .pipe(i18nLeverage({\n          jsonSpace: 2, // JSON format with 2 spaces\n          srcPath: 'app', // path to source root\n          distPath: 'dist', // path to dist root to fetch next default JSON files\n          finalize: false, // keep meta information\n          bundles: bundles // output bundles object\n        }))\n        .pipe(gulp.dest('dist')); // path to output next localized JSON files\n    });\n```\n\n### Leverage task with unbundle\n\n#### Note: If translation is done in locales/bundle.*.json in source for all the elements, contents of per-element json are discarded and replaced with those in the bundle. The per-element json files will be translated in [feedback task](#feedback-task).\n\n#### Input:\n  - Current localized bundle JSON files in source\n  - Current localized JSON files in source (contents are discarded)\n  - Current default JSON files in source\n  - Next default JSON files in dist\n\n#### Output:\n  - Next localized JSON files in dist\n  - Bundles object in gulpfile.js\n\n```javascript\n    var gulp = require('gulp');\n    var i18nLeverage = require('gulp-i18n-leverage');\n    var through = require('through2'); // for unbundle\n    var stripBom = require('strip-bom'); // for unbundle\n\n    var bundles = {};\n\n    gulp.task('leverage', function () {\n      return gulp.src([ 'app/**/locales/*.json', '!app/**/locales/bundle.*.json' ]) // exclude bundles\n        // replace contents with unbundled ones\n        .pipe(through.obj(function (file, enc, callback) {\n          var bundle, base = path.basename(file.path, '.json').match(/^(.*)[.]([^.]*)$/);\n          if (base) {\n            try {\n              bundle = JSON.parse(stripBom(fs.readFileSync(path.join(file.base, 'locales', 'bundle.' + base[2] + '.json'), 'utf8')));\n              if (bundle[base[1]]) {\n                file.contents = new Buffer(JSONstringify(bundle[base[1]], null, 2));\n              }\n            }\n            catch (ex) {}\n          }\n          callback(null, file);\n        }))\n        .pipe(i18nLeverage({\n          jsonSpace: 2, // JSON format with 2 spaces\n          srcPath: 'app', // path to source root\n          distPath: 'dist', // path to dist root to fetch next default JSON files\n          finalize: false, // keep meta information\n          bundles: bundles // output bundles object\n        }))\n        .pipe(gulp.dest('dist')); // path to output next localized JSON files\n    });\n```\n\n### Bundles task\n\n#### Input: \n  - Bundles object in gulpfile.js\n\n#### Output: \n  - Bundles JSON files in dist\n\n```javascript\n    var gulp = require('gulp');\n    var fs = require('fs');\n    var JSONstringify = require('json-stringify-safe');\n\n    var bundles; // constructed bundles\n\n    gulp.task('bundles', function (callback) {\n      var DEST_DIR = 'dist';\n      var localesPath = join.path(DEST_DIR, 'locales');\n\n      try {\n        fs.mkdirSync(localesPath);\n      }\n      catch (e) {}\n      for (var lang in bundles) {\n        bundles[lang].bundle = true;\n        if (lang) {\n          fs.writeFileSync(localesPath + '/bundle.' + lang + '.json', \n                            JSONstringify(bundles[lang], null, 2));\n        }\n        else {\n          fs.writeFileSync(DEST_DIR + '/bundle.json', \n                            JSONstringify(bundles[lang], null, 2));\n        }\n      }\n      callback();\n    });\n```\n\n### Export XLIFF task with [xliff-conv](https://github.com/t2ym/xliff-conv)\n\n#### Note: If the `todo` items in JSON files are removed, the corresponding `trans-unit`s are treated as `approved=\"yes\"` and `state=\"translated\"`.\n\n#### Input:\n  - Next bundles object in gulpfile.js\n\n#### Output:\n  - bundle.{lang}.xlf XLIFF in DEST_DIR/xliff\n\n```javascript\n    var gulp = require('gulp');\n    var through = require('through2');\n    var XliffConv = require('xliff-conv');\n\n    var bundles; // bundles object generated by preprocess and leverage tasks\n\n    // Generate bundles.{lang}.xlf\n    gulp.task('export-xliff', function (callback) {\n      var DEST_DIR = 'dist';\n      var srcLanguage = 'en';\n      var xliffPath = path.join(DEST_DIR, 'xliff');\n      var xliffConv = new XliffConv();\n      var promises = [];\n      try {\n        fs.mkdirSync(xliffPath);\n      }\n      catch (e) {\n      }\n      for (var lang in bundles) {\n        if (lang) {\n          (function (destLanguage) {\n            promises.push(new Promise(function (resolve, reject) {\n              xliffConv.parseJSON(bundles, {\n                srcLanguage: srcLanguage,\n                destLanguage: destLanguage\n              }, function (output) {\n                fs.writeFile(path.join(xliffPath, 'bundle.' + destLanguage + '.xlf'), output, resolve);\n              });\n            }));\n          })(lang);\n        }\n      }\n      Promise.all(promises).then(function (outputs) {\n        callback();\n      });\n    });\n```\n\n### Feedback task\n\n#### Note: Target custom element HTMLs must import [i18n-behavior.html](https://github.com/t2ym/i18n-behavior) directly.\n\n#### Input:\n  - Next localized JSON files in dist\n  - Next localized XLIFF files in dist\n  - Custom element HTMLs\n  - Non-custom-element HTMLs\n\n#### Output:\n  - Overwritten localized JSON files in source\n  - Overwritten default JSON files in source\n  - Overwritten bundle JSON files in source [if translation is done in bundles](#leverage-task-with-unbundle)\n  - Overwritten bundle XLIFF files in source if XLIFF import/export are setup\n\nOutputs are ready to commit in the repository\n\n```\n    var gulp = require('gulp');\n    var merge = require('merge-stream');\n    var i18nPreprocess = require('gulp-i18n-preprocess');\n\n    // Only applicable to development builds; Skip it in production builds\n    gulp.task('feedback', function () {\n      // Copy from dist\n      var locales = gulp.src([\n          'dist/**/locales/*.json',\n          'dist/**/xliff/bundle.*.xlf', // Add this item if xliff import and export are enabled\n          //'!dist/locales/bundle.*.json' // Remove this item if translation is done in bundles\n        ])\n        .pipe(gulp.dest('app'));\n\n      // Regenerate default JSON files\n      var elementDefault = gulp.src([ 'app/elements/**/*.html' ])\n        .pipe(i18nPreprocess({\n          replacingText: false,\n          jsonSpace: 2,\n          srcPath: 'app',\n          dropHtml: true,\n          attributesRepository: attributesRepository\n        }))\n        .pipe(gulp.dest('app/elements'));\n\n      // Regenerate default JSON files for non-custom-element HTMLs, i.e., i18n-dom-bind\n      var appDefault = gulp.src([ 'app/**/*.html', '!app/{elements,test}/**/*.html' ])\n        .pipe(i18nPreprocess({\n          replacingText: false,\n          jsonSpace: 2,\n          srcPath: 'app',\n          force: true,\n          dropHtml: true,\n          attributesRepository: attributesRepository\n        }))\n        .pipe(gulp.dest('app'));\n\n      return merge(locales, elementDefault, appDefault)\n        .pipe($.size({title: 'feedback'}));\n    });\n```\n\n### Integrate with Polymer CLI project templates by `polymer-build` library (highly experimental)\n\n#### Notes:\n  - As of [`polymer-build 0.4.0`](https://github.com/Polymer/polymer-build), `polymer-build` library is pre-release and subject to change.\n  - As of [`Polymer CLI 0.13.0`](https://github.com/Polymer/polymer-cli), the private API `userTransformers` is deprecated and no longer available.\n\n#### Set up `package.json` and the dependent packages of the following `gulpfile.js`\n\n```sh\n    npm init # if package.json is missing\n    npm install --save-dev gulp gulp-debug gulp-grep-contents \\\n      gulp-i18n-add-locales gulp-i18n-leverage gulp-i18n-preprocess \\\n      gulp-if gulp-ignore gulp-match gulp-merge gulp-size gulp-sort gulp-util \\\n      json-stringify-safe strip-bom through2 xliff-conv polymer-build plylog merge-stream\n```\n\n#### Gulp Filters:\n  - scan - Scan HTMLs and construct localizable attributes repository\n  - basenameSort - Sort source files according to their base names; Bundle files come first.\n  - dropDefaultJSON - Drop default JSON files to avoid overwriting new ones \n  - preprocess - Preprocess Polymer templates for I18N\n  - tmpJSON - Store extracted JSON in the temporary folder .tmp\n  - importXliff - Import XLIFF into JSON\n  - leverage - Merge changes in default JSON into localized JSON\n  - exportXliff - Generate bundles and export XLIFF\n  - feedback - Update JSON and XLIFF in sources\n  - debug - Show the list of processed files including untouched ones\n  - size - Show the total size of the processed files\n\n#### Gulp Tasks:\n  - `gulp locales --targets=\"{space separated list of target locales}\"`\n  - `gulp default` - Build with `polymer-build` library for `gulp`\n\n#### [gulpfile.js](https://gist.github.com/t2ym/c37990e422d4a19774ba1d749510c1b8#file-gulpfile-js): Put it in the root folder of the project\n```javascript\n    'use strict';\n\n    var gulp = require('gulp');\n    var gutil = require('gulp-util');\n    var debug = require('gulp-debug');\n    var gulpif = require('gulp-if');\n    var gulpignore = require('gulp-ignore');\n    var gulpmatch = require('gulp-match');\n    var sort = require('gulp-sort');\n    var grepContents = require('gulp-grep-contents');\n    var size = require('gulp-size');\n    var merge = require('gulp-merge');\n    var through = require('through2');\n    var path = require('path');\n    var stripBom = require('strip-bom');\n    var JSONstringify = require('json-stringify-safe');\n    var i18nPreprocess = require('gulp-i18n-preprocess');\n    var i18nLeverage = require('gulp-i18n-leverage');\n    var XliffConv = require('xliff-conv');\n    var i18nAddLocales = require('gulp-i18n-add-locales');\n\n    const logging = require('plylog');\n    const mergeStream = require('merge-stream');\n\n    const isPolymerCLI = global._babelPolyfill;\n\n    // Global object to store localizable attributes repository\n    var attributesRepository = {};\n\n    // Bundles object\n    var prevBundles = {};\n    var bundles = {};\n\n    var title = 'I18N transform';\n    var tmpDir = '.tmp';\n\n    var xliffOptions = {};\n\n    // Scan HTMLs and construct localizable attributes repository\n    var scan = gulpif('*.html', i18nPreprocess({\n      constructAttributesRepository: true, // construct attributes repository\n      attributesRepository: attributesRepository, // output object\n      srcPath: '.', // path to source root\n      attributesRepositoryPath: \n        'bower_components/i18n-behavior/i18n-attr-repo.html', // path to i18n-attr-repo.html\n      dropHtml: false // do not drop HTMLs\n    }));\n\n    var basenameSort = sort({\n      comparator: function(file1, file2) {\n        var base1 = path.basename(file1.path).replace(/^bundle[.]/, ' bundle.');\n        var base2 = path.basename(file2.path).replace(/^bundle[.]/, ' bundle.');\n        return base1.localeCompare(base2);\n      }\n    });\n\n    var dropDefaultJSON = gulpignore([ 'src/**/*.json', '!**/locales/*.json' ]);\n\n    var preprocess = gulpif('*.html', i18nPreprocess({\n      replacingText: true, // replace UI texts with {{annotations}}\n      jsonSpace: 2, // JSON format with 2 spaces\n      srcPath: '.', // path to source root\n      attributesRepository: attributesRepository // input attributes repository\n    }));\n\n    var tmpJSON = gulpif([ 'src/**/*.json', '!src/**/locales/*' ], gulp.dest(tmpDir));\n\n    var unbundleFiles = [];\n    var importXliff = through.obj(function (file, enc, callback) {\n      // bundle files must come earlier\n      unbundleFiles.push(file);\n      callback();\n    }, function (callback) {\n      var match;\n      var file;\n      var bundleFileMap = {};\n      var xliffConv = new XliffConv(xliffOptions);\n      while (unbundleFiles.length \u003e 0) {\n        file = unbundleFiles.shift();\n        if (path.basename(file.path).match(/^bundle[.]json$/)) {\n          prevBundles[''] = JSON.parse(stripBom(String(file.contents)));\n          bundleFileMap[''] = file;\n        }\n        else if (match = path.basename(file.path).match(/^bundle[.]([^.\\/]*)[.]json$/)) {\n          prevBundles[match[1]] = JSON.parse(stripBom(String(file.contents)));\n          bundleFileMap[match[1]] = file;\n        }\n        else if (match = path.basename(file.path).match(/^bundle[.]([^.\\/]*)[.]xlf$/)) {\n          xliffConv.parseXliff(String(file.contents), { bundle: prevBundles[match[1]] }, function (output) {\n            if (bundleFileMap[match[1]]) {\n              bundleFileMap[match[1]].contents = new Buffer(JSONstringify(output, null, 2));\n            }\n          });\n        }\n        else if (gulpmatch(file, '**/locales/*.json') \u0026\u0026\n                 (match = path.basename(file.path, '.json').match(/^([^.]*)[.]([^.]*)/))) {\n          if (prevBundles[match[2]] \u0026\u0026 prevBundles[match[2]][match[1]]) {\n            file.contents = new Buffer(JSONstringify(prevBundles[match[2]][match[1]], null, 2));\n          }\n        }\n        this.push(file);\n      }\n      callback();\n    });\n\n    var leverage = gulpif([ 'src/**/locales/*.json', '!**/locales/bundle.*.json' ], i18nLeverage({\n      jsonSpace: 2, // JSON format with 2 spaces\n      srcPath: '', // path to source root\n      distPath: '/' + tmpDir, // path to dist root to fetch next default JSON files\n      bundles: bundles // output bundles object\n    }));\n\n    var bundleFiles = [];\n    var exportXliff = through.obj(function (file, enc, callback) {\n      bundleFiles.push(file);\n      callback();\n    }, function (callback) {\n      var file;\n      var cwd = bundleFiles[0].cwd;\n      var base = bundleFiles[0].base;\n      var xliffConv = new XliffConv(xliffOptions);\n      var srcLanguage = 'en';\n      var promises = [];\n      var self = this;\n      var lang;\n      while (bundleFiles.length \u003e 0) {\n        file = bundleFiles.shift();\n        if (!gulpmatch(file, [ '**/bundle.json', '**/locales/bundle.*.json', '**/xliff/bundle.*.xlf' ])) {\n          this.push(file);\n        }\n      }\n      for (lang in bundles) {\n        bundles[lang].bundle = true;\n        this.push(new gutil.File({\n          cwd: cwd,\n          base: base,\n          path: lang ? path.join(cwd, 'locales', 'bundle.' + lang + '.json')\n                     : path.join(cwd, 'bundle.json'),\n          contents: new Buffer(JSONstringify(bundles[lang], null, 2))\n        }));\n      }\n      for (lang in bundles) {\n        if (lang) {\n          (function (destLanguage) {\n            promises.push(new Promise(function (resolve, reject) {\n              xliffConv.parseJSON(bundles, {\n                srcLanguage: srcLanguage,\n                destLanguage: destLanguage\n              }, function (output) {\n                self.push(new gutil.File({\n                  cwd: cwd,\n                  base: base,\n                  path: path.join(cwd, 'xliff', 'bundle.' + destLanguage + '.xlf'),\n                  contents: new Buffer(output)\n                }));\n                resolve();\n              });\n            }));\n          })(lang);\n        }\n      }\n      Promise.all(promises).then(function (outputs) {\n        callback();\n      });\n    });\n\n    var feedback = gulpif([ '**/bundle.json', '**/locales/*.json', '**/src/**/*.json', '**/xliff/bundle.*.xlf' ], gulp.dest('.'));\n\n    var config = {\n      // list of target locales to add\n      locales: gutil.env.targets ? gutil.env.targets.split(/ /) : []\n    }\n\n    // Gulp task to add locales to I18N-ready elements and pages\n    // Usage: gulp locales --targets=\"{space separated list of target locales}\"\n    gulp.task('locales', function() {\n      var elements = gulp.src([ 'src/**/*.html' ], { base: '.' })\n        .pipe(grepContents(/i18n-behavior.html/))\n        .pipe(grepContents(/\u003cdom-module /));\n\n      var pages = gulp.src([ 'index.html' ], { base: '.' })\n        .pipe(grepContents(/is=['\"]i18n-dom-bind['\"]/));\n\n      return merge(elements, pages)\n        .pipe(i18nAddLocales(config.locales))\n        .pipe(gulp.dest('.'))\n        .pipe(debug({ title: 'Add locales:'}))\n    });\n\n    if (isPolymerCLI) {\n      module.exports = {\n        transformers: [\n          scan,\n          basenameSort,\n          dropDefaultJSON,\n          preprocess,\n          tmpJSON,\n          importXliff,\n          leverage,\n          exportXliff,\n          feedback,\n          debug({ title: title }),\n          size({ title: title })\n        ]\n      };\n    }\n    else {\n      const polymer = require('polymer-build');\n      //const optimize = require('polymer-build/lib/optimize').optimize;\n      //const precache = require('polymer-build/lib/sw-precache');\n      const PolymerProject = polymer.PolymerProject;\n      const fork = polymer.forkStream;\n      const polymerConfig = require('./polymer.json');\n\n      //logging.setVerbose();\n\n      let project = new PolymerProject({\n        root: process.cwd(),\n        entrypoint: polymerConfig.entrypoint,\n        shell: polymerConfig.shell\n      });\n\n      gulp.task('default', () =\u003e {\n        // process source files in the project\n        let sources = project.sources()\n          .pipe(project.splitHtml())\n          // I18N processes\n          .pipe(scan)\n          .pipe(basenameSort)\n          .pipe(dropDefaultJSON)\n          .pipe(preprocess)\n          .pipe(tmpJSON)\n          .pipe(importXliff)\n          .pipe(leverage)\n          .pipe(exportXliff)\n          .pipe(feedback)\n          .pipe(debug({ title: title }))\n          .pipe(size({ title: title }))\n          // add compilers or optimizers here!\n          .pipe(project.rejoinHtml());\n\n        // process dependencies\n        let dependencies = project.dependencies()\n          .pipe(project.splitHtml())\n          // add compilers or optimizers here!\n          .pipe(project.rejoinHtml());\n\n        // merge the source and dependencies streams to we can analyze the project\n        let allFiles = mergeStream(sources, dependencies)\n          .pipe(project.analyze);\n\n        // fork the stream in case downstream transformers mutate the files\n        // this fork will vulcanize the project\n        let bundled = fork(allFiles)\n          .pipe(project.bundle)\n          // write to the bundled folder\n          .pipe(gulp.dest('build/bundled'));\n\n        let unbundled = fork(allFiles)\n          // write to the unbundled folder\n          .pipe(gulp.dest('build/unbundled'));\n\n        return mergeStream(bundled, unbundled);\n      });\n    }\n```\n\n## API\n\n`i18nLeverage(options)`\n\n### `options` object\n\n- jsonSpace: Number, default: 2 - JSON stringification parameter for formatting\n- srcPath: String, default: 'app' - Path to source root\n- distPath: String, default: 'dist' - Path to dist root to fetch next default JSON files\n- finalize: Boolean, default: false - Empty meta information if true\n- bundles: Object, default {} - Output bundles object\n\n## Quick Tour\n\n### Quick deployment of [`polymer-starter-kit-i18n`](https://github.com/t2ym/polymer-starter-kit-i18n)\n\n```\n    npm install -g polymer-cli\n    npm install -g generator-polymer-init-i18n-starter-kit\n    mkdir i18n-starter-kit\n    cd i18n-starter-kit\n    polymer init i18n-starter-kit\n    # Add Locales\n    npm run build locales -- --targets=\"de es fr ja zh-Hans\"\n    # Build\n    npm run build\n    # Translate XLIFF ./xliff/bundle.*.xlf\n    # Build and Merge Translation\n    npm run build\n    # App with Run-time I18N on http://localhost:8080\n    polymer serve\n    # App with Build-time I18N on http://localhost:8080\n    polymer serve build/bundled\n```\n\n### Change language\n\n##### 1. Press F12 to open debugger console on the browser\n\n##### 2. Navigate to the elements or DOM tab in the debugger\n\n##### 3. Change `lang` attribute of `html` element from \"en\" to other locales such as \"ja\"\n\n```html\n    \u003chtml lang=\"ja\"\u003e\n```\n\n### Update UI strings\n\n##### 1. Change any UI strings in the following HTMLs\n\n```\n    i18n-starter-kit/src/*.html\n```\n\n##### 2. Merge changes into JSON files\n\n```\n    cd i18n-starter-kit\n    npm run build\n```\n\n##### 3. Check diffs\n\n```\n    git diff\n```\n\n## License\n\n[BSD-2-Clause](https://github.com/t2ym/gulp-i18n-leverage/blob/master/LICENSE.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ft2ym%2Fgulp-i18n-leverage","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ft2ym%2Fgulp-i18n-leverage","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ft2ym%2Fgulp-i18n-leverage/lists"}