New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

ngx-i18nsupport

Package Overview
Dependencies
Maintainers
1
Versions
41
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

ngx-i18nsupport - npm Package Compare versions

Comparing version 0.2.0 to 0.2.1

.gitattributes

23

Changelog.md

@@ -0,1 +1,24 @@

<a name="0.2.1"></a>
# [0.2.1](https://github.com/martinroob/ngx-i18nsupport/compare/v0.2.0...v0.2.1) (2017-03-27)
### Bug Fixes
* **xliffmerge:** Wrong line endings in bin ([#12](https://github.com/martinroob/ngx-i18nsupport/issues/12))
<a name="0.2.0"></a>
# [0.2.0](https://github.com/martinroob/ngx-i18nsupport/compare/v0.1.0...v0.2.0) (2017-03-17)
### Bug Fixes
* **xliffmerge:** Wrong version displayed ([#2](https://github.com/martinroob/ngx-i18nsupport/issues/2)) (second try to fix it)
* **xliffmerge:** Code coverage display is too low ([#8](https://github.com/martinroob/ngx-i18nsupport/issues/8))
### Features
* **xliffmerge:** Added support for placeholders, linebreaks, embedded html ([#9](https://github.com/martinroob/ngx-i18nsupport/issues/9))
* **xliffmerge:** Added support for ngx-translate ([#5](https://github.com/martinroob/ngx-i18nsupport/issues/5))
* **documentation:** added ngx-translate integration page (as part of the wiki) ([#5](https://github.com/martinroob/ngx-i18nsupport/issues/5))
<a name="0.1.0"></a>

@@ -2,0 +25,0 @@ # [0.1.0](https://github.com/martinroob/ngx-i18nsupport/compare/v0.0.4...v0.1.0) (2017-03-10)

1

dist/common/command-output.js

@@ -12,2 +12,3 @@ /**

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var chalk = require("chalk");

@@ -14,0 +15,0 @@ var util = require("util");

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var fs = require("fs");

@@ -3,0 +4,0 @@ /**

16

dist/common/writer-to-string.js
"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var stream_1 = require("stream");

@@ -8,0 +14,0 @@ var util_1 = require("util");

"use strict";
var xliff_file_1 = require("./xliff-file");
var xmb_file_1 = require("./xmb-file");
var util_1 = require("util");
/**
* The Common interface of XliffFile and XmbFile.
* The merge process only uses this interface.
* Created by martin on 10.03.2017.
*/
/**
* Helper class to read file depending on format.
*/
var TranslationMessagesFileReader = (function () {
function TranslationMessagesFileReader() {
}
/**
* Read file function, result depends on format, either XliffFile or XmbFile.
* @param format
* @param path
* @param encoding
* @return {XliffFile}
*/
TranslationMessagesFileReader.fromFile = function (i18nFormat, path, encoding) {
if (i18nFormat === 'xlf') {
return xliff_file_1.XliffFile.fromFile(path, encoding);
}
if (i18nFormat === 'xmb') {
return xmb_file_1.XmbFile.fromFile(path, encoding);
}
throw new Error(util_1.format('oops, unsupported format "%s"', i18nFormat));
};
return TranslationMessagesFileReader;
}());
exports.TranslationMessagesFileReader = TranslationMessagesFileReader;
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=S:/experimente/ngx-i18nsupport/src/xliffmerge/i-translation-messages-file.js.map

@@ -6,2 +6,3 @@ /**

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
//# sourceMappingURL=S:/experimente/ngx-i18nsupport/src/xliffmerge/i-xliff-merge-options.js.map
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var xliff_merge_1 = require("./xliff-merge");

@@ -3,0 +4,0 @@ /**

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var file_util_1 = require("../common/file-util");

@@ -3,0 +4,0 @@ var util_1 = require("util");

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
/**

@@ -3,0 +4,0 @@ * Created by martin on 19.02.2017.

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var cheerio = require("cheerio");
var file_util_1 = require("../common/file-util");
var util_1 = require("util");
var xml_reader_1 = require("./xml-reader");
/**

@@ -138,8 +137,5 @@ * Created by martin on 23.02.2017.

var XliffFile = (function () {
function XliffFile() {
this._warnings = [];
this._numberOfTransUnitsWithMissingId = 0;
}
/**
* Read an xlf-File.
* Create an xlf-File from source.
* @param xmlString source read from file.
* @param path Path to file

@@ -150,11 +146,10 @@ * @param encoding optional encoding of the xml.

*/
XliffFile.fromFile = function (path, encoding) {
var xlf = new XliffFile();
var xmlContent = xml_reader_1.XmlReader.readXmlFileContent(path, encoding);
xlf.initializeFromContent(xmlContent.content, path, xmlContent.encoding);
return xlf;
};
function XliffFile(xmlString, path, encoding) {
this._warnings = [];
this._numberOfTransUnitsWithMissingId = 0;
this.initializeFromContent(xmlString, path, encoding);
}
XliffFile.prototype.initializeFromContent = function (xmlString, path, encoding) {
this.filename = path;
this.encoding = encoding;
this._filename = path;
this._encoding = encoding;
this.xliffContent = cheerio.load(xmlString, CheerioOptions);

@@ -269,7 +264,19 @@ if (this.xliffContent('xliff').length < 1) {

/**
* Save edited content to file.
* The filename where the data is read from.
*/
XliffFile.prototype.save = function () {
file_util_1.FileUtil.replaceContent(this.filename, this.xliffContent.xml(), this.encoding);
XliffFile.prototype.filename = function () {
return this._filename;
};
/**
* The encoding if the xml content (UTF-8, ISO-8859-1, ...)
*/
XliffFile.prototype.encoding = function () {
return this._encoding;
};
/**
* The xml to be saved after changes are made.
*/
XliffFile.prototype.editedContent = function () {
return this.xliffContent.xml();
};
return XliffFile;

@@ -276,0 +283,0 @@ }());

@@ -5,7 +5,13 @@ /**

"use strict";
var __extends = (this && this.__extends) || function (d, b) {
for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var XliffMergeError = (function (_super) {

@@ -12,0 +18,0 @@ __extends(XliffMergeError, _super);

@@ -7,2 +7,3 @@ /**

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var fs = require("fs");

@@ -9,0 +10,0 @@ var xliff_merge_error_1 = require("./xliff-merge-error");

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var program = require("commander");

@@ -9,4 +10,4 @@ var command_output_1 = require("../common/command-output");

var util_1 = require("util");
var i_translation_messages_file_1 = require("./i-translation-messages-file");
var ngx_translate_extractor_1 = require("./ngx-translate-extractor");
var translation_messages_file_reader_1 = require("./translation-messages-file-reader");
/**

@@ -147,3 +148,3 @@ * Created by martin on 17.02.2017.

var _this = this;
this.master = i_translation_messages_file_1.TranslationMessagesFileReader.fromFile(this.parameters.i18nFormat(), this.parameters.i18nFile(), this.parameters.encoding());
this.master = translation_messages_file_reader_1.TranslationMessagesFileReader.fromFile(this.parameters.i18nFormat(), this.parameters.i18nFile(), this.parameters.encoding());
this.master.warnings().forEach(function (warning) {

@@ -162,3 +163,3 @@ _this.commandOutput.warn(warning);

this.master.setSourceLanguage(this.parameters.defaultLanguage());
this.master.save();
translation_messages_file_reader_1.TranslationMessagesFileReader.save(this.master);
this.commandOutput.warn('changed master source-language="%s" to "%s"', sourceLang, this.parameters.defaultLanguage());

@@ -177,3 +178,3 @@ }

if (this.parameters.supportNgxTranslate()) {
var languageSpecificMessagesFile = i_translation_messages_file_1.TranslationMessagesFileReader.fromFile(this.parameters.i18nFormat(), languageXliffFile, this.parameters.encoding());
var languageSpecificMessagesFile = translation_messages_file_reader_1.TranslationMessagesFileReader.fromFile(this.parameters.i18nFormat(), languageXliffFile, this.parameters.encoding());
ngx_translate_extractor_1.NgxTranslateExtractor.extract(languageSpecificMessagesFile, this.parameters.generatedNgxTranslateFile(lang));

@@ -192,3 +193,3 @@ }

// read copy and set target-language
var languageSpecificMessagesFile = i_translation_messages_file_1.TranslationMessagesFileReader.fromFile(this.parameters.i18nFormat(), languageXliffFilePath, this.parameters.encoding());
var languageSpecificMessagesFile = translation_messages_file_reader_1.TranslationMessagesFileReader.fromFile(this.parameters.i18nFormat(), languageXliffFilePath, this.parameters.encoding());
languageSpecificMessagesFile.setTargetLanguage(lang);

@@ -201,3 +202,3 @@ // copy source to target

// write it to file
languageSpecificMessagesFile.save();
translation_messages_file_reader_1.TranslationMessagesFileReader.save(languageSpecificMessagesFile);
this.commandOutput.info('created new file "%s" for target-language="%s"', languageXliffFilePath, lang);

@@ -216,3 +217,3 @@ if (!isDefaultLang) {

// read lang specific file
var languageSpecificMessagesFile = i_translation_messages_file_1.TranslationMessagesFileReader.fromFile(this.parameters.i18nFormat(), languageXliffFilePath, this.parameters.encoding());
var languageSpecificMessagesFile = translation_messages_file_reader_1.TranslationMessagesFileReader.fromFile(this.parameters.i18nFormat(), languageXliffFilePath, this.parameters.encoding());
var isDefaultLang = (lang == this.parameters.defaultLanguage());

@@ -256,3 +257,3 @@ var newCount = 0;

// write it to file
languageSpecificMessagesFile.save();
translation_messages_file_reader_1.TranslationMessagesFileReader.save(languageSpecificMessagesFile);
this.commandOutput.info('updated file "%s" for target-language="%s"', languageXliffFilePath, lang);

@@ -259,0 +260,0 @@ if (newCount > 0 && !isDefaultLang) {

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var fs = require("fs");

@@ -7,4 +8,3 @@ var xliff_merge_1 = require("./xliff-merge");

var file_util_1 = require("../common/file-util");
var xliff_file_1 = require("./xliff-file");
var xmb_file_1 = require("./xmb-file");
var translation_messages_file_reader_1 = require("./translation-messages-file-reader");
/**

@@ -23,2 +23,17 @@ * Created by martin on 18.02.2017.

var SRCDIR = 'test/testdata/i18n/';
var ENCODING = 'UTF-8';
/**
* Helper function to read Xliff from File
* @type {string}
*/
function readXliff(path) {
return translation_messages_file_reader_1.TranslationMessagesFileReader.fromFile('xlf', path, ENCODING);
}
/**
* Helper function to read Xmb from File
* @type {string}
*/
function readXmb(path) {
return translation_messages_file_reader_1.TranslationMessagesFileReader.fromFile('xmb', path, ENCODING);
}
describe('test the tooling used in the tests', function () {

@@ -227,3 +242,3 @@ it('should write output to string (Test WriterToString)', function () {

file_util_1.FileUtil.copy(MASTER1SRC, MASTER);
var master = xliff_file_1.XliffFile.fromFile(MASTER);
var master = readXliff(MASTER);
expect(master.sourceLanguage()).toBe('en'); // master is german, but ng-18n extracts it as en

@@ -245,3 +260,3 @@ var ws = new writer_to_string_1.WriterToString();

expect(ws.writtenData()).toContain('changed master source-language="en" to "de"');
var newmaster = xliff_file_1.XliffFile.fromFile(MASTER);
var newmaster = readXliff(MASTER);
expect(newmaster.sourceLanguage()).toBe('de'); // master is german

@@ -265,3 +280,3 @@ done();

expect(ws.writtenData()).not.toContain('ERROR');
var langFile = xliff_file_1.XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('de'));
var langFile = readXliff(xliffMergeCmd.generatedI18nFile('de'));
expect(langFile.sourceLanguage()).toBe('de');

@@ -290,3 +305,3 @@ expect(langFile.targetLanguage()).toBe('de');

expect(ws.writtenData()).not.toContain('ERROR');
var langFileGerman = xliff_file_1.XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('de'));
var langFileGerman = readXliff(xliffMergeCmd.generatedI18nFile('de'));
expect(langFileGerman.sourceLanguage()).toBe('de');

@@ -298,3 +313,3 @@ expect(langFileGerman.targetLanguage()).toBe('de');

});
var langFileEnglish = xliff_file_1.XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('en'));
var langFileEnglish = readXliff(xliffMergeCmd.generatedI18nFile('en'));
expect(langFileEnglish.sourceLanguage()).toBe('de');

@@ -324,7 +339,7 @@ expect(langFileEnglish.targetLanguage()).toBe('en');

// now translate some texts in the English version
var langFileEnglish = xliff_file_1.XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('en'));
var langFileEnglish = readXliff(xliffMergeCmd.generatedI18nFile('en'));
var tu = langFileEnglish.transUnitWithId(ID_TRANSLATED_SCHLIESSEN);
expect(tu).toBeTruthy();
langFileEnglish.translate(tu, 'Close');
langFileEnglish.save();
translation_messages_file_reader_1.TranslationMessagesFileReader.save(langFileEnglish);
// next step, use another master

@@ -340,3 +355,3 @@ file_util_1.FileUtil.copy(MASTER2SRC, MASTER);

// look, that the new file contains the old translation
langFileEnglish = xliff_file_1.XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('en'));
langFileEnglish = readXliff(xliffMergeCmd.generatedI18nFile('en'));
expect(langFileEnglish.transUnitWithId(ID_TRANSLATED_SCHLIESSEN).targetContent()).toBe('Close');

@@ -364,9 +379,9 @@ // look, that the removed IDs are really removed.

// now translate some texts in the English version
var langFileEnglish = xliff_file_1.XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('en'));
var langFileEnglish = readXliff(xliffMergeCmd.generatedI18nFile('en'));
var tu = langFileEnglish.transUnitWithId(ID_WITH_PLACEHOLDER);
expect(tu).toBeTruthy();
langFileEnglish.translate(tu, 'Item <x id="INTERPOLATION"/> of <x id="INTERPOLATION_1"/> added.');
langFileEnglish.save();
translation_messages_file_reader_1.TranslationMessagesFileReader.save(langFileEnglish);
// look, that the new file contains the translation
langFileEnglish = xliff_file_1.XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('en'));
langFileEnglish = readXliff(xliffMergeCmd.generatedI18nFile('en'));
expect(langFileEnglish.transUnitWithId(ID_WITH_PLACEHOLDER).targetContent()).toBe('Item <x id="INTERPOLATION"></x> of <x id="INTERPOLATION_1"></x> added.');

@@ -392,3 +407,3 @@ done();

file_util_1.FileUtil.copy(MASTER1SRC, MASTER);
var master = xliff_file_1.XliffFile.fromFile(MASTER);
var master = readXliff(MASTER);
expect(master.transUnitWithId(ID_NODESC_NOMEANING).description()).toBeFalsy();

@@ -400,3 +415,3 @@ expect(master.transUnitWithId(ID_NODESC_NOMEANING).meaning()).toBeFalsy();

file_util_1.FileUtil.copy(MASTER1SRC, MASTER);
var master = xliff_file_1.XliffFile.fromFile(MASTER);
var master = readXliff(MASTER);
expect(master.transUnitWithId(ID_MONDAY).description()).toBe('ngx-translate');

@@ -421,3 +436,3 @@ expect(master.transUnitWithId(ID_MONDAY).meaning()).toBe('dateservice.monday');

expect(ws.writtenData()).not.toContain('ERROR');
var langFile = xliff_file_1.XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('de'));
var langFile = readXliff(xliffMergeCmd.generatedI18nFile('de'));
expect(langFile.transUnitWithId(ID_MONDAY).description()).toBe('ngx-translate');

@@ -543,3 +558,3 @@ expect(langFile.transUnitWithId(ID_MONDAY).meaning()).toBe('dateservice.monday');

expect(ws.writtenData()).not.toContain('ERROR');
var langFile = xmb_file_1.XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('de'));
var langFile = readXmb(xliffMergeCmd.generatedI18nFile('de'));
expect(langFile.sourceLanguage()).toBeFalsy(); // not supported in xmb

@@ -569,7 +584,7 @@ expect(langFile.targetLanguage()).toBeFalsy();

expect(ws.writtenData()).not.toContain('ERROR');
var langFileGerman = xmb_file_1.XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('de'));
var langFileGerman = readXmb(xliffMergeCmd.generatedI18nFile('de'));
langFileGerman.forEachTransUnit(function (tu) {
expect(tu.targetContent()).toBe(tu.sourceContent());
});
var langFileEnglish = xmb_file_1.XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('en'));
var langFileEnglish = readXmb(xliffMergeCmd.generatedI18nFile('en'));
langFileEnglish.forEachTransUnit(function (tu) {

@@ -597,7 +612,7 @@ expect(tu.targetContent()).toBe(tu.sourceContent());

// now translate some texts in the English version
var langFileEnglish = xmb_file_1.XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('en'));
var langFileEnglish = readXmb(xliffMergeCmd.generatedI18nFile('en'));
var tu = langFileEnglish.transUnitWithId(ID_TRANSLATED_MYFIRST);
expect(tu).toBeTruthy();
langFileEnglish.translate(tu, 'My first app');
langFileEnglish.save();
translation_messages_file_reader_1.TranslationMessagesFileReader.save(langFileEnglish);
// next step, use another master

@@ -613,3 +628,3 @@ file_util_1.FileUtil.copy(MASTER2SRC, MASTER);

// look, that the new file contains the old translation
langFileEnglish = xmb_file_1.XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('en'));
langFileEnglish = readXmb(xliffMergeCmd.generatedI18nFile('en'));
expect(langFileEnglish.transUnitWithId(ID_TRANSLATED_MYFIRST).targetContent()).toBe('My first app');

@@ -640,9 +655,9 @@ // look, that the new file contains the new translation

// now translate some texts in the English version
var langFileEnglish = xmb_file_1.XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('en'));
var langFileEnglish = readXmb(xliffMergeCmd.generatedI18nFile('en'));
var tu = langFileEnglish.transUnitWithId(ID_WITH_PLACEHOLDER);
expect(tu).toBeTruthy();
langFileEnglish.translate(tu, 'Item <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> of <ph name="INTERPOLATION_1"><ex>INTERPOLATION_1</ex></ph> added.');
langFileEnglish.save();
translation_messages_file_reader_1.TranslationMessagesFileReader.save(langFileEnglish);
// look, that the new file contains the translation
langFileEnglish = xmb_file_1.XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('en'));
langFileEnglish = readXmb(xliffMergeCmd.generatedI18nFile('en'));
expect(langFileEnglish.transUnitWithId(ID_WITH_PLACEHOLDER).targetContent()).toBe('Item <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> of <ph name="INTERPOLATION_1"><ex>INTERPOLATION_1</ex></ph> added.');

@@ -668,3 +683,3 @@ done();

file_util_1.FileUtil.copy(MASTER1SRC, MASTER);
var master = xmb_file_1.XmbFile.fromFile(MASTER);
var master = readXmb(MASTER);
expect(master.transUnitWithId(ID_NODESC_NOMEANING).description()).toBeFalsy();

@@ -676,3 +691,3 @@ expect(master.transUnitWithId(ID_NODESC_NOMEANING).meaning()).toBeFalsy();

file_util_1.FileUtil.copy(MASTER1SRC, MASTER);
var master = xmb_file_1.XmbFile.fromFile(MASTER);
var master = readXmb(MASTER);
expect(master.transUnitWithId(ID_MONDAY).description()).toBe('ngx-translate');

@@ -698,3 +713,3 @@ expect(master.transUnitWithId(ID_MONDAY).meaning()).toBe('dateservice.monday');

expect(ws.writtenData()).not.toContain('ERROR');
var langFile = xmb_file_1.XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('de'));
var langFile = readXmb(xliffMergeCmd.generatedI18nFile('de'));
expect(langFile.transUnitWithId(ID_MONDAY).description()).toBe('ngx-translate');

@@ -701,0 +716,0 @@ expect(langFile.transUnitWithId(ID_MONDAY).meaning()).toBe('dateservice.monday');

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var cheerio = require("cheerio");
var xml_reader_1 = require("./xml-reader");
var util_1 = require("util");
var file_util_1 = require("../common/file-util");
/**

@@ -111,8 +110,5 @@ * Created by martin on 10.03.2017.

var XmbFile = (function () {
function XmbFile() {
this._warnings = [];
this._numberOfTransUnitsWithMissingId = 0;
}
/**
* Read an xmb-File.
* Create an xmb-File from source.
* @param xmlString file content
* @param path Path to file

@@ -123,11 +119,10 @@ * @param encoding optional encoding of the xml.

*/
XmbFile.fromFile = function (path, encoding) {
var xmb = new XmbFile();
var xmlContent = xml_reader_1.XmlReader.readXmlFileContent(path, encoding);
xmb.initializeFromContent(xmlContent.content, path, xmlContent.encoding);
return xmb;
};
function XmbFile(xmlString, path, encoding) {
this._warnings = [];
this._numberOfTransUnitsWithMissingId = 0;
this.initializeFromContent(xmlString, path, encoding);
}
XmbFile.prototype.initializeFromContent = function (xmlString, path, encoding) {
this.filename = path;
this.encoding = encoding;
this._filename = path;
this._encoding = encoding;
this.xmbContent = cheerio.load(xmlString, CheerioOptions);

@@ -247,7 +242,19 @@ if (this.xmbContent('messagebundle').length != 1) {

/**
* Save edited content to file.
* The filename where the data is read from.
*/
XmbFile.prototype.save = function () {
file_util_1.FileUtil.replaceContent(this.filename, this.xmbContent.xml(), this.encoding);
XmbFile.prototype.filename = function () {
return this._filename;
};
/**
* The encoding if the xml content (UTF-8, ISO-8859-1, ...)
*/
XmbFile.prototype.encoding = function () {
return this._encoding;
};
/**
* The xml to be saved after changes are made.
*/
XmbFile.prototype.editedContent = function () {
return this.xmbContent.xml();
};
return XmbFile;

@@ -254,0 +261,0 @@ }());

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var file_util_1 = require("../common/file-util");

@@ -3,0 +4,0 @@ /**

{
"name": "ngx-i18nsupport",
"version": "0.2.0",
"description": "Some tooling to be used with the Angular 2 i18n worklflow",
"version": "0.2.1",
"description": "Some tooling to be used with the Angular 2 i18n workflow",
"main": "index.js",
"module": "./src",
"es2015": "./src",
"typings": "./ngx-i18nsupport.d.ts",
"bin": {

@@ -13,8 +16,8 @@ "xliffmerge": "./dist/xliffmerge/xliffmerge"

"scripts": {
"build": "tsc -p ./src/tsconfig.json && cpx ./src/xliffmerge/xliffmerge ./dist/xliffmerge && cpx package.json ./dist/out-tsc",
"build": "tsc -p ./src/tsconfig.json && cpx ./src/xliffmerge/xliffmerge ./dist/xliffmerge",
"xliffmerge": "npm link && xliffmerge",
"pretest": "npm run build",
"test": "./node_modules/.bin/jasmine-node dist/out-tsc",
"test": "./node_modules/.bin/jasmine-node dist",
"precover": "npm run build",
"cover": "./node_modules/.bin/istanbul cover --include-all-sources ./node_modules/jasmine-node/bin/jasmine-node dist/out-tsc"
"cover": "./node_modules/.bin/istanbul cover --root dist --include-all-sources ./node_modules/jasmine-node/bin/jasmine-node -- dist"
},

@@ -45,3 +48,3 @@ "repository": {

"jasmine-node": "^1.14.5",
"typescript": "^2.1.6"
"typescript": "^2.2.1"
},

@@ -48,0 +51,0 @@ "dependencies": {

@@ -15,2 +15,5 @@ [![Build Status][travis-badge]][travis-badge-url]

>There is also some support to use dynamic translations based on `ngx-translate` in parallel.
See details at the Wiki page [ngx translate usage](https://github.com/martinroob/ngx-i18nsupport/wiki/ngx-translate-usage)
## Table of Contents

@@ -92,2 +95,3 @@

"removeUnusedIds": true, // flag, if unused IDs should be removed during merge
"supportNgxTranslate": true, // flag to active json translation files for ngx-translate
"verbose": false, // controls output

@@ -94,0 +98,0 @@ "quiet": false, // controls output

@@ -11,5 +11,5 @@ {

"moduleResolution": "node",
"outDir": "../dist/out-tsc",
"outDir": "../dist",
"sourceMap": true,
"target": "es6",
"target": "es5",
"typeRoots": [

@@ -16,0 +16,0 @@ "../node_modules/@types"

@@ -5,2 +5,4 @@ import {XliffFile} from './xliff-file';

import {format} from 'util';
import {ITransUnit} from './i-trans-unit';
/**

@@ -11,64 +13,2 @@ * The Common interface of XliffFile and XmbFile.

*/
/**
* Helper class to read file depending on format.
*/
export class TranslationMessagesFileReader {
/**
* Read file function, result depends on format, either XliffFile or XmbFile.
* @param format
* @param path
* @param encoding
* @return {XliffFile}
*/
public static fromFile(i18nFormat: string, path: string, encoding: string): ITranslationMessagesFile {
if (i18nFormat === 'xlf') {
return XliffFile.fromFile(path, encoding);
}
if (i18nFormat === 'xmb') {
return XmbFile.fromFile(path, encoding);
}
throw new Error(format('oops, unsupported format "%s"', i18nFormat));
}
}
/**
* Interface of a translation unit in the file.
*/
export interface ITransUnit {
readonly id: string;
sourceContent(): string;
targetContent(): string;
targetState(): string;
/**
* the real xml element used for trans unit.
* @return {CheerioElement}
*/
asXmlElement(): CheerioElement;
/**
* Copy source to target to use it as dummy translation.
* (better than missing value)
*/
useSourceAsTarget(isDefaultLang: boolean);
/**
* Translate trans unit.
* (very simple, just for tests)
* @param translation the translated string
*/
translate(translation: string);
}
/**
* The Common interface of XliffFile and XmbFile.
* The merge process only uses this interface.
*/
export interface ITranslationMessagesFile {

@@ -159,6 +99,16 @@

/**
* Save edited content to file.
* The filename where the data is read from.
*/
save();
filename(): string;
/**
* The encoding if the xml content (UTF-8, ISO-8859-1, ...)
*/
encoding(): string;
/**
* The xml content to be saved after changes are made.
*/
editedContent(): string;
}

@@ -6,4 +6,4 @@ /**

const pkg = require(path.resolve(__dirname, '..', 'package.json'));
const pkg = require(path.resolve(__dirname, '..', '..', 'package.json'));
export const VERSION = (pkg? pkg.version : 'unknown');
import * as cheerio from "cheerio";
import {FileUtil} from '../common/file-util';
import {isNullOrUndefined, format} from 'util';
import {XmlReader} from './xml-reader';
import {ITranslationMessagesFile, ITransUnit} from './i-translation-messages-file';
import {ITranslationMessagesFile} from './i-translation-messages-file';
import {ITransUnit} from './i-trans-unit';
/**

@@ -19,2 +18,3 @@ * Created by martin on 23.02.2017.

decodeEntities: false,
};

@@ -36,2 +36,5 @@

/**
* the translated value (containing all markup, depends on the concrete format used).
*/
public targetContent(): string {

@@ -41,2 +44,34 @@ return cheerio('target', this._transUnit).html();

/**
* the translated value, but all placeholders are replaced with {{n}} (starting at 0)
* and all embedded html is replaced by direct html markup.
*/
targetContentNormalized(): string {
let directHtml = this.targetContent();
if (!directHtml) {
return directHtml;
}
let normalized = directHtml;
let re0: RegExp = /<x id="INTERPOLATION"><\/x>/g;
normalized = normalized.replace(re0, '{{0}}');
let reN: RegExp = /<x id="INTERPOLATION_(\d*)"><\/x>/g;
normalized = normalized.replace(reN, '{{$1}}');
let reStartBold: RegExp = /<x id="START_BOLD_TEXT" ctype="x-b"><\/x>/g;
normalized = normalized.replace(reStartBold, '<b>');
let reCloseBold: RegExp = /<x id="CLOSE_BOLD_TEXT" ctype="x-b"><\/x>/g;
normalized = normalized.replace(reCloseBold, '</b>');
let reStartAnyTag: RegExp = /<x id="START_TAG_(\w*)" ctype="x-(\w*)"><\/x>/g;
normalized = normalized.replace(reStartAnyTag, '<$2>');
let reCloseAnyTag: RegExp = /<x id="CLOSE_TAG_(\w*)" ctype="x-(\w*)"><\/x>/g;
normalized = normalized.replace(reCloseAnyTag, '</$2>');
return normalized;
}
/**
* State of the translation.
* (new, final, ...)
*/
public targetState(): string {

@@ -47,2 +82,23 @@ return cheerio('target', this._transUnit).attr('state');

/**
* The description set in the template as value of the i18n-attribute.
* e.g. i18n="mydescription".
* In xliff this is stored as a note element with attribute from="description".
*/
public description(): string {
let descriptionElem = cheerio('note', this._transUnit).filter((index, elem) => cheerio(elem).attr('from') == 'description');
return descriptionElem ? descriptionElem.html() : null;
}
/**
* The meaning (intent) set in the template as value of the i18n-attribute.
* This is the part in front of the | symbol.
* e.g. i18n="meaning|mydescription".
* In xliff this is stored as a note element with attribute from="meaning".
*/
public meaning(): string {
let meaningElem = cheerio('note', this._transUnit).filter((index, elem) => cheerio(elem).attr('from') == 'meaning');
return meaningElem ? meaningElem.html() : null;
}
/**
* the real xml element used for trans unit.

@@ -68,3 +124,6 @@ * Here it is a <trans-unit> element defined in XLIFF Spec.

}
target.html(translation);
let translationContainer: CheerioStatic = cheerio.load('<dummy>' + translation + '</dummy>', CheerioOptions);
let translationParts: Cheerio = translationContainer('dummy');
target.contents().remove();
translationParts.contents().each((index, element) => {target.append(cheerio(element));});
target.attr('state', 'final');

@@ -96,20 +155,6 @@ }

/**
* Read an xlf-File.
* @param path Path to file
* @param encoding optional encoding of the xml.
* This is read from the file, but if you know it before, you can avoid reading the file twice.
* @return {XliffFile}
*/
public static fromFile(path: string, encoding?: string): XliffFile {
let xlf = new XliffFile();
let xmlContent = XmlReader.readXmlFileContent(path, encoding);
xlf.initializeFromContent(xmlContent.content, path, xmlContent.encoding);
return xlf;
}
private _filename: string;
private filename: string;
private _encoding: string;
private encoding: string;
private xliffContent: CheerioStatic;

@@ -123,11 +168,23 @@

constructor() {
/**
* Create an xlf-File from source.
* @param xmlString source read from file.
* @param path Path to file
* @param encoding optional encoding of the xml.
* This is read from the file, but if you know it before, you can avoid reading the file twice.
* @return {XliffFile}
*/
constructor(xmlString: string, path: string, encoding: string) {
this._warnings = [];
this._numberOfTransUnitsWithMissingId = 0;
this.initializeFromContent(xmlString, path, encoding);
}
private initializeFromContent(xmlString: string, path: string, encoding: string): XliffFile {
this.filename = path;
this.encoding = encoding;
this._filename = path;
this._encoding = encoding;
this.xliffContent = cheerio.load(xmlString, CheerioOptions);
if (this.xliffContent('xliff').length < 1) {
throw new Error(format('File "%s" seems to be no xliff file (should contain an xliff element)', path));
}
return this;

@@ -252,7 +309,22 @@ }

/**
* Save edited content to file.
* The filename where the data is read from.
*/
public save() {
FileUtil.replaceContent(this.filename, this.xliffContent.xml(), this.encoding);
public filename(): string {
return this._filename;
}
/**
* The encoding if the xml content (UTF-8, ISO-8859-1, ...)
*/
public encoding(): string {
return this._encoding;
}
/**
* The xml to be saved after changes are made.
*/
public editedContent(): string {
return this.xliffContent.xml();
}
}

@@ -12,3 +12,3 @@ /**

import {isNullOrUndefined} from 'util';
import {ProgramOptions, IConfigFile} from './xliff-merge';
import {ProgramOptions, IConfigFile} from './i-xliff-merge-options';

@@ -27,2 +27,3 @@ export class XliffMergeParameters {

private _removeUnusedIds: boolean;
private _supportNgxTranslate: boolean;

@@ -143,2 +144,5 @@ public errorsFound: XliffMergeError[];

}
if (profile.supportNgxTranslate) {
this._supportNgxTranslate = profile.supportNgxTranslate;
}
} else {

@@ -224,3 +228,7 @@ this.warningsFound.push('did not find "xliffmergeOptions" in profile, using defaults');

commandOutput.debug('languages:\t%s', this.languages());
commandOutput.debug('languages:\t%s', this.languages());
commandOutput.debug('removeUnusedIds:\t%s', this.removeUnusedIds());
commandOutput.debug('supportNgxTranslate:\t%s', this.supportNgxTranslate());
}
/**

@@ -269,5 +277,5 @@ * Default-Language, default en.

/**
* evtl zu generierendes I18n-File mit den Übersetzungen für eine Sprache.
* @param lang
* @return {string}
* potentially to be generated I18n-File with the translations for one language.
* @param lang language shortcut
* @return {string} Path of file
*/

@@ -279,2 +287,11 @@ public generatedI18nFile(lang: string): string {

/**
* potentially to be generated translate-File for ngx-translate with the translations for one language.
* @param lang language shortcut
* @return {string} Path of file
*/
public generatedNgxTranslateFile(lang: string): string {
return this.genDir() + '/' + 'messages.' + lang + '.' + 'json';
}
/**
* The encoding used to write new XLIFF-files.

@@ -298,2 +315,6 @@ * @return {string}

}
public supportNgxTranslate(): boolean {
return (isNullOrUndefined(this._supportNgxTranslate)) ? false : this._supportNgxTranslate;
}
}
import fs = require("fs");
import child_process = require("child_process");
import {XliffMerge, ProgramOptions, IConfigFile} from './xliff-merge';
import {XliffMerge} from './xliff-merge';
import {ProgramOptions, IConfigFile} from './i-xliff-merge-options';
import {CommandOutput} from '../common/command-output';

@@ -8,5 +9,7 @@ import WritableStream = NodeJS.WritableStream;

import {FileUtil} from '../common/file-util';
import {ITranslationMessagesFile} from './i-translation-messages-file';
import {ITransUnit} from './i-trans-unit';
import {XliffFile} from './xliff-file';
import {ITransUnit, ITranslationMessagesFile} from './i-translation-messages-file';
import {XmbFile} from './xmb-file';
import {TranslationMessagesFileReader} from './translation-messages-file-reader';
/**

@@ -28,2 +31,20 @@ * Created by martin on 18.02.2017.

let ENCODING = 'UTF-8';
/**
* Helper function to read Xliff from File
* @type {string}
*/
function readXliff(path: string): XliffFile {
return <XliffFile> TranslationMessagesFileReader.fromFile('xlf', path, ENCODING);
}
/**
* Helper function to read Xmb from File
* @type {string}
*/
function readXmb(path: string): XmbFile {
return <XmbFile> TranslationMessagesFileReader.fromFile('xmb', path, ENCODING);
}
describe('test the tooling used in the tests', () => {

@@ -51,2 +72,22 @@ it('should write output to string (Test WriterToString)', () => {

it('should output version and used parameters when called with defaults and verbose flag', (done) => {
let ws: WriterToString = new WriterToString();
let commandOut = new CommandOutput(ws);
let xliffMergeCmd = new XliffMerge(commandOut, {verbose: true});
xliffMergeCmd.run();
expect(ws.writtenData()).toContain('xliffmerge version');
expect(ws.writtenData()).toContain('Used Parameters:');
done();
});
it('should not output version and used parameters when called with defaults and both verbose and quiet flag', (done) => {
let ws: WriterToString = new WriterToString();
let commandOut = new CommandOutput(ws);
let xliffMergeCmd = new XliffMerge(commandOut, {verbose: true, quiet: true});
xliffMergeCmd.run();
expect(ws.writtenData()).not.toContain('xliffmerge version');
expect(ws.writtenData()).not.toContain('Used Parameters:');
done();
});
it('should output an errror (no languages) when called with defaults', (done) => {

@@ -98,3 +139,3 @@ let ws: WriterToString = new WriterToString();

}
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {verbose: true}, profileContent);

@@ -114,3 +155,3 @@ xliffMergeCmd.run();

}
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {verbose: true}, profileContent);

@@ -131,3 +172,3 @@ xliffMergeCmd.run();

}
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {}, profileContent);

@@ -147,3 +188,3 @@ xliffMergeCmd.run();

}
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {}, profileContent);

@@ -163,3 +204,3 @@ xliffMergeCmd.run();

}
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {}, profileContent);

@@ -179,3 +220,3 @@ xliffMergeCmd.run();

}
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {}, profileContent);

@@ -194,3 +235,3 @@ xliffMergeCmd.run();

}
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {}, profileContent);

@@ -209,3 +250,3 @@ xliffMergeCmd.run();

}
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {verbose: true}, profileContent);

@@ -220,3 +261,2 @@ xliffMergeCmd.run();

describe('Merge process checks for format xlf', () => {
let MASTER1FILE = 'ngExtractedMaster1.xlf';

@@ -232,2 +272,3 @@ let MASTER2FILE = 'ngExtractedMaster2.xlf';

let ID_REMOVED_SUCHEN = "d17aee1ddf9fe1c0afe8440e02ef5ab906a69699"; // another removed ID
let ID_WITH_PLACEHOLDER = "af0819ea4a5db68737ebcabde2f5e432b66352e8";

@@ -244,3 +285,3 @@ beforeEach(() => {

FileUtil.copy(MASTER1SRC, MASTER);
let master: XliffFile = XliffFile.fromFile(MASTER);
let master: XliffFile = readXliff(MASTER);
expect(master.sourceLanguage()).toBe('en'); // master is german, but ng-18n extracts it as en

@@ -256,3 +297,3 @@ let ws: WriterToString = new WriterToString();

}
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent);

@@ -263,3 +304,3 @@ xliffMergeCmd.run();

expect(ws.writtenData()).toContain('changed master source-language="en" to "de"');
let newmaster: XliffFile = XliffFile.fromFile(MASTER);
let newmaster: XliffFile = readXliff(MASTER);
expect(newmaster.sourceLanguage()).toBe('de'); // master is german

@@ -280,7 +321,7 @@ done();

}
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent);
xliffMergeCmd.run();
expect(ws.writtenData()).not.toContain('ERROR');
let langFile: XliffFile = XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('de'));
let langFile: XliffFile = readXliff(xliffMergeCmd.generatedI18nFile('de'));
expect(langFile.sourceLanguage()).toBe('de');

@@ -306,7 +347,7 @@ expect(langFile.targetLanguage()).toBe('de');

}
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de', 'en']}, profileContent);
xliffMergeCmd.run();
expect(ws.writtenData()).not.toContain('ERROR');
let langFileGerman: XliffFile = XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('de'));
let langFileGerman: XliffFile = readXliff(xliffMergeCmd.generatedI18nFile('de'));
expect(langFileGerman.sourceLanguage()).toBe('de');

@@ -318,3 +359,3 @@ expect(langFileGerman.targetLanguage()).toBe('de');

});
let langFileEnglish: XliffFile = XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('en'));
let langFileEnglish: XliffFile = readXliff(xliffMergeCmd.generatedI18nFile('en'));
expect(langFileEnglish.sourceLanguage()).toBe('de');

@@ -340,3 +381,3 @@ expect(langFileEnglish.targetLanguage()).toBe('en');

}
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de', 'en']}, profileContent);

@@ -347,7 +388,7 @@ xliffMergeCmd.run();

// now translate some texts in the English version
let langFileEnglish: XliffFile = XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('en'));
let langFileEnglish: XliffFile = readXliff(xliffMergeCmd.generatedI18nFile('en'));
let tu: ITransUnit = langFileEnglish.transUnitWithId(ID_TRANSLATED_SCHLIESSEN);
expect(tu).toBeTruthy();
langFileEnglish.translate(tu, 'Close');
langFileEnglish.save();
TranslationMessagesFileReader.save(langFileEnglish);

@@ -365,3 +406,3 @@ // next step, use another master

// look, that the new file contains the old translation
langFileEnglish = XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('en'));
langFileEnglish = readXliff(xliffMergeCmd.generatedI18nFile('en'));
expect(langFileEnglish.transUnitWithId(ID_TRANSLATED_SCHLIESSEN).targetContent()).toBe('Close');

@@ -375,4 +416,174 @@

it('should translate messages with placeholder', (done) => {
FileUtil.copy(MASTER2SRC, MASTER);
let ws: WriterToString = new WriterToString();
let commandOut = new CommandOutput(ws);
let profileContent: IConfigFile = {
xliffmergeOptions: {
defaultLanguage: 'de',
srcDir: WORKDIR,
genDir: WORKDIR,
i18nFile: MASTERFILE
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de', 'en']}, profileContent);
xliffMergeCmd.run();
expect(ws.writtenData()).not.toContain('ERROR');
// now translate some texts in the English version
let langFileEnglish: ITranslationMessagesFile = readXliff(xliffMergeCmd.generatedI18nFile('en'));
let tu: ITransUnit = langFileEnglish.transUnitWithId(ID_WITH_PLACEHOLDER);
expect(tu).toBeTruthy();
langFileEnglish.translate(tu, 'Item <x id="INTERPOLATION"/> of <x id="INTERPOLATION_1"/> added.');
TranslationMessagesFileReader.save(langFileEnglish);
// look, that the new file contains the translation
langFileEnglish = readXliff(xliffMergeCmd.generatedI18nFile('en'));
expect(langFileEnglish.transUnitWithId(ID_WITH_PLACEHOLDER).targetContent()).toBe('Item <x id="INTERPOLATION"></x> of <x id="INTERPOLATION_1"></x> added.');
done();
});
});
describe('ngx-translate processing for format xlf', () => {
let MASTER1FILE = 'ngxtranslate.xlf';
let MASTER1SRC = SRCDIR + MASTER1FILE;
let MASTERFILE = 'messages.xlf';
let MASTER = WORKDIR + MASTERFILE;
let ID_NODESC_NOMEANING = "a8f10794864e49b16224b22faaf4a86229b6c53d"; // an ID without set meaning and description
let ID_MONDAY = "84e8cd8ba480129d90f512cc3462bb43efcf389f"; // an ID from ngxtranslate.xlf with meaning "x.y" and description "ngx-translate"
beforeEach(() => {
if (!fs.existsSync(WORKDIR)){
fs.mkdirSync(WORKDIR);
}
// cleanup workdir
FileUtil.deleteFolderContentRecursive(WORKDIR);
});
it('should return null for unset description and meaning in master xlf file', (done) => {
FileUtil.copy(MASTER1SRC, MASTER);
let master: XliffFile = readXliff(MASTER);
expect(master.transUnitWithId(ID_NODESC_NOMEANING).description()).toBeFalsy();
expect(master.transUnitWithId(ID_NODESC_NOMEANING).meaning()).toBeFalsy();
done();
});
it('should find description and meaning in master xlf file', (done) => {
FileUtil.copy(MASTER1SRC, MASTER);
let master: XliffFile = readXliff(MASTER);
expect(master.transUnitWithId(ID_MONDAY).description()).toBe('ngx-translate');
expect(master.transUnitWithId(ID_MONDAY).meaning()).toBe('dateservice.monday');
done();
});
it('should find description and meaning in translated xlf file', (done) => {
FileUtil.copy(MASTER1SRC, MASTER);
let ws: WriterToString = new WriterToString();
let commandOut = new CommandOutput(ws);
let profileContent: IConfigFile = {
xliffmergeOptions: {
defaultLanguage: 'de',
srcDir: WORKDIR,
genDir: WORKDIR,
i18nFile: MASTERFILE
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent);
xliffMergeCmd.run();
expect(ws.writtenData()).not.toContain('ERROR');
let langFile: XliffFile = readXliff(xliffMergeCmd.generatedI18nFile('de'));
expect(langFile.transUnitWithId(ID_MONDAY).description()).toBe('ngx-translate');
expect(langFile.transUnitWithId(ID_MONDAY).meaning()).toBe('dateservice.monday');
done();
});
it('should write translation json file for ngx-translate', (done) => {
FileUtil.copy(MASTER1SRC, MASTER);
let ws: WriterToString = new WriterToString();
let commandOut = new CommandOutput(ws);
let profileContent: IConfigFile = {
xliffmergeOptions: {
defaultLanguage: 'de',
srcDir: WORKDIR,
genDir: WORKDIR,
i18nFile: MASTERFILE,
supportNgxTranslate: true
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent);
xliffMergeCmd.run();
expect(ws.writtenData()).not.toContain('ERROR');
let translationJsonFilename = xliffMergeCmd.generatedNgxTranslateFile('de');
expect(FileUtil.exists(translationJsonFilename)).toBeTruthy();
let fileContent = FileUtil.read(translationJsonFilename, 'UTF-8');
let translation: any = JSON.parse(fileContent);
expect(translation).toBeTruthy();
expect(translation.myapp).toBeTruthy();
expect(translation.dateservice.monday).toBe("Montag");
expect(translation.dateservice.friday).toBe("Freitag");
done();
});
it('should handle placeholders in json file for ngx-translate', (done) => {
FileUtil.copy(MASTER1SRC, MASTER);
let ws: WriterToString = new WriterToString();
let commandOut = new CommandOutput(ws);
let profileContent: IConfigFile = {
xliffmergeOptions: {
defaultLanguage: 'de',
srcDir: WORKDIR,
genDir: WORKDIR,
i18nFile: MASTERFILE,
supportNgxTranslate: true
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent);
xliffMergeCmd.run();
expect(ws.writtenData()).not.toContain('ERROR');
let translationJsonFilename = xliffMergeCmd.generatedNgxTranslateFile('de');
expect(FileUtil.exists(translationJsonFilename)).toBeTruthy();
let fileContent = FileUtil.read(translationJsonFilename, 'UTF-8');
let translation: any = JSON.parse(fileContent);
expect(translation).toBeTruthy();
expect(translation.placeholders).toBeTruthy();
expect(translation.placeholders.test1placeholder).toBe('{{0}}: Eine Nachricht mit einem Platzhalter');
expect(translation.placeholders.test2placeholder).toBe('{{0}}: Eine Nachricht mit 2 Platzhaltern: {{1}}');
expect(translation.placeholders.test2placeholderRepeated).toBe('{{0}}: Eine Nachricht mit 2 Platzhaltern: {{0}} {{1}}');
done();
});
it('should handle embedded html markup in json file for ngx-translate', (done) => {
FileUtil.copy(MASTER1SRC, MASTER);
let ws: WriterToString = new WriterToString();
let commandOut = new CommandOutput(ws);
let profileContent: IConfigFile = {
xliffmergeOptions: {
defaultLanguage: 'de',
srcDir: WORKDIR,
genDir: WORKDIR,
i18nFile: MASTERFILE,
supportNgxTranslate: true
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent);
xliffMergeCmd.run();
expect(ws.writtenData()).not.toContain('ERROR');
let translationJsonFilename = xliffMergeCmd.generatedNgxTranslateFile('de');
expect(FileUtil.exists(translationJsonFilename)).toBeTruthy();
let fileContent = FileUtil.read(translationJsonFilename, 'UTF-8');
let translation: any = JSON.parse(fileContent);
expect(translation).toBeTruthy();
expect(translation.embeddedhtml).toBeTruthy();
expect(translation.embeddedhtml.bold).toBe('Diese Nachricht ist <b>WICHTIG</b>');
expect(translation.embeddedhtml.boldstrong).toBe('Diese Nachricht ist <b><strong>SEHR WICHTIG</strong></b>');
expect(translation.embeddedhtml.strange).toBe('Diese Nachricht ist <strange>{{0}}</strange>');
done();
});
});
describe('Merge process checks for format xmb', () => {

@@ -390,3 +601,4 @@

let ID_REMOVED_DESCRIPTION2 = "3274258156935474372"; // another removed ID
let ID_ADDED = "3274258156935474372"; // an ID that will be added in master2
let ID_ADDED = "8998006760999956868"; // an ID that will be added in master2
let ID_WITH_PLACEHOLDER = "9030312858648510700";

@@ -413,7 +625,7 @@ beforeEach(() => {

}
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent);
xliffMergeCmd.run();
expect(ws.writtenData()).not.toContain('ERROR');
let langFile: ITranslationMessagesFile = XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('de'));
let langFile: ITranslationMessagesFile = readXmb(xliffMergeCmd.generatedI18nFile('de'));
expect(langFile.sourceLanguage()).toBeFalsy(); // not supported in xmb

@@ -440,11 +652,11 @@ expect(langFile.targetLanguage()).toBeFalsy();

}
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de', 'en']}, profileContent);
xliffMergeCmd.run();
expect(ws.writtenData()).not.toContain('ERROR');
let langFileGerman: ITranslationMessagesFile = XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('de'));
let langFileGerman: ITranslationMessagesFile = readXmb(xliffMergeCmd.generatedI18nFile('de'));
langFileGerman.forEachTransUnit((tu: ITransUnit) => {
expect(tu.targetContent()).toBe(tu.sourceContent());
});
let langFileEnglish: ITranslationMessagesFile = XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('en'));
let langFileEnglish: ITranslationMessagesFile = readXmb(xliffMergeCmd.generatedI18nFile('en'));
langFileEnglish.forEachTransUnit((tu: ITransUnit) => {

@@ -468,3 +680,3 @@ expect(tu.targetContent()).toBe(tu.sourceContent());

}
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de', 'en']}, profileContent);

@@ -475,7 +687,7 @@ xliffMergeCmd.run();

// now translate some texts in the English version
let langFileEnglish: ITranslationMessagesFile = XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('en'));
let langFileEnglish: ITranslationMessagesFile = readXmb(xliffMergeCmd.generatedI18nFile('en'));
let tu: ITransUnit = langFileEnglish.transUnitWithId(ID_TRANSLATED_MYFIRST);
expect(tu).toBeTruthy();
langFileEnglish.translate(tu, 'My first app');
langFileEnglish.save();
TranslationMessagesFileReader.save(langFileEnglish);

@@ -489,9 +701,12 @@ // next step, use another master

expect(ws.writtenData()).not.toContain('ERROR');
expect(ws.writtenData()).toContain('merged 1 trans-units from master to "en"');
expect(ws.writtenData()).toContain('merged 2 trans-units from master to "en"');
expect(ws.writtenData()).toContain('removed 2 unused trans-units in "en"');
// look, that the new file contains the old translation
langFileEnglish = XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('en'));
langFileEnglish = readXmb(xliffMergeCmd.generatedI18nFile('en'));
expect(langFileEnglish.transUnitWithId(ID_TRANSLATED_MYFIRST).targetContent()).toBe('My first app');
// look, that the new file contains the new translation
expect(langFileEnglish.transUnitWithId(ID_ADDED)).toBeTruthy();
// look, that the removed IDs are really removed.

@@ -503,4 +718,178 @@ expect(langFileEnglish.transUnitWithId(ID_REMOVED_DESCRIPTION)).toBeFalsy();

it('should translate messages with placeholder in format xmb', (done) => {
FileUtil.copy(MASTER2SRC, MASTER);
let ws: WriterToString = new WriterToString();
let commandOut = new CommandOutput(ws);
let profileContent: IConfigFile = {
xliffmergeOptions: {
defaultLanguage: 'de',
srcDir: WORKDIR,
genDir: WORKDIR,
i18nFile: MASTERFILE,
i18nFormat: 'xmb'
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de', 'en']}, profileContent);
xliffMergeCmd.run();
expect(ws.writtenData()).not.toContain('ERROR');
// now translate some texts in the English version
let langFileEnglish: ITranslationMessagesFile = readXmb(xliffMergeCmd.generatedI18nFile('en'));
let tu: ITransUnit = langFileEnglish.transUnitWithId(ID_WITH_PLACEHOLDER);
expect(tu).toBeTruthy();
langFileEnglish.translate(tu, 'Item <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> of <ph name="INTERPOLATION_1"><ex>INTERPOLATION_1</ex></ph> added.');
TranslationMessagesFileReader.save(langFileEnglish);
// look, that the new file contains the translation
langFileEnglish = readXmb(xliffMergeCmd.generatedI18nFile('en'));
expect(langFileEnglish.transUnitWithId(ID_WITH_PLACEHOLDER).targetContent()).toBe('Item <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> of <ph name="INTERPOLATION_1"><ex>INTERPOLATION_1</ex></ph> added.');
done();
});
});
describe('ngx-translate processing for format xmb', () => {
let MASTER1FILE = 'ngxtranslate.xmb';
let MASTER1SRC = SRCDIR + MASTER1FILE;
let MASTERFILE = 'messages.xmb';
let MASTER = WORKDIR + MASTERFILE;
let ID_NODESC_NOMEANING = "2047558209369508311"; // an ID without set meaning and description
let ID_MONDAY = "6830980354990918030"; // an ID from ngxtranslate.xmb with meaning "x.y" and description "ngx-translate"
beforeEach(() => {
if (!fs.existsSync(WORKDIR)){
fs.mkdirSync(WORKDIR);
}
// cleanup workdir
FileUtil.deleteFolderContentRecursive(WORKDIR);
});
it('should return null for unset description and meaning in master xmb file', (done) => {
FileUtil.copy(MASTER1SRC, MASTER);
let master: XmbFile = readXmb(MASTER);
expect(master.transUnitWithId(ID_NODESC_NOMEANING).description()).toBeFalsy();
expect(master.transUnitWithId(ID_NODESC_NOMEANING).meaning()).toBeFalsy();
done();
});
it('should find description and meaning in master xmb file', (done) => {
FileUtil.copy(MASTER1SRC, MASTER);
let master: XmbFile = readXmb(MASTER);
expect(master.transUnitWithId(ID_MONDAY).description()).toBe('ngx-translate');
expect(master.transUnitWithId(ID_MONDAY).meaning()).toBe('dateservice.monday');
done();
});
it('should find description and meaning in translated xmb file', (done) => {
FileUtil.copy(MASTER1SRC, MASTER);
let ws: WriterToString = new WriterToString();
let commandOut = new CommandOutput(ws);
let profileContent: IConfigFile = {
xliffmergeOptions: {
defaultLanguage: 'de',
srcDir: WORKDIR,
genDir: WORKDIR,
i18nFile: MASTERFILE,
i18nFormat: 'xmb'
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent);
xliffMergeCmd.run();
expect(ws.writtenData()).not.toContain('ERROR');
let langFile: XmbFile = readXmb(xliffMergeCmd.generatedI18nFile('de'));
expect(langFile.transUnitWithId(ID_MONDAY).description()).toBe('ngx-translate');
expect(langFile.transUnitWithId(ID_MONDAY).meaning()).toBe('dateservice.monday');
done();
});
it('should write translation json file for ngx-translate', (done) => {
FileUtil.copy(MASTER1SRC, MASTER);
let ws: WriterToString = new WriterToString();
let commandOut = new CommandOutput(ws);
let profileContent: IConfigFile = {
xliffmergeOptions: {
defaultLanguage: 'de',
srcDir: WORKDIR,
genDir: WORKDIR,
i18nFile: MASTERFILE,
i18nFormat: 'xmb',
supportNgxTranslate: true
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent);
xliffMergeCmd.run();
expect(ws.writtenData()).not.toContain('ERROR');
let translationJsonFilename = xliffMergeCmd.generatedNgxTranslateFile('de');
expect(FileUtil.exists(translationJsonFilename)).toBeTruthy();
let fileContent = FileUtil.read(translationJsonFilename, 'UTF-8');
let translation: any = JSON.parse(fileContent);
expect(translation).toBeTruthy();
expect(translation.myapp).toBeTruthy();
expect(translation.dateservice.monday).toBe("Montag");
expect(translation.dateservice.friday).toBe("Freitag");
done();
});
it('should handle placeholders in json file for ngx-translate', (done) => {
FileUtil.copy(MASTER1SRC, MASTER);
let ws: WriterToString = new WriterToString();
let commandOut = new CommandOutput(ws);
let profileContent: IConfigFile = {
xliffmergeOptions: {
defaultLanguage: 'de',
srcDir: WORKDIR,
genDir: WORKDIR,
i18nFile: MASTERFILE,
i18nFormat: 'xmb',
supportNgxTranslate: true
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent);
xliffMergeCmd.run();
expect(ws.writtenData()).not.toContain('ERROR');
let translationJsonFilename = xliffMergeCmd.generatedNgxTranslateFile('de');
expect(FileUtil.exists(translationJsonFilename)).toBeTruthy();
let fileContent = FileUtil.read(translationJsonFilename, 'UTF-8');
let translation: any = JSON.parse(fileContent);
expect(translation).toBeTruthy();
expect(translation.placeholders).toBeTruthy();
expect(translation.placeholders.test1placeholder).toBe('{{0}}: Eine Nachricht mit einem Platzhalter');
expect(translation.placeholders.test2placeholder).toBe('{{0}}: Eine Nachricht mit 2 Platzhaltern: {{1}}');
expect(translation.placeholders.test2placeholderRepeated).toBe('{{0}}: Eine Nachricht mit 2 Platzhaltern: {{0}} {{1}}');
done();
});
it('should handle embedded html markup in json file for ngx-translate', (done) => {
FileUtil.copy(MASTER1SRC, MASTER);
let ws: WriterToString = new WriterToString();
let commandOut = new CommandOutput(ws);
let profileContent: IConfigFile = {
xliffmergeOptions: {
defaultLanguage: 'de',
srcDir: WORKDIR,
genDir: WORKDIR,
i18nFile: MASTERFILE,
i18nFormat: 'xmb',
supportNgxTranslate: true
}
};
let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent);
xliffMergeCmd.run();
expect(ws.writtenData()).not.toContain('ERROR');
let translationJsonFilename = xliffMergeCmd.generatedNgxTranslateFile('de');
expect(FileUtil.exists(translationJsonFilename)).toBeTruthy();
let fileContent = FileUtil.read(translationJsonFilename, 'UTF-8');
let translation: any = JSON.parse(fileContent);
expect(translation).toBeTruthy();
expect(translation.embeddedhtml).toBeTruthy();
expect(translation.embeddedhtml.bold).toBe('Diese Nachricht ist <b>WICHTIG</b>');
expect(translation.embeddedhtml.boldstrong).toBe('Diese Nachricht ist <b><strong>SEHR WICHTIG</strong></b>');
expect(translation.embeddedhtml.strange).toBe('Diese Nachricht ist <strange>{{0}}</strange>');
done();
});
});
});

@@ -9,3 +9,7 @@ import * as program from 'commander';

import {isNullOrUndefined} from 'util';
import {ITranslationMessagesFile, ITransUnit, TranslationMessagesFileReader} from './i-translation-messages-file';
import {ITranslationMessagesFile} from './i-translation-messages-file';
import {ProgramOptions, IConfigFile} from './i-xliff-merge-options';
import {NgxTranslateExtractor} from './ngx-translate-extractor';
import {ITransUnit} from './i-trans-unit';
import {TranslationMessagesFileReader} from './translation-messages-file-reader';

@@ -18,33 +22,2 @@ /**

export interface ProgramOptions {
quiet?: boolean;
verbose?: boolean;
profilePath?: string;
languages?: string[];
}
/**
* Definition of the possible values used in the config file
*/
export interface IConfigFile {
// content is wrapped in "xliffmergeOptions" to allow to use it embedded in another config file (e.g. tsconfig.json)
xliffmergeOptions?: IXliffMergeOptions;
}
export interface IXliffMergeOptions {
quiet?: boolean; // Flag to only output error messages
verbose?: boolean; // Flag to generate debug output messages
defaultLanguage?: string; // the default language (the language, which is used in the original templates)
languages?: string[]; // all languages, if not specified via commandline
srcDir?: string; // Directory, where the master file is
i18nFile?: string; // master file, if not absolute, it is relative to srcDir
i18nFormat?: string; // xlf or xmb
encoding?: string; // encoding to write xml
genDir?: string; // directory, where the files are written to
angularCompilerOptions?: {
genDir?: string; // same as genDir, just to be compatible with ng-xi18n
};
removeUnusedIds?: boolean;
}
export class XliffMerge {

@@ -185,2 +158,11 @@

/**
* Return the name of the generated ngx-translation file for given lang.
* @param lang
* @return {string}
*/
public generatedNgxTranslateFile(lang: string): string {
return this.parameters.generatedNgxTranslateFile(lang);
}
private readMaster() {

@@ -201,3 +183,3 @@ this.master = TranslationMessagesFileReader.fromFile(this.parameters.i18nFormat(), this.parameters.i18nFile(), this.parameters.encoding());

this.master.setSourceLanguage(this.parameters.defaultLanguage());
this.master.save();
TranslationMessagesFileReader.save(this.master);
this.commandOutput.warn('changed master source-language="%s" to "%s"', sourceLang, this.parameters.defaultLanguage());

@@ -215,2 +197,6 @@ }

}
if (this.parameters.supportNgxTranslate()) {
let languageSpecificMessagesFile: ITranslationMessagesFile = TranslationMessagesFileReader.fromFile(this.parameters.i18nFormat(), languageXliffFile, this.parameters.encoding());
NgxTranslateExtractor.extract(languageSpecificMessagesFile, this.parameters.generatedNgxTranslateFile(lang));
}
}

@@ -238,3 +224,3 @@

// write it to file
languageSpecificMessagesFile.save();
TranslationMessagesFileReader.save(languageSpecificMessagesFile);
this.commandOutput.info('created new file "%s" for target-language="%s"', languageXliffFilePath, lang);

@@ -293,3 +279,3 @@ if (!isDefaultLang) {

// write it to file
languageSpecificMessagesFile.save();
TranslationMessagesFileReader.save(languageSpecificMessagesFile);
this.commandOutput.info('updated file "%s" for target-language="%s"', languageXliffFilePath, lang);

@@ -296,0 +282,0 @@ if (newCount > 0 && !isDefaultLang) {

import * as cheerio from "cheerio";
import {ITranslationMessagesFile, ITransUnit} from './i-translation-messages-file';
import {XmlReader} from './xml-reader';
import {ITranslationMessagesFile} from './i-translation-messages-file';
import {isNullOrUndefined, format} from 'util';
import {FileUtil} from '../common/file-util';
import {ITransUnit} from './i-trans-unit';
/**

@@ -34,2 +33,5 @@ * Created by martin on 10.03.2017.

/**
* the translated value (containing all markup, depends on the concrete format used).
*/
public targetContent(): string {

@@ -40,2 +42,29 @@ // in fact, target and source are just the same in xmb

/**
* the translated value, but all placeholders are replaced with {{n}} (starting at 0)
* and all embedded html is replaced by direct html markup.
*/
targetContentNormalized(): string {
let directHtml = this.targetContent();
if (!directHtml) {
return directHtml;
}
let normalized = directHtml;
let re0: RegExp = /<ph name="INTERPOLATION"><ex>INTERPOLATION<\/ex><\/ph>/g;
normalized = normalized.replace(re0, '{{0}}');
let reN: RegExp = /<ph name="INTERPOLATION_1"><ex>INTERPOLATION_(\d*)<\/ex><\/ph>/g;
normalized = normalized.replace(reN, '{{$1}}');
let reStartAnyTag: RegExp = /<ph name="START_\w*"><ex>&amp;lt;(\w*)&amp;gt;<\/ex><\/ph>/g;
normalized = normalized.replace(reStartAnyTag, '<$1>');
let reCloseAnyTag: RegExp = /<ph name="CLOSE_\w*"><ex>&amp;lt;\/(\w*)&amp;gt;<\/ex><\/ph>/g;
normalized = normalized.replace(reCloseAnyTag, '</$1>');
return normalized;
}
/**
* State of the translation.
* (not supported in xmb)
*/
public targetState(): string {

@@ -46,2 +75,21 @@ return null; // not supported in xmb

/**
* The description set in the template as value of the i18n-attribute.
* e.g. i18n="mydescription".
* In xmb this is stored in the attribute "desc".
*/
public description(): string {
return cheerio(this._msg).attr('desc');
}
/**
* The meaning (intent) set in the template as value of the i18n-attribute.
* This is the part in front of the | symbol.
* e.g. i18n="meaning|mydescription".
* In xmb this is stored in the attribute "meaning".
*/
public meaning(): string {
return cheerio(this._msg).attr('meaning');
}
/**
* the real xml element used for trans unit.

@@ -77,20 +125,6 @@ * Here it is a <msg> element.

/**
* Read an xmb-File.
* @param path Path to file
* @param encoding optional encoding of the xml.
* This is read from the file, but if you know it before, you can avoid reading the file twice.
* @return {XmbFile}
*/
public static fromFile(path: string, encoding?: string): XmbFile {
let xmb = new XmbFile();
let xmlContent = XmlReader.readXmlFileContent(path, encoding);
xmb.initializeFromContent(xmlContent.content, path, xmlContent.encoding);
return xmb;
}
private _filename: string;
private filename: string;
private _encoding: string;
private encoding: string;
private xmbContent: CheerioStatic;

@@ -104,11 +138,23 @@

constructor() {
/**
* Create an xmb-File from source.
* @param xmlString file content
* @param path Path to file
* @param encoding optional encoding of the xml.
* This is read from the file, but if you know it before, you can avoid reading the file twice.
* @return {XmbFile}
*/
constructor(xmlString: string, path: string, encoding: string) {
this._warnings = [];
this._numberOfTransUnitsWithMissingId = 0;
this.initializeFromContent(xmlString, path, encoding);
}
private initializeFromContent(xmlString: string, path: string, encoding: string): XmbFile {
this.filename = path;
this.encoding = encoding;
this._filename = path;
this._encoding = encoding;
this.xmbContent = cheerio.load(xmlString, CheerioOptions);
if (this.xmbContent('messagebundle').length != 1) {
throw new Error(format('File "%s" seems to be no xmb file (should contain a messagebundle element)', path));
}
return this;

@@ -238,7 +284,22 @@ }

/**
* Save edited content to file.
* The filename where the data is read from.
*/
public save() {
FileUtil.replaceContent(this.filename, this.xmbContent.xml(), this.encoding);
public filename(): string {
return this._filename;
}
/**
* The encoding if the xml content (UTF-8, ISO-8859-1, ...)
*/
public encoding(): string {
return this._encoding;
}
/**
* The xml to be saved after changes are made.
*/
public editedContent(): string {
return this.xmbContent.xml();
}
}

@@ -10,2 +10,4 @@ This directory contains some testdata.

ngttranslate.xlf is an extracted master with some messages with description and meaning to be used with ngx-translate.
The xmb files are the same in principle, but for format xmb.

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc