From efa2cfcd37ecbf0f52d197c5c4c078c3e5cb52c7 Mon Sep 17 00:00:00 2001 From: Martin Roob Date: Wed, 5 Apr 2017 16:02:19 +0200 Subject: [PATCH 1/9] xmbfile with attached master --- src/tsconfig.json | 2 +- .../translation-messages-file-factory.ts | 54 ++++++++++++++-- .../translation-messages-file-reader.ts | 46 +++++++++++--- src/xliffmerge/xliff-merge.spec.ts | 62 ++++++++++++++++++- src/xliffmerge/xmb-file.ts | 60 +++++++++++++++--- 5 files changed, 201 insertions(+), 23 deletions(-) diff --git a/src/tsconfig.json b/src/tsconfig.json index 18196c5..db505ac 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -10,7 +10,7 @@ "moduleResolution": "node", "outDir": "../dist", "sourceMap": true, - "target": "es5", + "target": "es6", "typeRoots": [ "../node_modules/@types" ] diff --git a/src/xliffmerge/translation-messages-file-factory.ts b/src/xliffmerge/translation-messages-file-factory.ts index e49e86d..4de8595 100644 --- a/src/xliffmerge/translation-messages-file-factory.ts +++ b/src/xliffmerge/translation-messages-file-factory.ts @@ -15,22 +15,66 @@ export class TranslationMessagesFileFactory { /** * Read file function, result depends on format, either XliffFile or XmbFile. - * @param format + * @param format currently 'xlf' or 'xmb' are supported * @param xmlContent the file content * @param path the path of the file (only used to remember it) - * @param encoding - * @return {XliffFile} + * @param encoding utf-8, ... used to parse XML. + * This is read from the file, but if you know it before, you can avoid reading the file twice. + * @param optionalMaster in case of xmb the master file, that contains the original texts. + * (this is used to support state infos, that are based on comparing original with translated version) + * Ignored for other formats. + * @return {ITranslationMessagesFile} either XliffFile or XmbFile */ - public static fromFileContent(i18nFormat: string, xmlContent: string, path: string, encoding: string): ITranslationMessagesFile { + public static fromFileContent(i18nFormat: string, + xmlContent: string, + path: string, + encoding: string, + optionalMaster?: {xmlContent: string, path: string, encoding: string}): ITranslationMessagesFile { if (i18nFormat === 'xlf') { return new XliffFile(xmlContent, path, encoding); } if (i18nFormat === 'xmb') { - return new XmbFile(xmlContent, path, encoding); + return new XmbFile(xmlContent, path, encoding, optionalMaster); } else { throw new Error(format('oops, unsupported format "%s"', i18nFormat)); } } + /** + * Read file function for any file with unknown format. + * This functions tries to guess the format based on the filename and the content of the file. + * Result depends on detected format, either XliffFile or XmbFile. + * @param xmlContent the file content + * @param path the path of the file (only used to remember it) + * @param encoding utf-8, ... used to parse XML. + * This is read from the file, but if you know it before, you can avoid reading the file twice. + * @param optionalMaster in case of xmb the master file, that contains the original texts. + * (this is used to support state infos, that are based on comparing original with translated version) + * Ignored for other formats. + * @return {ITranslationMessagesFile} either XliffFile or XmbFile + */ + public static fromUnknownFormatFileContent(xmlContent: string, + path: string, + encoding: string, + optionalMaster?: {xmlContent: string, path: string, encoding: string}): ITranslationMessagesFile { + let formatCandidates = ['xlf', 'xmb']; + if (path && path.endsWith('xmb')) { + formatCandidates = ['xmb', 'xlf']; + } + // try all candidate formats to get the rigth one + for (let i = 0; i < formatCandidates.length; i++) { + const format = formatCandidates[i]; + try { + const translationFile = TranslationMessagesFileFactory.fromFileContent(format, xmlContent, path, encoding, optionalMaster); + if (translationFile) { + return translationFile; + } + } catch (e) { + // seams to be the wrong format + } + } + throw new Error(format('could not identify file format, it is neiter XLIFF nor XMB')); + } + } diff --git a/src/xliffmerge/translation-messages-file-reader.ts b/src/xliffmerge/translation-messages-file-reader.ts index 1ce6d49..9a68b45 100644 --- a/src/xliffmerge/translation-messages-file-reader.ts +++ b/src/xliffmerge/translation-messages-file-reader.ts @@ -7,6 +7,7 @@ import {XliffFile} from './xliff-file'; import {XmbFile} from './xmb-file'; import {format} from 'util'; import {FileUtil} from '../common/file-util'; +import {TranslationMessagesFileFactory} from './translation-messages-file-factory'; /** * Helper class to read translation files depending on format. @@ -20,15 +21,46 @@ export class TranslationMessagesFileReader { * @param encoding * @return {XliffFile} */ - public static fromFile(i18nFormat: string, path: string, encoding: string): ITranslationMessagesFile { + public static fromFile(i18nFormat: string, + path: string, + encoding: string, + optionalMasterFilePath?: string): ITranslationMessagesFile { let xmlContent = XmlReader.readXmlFileContent(path, encoding); - if (i18nFormat === 'xlf') { - return new XliffFile(xmlContent.content, path, xmlContent.encoding); - } - if (i18nFormat === 'xmb') { - return new XmbFile(xmlContent.content, path, xmlContent.encoding); + let optionalMaster = TranslationMessagesFileReader.masterFileContent(optionalMasterFilePath, encoding); + return TranslationMessagesFileFactory.fromFileContent(i18nFormat, xmlContent.content, path, xmlContent.encoding, optionalMaster); + } + + /** + * Read file function, result depends on format, either XliffFile or XmbFile. + * @param format + * @param path + * @param encoding + * @return {XliffFile} + */ + public static fromUnknownFormatFile(path: string, + encoding: string, + optionalMasterFilePath?: string): ITranslationMessagesFile { + let xmlContent = XmlReader.readXmlFileContent(path, encoding); + let optionalMaster = TranslationMessagesFileReader.masterFileContent(optionalMasterFilePath, encoding); + return TranslationMessagesFileFactory.fromUnknownFormatFileContent(xmlContent.content, path, xmlContent.encoding, optionalMaster); + } + + /** + * Read master xmb file + * @param optionalMasterFilePath + * @param encoding + * @return {any} content and encoding of file + */ + private static masterFileContent(optionalMasterFilePath: string, encoding: string): {xmlContent: string, path: string, encoding: string} { + if (optionalMasterFilePath) { + let masterXmlContent = XmlReader.readXmlFileContent(optionalMasterFilePath, encoding); + return { + xmlContent: masterXmlContent.content, + path: optionalMasterFilePath, + encoding: masterXmlContent.encoding + }; } else { - throw new Error(format('oops, unsupported format "%s"', i18nFormat)); + return null; } } diff --git a/src/xliffmerge/xliff-merge.spec.ts b/src/xliffmerge/xliff-merge.spec.ts index 62f41fb..41b27f7 100644 --- a/src/xliffmerge/xliff-merge.spec.ts +++ b/src/xliffmerge/xliff-merge.spec.ts @@ -45,6 +45,14 @@ describe('XliffMerge test spec', () => { return TranslationMessagesFileReader.fromFile('xmb', path, ENCODING); } + /** + * Helper function to read Xmb from 2 Files, the xmb and the master + * @type {string} + */ + function readXmbWithMaster(path: string, masterPath: string): XmbFile { + return TranslationMessagesFileReader.fromFile('xmb', path, ENCODING, masterPath); + } + describe('test the tooling used in the tests', () => { it('should write output to string (Test WriterToString)', () => { let ws: WriterToString = new WriterToString(); @@ -603,8 +611,8 @@ describe('XliffMerge test spec', () => { xliffMergeCmd.run(); expect(ws.writtenData()).not.toContain('ERROR'); let langFile: ITranslationMessagesFile = readXmb(xliffMergeCmd.generatedI18nFile('de')); - expect(langFile.sourceLanguage()).toBeFalsy(); // not supported in xmb - expect(langFile.targetLanguage()).toBeFalsy(); + expect(langFile.sourceLanguage()).toBeFalsy(); // not supported in xmb when there is no master + expect(langFile.targetLanguage()).toBe('de'); // guess from filename langFile.forEachTransUnit((tu: ITransUnit) => { expect(tu.targetContent()).toBe(tu.sourceContent()); expect(tu.targetState()).toBeFalsy(); @@ -717,6 +725,56 @@ describe('XliffMerge test spec', () => { done(); }); + it('should return status new for all trans units using format xmb with master 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', 'en']}, profileContent); + xliffMergeCmd.run(); + expect(ws.writtenData()).not.toContain('ERROR'); + let langFileEnglish: ITranslationMessagesFile = readXmbWithMaster(xliffMergeCmd.generatedI18nFile('en'), MASTER); + langFileEnglish.forEachTransUnit((tu: ITransUnit) => { + expect(tu.targetState()).toBe('new'); + }); + done(); + }); + + it('should return status final for a translated trans unit using format xmb with master 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', 'en']}, profileContent); + xliffMergeCmd.run(); + expect(ws.writtenData()).not.toContain('ERROR'); + let langFileEnglish: ITranslationMessagesFile = readXmbWithMaster(xliffMergeCmd.generatedI18nFile('en'), MASTER); + // now translate some texts in the English version + let tu: ITransUnit = langFileEnglish.transUnitWithId(ID_TRANSLATED_MYFIRST); + expect(tu).toBeTruthy(); + expect(tu.sourceContent()).toBe('Meine erste I18N-Anwendung'); + expect(tu.targetState()).toBe('new'); + langFileEnglish.translate(tu, 'my first i18n application'); + expect(tu.targetState()).toBe('final'); + done(); + }); + }); describe('ngx-translate processing for format xmb', () => { diff --git a/src/xliffmerge/xmb-file.ts b/src/xliffmerge/xmb-file.ts index 1493288..aa722da 100644 --- a/src/xliffmerge/xmb-file.ts +++ b/src/xliffmerge/xmb-file.ts @@ -18,7 +18,7 @@ const CheerioOptions: CheerioOptionsInterface = { class TransUnit implements ITransUnit { - constructor(private _msg: CheerioElement, private _id: string) { + constructor(private _msg: CheerioElement, private _id: string, private _sourceTransUnitFromMaster: ITransUnit) { } @@ -64,8 +64,17 @@ class TransUnit implements ITransUnit { /** * State of the translation. * (not supported in xmb) + * If we have a master, we assumed it is translated if the content is not the same as the masters one. */ public targetState(): string { + if (this._sourceTransUnitFromMaster) { + let sourceContent = this._sourceTransUnitFromMaster.sourceContent(); + if (!sourceContent || sourceContent == this.targetContent()) { + return 'new'; + } else { + return 'final'; + } + } return null; // not supported in xmb } @@ -131,27 +140,37 @@ export class XmbFile implements ITranslationMessagesFile { private _warnings: string[]; private _numberOfTransUnitsWithMissingId: number; + // attached master file, if any + // used as source to determine state ... + private _masterFile: XmbFile; + /** * 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. + * @param optionalMaster in case of xmb the master file, that contains the original texts. + * (this is used to support state infos, that are based on comparing original with translated version) * @return {XmbFile} */ - constructor(xmlString: string, path: string, encoding: string) { + constructor(xmlString: string, path: string, encoding: string, optionalMaster?: {xmlContent: string, path: string, encoding: string}) { this._warnings = []; this._numberOfTransUnitsWithMissingId = 0; - this.initializeFromContent(xmlString, path, encoding); + this.initializeFromContent(xmlString, path, encoding, optionalMaster); } - private initializeFromContent(xmlString: string, path: string, encoding: string): XmbFile { + private initializeFromContent(xmlString: string, path: string, encoding: string, optionalMaster?: {xmlContent: string, path: string, encoding: string}): XmbFile { 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)); } + if (optionalMaster) { + this._masterFile = new XmbFile(optionalMaster.xmlContent, optionalMaster.path, optionalMaster.encoding); + // TODO check, wether this can be the master ... + } return this; } @@ -165,7 +184,11 @@ export class XmbFile implements ITranslationMessagesFile { this._warnings.push(format('oops, msg without "id" found in master, please check file %s', this.filename)); this._numberOfTransUnitsWithMissingId++; } - this.transUnits.push(new TransUnit(msg, id)); + let masterUnit: ITransUnit = null; + if (this._masterFile) { + masterUnit = this._masterFile.transUnitWithId(id); + } + this.transUnits.push(new TransUnit(msg, id, masterUnit)); }); } } @@ -200,13 +223,33 @@ export class XmbFile implements ITranslationMessagesFile { return this._numberOfTransUnitsWithMissingId; } + /** + * Guess language from filename. + * If filename is foo.xy.xmb, than language is assumed to be xy. + * @return {string} Language or null + */ + private guessLanguageFromFilename(): string { + if (this._filename) { + let parts: string[] = this._filename.split('.'); + if (parts.length > 2 && parts[parts.length -1].toLowerCase() == 'xmb') { + return parts[parts.length - 2]; + } + } + return null; + } + /** * Get source language. * Unsupported in xmb. + * Try to guess it from master filename if any.. * @return {string} */ public sourceLanguage(): string { - return null; + if (this._masterFile) { + return this._masterFile.guessLanguageFromFilename(); + } else { + return null; + } } /** @@ -221,10 +264,11 @@ export class XmbFile implements ITranslationMessagesFile { /** * Get target language. * Unsupported in xmb. + * Try to guess it from filename if any.. * @return {string} */ public targetLanguage(): string { - return null; + return this.guessLanguageFromFilename(); } /** @@ -283,7 +327,7 @@ export class XmbFile implements ITranslationMessagesFile { } /** - * The encoding if the xml content (UTF-8, ISO-8859-1, ...) + * The encoding of the xml content (UTF-8, ISO-8859-1, ...) */ public encoding(): string { return this._encoding; From 9143f6ecbb710df5f7c0139cea15fec9456c43d0 Mon Sep 17 00:00:00 2001 From: Martin Roob Date: Fri, 7 Apr 2017 14:18:27 +0200 Subject: [PATCH 2/9] handling xmb with master file --- src/tsconfig.json | 2 +- src/xliffmerge/i-translation-messages-file.ts | 6 ++ .../translation-messages-file-factory.ts | 54 +++++++++++++-- .../translation-messages-file-reader.ts | 46 +++++++++++-- src/xliffmerge/xliff-file.ts | 8 +++ src/xliffmerge/xliff-merge.spec.ts | 62 ++++++++++++++++- src/xliffmerge/xmb-file.ts | 69 ++++++++++++++++--- 7 files changed, 224 insertions(+), 23 deletions(-) diff --git a/src/tsconfig.json b/src/tsconfig.json index 18196c5..db505ac 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -10,7 +10,7 @@ "moduleResolution": "node", "outDir": "../dist", "sourceMap": true, - "target": "es5", + "target": "es6", "typeRoots": [ "../node_modules/@types" ] diff --git a/src/xliffmerge/i-translation-messages-file.ts b/src/xliffmerge/i-translation-messages-file.ts index 40eed46..6016372 100644 --- a/src/xliffmerge/i-translation-messages-file.ts +++ b/src/xliffmerge/i-translation-messages-file.ts @@ -11,6 +11,12 @@ import {ITransUnit} from './i-trans-unit'; */ export interface ITranslationMessagesFile { + /** + * File type. + * Currently 'XLIFF 1.2' or 'XMB' + */ + fileType(): string; + /** * warnings found in the file */ diff --git a/src/xliffmerge/translation-messages-file-factory.ts b/src/xliffmerge/translation-messages-file-factory.ts index e49e86d..4de8595 100644 --- a/src/xliffmerge/translation-messages-file-factory.ts +++ b/src/xliffmerge/translation-messages-file-factory.ts @@ -15,22 +15,66 @@ export class TranslationMessagesFileFactory { /** * Read file function, result depends on format, either XliffFile or XmbFile. - * @param format + * @param format currently 'xlf' or 'xmb' are supported * @param xmlContent the file content * @param path the path of the file (only used to remember it) - * @param encoding - * @return {XliffFile} + * @param encoding utf-8, ... used to parse XML. + * This is read from the file, but if you know it before, you can avoid reading the file twice. + * @param optionalMaster in case of xmb the master file, that contains the original texts. + * (this is used to support state infos, that are based on comparing original with translated version) + * Ignored for other formats. + * @return {ITranslationMessagesFile} either XliffFile or XmbFile */ - public static fromFileContent(i18nFormat: string, xmlContent: string, path: string, encoding: string): ITranslationMessagesFile { + public static fromFileContent(i18nFormat: string, + xmlContent: string, + path: string, + encoding: string, + optionalMaster?: {xmlContent: string, path: string, encoding: string}): ITranslationMessagesFile { if (i18nFormat === 'xlf') { return new XliffFile(xmlContent, path, encoding); } if (i18nFormat === 'xmb') { - return new XmbFile(xmlContent, path, encoding); + return new XmbFile(xmlContent, path, encoding, optionalMaster); } else { throw new Error(format('oops, unsupported format "%s"', i18nFormat)); } } + /** + * Read file function for any file with unknown format. + * This functions tries to guess the format based on the filename and the content of the file. + * Result depends on detected format, either XliffFile or XmbFile. + * @param xmlContent the file content + * @param path the path of the file (only used to remember it) + * @param encoding utf-8, ... used to parse XML. + * This is read from the file, but if you know it before, you can avoid reading the file twice. + * @param optionalMaster in case of xmb the master file, that contains the original texts. + * (this is used to support state infos, that are based on comparing original with translated version) + * Ignored for other formats. + * @return {ITranslationMessagesFile} either XliffFile or XmbFile + */ + public static fromUnknownFormatFileContent(xmlContent: string, + path: string, + encoding: string, + optionalMaster?: {xmlContent: string, path: string, encoding: string}): ITranslationMessagesFile { + let formatCandidates = ['xlf', 'xmb']; + if (path && path.endsWith('xmb')) { + formatCandidates = ['xmb', 'xlf']; + } + // try all candidate formats to get the rigth one + for (let i = 0; i < formatCandidates.length; i++) { + const format = formatCandidates[i]; + try { + const translationFile = TranslationMessagesFileFactory.fromFileContent(format, xmlContent, path, encoding, optionalMaster); + if (translationFile) { + return translationFile; + } + } catch (e) { + // seams to be the wrong format + } + } + throw new Error(format('could not identify file format, it is neiter XLIFF nor XMB')); + } + } diff --git a/src/xliffmerge/translation-messages-file-reader.ts b/src/xliffmerge/translation-messages-file-reader.ts index 1ce6d49..9a68b45 100644 --- a/src/xliffmerge/translation-messages-file-reader.ts +++ b/src/xliffmerge/translation-messages-file-reader.ts @@ -7,6 +7,7 @@ import {XliffFile} from './xliff-file'; import {XmbFile} from './xmb-file'; import {format} from 'util'; import {FileUtil} from '../common/file-util'; +import {TranslationMessagesFileFactory} from './translation-messages-file-factory'; /** * Helper class to read translation files depending on format. @@ -20,15 +21,46 @@ export class TranslationMessagesFileReader { * @param encoding * @return {XliffFile} */ - public static fromFile(i18nFormat: string, path: string, encoding: string): ITranslationMessagesFile { + public static fromFile(i18nFormat: string, + path: string, + encoding: string, + optionalMasterFilePath?: string): ITranslationMessagesFile { let xmlContent = XmlReader.readXmlFileContent(path, encoding); - if (i18nFormat === 'xlf') { - return new XliffFile(xmlContent.content, path, xmlContent.encoding); - } - if (i18nFormat === 'xmb') { - return new XmbFile(xmlContent.content, path, xmlContent.encoding); + let optionalMaster = TranslationMessagesFileReader.masterFileContent(optionalMasterFilePath, encoding); + return TranslationMessagesFileFactory.fromFileContent(i18nFormat, xmlContent.content, path, xmlContent.encoding, optionalMaster); + } + + /** + * Read file function, result depends on format, either XliffFile or XmbFile. + * @param format + * @param path + * @param encoding + * @return {XliffFile} + */ + public static fromUnknownFormatFile(path: string, + encoding: string, + optionalMasterFilePath?: string): ITranslationMessagesFile { + let xmlContent = XmlReader.readXmlFileContent(path, encoding); + let optionalMaster = TranslationMessagesFileReader.masterFileContent(optionalMasterFilePath, encoding); + return TranslationMessagesFileFactory.fromUnknownFormatFileContent(xmlContent.content, path, xmlContent.encoding, optionalMaster); + } + + /** + * Read master xmb file + * @param optionalMasterFilePath + * @param encoding + * @return {any} content and encoding of file + */ + private static masterFileContent(optionalMasterFilePath: string, encoding: string): {xmlContent: string, path: string, encoding: string} { + if (optionalMasterFilePath) { + let masterXmlContent = XmlReader.readXmlFileContent(optionalMasterFilePath, encoding); + return { + xmlContent: masterXmlContent.content, + path: optionalMasterFilePath, + encoding: masterXmlContent.encoding + }; } else { - throw new Error(format('oops, unsupported format "%s"', i18nFormat)); + return null; } } diff --git a/src/xliffmerge/xliff-file.ts b/src/xliffmerge/xliff-file.ts index 68b5c2d..80356d9 100644 --- a/src/xliffmerge/xliff-file.ts +++ b/src/xliffmerge/xliff-file.ts @@ -183,6 +183,14 @@ export class XliffFile implements ITranslationMessagesFile { return this; } + /** + * File type. + * Here 'XLIFF 1.2' + */ + public fileType(): string { + return 'XLIFF 1.2'; + } + public forEachTransUnit(callback: ((transunit: ITransUnit) => void)) { this.initializeTransUnits(); this.transUnits.forEach((tu) => callback(tu)); diff --git a/src/xliffmerge/xliff-merge.spec.ts b/src/xliffmerge/xliff-merge.spec.ts index 62f41fb..41b27f7 100644 --- a/src/xliffmerge/xliff-merge.spec.ts +++ b/src/xliffmerge/xliff-merge.spec.ts @@ -45,6 +45,14 @@ describe('XliffMerge test spec', () => { return TranslationMessagesFileReader.fromFile('xmb', path, ENCODING); } + /** + * Helper function to read Xmb from 2 Files, the xmb and the master + * @type {string} + */ + function readXmbWithMaster(path: string, masterPath: string): XmbFile { + return TranslationMessagesFileReader.fromFile('xmb', path, ENCODING, masterPath); + } + describe('test the tooling used in the tests', () => { it('should write output to string (Test WriterToString)', () => { let ws: WriterToString = new WriterToString(); @@ -603,8 +611,8 @@ describe('XliffMerge test spec', () => { xliffMergeCmd.run(); expect(ws.writtenData()).not.toContain('ERROR'); let langFile: ITranslationMessagesFile = readXmb(xliffMergeCmd.generatedI18nFile('de')); - expect(langFile.sourceLanguage()).toBeFalsy(); // not supported in xmb - expect(langFile.targetLanguage()).toBeFalsy(); + expect(langFile.sourceLanguage()).toBeFalsy(); // not supported in xmb when there is no master + expect(langFile.targetLanguage()).toBe('de'); // guess from filename langFile.forEachTransUnit((tu: ITransUnit) => { expect(tu.targetContent()).toBe(tu.sourceContent()); expect(tu.targetState()).toBeFalsy(); @@ -717,6 +725,56 @@ describe('XliffMerge test spec', () => { done(); }); + it('should return status new for all trans units using format xmb with master 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', 'en']}, profileContent); + xliffMergeCmd.run(); + expect(ws.writtenData()).not.toContain('ERROR'); + let langFileEnglish: ITranslationMessagesFile = readXmbWithMaster(xliffMergeCmd.generatedI18nFile('en'), MASTER); + langFileEnglish.forEachTransUnit((tu: ITransUnit) => { + expect(tu.targetState()).toBe('new'); + }); + done(); + }); + + it('should return status final for a translated trans unit using format xmb with master 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', 'en']}, profileContent); + xliffMergeCmd.run(); + expect(ws.writtenData()).not.toContain('ERROR'); + let langFileEnglish: ITranslationMessagesFile = readXmbWithMaster(xliffMergeCmd.generatedI18nFile('en'), MASTER); + // now translate some texts in the English version + let tu: ITransUnit = langFileEnglish.transUnitWithId(ID_TRANSLATED_MYFIRST); + expect(tu).toBeTruthy(); + expect(tu.sourceContent()).toBe('Meine erste I18N-Anwendung'); + expect(tu.targetState()).toBe('new'); + langFileEnglish.translate(tu, 'my first i18n application'); + expect(tu.targetState()).toBe('final'); + done(); + }); + }); describe('ngx-translate processing for format xmb', () => { diff --git a/src/xliffmerge/xmb-file.ts b/src/xliffmerge/xmb-file.ts index 1493288..1411fec 100644 --- a/src/xliffmerge/xmb-file.ts +++ b/src/xliffmerge/xmb-file.ts @@ -18,7 +18,7 @@ const CheerioOptions: CheerioOptionsInterface = { class TransUnit implements ITransUnit { - constructor(private _msg: CheerioElement, private _id: string) { + constructor(private _msg: CheerioElement, private _id: string, private _sourceTransUnitFromMaster: ITransUnit) { } @@ -64,8 +64,17 @@ class TransUnit implements ITransUnit { /** * State of the translation. * (not supported in xmb) + * If we have a master, we assumed it is translated if the content is not the same as the masters one. */ public targetState(): string { + if (this._sourceTransUnitFromMaster) { + let sourceContent = this._sourceTransUnitFromMaster.sourceContent(); + if (!sourceContent || sourceContent == this.targetContent()) { + return 'new'; + } else { + return 'final'; + } + } return null; // not supported in xmb } @@ -131,27 +140,38 @@ export class XmbFile implements ITranslationMessagesFile { private _warnings: string[]; private _numberOfTransUnitsWithMissingId: number; + // attached master file, if any + // used as source to determine state ... + private _masterFile: XmbFile; + /** * 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. + * @param optionalMaster in case of xmb the master file, that contains the original texts. + * (this is used to support state infos, that are based on comparing original with translated version) * @return {XmbFile} */ - constructor(xmlString: string, path: string, encoding: string) { + constructor(xmlString: string, path: string, encoding: string, optionalMaster?: {xmlContent: string, path: string, encoding: string}) { this._warnings = []; this._numberOfTransUnitsWithMissingId = 0; - this.initializeFromContent(xmlString, path, encoding); + this.initializeFromContent(xmlString, path, encoding, optionalMaster); } - private initializeFromContent(xmlString: string, path: string, encoding: string): XmbFile { + private initializeFromContent(xmlString: string, path: string, encoding: string, optionalMaster?: {xmlContent: string, path: string, encoding: string}): XmbFile { +console.log('creating xmb file', xmlString.length, path, optionalMaster); 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)); } + if (optionalMaster) { + this._masterFile = new XmbFile(optionalMaster.xmlContent, optionalMaster.path, optionalMaster.encoding); + // TODO check, wether this can be the master ... + } return this; } @@ -165,11 +185,23 @@ export class XmbFile implements ITranslationMessagesFile { this._warnings.push(format('oops, msg without "id" found in master, please check file %s', this.filename)); this._numberOfTransUnitsWithMissingId++; } - this.transUnits.push(new TransUnit(msg, id)); + let masterUnit: ITransUnit = null; + if (this._masterFile) { + masterUnit = this._masterFile.transUnitWithId(id); + } + this.transUnits.push(new TransUnit(msg, id, masterUnit)); }); } } + /** + * File type. + * Here 'XMB' + */ + public fileType(): string { + return 'XMB'; + } + public forEachTransUnit(callback: ((transunit: ITransUnit) => void)) { this.initializeTransUnits(); this.transUnits.forEach((tu) => callback(tu)); @@ -200,13 +232,33 @@ export class XmbFile implements ITranslationMessagesFile { return this._numberOfTransUnitsWithMissingId; } + /** + * Guess language from filename. + * If filename is foo.xy.xmb, than language is assumed to be xy. + * @return {string} Language or null + */ + private guessLanguageFromFilename(): string { + if (this._filename) { + let parts: string[] = this._filename.split('.'); + if (parts.length > 2 && parts[parts.length -1].toLowerCase() == 'xmb') { + return parts[parts.length - 2]; + } + } + return null; + } + /** * Get source language. * Unsupported in xmb. + * Try to guess it from master filename if any.. * @return {string} */ public sourceLanguage(): string { - return null; + if (this._masterFile) { + return this._masterFile.guessLanguageFromFilename(); + } else { + return null; + } } /** @@ -221,10 +273,11 @@ export class XmbFile implements ITranslationMessagesFile { /** * Get target language. * Unsupported in xmb. + * Try to guess it from filename if any.. * @return {string} */ public targetLanguage(): string { - return null; + return this.guessLanguageFromFilename(); } /** @@ -283,7 +336,7 @@ export class XmbFile implements ITranslationMessagesFile { } /** - * The encoding if the xml content (UTF-8, ISO-8859-1, ...) + * The encoding of the xml content (UTF-8, ISO-8859-1, ...) */ public encoding(): string { return this._encoding; From ba21fa8ae37f5e356f06b045cae6301c46809cec Mon Sep 17 00:00:00 2001 From: Martin Roob Date: Mon, 10 Apr 2017 14:58:39 +0200 Subject: [PATCH 3/9] using ngx-i18nsupport-lib --- package.json | 12 +- spec/support/jasmine.json | 2 +- src/common/command-output.js | 124 -- src/common/command-output.ts | 2 +- src/common/file-util.js | 96 - src/common/file-util.ts | 3 +- src/common/writer-to-string.js | 45 - src/index.ts | 6 - src/xliffmerge/i-trans-unit.ts | 62 - src/xliffmerge/i-translation-messages-file.js | 34 - src/xliffmerge/i-translation-messages-file.ts | 117 -- src/xliffmerge/ngx-translate-extractor.ts | 3 +- .../translation-messages-file-factory.ts | 80 - .../translation-messages-file-reader.ts | 8 +- src/xliffmerge/version.js | 7 - src/xliffmerge/xliff-file.js | 218 --- src/xliffmerge/xliff-file.ts | 330 ---- src/xliffmerge/xliff-merge-error.js | 20 - src/xliffmerge/xliff-merge-parameters.js | 264 --- src/xliffmerge/xliff-merge-parameters.ts | 2 +- src/xliffmerge/xliff-merge.js | 240 --- src/xliffmerge/xliff-merge.spec.js | 430 ----- src/xliffmerge/xliff-merge.spec.ts | 46 +- src/xliffmerge/xliff-merge.ts | 3 +- src/xliffmerge/xmb-file.js | 211 --- src/xliffmerge/xmb-file.ts | 351 ---- src/xliffmerge/xml-reader.js | 49 - yarn.lock | 1628 +++++++++++++++++ 28 files changed, 1665 insertions(+), 2728 deletions(-) delete mode 100644 src/common/command-output.js delete mode 100644 src/common/file-util.js delete mode 100644 src/common/writer-to-string.js delete mode 100644 src/index.ts delete mode 100644 src/xliffmerge/i-trans-unit.ts delete mode 100644 src/xliffmerge/i-translation-messages-file.js delete mode 100644 src/xliffmerge/i-translation-messages-file.ts delete mode 100644 src/xliffmerge/translation-messages-file-factory.ts delete mode 100644 src/xliffmerge/version.js delete mode 100644 src/xliffmerge/xliff-file.js delete mode 100644 src/xliffmerge/xliff-file.ts delete mode 100644 src/xliffmerge/xliff-merge-error.js delete mode 100644 src/xliffmerge/xliff-merge-parameters.js delete mode 100644 src/xliffmerge/xliff-merge.js delete mode 100644 src/xliffmerge/xliff-merge.spec.js delete mode 100644 src/xliffmerge/xmb-file.js delete mode 100644 src/xliffmerge/xmb-file.ts delete mode 100644 src/xliffmerge/xml-reader.js create mode 100644 yarn.lock diff --git a/package.json b/package.json index 7a89ae5..de0385c 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,6 @@ "main": "index.js", "module": "./src", "es2015": "./src", - "typings": "./ngx-i18nsupport.d.ts", "bin": { "xliffmerge": "./dist/xliffmerge/xliffmerge" }, @@ -18,7 +17,8 @@ "pretest": "npm run build", "test": "./node_modules/.bin/jasmine-node dist", "precover": "npm run build", - "cover": "./node_modules/.bin/istanbul cover --root dist --include-all-sources ./node_modules/jasmine-node/bin/jasmine-node -- dist" + "cover": "./node_modules/.bin/istanbul cover --root dist --include-all-sources ./node_modules/jasmine-node/bin/jasmine-node -- dist", + "prepublish": "npm run build" }, "repository": { "type": "git", @@ -28,7 +28,8 @@ "i18n", "tooling", "angular", - "xliff" + "xliff", + "xmb" ], "author": "Martin Roob (www.roobsoft.de)", "license": "MIT", @@ -37,7 +38,6 @@ }, "homepage": "https://github.com/martinroob/ngx-i18nsupport#readme", "devDependencies": { - "@types/cheerio": "^0.17.31", "@types/jasmine": "^2.5.43", "@types/node": "^7.0.5", "coveralls": "^2.11.16", @@ -48,7 +48,7 @@ }, "dependencies": { "chalk": "^1.1.3", - "cheerio": "^0.22.0", - "commander": "^2.9.0" + "commander": "^2.9.0", + "ngx-i18nsupport-lib": "^0.0.2" } } diff --git a/spec/support/jasmine.json b/spec/support/jasmine.json index a460644..e2a914c 100644 --- a/spec/support/jasmine.json +++ b/spec/support/jasmine.json @@ -1,5 +1,5 @@ { - "spec_dir": "dist/out-tsc", + "spec_dir": "dist", "spec_files": [ "**/*[sS]pec.js" ], diff --git a/src/common/command-output.js b/src/common/command-output.js deleted file mode 100644 index 29c51ad..0000000 --- a/src/common/command-output.js +++ /dev/null @@ -1,124 +0,0 @@ -/** - * Created by martin on 17.02.2017. - * Very simple class to control the output of a command. - * Output can be errors, warnings, infos and debug-Outputs. - * The output can be controlled via 2 flags, quiet and verbose. - * If quit is enabled only error messages are shown. - * If verbose is enabled, everything is shown. - * If both are not enabled (the default) errors, warnings and infos are shown. - * If not are enabled (strange), we assumed the default. - */ -"use strict"; -var chalk = require("chalk"); -var util = require("util"); -var LogLevel; -(function (LogLevel) { - LogLevel[LogLevel["ERROR"] = 0] = "ERROR"; - LogLevel[LogLevel["WARN"] = 1] = "WARN"; - LogLevel[LogLevel["INFO"] = 2] = "INFO"; - LogLevel[LogLevel["DEBUG"] = 3] = "DEBUG"; -})(LogLevel || (LogLevel = {})); -var CommandOutput = (function () { - function CommandOutput(stdout) { - this._quiet = false; - this._verbose = false; - if (stdout) { - this.outputStream = stdout; - } - else { - this.outputStream = process.stdout; - } - } - CommandOutput.prototype.setVerbose = function () { - this._verbose = true; - }; - CommandOutput.prototype.setQuiet = function () { - this._quiet = true; - }; - /** - * Test, wether verbose is enabled. - * @return {boolean} - */ - CommandOutput.prototype.verbose = function () { - return this._verbose; - }; - /** - * Test, wether queit is enabled. - * @return {boolean} - */ - CommandOutput.prototype.quiet = function () { - return this._quiet; - }; - CommandOutput.prototype.error = function (msg) { - var params = []; - for (var _i = 1; _i < arguments.length; _i++) { - params[_i - 1] = arguments[_i]; - } - this.log(LogLevel.ERROR, msg, params); - }; - CommandOutput.prototype.warn = function (msg) { - var params = []; - for (var _i = 1; _i < arguments.length; _i++) { - params[_i - 1] = arguments[_i]; - } - this.log(LogLevel.WARN, msg, params); - }; - CommandOutput.prototype.info = function (msg) { - var params = []; - for (var _i = 1; _i < arguments.length; _i++) { - params[_i - 1] = arguments[_i]; - } - this.log(LogLevel.INFO, msg, params); - }; - CommandOutput.prototype.debug = function (msg) { - var params = []; - for (var _i = 1; _i < arguments.length; _i++) { - params[_i - 1] = arguments[_i]; - } - this.log(LogLevel.DEBUG, msg, params); - }; - CommandOutput.prototype.log = function (level, msg, params) { - if (!this.isOutputEnabled(level)) { - return; - } - var coloredMessage; - switch (level) { - case LogLevel.ERROR: - coloredMessage = chalk.red('ERROR: ' + msg); - break; - case LogLevel.WARN: - coloredMessage = chalk.magenta('WARNING: ' + msg); - break; - default: - coloredMessage = chalk.gray('* ' + msg); - break; - } - var outMsg = util.format.apply(util, [coloredMessage].concat(params)); - this.outputStream.write(outMsg + '\n'); - }; - CommandOutput.prototype.isOutputEnabled = function (level) { - var quietEnabled, verboseEnabled; - if (this._quiet && this._verbose) { - quietEnabled = false; - verboseEnabled = false; - } - else { - quietEnabled = this._quiet; - verboseEnabled = this._verbose; - } - switch (level) { - case LogLevel.ERROR: - return true; // always output errors - case LogLevel.WARN: - return (!quietEnabled); - case LogLevel.INFO: - return (verboseEnabled && !quietEnabled); - case LogLevel.DEBUG: - return verboseEnabled; - default: - return true; - } - }; - return CommandOutput; -}()); -exports.CommandOutput = CommandOutput; diff --git a/src/common/command-output.ts b/src/common/command-output.ts index 3b3c366..a73630a 100644 --- a/src/common/command-output.ts +++ b/src/common/command-output.ts @@ -11,7 +11,7 @@ import * as chalk from "chalk"; import WritableStream = NodeJS.WritableStream; -import util = require('util'); +import * as util from "util"; enum LogLevel { "ERROR", diff --git a/src/common/file-util.js b/src/common/file-util.js deleted file mode 100644 index fe9b06b..0000000 --- a/src/common/file-util.js +++ /dev/null @@ -1,96 +0,0 @@ -"use strict"; -var fs = require("fs"); -/** - * Created by martin on 17.02.2017. - * Some (a few) simple utils for file operations. - * Just for convenience. - */ -var FileUtil = (function () { - function FileUtil() { - } - /** - * Check for existence. - * @param filename - * @return {boolean} - */ - FileUtil.exists = function (filename) { - return fs.existsSync(filename); - }; - /** - * Read a file. - * @param filename - * @param encoding - * @return {string} - */ - FileUtil.read = function (filename, encoding) { - return fs.readFileSync(filename, encoding); - }; - /** - * Write a file with given content. - * @param filename - * @param newContent - * @param encoding - */ - FileUtil.replaceContent = function (filename, newContent, encoding) { - fs.writeFileSync(filename, newContent, { encoding: encoding }); - }; - FileUtil.copy = function (srcFile, destFile) { - var BUF_LENGTH = 64 * 1024; - var buff = new Buffer(BUF_LENGTH); - var fdr = fs.openSync(srcFile, 'r'); - var fdw = fs.openSync(destFile, 'w'); - var bytesRead = 1; - var pos = 0; - while (bytesRead > 0) { - bytesRead = fs.readSync(fdr, buff, 0, BUF_LENGTH, pos); - fs.writeSync(fdw, buff, 0, bytesRead); - pos += bytesRead; - } - fs.closeSync(fdr); - fs.closeSync(fdw); - }; - /** - * Delete the folder and all of its content (rm -rf). - * @param path - */ - FileUtil.deleteFolderRecursive = function (path) { - var files = []; - if (fs.existsSync(path)) { - files = fs.readdirSync(path); - files.forEach(function (file, index) { - var curPath = path + "/" + file; - if (fs.lstatSync(curPath).isDirectory()) { - FileUtil.deleteFolderRecursive(curPath); - } - else { - fs.unlinkSync(curPath); - } - }); - fs.rmdirSync(path); - } - }; - ; - /** - * Delete folders content recursively, but do not delete folder. - * Folder is left empty at the end. - * @param path - */ - FileUtil.deleteFolderContentRecursive = function (path) { - var files = []; - if (fs.existsSync(path)) { - files = fs.readdirSync(path); - files.forEach(function (file, index) { - var curPath = path + "/" + file; - if (fs.lstatSync(curPath).isDirectory()) { - FileUtil.deleteFolderRecursive(curPath); - } - else { - fs.unlinkSync(curPath); - } - }); - } - }; - ; - return FileUtil; -}()); -exports.FileUtil = FileUtil; diff --git a/src/common/file-util.ts b/src/common/file-util.ts index ed811e2..050264a 100644 --- a/src/common/file-util.ts +++ b/src/common/file-util.ts @@ -1,4 +1,5 @@ -import fs = require("fs"); +import * as fs from "fs"; + /** * Created by martin on 17.02.2017. * Some (a few) simple utils for file operations. diff --git a/src/common/writer-to-string.js b/src/common/writer-to-string.js deleted file mode 100644 index 1373cef..0000000 --- a/src/common/writer-to-string.js +++ /dev/null @@ -1,45 +0,0 @@ -"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 stream_1 = require("stream"); -var util_1 = require("util"); -/** - * Created by martin on 20.02.2017. - * A helper class for testing. - * Can be used as a WritableStream and writes everything (synchronously) into a string, - * that can easily be read by the tests. - */ -var WriterToString = (function (_super) { - __extends(WriterToString, _super); - function WriterToString() { - var _this = _super.call(this) || this; - _this.resultString = ''; - return _this; - } - WriterToString.prototype._write = function (chunk, encoding, callback) { - var chunkString; - if (util_1.isString(chunk)) { - chunkString = chunk; - } - else if (chunk instanceof Buffer) { - chunkString = chunk.toString(); - } - else { - chunkString = new Buffer(chunk).toString(encoding); - } - this.resultString = this.resultString + chunkString; - callback(); - }; - /** - * Returns a string of everything, that was written to the stream so far. - * @return {string} - */ - WriterToString.prototype.writtenData = function () { - return this.resultString; - }; - return WriterToString; -}(stream_1.Writable)); -exports.WriterToString = WriterToString; diff --git a/src/index.ts b/src/index.ts deleted file mode 100644 index 235f033..0000000 --- a/src/index.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * Created by martin on 19.03.2017. - */ -export {TranslationMessagesFileFactory} from './xliffmerge/translation-messages-file-factory'; -export {ITranslationMessagesFile} from './xliffmerge/i-translation-messages-file'; -export {ITransUnit} from './xliffmerge/i-trans-unit'; diff --git a/src/xliffmerge/i-trans-unit.ts b/src/xliffmerge/i-trans-unit.ts deleted file mode 100644 index 557ae66..0000000 --- a/src/xliffmerge/i-trans-unit.ts +++ /dev/null @@ -1,62 +0,0 @@ -/** - * Created by martin on 19.03.2017. - */ - -/** - * Interface of a translation unit in a translation messages file. - */ -export interface ITransUnit { - - readonly id: string; - - sourceContent(): string; - - /** - * the translated value (containing all markup, depends on the concrete format used). - */ - targetContent(): string; - - /** - * 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; - - /** - * State of the translation. - * (new, final, ...) - */ - targetState(): string; - - /** - * The description set in the template as value of the i18n-attribute. - * e.g. i18n="mydescription". - */ - description(): string; - - /** - * 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". - */ - meaning(): 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); -} diff --git a/src/xliffmerge/i-translation-messages-file.js b/src/xliffmerge/i-translation-messages-file.js deleted file mode 100644 index de89c30..0000000 --- a/src/xliffmerge/i-translation-messages-file.js +++ /dev/null @@ -1,34 +0,0 @@ -"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; diff --git a/src/xliffmerge/i-translation-messages-file.ts b/src/xliffmerge/i-translation-messages-file.ts deleted file mode 100644 index 6016372..0000000 --- a/src/xliffmerge/i-translation-messages-file.ts +++ /dev/null @@ -1,117 +0,0 @@ -import {XliffFile} from './xliff-file'; -import {XmbFile} from './xmb-file'; -import {FileUtil} from '../common/file-util'; -import {format} from 'util'; -import {ITransUnit} from './i-trans-unit'; - -/** - * The Common interface of XliffFile and XmbFile. - * The merge process only uses this interface. - * Created by martin on 10.03.2017. - */ -export interface ITranslationMessagesFile { - - /** - * File type. - * Currently 'XLIFF 1.2' or 'XMB' - */ - fileType(): string; - - /** - * warnings found in the file - */ - warnings(): string[]; - - /** - * Number of translations found in the file. - */ - numberOfTransUnits(): number; - - /** - * Number of translations without id found in the file. - */ - numberOfTransUnitsWithMissingId(): number; - - /** - * Get source language. - * @return {string} - */ - sourceLanguage(): string; - - /** - * Edit the source language. - * @param language - */ - setSourceLanguage(language: string); - - /** - * Get target language. - * @return {string} - */ - targetLanguage(): string; - - /** - * Edit the target language. - * @param language - */ - setTargetLanguage(language: string); - - /** - * Loop over all Translation Units. - * @param callback - */ - forEachTransUnit(callback: ((transunit: ITransUnit) => void)); - - /** - * Get trans-unit with given id. - * @param id - * @return {Cheerio} - */ - transUnitWithId(id: string): ITransUnit; - - /** - * Edit functions following her - */ - - /** - * Add a new trans-unit. - * @param transUnit - */ - addNewTransUnit(transUnit: ITransUnit); - - /** - * Remove the trans-unit with the given id. - * @param id - */ - removeTransUnitWithId(id: string); - - /** - * Copy source to target to use it as dummy translation. - * (better than missing value) - */ - useSourceAsTarget(transUnit: ITransUnit, isDefaultLang: boolean); - - /** - * Translate a given trans unit. - * (very simple, just for tests) - * @param transUnit - * @param translation the translated string - */ - translate(transUnit: ITransUnit, translation: string); - - /** - * The filename where the data is read from. - */ - 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; - -} \ No newline at end of file diff --git a/src/xliffmerge/ngx-translate-extractor.ts b/src/xliffmerge/ngx-translate-extractor.ts index 9863019..16c5a89 100644 --- a/src/xliffmerge/ngx-translate-extractor.ts +++ b/src/xliffmerge/ngx-translate-extractor.ts @@ -1,7 +1,6 @@ -import {ITranslationMessagesFile} from './i-translation-messages-file'; +import {ITranslationMessagesFile, ITransUnit} from 'ngx-i18nsupport-lib'; import {FileUtil} from '../common/file-util'; import {isNullOrUndefined} from 'util'; -import {ITransUnit} from './i-trans-unit'; /** * Created by roobm on 15.03.2017. * A tool for extracting messages in ngx-translate format. diff --git a/src/xliffmerge/translation-messages-file-factory.ts b/src/xliffmerge/translation-messages-file-factory.ts deleted file mode 100644 index 4de8595..0000000 --- a/src/xliffmerge/translation-messages-file-factory.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * Created by roobm on 21.03.2017. - */ -import {ITranslationMessagesFile} from './i-translation-messages-file'; -import {XmlReader} from './xml-reader'; -import {XliffFile} from './xliff-file'; -import {XmbFile} from './xmb-file'; -import {format} from 'util'; - -/** - * Helper class to read translation files depending on format. - * This is part of the public api - */ -export class TranslationMessagesFileFactory { - - /** - * Read file function, result depends on format, either XliffFile or XmbFile. - * @param format currently 'xlf' or 'xmb' are supported - * @param xmlContent the file content - * @param path the path of the file (only used to remember it) - * @param encoding utf-8, ... used to parse XML. - * This is read from the file, but if you know it before, you can avoid reading the file twice. - * @param optionalMaster in case of xmb the master file, that contains the original texts. - * (this is used to support state infos, that are based on comparing original with translated version) - * Ignored for other formats. - * @return {ITranslationMessagesFile} either XliffFile or XmbFile - */ - public static fromFileContent(i18nFormat: string, - xmlContent: string, - path: string, - encoding: string, - optionalMaster?: {xmlContent: string, path: string, encoding: string}): ITranslationMessagesFile { - if (i18nFormat === 'xlf') { - return new XliffFile(xmlContent, path, encoding); - } - if (i18nFormat === 'xmb') { - return new XmbFile(xmlContent, path, encoding, optionalMaster); - } else { - throw new Error(format('oops, unsupported format "%s"', i18nFormat)); - } - } - - /** - * Read file function for any file with unknown format. - * This functions tries to guess the format based on the filename and the content of the file. - * Result depends on detected format, either XliffFile or XmbFile. - * @param xmlContent the file content - * @param path the path of the file (only used to remember it) - * @param encoding utf-8, ... used to parse XML. - * This is read from the file, but if you know it before, you can avoid reading the file twice. - * @param optionalMaster in case of xmb the master file, that contains the original texts. - * (this is used to support state infos, that are based on comparing original with translated version) - * Ignored for other formats. - * @return {ITranslationMessagesFile} either XliffFile or XmbFile - */ - public static fromUnknownFormatFileContent(xmlContent: string, - path: string, - encoding: string, - optionalMaster?: {xmlContent: string, path: string, encoding: string}): ITranslationMessagesFile { - let formatCandidates = ['xlf', 'xmb']; - if (path && path.endsWith('xmb')) { - formatCandidates = ['xmb', 'xlf']; - } - // try all candidate formats to get the rigth one - for (let i = 0; i < formatCandidates.length; i++) { - const format = formatCandidates[i]; - try { - const translationFile = TranslationMessagesFileFactory.fromFileContent(format, xmlContent, path, encoding, optionalMaster); - if (translationFile) { - return translationFile; - } - } catch (e) { - // seams to be the wrong format - } - } - throw new Error(format('could not identify file format, it is neiter XLIFF nor XMB')); - } - -} - diff --git a/src/xliffmerge/translation-messages-file-reader.ts b/src/xliffmerge/translation-messages-file-reader.ts index 9a68b45..672d4a0 100644 --- a/src/xliffmerge/translation-messages-file-reader.ts +++ b/src/xliffmerge/translation-messages-file-reader.ts @@ -1,13 +1,9 @@ /** * Created by roobm on 21.03.2017. */ -import {ITranslationMessagesFile} from './i-translation-messages-file'; -import {XmlReader} from './xml-reader'; -import {XliffFile} from './xliff-file'; -import {XmbFile} from './xmb-file'; -import {format} from 'util'; +import {TranslationMessagesFileFactory, ITranslationMessagesFile, ITransUnit} from 'ngx-i18nsupport-lib'; import {FileUtil} from '../common/file-util'; -import {TranslationMessagesFileFactory} from './translation-messages-file-factory'; +import {XmlReader} from './xml-reader'; /** * Helper class to read translation files depending on format. diff --git a/src/xliffmerge/version.js b/src/xliffmerge/version.js deleted file mode 100644 index fa34032..0000000 --- a/src/xliffmerge/version.js +++ /dev/null @@ -1,7 +0,0 @@ -"use strict"; -/** - * Created by martin on 19.02.2017. - */ -var path = require("path"); -var pkg = require(path.resolve(__dirname, '..', 'package.json')); -exports.VERSION = (pkg ? pkg.version : 'unknown'); diff --git a/src/xliffmerge/xliff-file.js b/src/xliffmerge/xliff-file.js deleted file mode 100644 index 3fc919f..0000000 --- a/src/xliffmerge/xliff-file.js +++ /dev/null @@ -1,218 +0,0 @@ -"use strict"; -var cheerio = require("cheerio"); -var file_util_1 = require("../common/file-util"); -var util_1 = require("util"); -var xml_reader_1 = require("./xml-reader"); -/** - * Created by martin on 23.02.2017. - * Ab xliff file read from a source file. - * Defines some relevant get and set method for reading and modifying such a file. - */ -/** - * Read-Options for cheerio, enable xml mode. - * @type {{xmlMode: boolean}} - */ -var CheerioOptions = { - xmlMode: true, - decodeEntities: false, -}; -var TransUnit = (function () { - function TransUnit(_transUnit, _id) { - this._transUnit = _transUnit; - this._id = _id; - } - Object.defineProperty(TransUnit.prototype, "id", { - get: function () { - return this._id; - }, - enumerable: true, - configurable: true - }); - TransUnit.prototype.sourceContent = function () { - return cheerio('source', this._transUnit).html(); - }; - TransUnit.prototype.targetContent = function () { - return cheerio('target', this._transUnit).html(); - }; - TransUnit.prototype.targetState = function () { - return cheerio('target', this._transUnit).attr('state'); - }; - /** - * the real xml element used for trans unit. - * Here it is a element defined in XLIFF Spec. - * @return {CheerioElement} - */ - TransUnit.prototype.asXmlElement = function () { - return this._transUnit; - }; - /** - * Translate trans unit. - * (very simple, just for tests) - * @param translation the translated string - */ - TransUnit.prototype.translate = function (translation) { - var target = cheerio('target', this._transUnit); - if (!target) { - var source = cheerio('source', this._transUnit); - source.parent().append(''); - target = cheerio('target', source.parent()); - } - target.html(translation); - target.attr('state', 'final'); - }; - /** - * Copy source to target to use it as dummy translation. - * (better than missing value) - */ - TransUnit.prototype.useSourceAsTarget = function (isDefaultLang) { - var source = cheerio('source', this._transUnit); - var target = cheerio('target', this._transUnit); - if (!target) { - source.parent().append(''); - target = cheerio('target', source.parent()); - } - target.html(source.html()); - if (isDefaultLang) { - target.attr('state', 'final'); - } - else { - target.attr('state', 'new'); - } - }; - return TransUnit; -}()); -var XliffFile = (function () { - function XliffFile() { - this._warnings = []; - this._numberOfTransUnitsWithMissingId = 0; - } - /** - * 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} - */ - 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; - }; - XliffFile.prototype.initializeFromContent = function (xmlString, path, encoding) { - this.filename = path; - this.encoding = encoding; - this.xliffContent = cheerio.load(xmlString, CheerioOptions); - return this; - }; - XliffFile.prototype.forEachTransUnit = function (callback) { - this.initializeTransUnits(); - this.transUnits.forEach(function (tu) { return callback(tu); }); - }; - XliffFile.prototype.warnings = function () { - this.initializeTransUnits(); - return this._warnings; - }; - XliffFile.prototype.numberOfTransUnits = function () { - this.initializeTransUnits(); - return this.transUnits.length; - }; - XliffFile.prototype.numberOfTransUnitsWithMissingId = function () { - this.initializeTransUnits(); - return this._numberOfTransUnitsWithMissingId; - }; - /** - * Get trans-unit with given id. - * @param id - * @return {Cheerio} - */ - XliffFile.prototype.transUnitWithId = function (id) { - this.initializeTransUnits(); - return this.transUnits.find(function (tu) { return tu.id == id; }); - }; - XliffFile.prototype.initializeTransUnits = function () { - var _this = this; - if (util_1.isNullOrUndefined(this.transUnits)) { - this.transUnits = []; - var transUnitsInFile = this.xliffContent('trans-unit'); - transUnitsInFile.each(function (index, transunit) { - var id = cheerio(transunit).attr('id'); - if (!id) { - _this._warnings.push(util_1.format('oops, trans-unit without "id" found in master, please check file %s', _this.filename)); - _this._numberOfTransUnitsWithMissingId++; - } - _this.transUnits.push(new TransUnit(transunit, id)); - }); - } - }; - /** - * Get source language. - * @return {string} - */ - XliffFile.prototype.sourceLanguage = function () { - return this.xliffContent('file').attr('source-language'); - }; - /** - * Edit the source language. - * @param language - */ - XliffFile.prototype.setSourceLanguage = function (language) { - this.xliffContent('file').attr('source-language', language); - }; - /** - * Get target language. - * @return {string} - */ - XliffFile.prototype.targetLanguage = function () { - return this.xliffContent('file').attr('target-language'); - }; - /** - * Edit the target language. - * @param language - */ - XliffFile.prototype.setTargetLanguage = function (language) { - this.xliffContent('file').attr('target-language', language); - }; - /** - * Copy source to target to use it as dummy translation. - * (better than missing value) - */ - XliffFile.prototype.useSourceAsTarget = function (transUnit, isDefaultLang) { - transUnit.useSourceAsTarget(isDefaultLang); - }; - /** - * Translate a given trans unit. - * (very simple, just for tests) - * @param transUnit - * @param translation the translated string - */ - XliffFile.prototype.translate = function (transUnit, translation) { - transUnit.translate(translation); - }; - /** - * Add a new trans-unit. - * @param transUnit - */ - XliffFile.prototype.addNewTransUnit = function (transUnit) { - this.xliffContent('body').append(cheerio(transUnit.asXmlElement())); - this.initializeTransUnits(); - this.transUnits.push(transUnit); - }; - /** - * Remove the trans-unit with the given id. - * @param id - */ - XliffFile.prototype.removeTransUnitWithId = function (id) { - this.xliffContent('#' + id).remove(); - this.initializeTransUnits(); - this.transUnits = this.transUnits.filter(function (tu) { return tu.id != id; }); - }; - /** - * Save edited content to file. - */ - XliffFile.prototype.save = function () { - file_util_1.FileUtil.replaceContent(this.filename, this.xliffContent.xml(), this.encoding); - }; - return XliffFile; -}()); -exports.XliffFile = XliffFile; diff --git a/src/xliffmerge/xliff-file.ts b/src/xliffmerge/xliff-file.ts deleted file mode 100644 index 80356d9..0000000 --- a/src/xliffmerge/xliff-file.ts +++ /dev/null @@ -1,330 +0,0 @@ -import * as cheerio from "cheerio"; -import {isNullOrUndefined, format} from 'util'; -import {ITranslationMessagesFile} from './i-translation-messages-file'; -import {ITransUnit} from './i-trans-unit'; -/** - * Created by martin on 23.02.2017. - * Ab xliff file read from a source file. - * Defines some relevant get and set method for reading and modifying such a file. - */ - -/** - * Read-Options for cheerio, enable xml mode. - * @type {{xmlMode: boolean}} - */ -const CheerioOptions: CheerioOptionsInterface = { - xmlMode: true, - decodeEntities: false, - -}; - -class TransUnit implements ITransUnit { - - constructor(private _transUnit: CheerioElement, private _id: string) { - - } - - public get id(): string { - return this._id; - } - - public sourceContent(): string { - return cheerio('source', this._transUnit).html(); - } - - /** - * the translated value (containing all markup, depends on the concrete format used). - */ - public targetContent(): string { - 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>/g; - normalized = normalized.replace(re0, '{{0}}'); - let reN: RegExp = /<\/x>/g; - normalized = normalized.replace(reN, '{{$1}}'); - - let reStartBold: RegExp = /<\/x>/g; - normalized = normalized.replace(reStartBold, ''); - let reCloseBold: RegExp = /<\/x>/g; - normalized = normalized.replace(reCloseBold, ''); - - let reStartAnyTag: RegExp = /<\/x>/g; - normalized = normalized.replace(reStartAnyTag, '<$2>'); - let reCloseAnyTag: RegExp = /<\/x>/g; - normalized = normalized.replace(reCloseAnyTag, ''); - - return normalized; - } - - /** - * State of the translation. - * (new, final, ...) - */ - public targetState(): string { - 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. - * Here it is a element defined in XLIFF Spec. - * @return {CheerioElement} - */ - public asXmlElement(): CheerioElement { - return this._transUnit; - } - - /** - * Translate trans unit. - * (very simple, just for tests) - * @param translation the translated string - */ - public translate(translation: string) { - let target = cheerio('target', this._transUnit); - if (!target) { - let source = cheerio('source', this._transUnit); - source.parent().append(''); - target = cheerio('target', source.parent()); - } - let translationContainer: CheerioStatic = cheerio.load('' + translation + '', CheerioOptions); - let translationParts: Cheerio = translationContainer('dummy'); - target.contents().remove(); - translationParts.contents().each((index, element) => {target.append(cheerio(element));}); - target.attr('state', 'final'); - } - - /** - * Copy source to target to use it as dummy translation. - * (better than missing value) - */ - public useSourceAsTarget(isDefaultLang: boolean) { - let source = cheerio('source', this._transUnit); - let target = cheerio('target', this._transUnit); - if (!target) { - source.parent().append(''); - target = cheerio('target', source.parent()); - } - target.html(source.html()); - if (isDefaultLang) { - target.attr('state', 'final'); - } else { - target.attr('state', 'new'); - } - } - -} - -export class XliffFile implements ITranslationMessagesFile { - - private _filename: string; - - private _encoding: string; - - private xliffContent: CheerioStatic; - - // trans-unit elements and their id from the file - private transUnits: ITransUnit[]; - - private _warnings: string[]; - private _numberOfTransUnitsWithMissingId: number; - - /** - * 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.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; - } - - /** - * File type. - * Here 'XLIFF 1.2' - */ - public fileType(): string { - return 'XLIFF 1.2'; - } - - public forEachTransUnit(callback: ((transunit: ITransUnit) => void)) { - this.initializeTransUnits(); - this.transUnits.forEach((tu) => callback(tu)); - } - - public warnings(): string[] { - this.initializeTransUnits(); - return this._warnings; - } - - public numberOfTransUnits(): number { - this.initializeTransUnits(); - return this.transUnits.length; - } - - public numberOfTransUnitsWithMissingId(): number { - this.initializeTransUnits(); - return this._numberOfTransUnitsWithMissingId; - } - - /** - * Get trans-unit with given id. - * @param id - * @return {Cheerio} - */ - public transUnitWithId(id: string): ITransUnit { - this.initializeTransUnits(); - return this.transUnits.find((tu) => tu.id == id); - } - - private initializeTransUnits() { - if (isNullOrUndefined(this.transUnits)) { - this.transUnits = []; - let transUnitsInFile = this.xliffContent('trans-unit'); - transUnitsInFile.each((index, transunit: CheerioElement) => { - let id = cheerio(transunit).attr('id'); - if (!id) { - this._warnings.push(format('oops, trans-unit without "id" found in master, please check file %s', this.filename)); - this._numberOfTransUnitsWithMissingId++; - } - this.transUnits.push(new TransUnit(transunit, id)); - }); - } - } - - /** - * Get source language. - * @return {string} - */ - public sourceLanguage(): string { - return this.xliffContent('file').attr('source-language'); - } - - /** - * Edit the source language. - * @param language - */ - public setSourceLanguage(language: string) { - this.xliffContent('file').attr('source-language', language); - } - - /** - * Get target language. - * @return {string} - */ - public targetLanguage(): string { - return this.xliffContent('file').attr('target-language'); - } - - /** - * Edit the target language. - * @param language - */ - public setTargetLanguage(language: string) { - this.xliffContent('file').attr('target-language', language); - } - - /** - * Copy source to target to use it as dummy translation. - * (better than missing value) - */ - public useSourceAsTarget(transUnit: ITransUnit, isDefaultLang: boolean) { - transUnit.useSourceAsTarget(isDefaultLang); - } - - /** - * Translate a given trans unit. - * (very simple, just for tests) - * @param transUnit - * @param translation the translated string - */ - public translate(transUnit: ITransUnit, translation: string) { - transUnit.translate(translation); - } - - /** - * Add a new trans-unit. - * @param transUnit - */ - public addNewTransUnit(transUnit: ITransUnit) { - this.xliffContent('body').append(cheerio(transUnit.asXmlElement())); - this.initializeTransUnits(); - this.transUnits.push(transUnit); - } - - /** - * Remove the trans-unit with the given id. - * @param id - */ - public removeTransUnitWithId(id: string) { - this.xliffContent('#' + id).remove(); - this.initializeTransUnits(); - this.transUnits = this.transUnits.filter((tu) => tu.id != id); - } - - /** - * The filename where the data is read from. - */ - 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(); - } - -} \ No newline at end of file diff --git a/src/xliffmerge/xliff-merge-error.js b/src/xliffmerge/xliff-merge-error.js deleted file mode 100644 index ad901be..0000000 --- a/src/xliffmerge/xliff-merge-error.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * Created by martin on 17.02.2017. - */ -"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 XliffMergeError = (function (_super) { - __extends(XliffMergeError, _super); - function XliffMergeError(msg) { - var _this = _super.call(this, msg) || this; - // Set the prototype explicitly. - Object.setPrototypeOf(_this, XliffMergeError.prototype); - return _this; - } - return XliffMergeError; -}(Error)); -exports.XliffMergeError = XliffMergeError; diff --git a/src/xliffmerge/xliff-merge-parameters.js b/src/xliffmerge/xliff-merge-parameters.js deleted file mode 100644 index 0827fca..0000000 --- a/src/xliffmerge/xliff-merge-parameters.js +++ /dev/null @@ -1,264 +0,0 @@ -/** - * Created by martin on 17.02.2017. - * Collection of all parameters used by the tool. - * The parameters are read form the profile or defaults are used. - */ -"use strict"; -var fs = require("fs"); -var xliff_merge_error_1 = require("./xliff-merge-error"); -var util_1 = require("util"); -var XliffMergeParameters = (function () { - function XliffMergeParameters() { - this.errorsFound = []; - this.warningsFound = []; - } - /** - * Create Parameters. - * @param options command options - * @param profileContent given profile (if not, it is read from the profile path from options). - * @return {XliffMergeParameters} - */ - XliffMergeParameters.createFromOptions = function (options, profileContent) { - var parameters = new XliffMergeParameters(); - parameters.configure(options, profileContent); - return parameters; - }; - /** - * Initialize me from the profile content. - * (public only for test usage). - * @param options - * @param profileContent if null, read it from profile. - */ - XliffMergeParameters.prototype.configure = function (options, profileContent) { - this.errorsFound = []; - this.warningsFound = []; - if (!profileContent) { - profileContent = this.readProfile(options); - } - var validProfile = (!!profileContent); - if (options.quiet) { - this._quiet = options.quiet; - } - if (options.verbose) { - this._verbose = options.verbose; - } - if (validProfile) { - this.initializeFromConfig(profileContent); - // if languages are given as parameters, they ovveride everything said in profile - if (!!options.languages && options.languages.length > 0) { - this._languages = options.languages; - if (!this._defaultLanguage) { - this._defaultLanguage = this._languages[0]; - } - } - this.checkParameters(); - } - }; - /** - * Read profile. - * @param profilePath - * @return the read profile (empty, if none, null if errors) - */ - XliffMergeParameters.prototype.readProfile = function (options) { - var profilePath = options.profilePath; - if (!profilePath) { - return {}; - } - var content; - try { - content = fs.readFileSync(profilePath, 'UTF-8'); - } - catch (err) { - this.errorsFound.push(new xliff_merge_error_1.XliffMergeError('could not read profile "' + profilePath + '"')); - return null; - } - var profileContent = JSON.parse(content); - return profileContent; - }; - XliffMergeParameters.prototype.initializeFromConfig = function (profileContent) { - if (!profileContent) { - return; - } - var profile = profileContent.xliffmergeOptions; - if (profile) { - if (!util_1.isNullOrUndefined(profile.quiet)) { - this._quiet = profile.quiet; - } - if (!util_1.isNullOrUndefined(profile.verbose)) { - this._verbose = profile.verbose; - } - if (profile.defaultLanguage) { - this._defaultLanguage = profile.defaultLanguage; - } - if (profile.languages) { - this._languages = profile.languages; - } - if (profile.srcDir) { - this._srcDir = profile.srcDir; - } - if (profile.angularCompilerOptions) { - if (profile.angularCompilerOptions.genDir) - this._genDir = profile.angularCompilerOptions.genDir; - } - if (profile.genDir) { - // this must be after angularCompilerOptions to be preferred - this._genDir = profile.genDir; - } - if (profile.i18nFile) { - this._i18nFile = profile.i18nFile; - } - if (profile.i18nFormat) { - this._i18nFormat = profile.i18nFormat; - } - if (profile.encoding) { - this._encoding = profile.encoding; - } - if (profile.removeUnusedIds) { - this._removeUnusedIds = profile.removeUnusedIds; - } - } - else { - this.warningsFound.push('did not find "xliffmergeOptions" in profile, using defaults'); - } - }; - /** - * Check all Parameters, wether they are complete and consistent. - * if something is wrong with the parameters, it is collected in errorsFound. - */ - XliffMergeParameters.prototype.checkParameters = function () { - var _this = this; - this.checkLanguageSyntax(this.defaultLanguage()); - if (this.languages().length == 0) { - this.errorsFound.push(new xliff_merge_error_1.XliffMergeError('no languages specified')); - } - this.languages().forEach(function (lang) { - _this.checkLanguageSyntax(lang); - }); - var stats; - var err; - // srcDir should exists - try { - stats = fs.statSync(this.srcDir()); - } - catch (e) { - err = e; - } - if (!!err || !stats.isDirectory()) { - this.errorsFound.push(new xliff_merge_error_1.XliffMergeError('srcDir "' + this.srcDir() + '" is not a directory')); - } - // genDir should exists - try { - stats = fs.statSync(this.genDir()); - } - catch (e) { - err = e; - } - if (!!err || !stats.isDirectory()) { - this.errorsFound.push(new xliff_merge_error_1.XliffMergeError('genDir "' + this.genDir() + '" is not a directory')); - } - // master file MUST exist - try { - fs.accessSync(this.i18nFile(), fs.constants.R_OK); - } - catch (err) { - this.errorsFound.push(new xliff_merge_error_1.XliffMergeError('i18nFile "' + this.i18nFile() + '" is not readable')); - } - // i18nFormat must be xlf or xmb - if (!(this.i18nFormat() === 'xlf' || this.i18nFormat() === 'xmb')) { - this.errorsFound.push(new xliff_merge_error_1.XliffMergeError('i18nFormat "' + this.i18nFormat() + '" invalid, must be "xlf" or "xmb"')); - } - }; - /** - * Check syntax of language. - * Must be compatible with XML Schema type xsd:language. - * Pattern: [a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})* - * @param lang - */ - XliffMergeParameters.prototype.checkLanguageSyntax = function (lang) { - var pattern = /^[a-zA-Z]{1,8}(-[a-zA-Z0-9]{1,8})*$/; - if (!pattern.test(lang)) { - this.errorsFound.push(new xliff_merge_error_1.XliffMergeError('language "' + lang + '" is not valid')); - } - }; - XliffMergeParameters.prototype.verbose = function () { - return (util_1.isNullOrUndefined(this._verbose)) ? false : this._verbose; - }; - XliffMergeParameters.prototype.quiet = function () { - return (util_1.isNullOrUndefined(this._quiet)) ? false : this._quiet; - }; - /** - * Debug output all parameters to commandOutput. - */ - XliffMergeParameters.prototype.showAllParameters = function (commandOutput) { - commandOutput.debug('xliffmerge Used Parameters:'); - commandOutput.debug('defaultLanguage:\t"%s"', this.defaultLanguage()); - commandOutput.debug('srcDir:\t"%s"', this.srcDir()); - commandOutput.debug('genDir:\t"%s"', this.genDir()); - commandOutput.debug('i18nFile:\t"%s"', this.i18nFile()); - commandOutput.debug('languages:\t%s', this.languages()); - }; - /** - * Default-Language, default en. - * @return {string} - */ - XliffMergeParameters.prototype.defaultLanguage = function () { - return this._defaultLanguage ? this._defaultLanguage : 'en'; - }; - /** - * Liste der zu bearbeitenden Sprachen. - * @return {string[]} - */ - XliffMergeParameters.prototype.languages = function () { - return this._languages ? this._languages : []; - }; - /** - * src directory, where the master xlif is located. - * @return {string} - */ - XliffMergeParameters.prototype.srcDir = function () { - return this._srcDir ? this._srcDir : '.'; - }; - /** - * The master xlif file (the one generated by ng-xi18n). - * Default is /messages.xlf. - * @return {string} - */ - XliffMergeParameters.prototype.i18nFile = function () { - return this.srcDir() + '/' + (this._i18nFile ? this._i18nFile : 'messages.' + this.i18nFormat()); - }; - /** - * Format of the master xlif file. - * Default is "xlf", possible are "xlf" or "xmb". - * @return {string} - */ - XliffMergeParameters.prototype.i18nFormat = function () { - return (this._i18nFormat ? this._i18nFormat : 'xlf'); - }; - /** - * evtl zu generierendes I18n-File mit den Übersetzungen für eine Sprache. - * @param lang - * @return {string} - */ - XliffMergeParameters.prototype.generatedI18nFile = function (lang) { - return this.genDir() + '/' + 'messages.' + lang + '.' + this.i18nFormat(); - }; - /** - * The encoding used to write new XLIFF-files. - * @return {string} - */ - XliffMergeParameters.prototype.encoding = function () { - return this._encoding ? this._encoding : 'UTF-8'; - }; - /** - * Output-Directory, where the output is written to. - * Default is . - */ - XliffMergeParameters.prototype.genDir = function () { - return this._genDir ? this._genDir : this.srcDir(); - }; - XliffMergeParameters.prototype.removeUnusedIds = function () { - return (util_1.isNullOrUndefined(this._removeUnusedIds)) ? true : this._removeUnusedIds; - }; - return XliffMergeParameters; -}()); -exports.XliffMergeParameters = XliffMergeParameters; diff --git a/src/xliffmerge/xliff-merge-parameters.ts b/src/xliffmerge/xliff-merge-parameters.ts index 0d1c34e..f1b2732 100644 --- a/src/xliffmerge/xliff-merge-parameters.ts +++ b/src/xliffmerge/xliff-merge-parameters.ts @@ -4,7 +4,7 @@ * The parameters are read form the profile or defaults are used. */ -import fs = require("fs"); +import * as fs from "fs"; import {XliffMergeError} from './xliff-merge-error'; import {Stats} from 'fs'; import {CommandOutput} from '../common/command-output'; diff --git a/src/xliffmerge/xliff-merge.js b/src/xliffmerge/xliff-merge.js deleted file mode 100644 index bf0c8ae..0000000 --- a/src/xliffmerge/xliff-merge.js +++ /dev/null @@ -1,240 +0,0 @@ -"use strict"; -var program = require("commander"); -var command_output_1 = require("../common/command-output"); -var xliff_merge_parameters_1 = require("./xliff-merge-parameters"); -var xliff_merge_error_1 = require("./xliff-merge-error"); -var file_util_1 = require("../common/file-util"); -var version_1 = require("./version"); -var util_1 = require("util"); -var i_translation_messages_file_1 = require("./i-translation-messages-file"); -var XliffMerge = (function () { - function XliffMerge(commandOutput, options) { - this.commandOutput = commandOutput; - this.options = options; - this.parameters = null; - } - XliffMerge.main = function (argv) { - var options = XliffMerge.parseArgs(argv); - var result = new XliffMerge(new command_output_1.CommandOutput(process.stdout), options).run(); - process.exit(result); - }; - XliffMerge.parseArgs = function (argv) { - var languages = null; - delete program.verbose; - delete program.quiet; - delete program.profilePath; - delete program.languages; - program - .version(version_1.VERSION) - .arguments('') - .option('-p, --profile [configfile]', 'a json configuration file containing all relevant parameters (see details below)') - .option('-v, --verbose', 'show some output for debugging purposes') - .option('-q, --quiet', 'only show errors, nothing else') - .on('--help', function () { - console.log(' has to be a valid language short string, e,g. "en", "de", "de-ch"'); - console.log(''); - console.log(' configfile can contain the following values:'); - console.log('\tquiet verbose defaultLanguage languages srcDir i18nFile i18nFormat encoding genDir removeUnusedIds'); - console.log('\tfor details please consult the home page https://github.com/martinroob/ngx-i18nsupport'); - }) - .action(function (languageArray) { - languages = languageArray; - }) - .parse(argv); - var options = { - languages: languages - }; - if (program.profile) { - options.profilePath = program.profile; - } - if (program.quiet) { - options.quiet = true; - } - if (program.verbose && program.verbose > 0) { - options.verbose = true; - } - return options; - }; - /** - * For Tests, create instance with given profile - * @param commandOutput - * @param options - * @param profileContent - */ - XliffMerge.createFromOptions = function (commandOutput, options, profileContent) { - var instance = new XliffMerge(commandOutput, options); - instance.parameters = xliff_merge_parameters_1.XliffMergeParameters.createFromOptions(options, profileContent); - return instance; - }; - XliffMerge.prototype.run = function () { - try { - this.doRun(); - return 0; - } - catch (err) { - if (err instanceof xliff_merge_error_1.XliffMergeError) { - this.commandOutput.error(err.message); - return -1; - } - else { - // unhandled - this.commandOutput.error('oops ' + err); - throw err; - } - } - }; - /** - * Ausführen merge-Process. - */ - XliffMerge.prototype.doRun = function () { - var _this = this; - if (this.options && this.options.quiet) { - this.commandOutput.setQuiet(); - } - if (this.options && this.options.verbose) { - this.commandOutput.setVerbose(); - } - if (!this.parameters) { - this.parameters = xliff_merge_parameters_1.XliffMergeParameters.createFromOptions(this.options); - } - this.commandOutput.info('xliffmerge version %s', version_1.VERSION); - if (this.parameters.verbose()) { - this.parameters.showAllParameters(this.commandOutput); - } - if (this.parameters.errorsFound.length > 0) { - for (var _i = 0, _a = this.parameters.errorsFound; _i < _a.length; _i++) { - var err = _a[_i]; - this.commandOutput.error(err.message); - } - return; - } - if (this.parameters.warningsFound.length > 0) { - for (var _b = 0, _c = this.parameters.warningsFound; _b < _c.length; _b++) { - var warn = _c[_b]; - this.commandOutput.warn(warn); - } - } - this.readMaster(); - this.parameters.languages().forEach(function (lang) { - _this.processLanguage(lang); - }); - }; - /** - * Return the name of the generated file for given lang. - * @param lang - * @return {string} - */ - XliffMerge.prototype.generatedI18nFile = function (lang) { - return this.parameters.generatedI18nFile(lang); - }; - XliffMerge.prototype.readMaster = function () { - var _this = this; - this.master = i_translation_messages_file_1.TranslationMessagesFileReader.fromFile(this.parameters.i18nFormat(), this.parameters.i18nFile(), this.parameters.encoding()); - this.master.warnings().forEach(function (warning) { - _this.commandOutput.warn(warning); - }); - var count = this.master.numberOfTransUnits(); - var missingIdCount = this.master.numberOfTransUnitsWithMissingId(); - this.commandOutput.info('master contains %s trans-units', count); - if (missingIdCount > 0) { - this.commandOutput.warn('master contains %s trans-units, but there are %s without id', count, missingIdCount); - } - var sourceLang = this.master.sourceLanguage(); - if (sourceLang && sourceLang !== this.parameters.defaultLanguage()) { - this.commandOutput.warn('master says to have source-language="%s", should be "%s" (your defaultLanguage)', sourceLang, this.parameters.defaultLanguage()); - this.master.setSourceLanguage(this.parameters.defaultLanguage()); - this.master.save(); - this.commandOutput.warn('changed master source-language="%s" to "%s"', sourceLang, this.parameters.defaultLanguage()); - } - }; - XliffMerge.prototype.processLanguage = function (lang) { - this.commandOutput.debug('processing language %s', lang); - var languageXliffFile = this.parameters.generatedI18nFile(lang); - if (!file_util_1.FileUtil.exists(languageXliffFile)) { - this.createUntranslatedXliff(lang, languageXliffFile); - } - else { - this.mergeMasterTo(lang, languageXliffFile); - } - }; - /** - * create a new file for the language, which contains no translations, but all keys. - * in principle, this is just a copy of the master with target-language set. - * @param lang - * @param languageXliffFilePath - */ - XliffMerge.prototype.createUntranslatedXliff = function (lang, languageXliffFilePath) { - // copy master ... - file_util_1.FileUtil.copy(this.parameters.i18nFile(), languageXliffFilePath); - // read copy and set target-language - var languageSpecificMessagesFile = i_translation_messages_file_1.TranslationMessagesFileReader.fromFile(this.parameters.i18nFormat(), languageXliffFilePath, this.parameters.encoding()); - languageSpecificMessagesFile.setTargetLanguage(lang); - // copy source to target - var isDefaultLang = (lang == this.parameters.defaultLanguage()); - languageSpecificMessagesFile.forEachTransUnit(function (transUnit) { - languageSpecificMessagesFile.useSourceAsTarget(transUnit, isDefaultLang); - }); - // write it to file - languageSpecificMessagesFile.save(); - this.commandOutput.info('created new file "%s" for target-language="%s"', languageXliffFilePath, lang); - if (!isDefaultLang) { - this.commandOutput.warn('please translate file "%s" to target-language="%s"', languageXliffFilePath, lang); - } - }; - /** - * Merge all - * @param lang - * @param languageXliffFilePath - */ - XliffMerge.prototype.mergeMasterTo = function (lang, languageXliffFilePath) { - var _this = this; - // read lang specific file - var languageSpecificMessagesFile = i_translation_messages_file_1.TranslationMessagesFileReader.fromFile(this.parameters.i18nFormat(), languageXliffFilePath, this.parameters.encoding()); - var isDefaultLang = (lang == this.parameters.defaultLanguage()); - var newCount = 0; - this.master.forEachTransUnit(function (masterTransUnit) { - var transUnit = languageSpecificMessagesFile.transUnitWithId(masterTransUnit.id); - if (!transUnit) { - // oops, no translation, must be a new key, so add it - languageSpecificMessagesFile.useSourceAsTarget(masterTransUnit, isDefaultLang); - languageSpecificMessagesFile.addNewTransUnit(masterTransUnit); - newCount++; - } - }); - if (newCount > 0) { - this.commandOutput.warn('merged %s trans-units from master to "%s"', newCount, lang); - } - // remove all elements that are no longer used - var removeCount = 0; - languageSpecificMessagesFile.forEachTransUnit(function (transUnit) { - var existsInMaster = !util_1.isNullOrUndefined(_this.master.transUnitWithId(transUnit.id)); - if (!existsInMaster) { - if (_this.parameters.removeUnusedIds()) { - languageSpecificMessagesFile.removeTransUnitWithId(transUnit.id); - } - removeCount++; - } - }); - if (removeCount > 0) { - if (this.parameters.removeUnusedIds()) { - this.commandOutput.warn('removed %s unused trans-units in "%s"', removeCount, lang); - } - else { - this.commandOutput.warn('keeping %s unused trans-units in "%s", because removeUnused is disabled', removeCount, lang); - } - } - if (newCount == 0 && removeCount == 0) { - this.commandOutput.info('file for "%s" was up to date', lang); - } - else { - // write it to file - languageSpecificMessagesFile.save(); - this.commandOutput.info('updated file "%s" for target-language="%s"', languageXliffFilePath, lang); - if (newCount > 0 && !isDefaultLang) { - this.commandOutput.warn('please translate file "%s" to target-language="%s"', languageXliffFilePath, lang); - } - } - }; - return XliffMerge; -}()); -exports.XliffMerge = XliffMerge; diff --git a/src/xliffmerge/xliff-merge.spec.js b/src/xliffmerge/xliff-merge.spec.js deleted file mode 100644 index 22f35bc..0000000 --- a/src/xliffmerge/xliff-merge.spec.js +++ /dev/null @@ -1,430 +0,0 @@ -"use strict"; -var fs = require("fs"); -var xliff_merge_1 = require("./xliff-merge"); -var command_output_1 = require("../common/command-output"); -var writer_to_string_1 = require("../common/writer-to-string"); -var file_util_1 = require("../common/file-util"); -var xliff_file_1 = require("./xliff-file"); -var xmb_file_1 = require("./xmb-file"); -/** - * Created by martin on 18.02.2017. - * Testcases for XliffMerge. - */ -describe('XliffMerge test spec', function () { - var MASTER1FILE = 'ngExtractedMaster1.xlf'; - var MASTER2FILE = 'ngExtractedMaster2.xlf'; - var MASTER1XMBFILE = 'ngExtractedMaster1.xmb'; - var MASTER2XMBFILE = 'ngExtractedMaster2.xmb'; - var SRCDIR = 'test/testdata/i18n/'; - var MASTER1SRC = SRCDIR + MASTER1FILE; - var MASTER2SRC = SRCDIR + MASTER2FILE; - var MASTER1XMBSRC = SRCDIR + MASTER1XMBFILE; - var MASTER2XMBSRC = SRCDIR + MASTER2XMBFILE; - /** - * Workdir, not in git. - * Cleaned up for every test. - * Tests, that work on files, copy everything they need into this directory. - * @type {string} - */ - var WORKDIR = 'test/work/'; - var MASTERFILE = 'messages.xlf'; - var MASTER = WORKDIR + MASTERFILE; - var MASTERXMBFILE = 'messages.xmb'; - var MASTERXMB = WORKDIR + MASTERXMBFILE; - describe('test the tooling used in the tests', function () { - it('should write output to string (Test WriterToString)', function () { - var ws = new writer_to_string_1.WriterToString(); - ws.write('test test test\n'); - ws.write('line 2'); - expect(ws.writtenData()).toContain('line 2'); - }); - }); - describe('command line and configuration checks', function () { - it('should parse -v option', function () { - var options = xliff_merge_1.XliffMerge.parseArgs(['node', 'xliffmerge', '-v']); - expect(options.verbose).toBeTruthy(); - expect(options.quiet).toBeFalsy(); - }); - it('should parse -q option', function () { - var options = xliff_merge_1.XliffMerge.parseArgs(['node', 'xliffmerge', '-q']); - expect(options.quiet).toBeTruthy(); - expect(options.verbose).toBeFalsy(); - }); - it('should output an errror (no languages) when called with defaults', function (done) { - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var xliffMergeCmd = new xliff_merge_1.XliffMerge(commandOut, {}); - xliffMergeCmd.run(); - expect(ws.writtenData()).toContain('ERROR'); - expect(ws.writtenData()).toContain('no languages specified'); - done(); - }); - it('should output an errror (i18nfile) when called with defaults', function (done) { - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var xliffMergeCmd = new xliff_merge_1.XliffMerge(commandOut, { languages: ['de', 'en'] }); - xliffMergeCmd.run(); - expect(ws.writtenData()).toContain('ERROR'); - expect(ws.writtenData()).toContain('i18nFile'); - done(); - }); - it('should output an errror (could not read) when called with a non existing profile', function (done) { - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var xliffMergeCmd = new xliff_merge_1.XliffMerge(commandOut, { verbose: true, profilePath: 'lmaa' }); - xliffMergeCmd.run(); - expect(ws.writtenData()).toContain('ERROR'); - expect(ws.writtenData()).toContain('could not read profile'); - done(); - }); - it('should read test config file', function (done) { - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, { profilePath: './test/testdata/xliffmergeconfig.json', verbose: true }, null); - xliffMergeCmd.run(); - expect(ws.writtenData()).toContain('languages: de,en'); - done(); - }); - it('should output an errror (srcDir not readable) when called with a non existing srcDir', function (done) { - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var profileContent = { - xliffmergeOptions: { - srcDir: 'lmaa', - } - }; - var xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, { verbose: true }, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).toContain('ERROR'); - expect(ws.writtenData()).toContain('srcDir "lmaa" is not a directory'); - done(); - }); - it('should output an errror (genDir not existing) when called with a non existing genDir', function (done) { - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var profileContent = { - xliffmergeOptions: { - genDir: 'lmaa', - } - }; - var xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, { verbose: true }, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).toContain('ERROR'); - expect(ws.writtenData()).toContain('genDir "lmaa" is not a directory'); - done(); - }); - it('should output an errror (i18nFile is not readable) when called with a non existing master file', function (done) { - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var profileContent = { - xliffmergeOptions: { - srcDir: 'test/testdata', - i18nFile: 'nonexistingmaster.xlf' - } - }; - var xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, {}, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).toContain('ERROR'); - expect(ws.writtenData()).toContain('i18nFile "test/testdata/nonexistingmaster.xlf" is not readable'); - done(); - }); - it('should output an errror (language not valid) when called with an invalid language code', function (done) { - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var profileContent = { - xliffmergeOptions: { - defaultLanguage: 'de/ch', - } - }; - var xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, {}, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).toContain('ERROR'); - expect(ws.writtenData()).toContain('language "de/ch" is not valid'); - done(); - }); - it('should output an errror (i18nFormat invalid) when called with an invalid i18n format', function (done) { - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var profileContent = { - xliffmergeOptions: { - i18nFormat: 'unknown', - } - }; - var xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, {}, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).toContain('ERROR'); - expect(ws.writtenData()).toContain('i18nFormat "unknown" invalid'); - done(); - }); - it('should accept i18n format xlf', function (done) { - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var profileContent = { - xliffmergeOptions: { - i18nFormat: 'xlf', - } - }; - var xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, {}, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).not.toContain('i18nFormat'); - done(); - }); - it('should accept i18n format xmb', function (done) { - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var profileContent = { - xliffmergeOptions: { - i18nFormat: 'xmb', - } - }; - var xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, {}, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).not.toContain('i18nFormat'); - done(); - }); - it('should read languages from config file', function (done) { - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var profileContent = { - xliffmergeOptions: { - languages: ['de', 'en', 'fr'], - } - }; - var xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, { verbose: true }, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).toContain('languages: de,en,fr'); - done(); - }); - }); - describe('Merge process checks for format xlf', function () { - var ID_TRANSLATED_SCHLIESSEN = "1ead0ad1063d0c9e005fe56c9529aef4c1ef9d21"; // an ID from ngExtractedMaster1.xlf - var ID_REMOVED_STARTSEITE = "c536247d71822c272f8e9155f831e0efb5aa0d31"; // an ID that will be removed in master2 - var ID_REMOVED_SUCHEN = "d17aee1ddf9fe1c0afe8440e02ef5ab906a69699"; // another removed ID - beforeEach(function () { - if (!fs.existsSync(WORKDIR)) { - fs.mkdirSync(WORKDIR); - } - // cleanup workdir - file_util_1.FileUtil.deleteFolderContentRecursive(WORKDIR); - }); - it('should fix source language, if the masters lang is not the default', function (done) { - file_util_1.FileUtil.copy(MASTER1SRC, MASTER); - var master = xliff_file_1.XliffFile.fromFile(MASTER); - expect(master.sourceLanguage()).toBe('en'); // master is german, but ng-18n extracts it as en - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var profileContent = { - xliffmergeOptions: { - defaultLanguage: 'de', - srcDir: WORKDIR, - genDir: WORKDIR, - i18nFile: MASTERFILE, - } - }; - var xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, { languages: ['de'] }, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).not.toContain('ERROR'); - expect(ws.writtenData()).toContain('master says to have source-language="en"'); - expect(ws.writtenData()).toContain('changed master source-language="en" to "de"'); - var newmaster = xliff_file_1.XliffFile.fromFile(MASTER); - expect(newmaster.sourceLanguage()).toBe('de'); // master is german - done(); - }); - it('should generate translated file for default language de from master', function (done) { - file_util_1.FileUtil.copy(MASTER1SRC, MASTER); - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var profileContent = { - xliffmergeOptions: { - defaultLanguage: 'de', - srcDir: WORKDIR, - genDir: WORKDIR, - i18nFile: MASTERFILE - } - }; - var xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, { languages: ['de'] }, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).not.toContain('ERROR'); - var langFile = xliff_file_1.XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('de')); - expect(langFile.sourceLanguage()).toBe('de'); - expect(langFile.targetLanguage()).toBe('de'); - langFile.forEachTransUnit(function (tu) { - expect(tu.targetContent()).toBe(tu.sourceContent()); - expect(tu.targetState()).toBe('final'); - }); - done(); - }); - it('should generate translated file for all languages', function (done) { - file_util_1.FileUtil.copy(MASTER1SRC, MASTER); - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var profileContent = { - xliffmergeOptions: { - defaultLanguage: 'de', - srcDir: WORKDIR, - genDir: WORKDIR, - i18nFile: MASTERFILE - } - }; - var xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, { languages: ['de', 'en'] }, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).not.toContain('ERROR'); - var langFileGerman = xliff_file_1.XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('de')); - expect(langFileGerman.sourceLanguage()).toBe('de'); - expect(langFileGerman.targetLanguage()).toBe('de'); - langFileGerman.forEachTransUnit(function (tu) { - expect(tu.targetContent()).toBe(tu.sourceContent()); - expect(tu.targetState()).toBe('final'); - }); - var langFileEnglish = xliff_file_1.XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('en')); - expect(langFileEnglish.sourceLanguage()).toBe('de'); - expect(langFileEnglish.targetLanguage()).toBe('en'); - langFileEnglish.forEachTransUnit(function (tu) { - expect(tu.targetContent()).toBe(tu.sourceContent()); - expect(tu.targetState()).toBe('new'); - }); - done(); - }); - it('should merge translated file for all languages', function (done) { - file_util_1.FileUtil.copy(MASTER1SRC, MASTER); - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var profileContent = { - xliffmergeOptions: { - defaultLanguage: 'de', - srcDir: WORKDIR, - genDir: WORKDIR, - i18nFile: MASTERFILE - } - }; - var xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, { languages: ['de', 'en'] }, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).not.toContain('ERROR'); - // now translate some texts in the English version - var langFileEnglish = xliff_file_1.XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('en')); - var tu = langFileEnglish.transUnitWithId(ID_TRANSLATED_SCHLIESSEN); - expect(tu).toBeTruthy(); - langFileEnglish.translate(tu, 'Close'); - langFileEnglish.save(); - // next step, use another master - file_util_1.FileUtil.copy(MASTER2SRC, MASTER); - ws = new writer_to_string_1.WriterToString(); - commandOut = new command_output_1.CommandOutput(ws); - xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, { languages: ['de', 'en'] }, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).not.toContain('ERROR'); - expect(ws.writtenData()).toContain('merged 12 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 = xliff_file_1.XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('en')); - expect(langFileEnglish.transUnitWithId(ID_TRANSLATED_SCHLIESSEN).targetContent()).toBe('Close'); - // look, that the removed IDs are really removed. - expect(langFileEnglish.transUnitWithId(ID_REMOVED_STARTSEITE)).toBeFalsy(); - expect(langFileEnglish.transUnitWithId(ID_REMOVED_SUCHEN)).toBeFalsy(); - done(); - }); - }); - describe('Merge process checks for format xmb', function () { - var ID_TRANSLATED_MYFIRST = "2047558209369508311"; // an ID from ngExtractedMaster1.xlf - var ID_REMOVED_DESCRIPTION = "7499557905529977371"; // an ID that will be removed in master2 - var ID_REMOVED_DESCRIPTION2 = "3274258156935474372"; // another removed ID - var ID_ADDED = "3274258156935474372"; // an ID that will be added in master2 - beforeEach(function () { - if (!fs.existsSync(WORKDIR)) { - fs.mkdirSync(WORKDIR); - } - // cleanup workdir - file_util_1.FileUtil.deleteFolderContentRecursive(WORKDIR); - }); - it('should generate translated file for default language de from xmb master', function (done) { - file_util_1.FileUtil.copy(MASTER1XMBSRC, MASTERXMB); - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var profileContent = { - xliffmergeOptions: { - defaultLanguage: 'de', - srcDir: WORKDIR, - genDir: WORKDIR, - i18nFile: MASTERXMBFILE, - i18nFormat: 'xmb' - } - }; - var xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, { languages: ['de'] }, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).not.toContain('ERROR'); - var langFile = xmb_file_1.XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('de')); - expect(langFile.sourceLanguage()).toBeFalsy(); // not supported in xmb - expect(langFile.targetLanguage()).toBeFalsy(); - langFile.forEachTransUnit(function (tu) { - expect(tu.targetContent()).toBe(tu.sourceContent()); - expect(tu.targetState()).toBeFalsy(); - }); - done(); - }); - it('should generate translated file for all languages using format xmb', function (done) { - file_util_1.FileUtil.copy(MASTER1XMBSRC, MASTERXMB); - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var profileContent = { - xliffmergeOptions: { - defaultLanguage: 'de', - srcDir: WORKDIR, - genDir: WORKDIR, - i18nFile: MASTERXMBFILE, - i18nFormat: 'xmb' - } - }; - var xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, { languages: ['de', 'en'] }, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).not.toContain('ERROR'); - var langFileGerman = xmb_file_1.XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('de')); - langFileGerman.forEachTransUnit(function (tu) { - expect(tu.targetContent()).toBe(tu.sourceContent()); - }); - var langFileEnglish = xmb_file_1.XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('en')); - langFileEnglish.forEachTransUnit(function (tu) { - expect(tu.targetContent()).toBe(tu.sourceContent()); - }); - done(); - }); - it('should merge translated file for all languages using format xmb', function (done) { - file_util_1.FileUtil.copy(MASTER1XMBSRC, MASTERXMB); - var ws = new writer_to_string_1.WriterToString(); - var commandOut = new command_output_1.CommandOutput(ws); - var profileContent = { - xliffmergeOptions: { - defaultLanguage: 'de', - srcDir: WORKDIR, - genDir: WORKDIR, - i18nFile: MASTERXMBFILE, - i18nFormat: 'xmb' - } - }; - var xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, { languages: ['de', 'en'] }, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).not.toContain('ERROR'); - // now translate some texts in the English version - var langFileEnglish = xmb_file_1.XmbFile.fromFile(xliffMergeCmd.generatedI18nFile('en')); - var tu = langFileEnglish.transUnitWithId(ID_TRANSLATED_MYFIRST); - expect(tu).toBeTruthy(); - langFileEnglish.translate(tu, 'My first app'); - langFileEnglish.save(); - // next step, use another master - file_util_1.FileUtil.copy(MASTER2XMBSRC, MASTER); - ws = new writer_to_string_1.WriterToString(); - commandOut = new command_output_1.CommandOutput(ws); - xliffMergeCmd = xliff_merge_1.XliffMerge.createFromOptions(commandOut, { languages: ['de', 'en'] }, profileContent); - xliffMergeCmd.run(); - expect(ws.writtenData()).not.toContain('ERROR'); - expect(ws.writtenData()).toContain('merged 100 trans-units from master to "en"'); - expect(ws.writtenData()).toContain('removed 100 unused trans-units in "en"'); - // look, that the new file contains the old translation - langFileEnglish = xliff_file_1.XliffFile.fromFile(xliffMergeCmd.generatedI18nFile('en')); - expect(langFileEnglish.transUnitWithId(ID_TRANSLATED_MYFIRST).targetContent()).toBe('My first app'); - // look, that the removed IDs are really removed. - expect(langFileEnglish.transUnitWithId(ID_REMOVED_DESCRIPTION)).toBeFalsy(); - expect(langFileEnglish.transUnitWithId(ID_REMOVED_DESCRIPTION2)).toBeFalsy(); - done(); - }); - }); -}); diff --git a/src/xliffmerge/xliff-merge.spec.ts b/src/xliffmerge/xliff-merge.spec.ts index 41b27f7..3833daf 100644 --- a/src/xliffmerge/xliff-merge.spec.ts +++ b/src/xliffmerge/xliff-merge.spec.ts @@ -1,16 +1,14 @@ -import fs = require("fs"); -import child_process = require("child_process"); +import * as fs from "fs"; +import * as child_process from "child_process"; import {XliffMerge} from './xliff-merge'; import {ProgramOptions, IConfigFile} from './i-xliff-merge-options'; import {CommandOutput} from '../common/command-output'; import WritableStream = NodeJS.WritableStream; import {WriterToString} from '../common/writer-to-string'; 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 {XmbFile} from './xmb-file'; +import {TranslationMessagesFileFactory, ITranslationMessagesFile, ITransUnit} from 'ngx-i18nsupport-lib'; import {TranslationMessagesFileReader} from './translation-messages-file-reader'; + /** * Created by martin on 18.02.2017. * Testcases for XliffMerge. @@ -33,24 +31,24 @@ describe('XliffMerge test spec', () => { * Helper function to read Xliff from File * @type {string} */ - function readXliff(path: string): XliffFile { - return TranslationMessagesFileReader.fromFile('xlf', path, ENCODING); + function readXliff(path: string): ITranslationMessagesFile { + return TranslationMessagesFileReader.fromFile('xlf', path, ENCODING); } /** * Helper function to read Xmb from File * @type {string} */ - function readXmb(path: string): XmbFile { - return TranslationMessagesFileReader.fromFile('xmb', path, ENCODING); + function readXmb(path: string): ITranslationMessagesFile { + return TranslationMessagesFileReader.fromFile('xmb', path, ENCODING); } /** * Helper function to read Xmb from 2 Files, the xmb and the master * @type {string} */ - function readXmbWithMaster(path: string, masterPath: string): XmbFile { - return TranslationMessagesFileReader.fromFile('xmb', path, ENCODING, masterPath); + function readXmbWithMaster(path: string, masterPath: string): ITranslationMessagesFile { + return TranslationMessagesFileReader.fromFile('xmb', path, ENCODING, masterPath); } describe('test the tooling used in the tests', () => { @@ -277,7 +275,7 @@ describe('XliffMerge test spec', () => { it('should fix source language, if the masters lang is not the default', (done) => { FileUtil.copy(MASTER1SRC, MASTER); - let master: XliffFile = readXliff(MASTER); + let master: ITranslationMessagesFile = readXliff(MASTER); expect(master.sourceLanguage()).toBe('en'); // master is german, but ng-18n extracts it as en let ws: WriterToString = new WriterToString(); let commandOut = new CommandOutput(ws); @@ -294,7 +292,7 @@ describe('XliffMerge test spec', () => { expect(ws.writtenData()).not.toContain('ERROR'); expect(ws.writtenData()).toContain('master says to have source-language="en"'); expect(ws.writtenData()).toContain('changed master source-language="en" to "de"'); - let newmaster: XliffFile = readXliff(MASTER); + let newmaster: ITranslationMessagesFile = readXliff(MASTER); expect(newmaster.sourceLanguage()).toBe('de'); // master is german done(); }); @@ -314,7 +312,7 @@ describe('XliffMerge test spec', () => { let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent); xliffMergeCmd.run(); expect(ws.writtenData()).not.toContain('ERROR'); - let langFile: XliffFile = readXliff(xliffMergeCmd.generatedI18nFile('de')); + let langFile: ITranslationMessagesFile = readXliff(xliffMergeCmd.generatedI18nFile('de')); expect(langFile.sourceLanguage()).toBe('de'); expect(langFile.targetLanguage()).toBe('de'); langFile.forEachTransUnit((tu: ITransUnit) => { @@ -339,14 +337,14 @@ describe('XliffMerge test spec', () => { let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de', 'en']}, profileContent); xliffMergeCmd.run(); expect(ws.writtenData()).not.toContain('ERROR'); - let langFileGerman: XliffFile = readXliff(xliffMergeCmd.generatedI18nFile('de')); + let langFileGerman: ITranslationMessagesFile = readXliff(xliffMergeCmd.generatedI18nFile('de')); expect(langFileGerman.sourceLanguage()).toBe('de'); expect(langFileGerman.targetLanguage()).toBe('de'); langFileGerman.forEachTransUnit((tu: ITransUnit) => { expect(tu.targetContent()).toBe(tu.sourceContent()); expect(tu.targetState()).toBe('final'); }); - let langFileEnglish: XliffFile = readXliff(xliffMergeCmd.generatedI18nFile('en')); + let langFileEnglish: ITranslationMessagesFile = readXliff(xliffMergeCmd.generatedI18nFile('en')); expect(langFileEnglish.sourceLanguage()).toBe('de'); expect(langFileEnglish.targetLanguage()).toBe('en'); langFileEnglish.forEachTransUnit((tu: ITransUnit) => { @@ -373,7 +371,7 @@ describe('XliffMerge test spec', () => { expect(ws.writtenData()).not.toContain('ERROR'); // now translate some texts in the English version - let langFileEnglish: XliffFile = readXliff(xliffMergeCmd.generatedI18nFile('en')); + let langFileEnglish: ITranslationMessagesFile = readXliff(xliffMergeCmd.generatedI18nFile('en')); let tu: ITransUnit = langFileEnglish.transUnitWithId(ID_TRANSLATED_SCHLIESSEN); expect(tu).toBeTruthy(); langFileEnglish.translate(tu, 'Close'); @@ -451,7 +449,7 @@ describe('XliffMerge test spec', () => { it('should return null for unset description and meaning in master xlf file', (done) => { FileUtil.copy(MASTER1SRC, MASTER); - let master: XliffFile = readXliff(MASTER); + let master: ITranslationMessagesFile = readXliff(MASTER); expect(master.transUnitWithId(ID_NODESC_NOMEANING).description()).toBeFalsy(); expect(master.transUnitWithId(ID_NODESC_NOMEANING).meaning()).toBeFalsy(); done(); @@ -459,7 +457,7 @@ describe('XliffMerge test spec', () => { it('should find description and meaning in master xlf file', (done) => { FileUtil.copy(MASTER1SRC, MASTER); - let master: XliffFile = readXliff(MASTER); + let master: ITranslationMessagesFile = readXliff(MASTER); expect(master.transUnitWithId(ID_MONDAY).description()).toBe('ngx-translate'); expect(master.transUnitWithId(ID_MONDAY).meaning()).toBe('dateservice.monday'); done(); @@ -480,7 +478,7 @@ describe('XliffMerge test spec', () => { let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent); xliffMergeCmd.run(); expect(ws.writtenData()).not.toContain('ERROR'); - let langFile: XliffFile = readXliff(xliffMergeCmd.generatedI18nFile('de')); + let langFile: ITranslationMessagesFile = readXliff(xliffMergeCmd.generatedI18nFile('de')); expect(langFile.transUnitWithId(ID_MONDAY).description()).toBe('ngx-translate'); expect(langFile.transUnitWithId(ID_MONDAY).meaning()).toBe('dateservice.monday'); done(); @@ -797,7 +795,7 @@ describe('XliffMerge test spec', () => { it('should return null for unset description and meaning in master xmb file', (done) => { FileUtil.copy(MASTER1SRC, MASTER); - let master: XmbFile = readXmb(MASTER); + let master: ITranslationMessagesFile = readXmb(MASTER); expect(master.transUnitWithId(ID_NODESC_NOMEANING).description()).toBeFalsy(); expect(master.transUnitWithId(ID_NODESC_NOMEANING).meaning()).toBeFalsy(); done(); @@ -805,7 +803,7 @@ describe('XliffMerge test spec', () => { it('should find description and meaning in master xmb file', (done) => { FileUtil.copy(MASTER1SRC, MASTER); - let master: XmbFile = readXmb(MASTER); + let master: ITranslationMessagesFile = readXmb(MASTER); expect(master.transUnitWithId(ID_MONDAY).description()).toBe('ngx-translate'); expect(master.transUnitWithId(ID_MONDAY).meaning()).toBe('dateservice.monday'); done(); @@ -827,7 +825,7 @@ describe('XliffMerge test spec', () => { let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent); xliffMergeCmd.run(); expect(ws.writtenData()).not.toContain('ERROR'); - let langFile: XmbFile = readXmb(xliffMergeCmd.generatedI18nFile('de')); + let langFile: ITranslationMessagesFile = readXmb(xliffMergeCmd.generatedI18nFile('de')); expect(langFile.transUnitWithId(ID_MONDAY).description()).toBe('ngx-translate'); expect(langFile.transUnitWithId(ID_MONDAY).meaning()).toBe('dateservice.monday'); done(); diff --git a/src/xliffmerge/xliff-merge.ts b/src/xliffmerge/xliff-merge.ts index 1c54308..363a426 100644 --- a/src/xliffmerge/xliff-merge.ts +++ b/src/xliffmerge/xliff-merge.ts @@ -6,10 +6,9 @@ import {FileUtil} from '../common/file-util'; import {VERSION} from './version'; import WritableStream = NodeJS.WritableStream; import {isNullOrUndefined} from 'util'; -import {ITranslationMessagesFile} from './i-translation-messages-file'; +import {ITranslationMessagesFile, ITransUnit} from 'ngx-i18nsupport-lib'; 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'; /** diff --git a/src/xliffmerge/xmb-file.js b/src/xliffmerge/xmb-file.js deleted file mode 100644 index 653b58f..0000000 --- a/src/xliffmerge/xmb-file.js +++ /dev/null @@ -1,211 +0,0 @@ -"use strict"; -var xml_reader_1 = require("./xml-reader"); -var util_1 = require("util"); -var file_util_1 = require("../common/file-util"); -/** - * Created by martin on 10.03.2017. - * xmb-File access. - */ -/** - * Read-Options for cheerio, enable xml mode. - * @type {{xmlMode: boolean}} - */ -var CheerioOptions = { - xmlMode: true, - decodeEntities: false, -}; -var TransUnit = (function () { - function TransUnit(_msg, _id) { - this._msg = _msg; - this._id = _id; - } - Object.defineProperty(TransUnit.prototype, "_transUnitElement", { - get: function () { - return this._msg; - }, - enumerable: true, - configurable: true - }); - Object.defineProperty(TransUnit.prototype, "id", { - get: function () { - return this._id; - }, - enumerable: true, - configurable: true - }); - TransUnit.prototype.sourceContent = function () { - return cheerio(this._msg).html(); - }; - TransUnit.prototype.targetContent = function () { - // in fact, target and source are just the same in xmb - return cheerio(this._msg).html(); - }; - TransUnit.prototype.targetState = function () { - return null; // not supported in xmb - }; - /** - * the real xml element used for trans unit. - * Here it is a element. - * @return {CheerioElement} - */ - TransUnit.prototype.asXmlElement = function () { - return this._msg; - }; - /** - * Copy source to target to use it as dummy translation. - * (better than missing value) - * In xmb there is nothing to do, because there is only a target, no source. - */ - TransUnit.prototype.useSourceAsTarget = function (isDefaultLang) { - }; - /** - * Translate trans unit. - * (very simple, just for tests) - * @param translation the translated string - */ - TransUnit.prototype.translate = function (translation) { - var target = cheerio(this._msg); - target.html(translation); - }; - return TransUnit; -}()); -var XmbFile = (function () { - function XmbFile() { - this._warnings = []; - this._numberOfTransUnitsWithMissingId = 0; - } - /** - * 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} - */ - 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; - }; - XmbFile.prototype.initializeFromContent = function (xmlString, path, encoding) { - this.filename = path; - this.encoding = encoding; - this.xmbContent = cheerio.load(xmlString, CheerioOptions); - return this; - }; - XmbFile.prototype.initializeTransUnits = function () { - var _this = this; - if (util_1.isNullOrUndefined(this.transUnits)) { - this.transUnits = []; - var transUnitsInFile = this.xmbContent('msg'); - transUnitsInFile.each(function (index, msg) { - var id = cheerio(msg).attr('id'); - if (!id) { - _this._warnings.push(util_1.format('oops, msg without "id" found in master, please check file %s', _this.filename)); - _this._numberOfTransUnitsWithMissingId++; - } - _this.transUnits.push(new TransUnit(msg, id)); - }); - } - }; - XmbFile.prototype.forEachTransUnit = function (callback) { - this.initializeTransUnits(); - this.transUnits.forEach(function (tu) { return callback(tu); }); - }; - /** - * Get trans-unit with given id. - * @param id - * @return {Cheerio} - */ - XmbFile.prototype.transUnitWithId = function (id) { - this.initializeTransUnits(); - return this.transUnits.find(function (tu) { return tu.id == id; }); - }; - XmbFile.prototype.warnings = function () { - this.initializeTransUnits(); - return this._warnings; - }; - XmbFile.prototype.numberOfTransUnits = function () { - this.initializeTransUnits(); - return this.transUnits.length; - }; - XmbFile.prototype.numberOfTransUnitsWithMissingId = function () { - this.initializeTransUnits(); - return this._numberOfTransUnitsWithMissingId; - }; - /** - * Get source language. - * Unsupported in xmb. - * @return {string} - */ - XmbFile.prototype.sourceLanguage = function () { - return null; - }; - /** - * Edit the source language. - * Unsupported in xmb. - * @param language - */ - XmbFile.prototype.setSourceLanguage = function (language) { - // do nothing, xmb has no notation for this. - }; - /** - * Get target language. - * Unsupported in xmb. - * @return {string} - */ - XmbFile.prototype.targetLanguage = function () { - return null; - }; - /** - * Edit the target language. - * Unsupported in xmb. - * @param language - */ - XmbFile.prototype.setTargetLanguage = function (language) { - // do nothing, xmb has no notation for this. - }; - /** - * Copy source to target to use it as dummy translation. - * (better than missing value) - * In xmb there is nothing to do, because there is only a target, no source. - */ - XmbFile.prototype.useSourceAsTarget = function (transUnit, isDefaultLang) { - transUnit.useSourceAsTarget(isDefaultLang); - }; - /** - * Translate a given trans unit. - * (very simple, just for tests) - * @param transUnit - * @param translation the translated string - */ - XmbFile.prototype.translate = function (transUnit, translation) { - transUnit.translate(translation); - }; - /** - * Add a new trans-unit. - * @param transUnit - */ - XmbFile.prototype.addNewTransUnit = function (transUnit) { - this.xmbContent('messagebundle').append(cheerio(transUnit.asXmlElement())); - this.initializeTransUnits(); - this.transUnits.push(transUnit); - }; - /** - * Remove the trans-unit with the given id. - * @param id - */ - XmbFile.prototype.removeTransUnitWithId = function (id) { - this.xmbContent('#' + id).remove(); - this.initializeTransUnits(); - this.transUnits = this.transUnits.filter(function (tu) { return tu.id != id; }); - }; - /** - * Save edited content to file. - */ - XmbFile.prototype.save = function () { - file_util_1.FileUtil.replaceContent(this.filename, this.xmbContent.xml(), this.encoding); - }; - return XmbFile; -}()); -exports.XmbFile = XmbFile; diff --git a/src/xliffmerge/xmb-file.ts b/src/xliffmerge/xmb-file.ts deleted file mode 100644 index 19ed0d0..0000000 --- a/src/xliffmerge/xmb-file.ts +++ /dev/null @@ -1,351 +0,0 @@ -import * as cheerio from "cheerio"; -import {ITranslationMessagesFile} from './i-translation-messages-file'; -import {isNullOrUndefined, format} from 'util'; -import {ITransUnit} from './i-trans-unit'; -/** - * Created by martin on 10.03.2017. - * xmb-File access. - */ - -/** - * Read-Options for cheerio, enable xml mode. - * @type {{xmlMode: boolean}} - */ -const CheerioOptions: CheerioOptionsInterface = { - xmlMode: true, - decodeEntities: false, -}; - -class TransUnit implements ITransUnit { - - constructor(private _msg: CheerioElement, private _id: string, private _sourceTransUnitFromMaster: ITransUnit) { - - } - - public get id(): string { - return this._id; - } - - public sourceContent(): string { - return cheerio(this._msg).html(); - } - - /** - * the translated value (containing all markup, depends on the concrete format used). - */ - public targetContent(): string { - // in fact, target and source are just the same in xmb - return cheerio(this._msg).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 = /INTERPOLATION<\/ex><\/ph>/g; - normalized = normalized.replace(re0, '{{0}}'); - let reN: RegExp = /INTERPOLATION_(\d*)<\/ex><\/ph>/g; - normalized = normalized.replace(reN, '{{$1}}'); - - let reStartAnyTag: RegExp = /&lt;(\w*)&gt;<\/ex><\/ph>/g; - normalized = normalized.replace(reStartAnyTag, '<$1>'); - let reCloseAnyTag: RegExp = /&lt;\/(\w*)&gt;<\/ex><\/ph>/g; - normalized = normalized.replace(reCloseAnyTag, ''); - - return normalized; - } - - /** - * State of the translation. - * (not supported in xmb) - * If we have a master, we assumed it is translated if the content is not the same as the masters one. - */ - public targetState(): string { - if (this._sourceTransUnitFromMaster) { - let sourceContent = this._sourceTransUnitFromMaster.sourceContent(); - if (!sourceContent || sourceContent == this.targetContent()) { - return 'new'; - } else { - return 'final'; - } - } - 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. - * Here it is a element. - * @return {CheerioElement} - */ - public asXmlElement(): CheerioElement { - return this._msg; - } - - /** - * Copy source to target to use it as dummy translation. - * (better than missing value) - * In xmb there is nothing to do, because there is only a target, no source. - */ - public useSourceAsTarget(isDefaultLang: boolean) { - } - - /** - * Translate trans unit. - * (very simple, just for tests) - * @param translation the translated string - */ - public translate(translation: string) { - let target = cheerio(this._msg); - target.html(translation); - } - -} - -export class XmbFile implements ITranslationMessagesFile { - - private _filename: string; - - private _encoding: string; - - private xmbContent: CheerioStatic; - - // trans-unit elements and their id from the file - private transUnits: ITransUnit[]; - - private _warnings: string[]; - private _numberOfTransUnitsWithMissingId: number; - - // attached master file, if any - // used as source to determine state ... - private _masterFile: XmbFile; - - /** - * 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. - * @param optionalMaster in case of xmb the master file, that contains the original texts. - * (this is used to support state infos, that are based on comparing original with translated version) - * @return {XmbFile} - */ - constructor(xmlString: string, path: string, encoding: string, optionalMaster?: {xmlContent: string, path: string, encoding: string}) { - this._warnings = []; - this._numberOfTransUnitsWithMissingId = 0; - this.initializeFromContent(xmlString, path, encoding, optionalMaster); - } - - private initializeFromContent(xmlString: string, path: string, encoding: string, optionalMaster?: {xmlContent: string, path: string, encoding: string}): XmbFile { - 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)); - } - if (optionalMaster) { - this._masterFile = new XmbFile(optionalMaster.xmlContent, optionalMaster.path, optionalMaster.encoding); - // TODO check, wether this can be the master ... - } - return this; - } - - private initializeTransUnits() { - if (isNullOrUndefined(this.transUnits)) { - this.transUnits = []; - let transUnitsInFile = this.xmbContent('msg'); - transUnitsInFile.each((index, msg: CheerioElement) => { - let id = cheerio(msg).attr('id'); - if (!id) { - this._warnings.push(format('oops, msg without "id" found in master, please check file %s', this.filename)); - this._numberOfTransUnitsWithMissingId++; - } - let masterUnit: ITransUnit = null; - if (this._masterFile) { - masterUnit = this._masterFile.transUnitWithId(id); - } - this.transUnits.push(new TransUnit(msg, id, masterUnit)); - }); - } - } - - /** - * File type. - * Here 'XMB' - */ - public fileType(): string { - return 'XMB'; - } - - public forEachTransUnit(callback: ((transunit: ITransUnit) => void)) { - this.initializeTransUnits(); - this.transUnits.forEach((tu) => callback(tu)); - } - - /** - * Get trans-unit with given id. - * @param id - * @return {Cheerio} - */ - public transUnitWithId(id: string): ITransUnit { - this.initializeTransUnits(); - return this.transUnits.find((tu) => tu.id == id); - } - - public warnings(): string[] { - this.initializeTransUnits(); - return this._warnings; - } - - public numberOfTransUnits(): number { - this.initializeTransUnits(); - return this.transUnits.length; - } - - public numberOfTransUnitsWithMissingId(): number { - this.initializeTransUnits(); - return this._numberOfTransUnitsWithMissingId; - } - - /** - * Guess language from filename. - * If filename is foo.xy.xmb, than language is assumed to be xy. - * @return {string} Language or null - */ - private guessLanguageFromFilename(): string { - if (this._filename) { - let parts: string[] = this._filename.split('.'); - if (parts.length > 2 && parts[parts.length -1].toLowerCase() == 'xmb') { - return parts[parts.length - 2]; - } - } - return null; - } - - /** - * Get source language. - * Unsupported in xmb. - * Try to guess it from master filename if any.. - * @return {string} - */ - public sourceLanguage(): string { - if (this._masterFile) { - return this._masterFile.guessLanguageFromFilename(); - } else { - return null; - } - } - - /** - * Edit the source language. - * Unsupported in xmb. - * @param language - */ - public setSourceLanguage(language: string) { - // do nothing, xmb has no notation for this. - } - - /** - * Get target language. - * Unsupported in xmb. - * Try to guess it from filename if any.. - * @return {string} - */ - public targetLanguage(): string { - return this.guessLanguageFromFilename(); - } - - /** - * Edit the target language. - * Unsupported in xmb. - * @param language - */ - public setTargetLanguage(language: string) { - // do nothing, xmb has no notation for this. - } - - /** - * Copy source to target to use it as dummy translation. - * (better than missing value) - * In xmb there is nothing to do, because there is only a target, no source. - */ - public useSourceAsTarget(transUnit: ITransUnit, isDefaultLang: boolean) { - transUnit.useSourceAsTarget(isDefaultLang); - } - - /** - * Translate a given trans unit. - * (very simple, just for tests) - * @param transUnit - * @param translation the translated string - */ - public translate(transUnit: ITransUnit, translation: string) { - transUnit.translate(translation); - } - - /** - * Add a new trans-unit. - * @param transUnit - */ - public addNewTransUnit(transUnit: ITransUnit) { - this.xmbContent('messagebundle').append(cheerio(transUnit.asXmlElement())); - this.initializeTransUnits(); - this.transUnits.push(transUnit); - } - - /** - * Remove the trans-unit with the given id. - * @param id - */ - public removeTransUnitWithId(id: string) { - this.xmbContent('#' + id).remove(); - this.initializeTransUnits(); - this.transUnits = this.transUnits.filter((tu) => tu.id != id); - } - - /** - * The filename where the data is read from. - */ - public filename(): string { - return this._filename; - } - - /** - * The encoding of 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(); - } - -} \ No newline at end of file diff --git a/src/xliffmerge/xml-reader.js b/src/xliffmerge/xml-reader.js deleted file mode 100644 index ff760c4..0000000 --- a/src/xliffmerge/xml-reader.js +++ /dev/null @@ -1,49 +0,0 @@ -"use strict"; -var file_util_1 = require("../common/file-util"); -/** - * Created by martin on 10.03.2017. - * Helper class to read XMl with a correct encoding. - */ -var XmlReader = (function () { - function XmlReader() { - } - /** - * Read an xml-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 file content and encoding found in the file. - */ - XmlReader.readXmlFileContent = function (path, encoding) { - if (!encoding) { - encoding = XmlReader.DEFAULT_ENCODING; - } - var content = file_util_1.FileUtil.read(path, encoding); - var foundEncoding = XmlReader.encodingFromXml(content); - if (foundEncoding != encoding) { - // read again with the correct encoding - content = file_util_1.FileUtil.read(path, foundEncoding); - } - return { - content: content, - encoding: foundEncoding - }; - }; - /** - * Read the encoding from the xml. - * xml File starts with .. encoding=".." - * @param xmlString - * @return {any} - */ - XmlReader.encodingFromXml = function (xmlString) { - var index = xmlString.indexOf('encoding="'); - if (index < 0) { - return this.DEFAULT_ENCODING; // default in xml if not explicitly set - } - var endIndex = xmlString.indexOf('"', index + 10); // 10 = length of 'encoding="' - return xmlString.substring(index + 10, endIndex); - }; - return XmlReader; -}()); -XmlReader.DEFAULT_ENCODING = 'UTF-8'; -exports.XmlReader = XmlReader; diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..95e0784 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,1628 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/jasmine@^2.5.43": + version "2.5.47" + resolved "https://registry.yarnpkg.com/@types/jasmine/-/jasmine-2.5.47.tgz#bbba9bcf0e95e7890c6f4a47394e8bacaa960eb6" + +"@types/node@^7.0.5": + version "7.0.12" + resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.12.tgz#ae5f67a19c15f752148004db07cbbb372e69efc9" + +abbrev@1, abbrev@1.0.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" + +ajv@^4.9.1: + version "4.11.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.6.tgz#947e93049790942b2a2d60a8289b28924d39f987" + dependencies: + co "^4.6.0" + json-stable-stringify "^1.0.1" + +align-text@^0.1.1, align-text@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/align-text/-/align-text-0.1.4.tgz#0cd90a561093f35d0a99256c22b7069433fad117" + dependencies: + kind-of "^3.0.2" + longest "^1.0.1" + repeat-string "^1.5.2" + +amdefine@>=0.0.4: + version "1.0.1" + resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5" + +ansi-regex@^2.0.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" + +ansi-styles@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe" + +anymatch@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-1.3.0.tgz#a3e52fa39168c825ff57b0248126ce5a8ff95507" + dependencies: + arrify "^1.0.0" + micromatch "^2.1.5" + +aproba@^1.0.3: + version "1.1.1" + resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.1.1.tgz#95d3600f07710aa0e9298c726ad5ecf2eacbabab" + +are-we-there-yet@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.2.tgz#80e470e95a084794fe1899262c5667c6e88de1b3" + dependencies: + delegates "^1.0.0" + readable-stream "^2.0.0 || ^1.1.13" + +argparse@^1.0.7: + version "1.0.9" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.9.tgz#73d83bc263f86e97f8cc4f6bae1b0e90a7d22c86" + dependencies: + sprintf-js "~1.0.2" + +arr-diff@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-2.0.0.tgz#8f3b827f955a8bd669697e4a4256ac3ceae356cf" + dependencies: + arr-flatten "^1.0.1" + +arr-flatten@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.0.1.tgz#e5ffe54d45e19f32f216e91eb99c8ce892bb604b" + +array-filter@~0.0.0: + version "0.0.1" + resolved "https://registry.yarnpkg.com/array-filter/-/array-filter-0.0.1.tgz#7da8cf2e26628ed732803581fd21f67cacd2eeec" + +array-map@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-map/-/array-map-0.0.0.tgz#88a2bab73d1cf7bcd5c1b118a003f66f665fa662" + +array-reduce@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/array-reduce/-/array-reduce-0.0.0.tgz#173899d3ffd1c7d9383e4479525dbe278cab5f2b" + +array-unique@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.2.1.tgz#a1d97ccafcbc2625cc70fadceb36a50c58b01a53" + +arrify@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" + +asn1@~0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" + +assert-plus@1.0.0, assert-plus@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" + +assert-plus@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.2.0.tgz#d74e1b87e7affc0db8aadb7021f3fe48101ab234" + +async-each@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.1.tgz#19d386a1d9edc6e7c1c85d388aedbcc56d33602d" + +async@1.x, async@^1.4.0: + version "1.5.2" + resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + +aws-sign2@~0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.6.0.tgz#14342dd38dbcc94d0e5b87d763cd63612c0e794f" + +aws4@^1.2.1: + version "1.6.0" + resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" + +babel-runtime@^6.9.2: + version "6.23.0" + resolved "https://registry.yarnpkg.com/babel-runtime/-/babel-runtime-6.23.0.tgz#0a9489f144de70efb3ce4300accdb329e2fc543b" + dependencies: + core-js "^2.4.0" + regenerator-runtime "^0.10.0" + +balanced-match@^0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-0.4.2.tgz#cb3f3e3c732dc0f01ee70b403f302e61d7709838" + +bcrypt-pbkdf@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.1.tgz#63bc5dcb61331b92bc05fd528953c33462a06f8d" + dependencies: + tweetnacl "^0.14.3" + +binary-extensions@^1.0.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.8.0.tgz#48ec8d16df4377eae5fa5884682480af4d95c774" + +block-stream@*: + version "0.0.9" + resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a" + dependencies: + inherits "~2.0.0" + +boolbase@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" + +boom@2.x.x: + version "2.10.1" + resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" + dependencies: + hoek "2.x.x" + +brace-expansion@^1.0.0: + version "1.1.7" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.7.tgz#3effc3c50e000531fb720eaff80f0ae8ef23cf59" + dependencies: + balanced-match "^0.4.1" + concat-map "0.0.1" + +braces@^1.8.2: + version "1.8.5" + resolved "https://registry.yarnpkg.com/braces/-/braces-1.8.5.tgz#ba77962e12dff969d6b76711e914b737857bf6a7" + dependencies: + expand-range "^1.8.1" + preserve "^0.2.0" + repeat-element "^1.1.2" + +buffer-shims@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/buffer-shims/-/buffer-shims-1.0.0.tgz#9978ce317388c649ad8793028c3477ef044a8b51" + +camelcase@^1.0.2: + version "1.2.1" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" + +caseless@~0.11.0: + version "0.11.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.11.0.tgz#715b96ea9841593cc33067923f5ec60ebda4f7d7" + +caseless@~0.12.0: + version "0.12.0" + resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" + +center-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" + dependencies: + align-text "^0.1.3" + lazy-cache "^1.0.3" + +chalk@^1.1.1, chalk@^1.1.3: + version "1.1.3" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98" + dependencies: + ansi-styles "^2.2.1" + escape-string-regexp "^1.0.2" + has-ansi "^2.0.0" + strip-ansi "^3.0.0" + supports-color "^2.0.0" + +cheerio@^0.22.0: + version "0.22.0" + resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" + dependencies: + css-select "~1.2.0" + dom-serializer "~0.1.0" + entities "~1.1.1" + htmlparser2 "^3.9.1" + lodash.assignin "^4.0.9" + lodash.bind "^4.1.4" + lodash.defaults "^4.0.1" + lodash.filter "^4.4.0" + lodash.flatten "^4.2.0" + lodash.foreach "^4.3.0" + lodash.map "^4.4.0" + lodash.merge "^4.4.0" + lodash.pick "^4.2.1" + lodash.reduce "^4.4.0" + lodash.reject "^4.4.0" + lodash.some "^4.4.0" + +chokidar@^1.6.0: + version "1.6.1" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" + dependencies: + anymatch "^1.3.0" + async-each "^1.0.0" + glob-parent "^2.0.0" + inherits "^2.0.1" + is-binary-path "^1.0.0" + is-glob "^2.0.0" + path-is-absolute "^1.0.0" + readdirp "^2.0.0" + optionalDependencies: + fsevents "^1.0.0" + +cliui@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-2.1.0.tgz#4b475760ff80264c762c3a1719032e91c7fea0d1" + dependencies: + center-align "^0.1.1" + right-align "^0.1.1" + wordwrap "0.0.2" + +co@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/co/-/co-4.6.0.tgz#6ea6bdf3d853ae54ccb8e47bfa0bf3f9031fb184" + +code-point-at@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" + +coffee-script@>=1.0.1: + version "1.12.4" + resolved "https://registry.yarnpkg.com/coffee-script/-/coffee-script-1.12.4.tgz#fe1bced97fe1fb3927b998f2b45616e0658be1ff" + +combined-stream@^1.0.5, combined-stream@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.5.tgz#938370a57b4a51dea2c77c15d5c5fdf895164009" + dependencies: + delayed-stream "~1.0.0" + +commander@^2.9.0: + version "2.9.0" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.9.0.tgz#9c99094176e12240cb22d6c5146098400fe0f7d4" + dependencies: + graceful-readlink ">= 1.0.0" + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + +console-control-strings@^1.0.0, console-control-strings@~1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e" + +core-js@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.4.1.tgz#4de911e667b0eae9124e34254b53aea6fc618d3e" + +core-util-is@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" + +coveralls@^2.11.16: + version "2.13.0" + resolved "https://registry.yarnpkg.com/coveralls/-/coveralls-2.13.0.tgz#df933876e8c6f478efb04f4d3ab70dc96b7e5a8e" + dependencies: + js-yaml "3.6.1" + lcov-parse "0.0.10" + log-driver "1.2.5" + minimist "1.2.0" + request "2.79.0" + +cpx@^1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/cpx/-/cpx-1.5.0.tgz#185be018511d87270dedccc293171e37655ab88f" + dependencies: + babel-runtime "^6.9.2" + chokidar "^1.6.0" + duplexer "^0.1.1" + glob "^7.0.5" + glob2base "^0.0.12" + minimatch "^3.0.2" + mkdirp "^0.5.1" + resolve "^1.1.7" + safe-buffer "^5.0.1" + shell-quote "^1.6.1" + subarg "^1.0.0" + +cryptiles@2.x.x: + version "2.0.5" + resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" + dependencies: + boom "2.x.x" + +css-select@~1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" + dependencies: + boolbase "~1.0.0" + css-what "2.1" + domutils "1.5.1" + nth-check "~1.0.1" + +css-what@2.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" + +dashdash@^1.12.0: + version "1.14.1" + resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" + dependencies: + assert-plus "^1.0.0" + +debug@^2.2.0: + version "2.6.3" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.3.tgz#0f7eb8c30965ec08c72accfa0130c8b79984141d" + dependencies: + ms "0.7.2" + +decamelize@^1.0.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + +deep-extend@~0.4.0: + version "0.4.1" + resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.4.1.tgz#efe4113d08085f4e6f9687759810f807469e2253" + +deep-is@~0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.3.tgz#b369d6fb5dbc13eecf524f91b070feedc357cf34" + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + +delegates@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" + +dom-serializer@0, dom-serializer@~0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" + dependencies: + domelementtype "~1.1.1" + entities "~1.1.1" + +domelementtype@1, domelementtype@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + +domelementtype@~1.1.1: + version "1.1.3" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" + +domhandler@^2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" + dependencies: + domelementtype "1" + +domutils@1.5.1, domutils@^1.5.1: + version "1.5.1" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" + dependencies: + dom-serializer "0" + domelementtype "1" + +duplexer@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" + +ecc-jsbn@~0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.1.tgz#0fc73a9ed5f0d53c38193398523ef7e543777505" + dependencies: + jsbn "~0.1.0" + +entities@^1.1.1, entities@~1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" + +escape-string-regexp@^1.0.2: + version "1.0.5" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" + +escodegen@1.8.x: + version "1.8.1" + resolved "https://registry.yarnpkg.com/escodegen/-/escodegen-1.8.1.tgz#5a5b53af4693110bebb0867aa3430dd3b70a1018" + dependencies: + esprima "^2.7.1" + estraverse "^1.9.1" + esutils "^2.0.2" + optionator "^0.8.1" + optionalDependencies: + source-map "~0.2.0" + +esprima@2.7.x, esprima@^2.6.0, esprima@^2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/esprima/-/esprima-2.7.3.tgz#96e3b70d5779f6ad49cd032673d1c312767ba581" + +estraverse@^1.9.1: + version "1.9.3" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-1.9.3.tgz#af67f2dc922582415950926091a4005d29c9bb44" + +esutils@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.2.tgz#0abf4f1caa5bcb1f7a9d8acc6dea4faaa04bac9b" + +expand-brackets@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/expand-brackets/-/expand-brackets-0.1.5.tgz#df07284e342a807cd733ac5af72411e581d1177b" + dependencies: + is-posix-bracket "^0.1.0" + +expand-range@^1.8.1: + version "1.8.2" + resolved "https://registry.yarnpkg.com/expand-range/-/expand-range-1.8.2.tgz#a299effd335fe2721ebae8e257ec79644fc85337" + dependencies: + fill-range "^2.1.0" + +extend@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.0.tgz#5a474353b9f3353ddd8176dfd37b91c83a46f1d4" + +extglob@^0.3.1: + version "0.3.2" + resolved "https://registry.yarnpkg.com/extglob/-/extglob-0.3.2.tgz#2e18ff3d2f49ab2765cec9023f011daa8d8349a1" + dependencies: + is-extglob "^1.0.0" + +extsprintf@1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.0.2.tgz#e1080e0658e300b06294990cc70e1502235fd550" + +fast-levenshtein@~2.0.4: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + +filename-regex@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/filename-regex/-/filename-regex-2.0.0.tgz#996e3e80479b98b9897f15a8a58b3d084e926775" + +fileset@~0.1.5: + version "0.1.8" + resolved "https://registry.yarnpkg.com/fileset/-/fileset-0.1.8.tgz#506b91a9396eaa7e32fb42a84077c7a0c736b741" + dependencies: + glob "3.x" + minimatch "0.x" + +fill-range@^2.1.0: + version "2.2.3" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-2.2.3.tgz#50b77dfd7e469bc7492470963699fe7a8485a723" + dependencies: + is-number "^2.1.0" + isobject "^2.0.0" + randomatic "^1.1.3" + repeat-element "^1.1.2" + repeat-string "^1.5.2" + +find-index@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/find-index/-/find-index-0.1.1.tgz#675d358b2ca3892d795a1ab47232f8b6e2e0dde4" + +for-in@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + +for-own@^0.1.4: + version "0.1.5" + resolved "https://registry.yarnpkg.com/for-own/-/for-own-0.1.5.tgz#5265c681a4f294dabbf17c9509b6763aa84510ce" + dependencies: + for-in "^1.0.1" + +forever-agent@~0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" + +form-data@~2.1.1: + version "2.1.4" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.1.4.tgz#33c183acf193276ecaa98143a69e94bfee1750d1" + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.5" + mime-types "^2.1.12" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + +fsevents@^1.0.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-1.1.1.tgz#f19fd28f43eeaf761680e519a203c4d0b3d31aff" + dependencies: + nan "^2.3.0" + node-pre-gyp "^0.6.29" + +fstream-ignore@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/fstream-ignore/-/fstream-ignore-1.0.5.tgz#9c31dae34767018fe1d249b24dada67d092da105" + dependencies: + fstream "^1.0.0" + inherits "2" + minimatch "^3.0.0" + +fstream@^1.0.0, fstream@^1.0.10, fstream@^1.0.2: + version "1.0.11" + resolved "https://registry.yarnpkg.com/fstream/-/fstream-1.0.11.tgz#5c1fb1f117477114f0632a0eb4b71b3cb0fd3171" + dependencies: + graceful-fs "^4.1.2" + inherits "~2.0.0" + mkdirp ">=0.5 0" + rimraf "2" + +gauge@~2.7.1: + version "2.7.3" + resolved "https://registry.yarnpkg.com/gauge/-/gauge-2.7.3.tgz#1c23855f962f17b3ad3d0dc7443f304542edfe09" + dependencies: + aproba "^1.0.3" + console-control-strings "^1.0.0" + has-unicode "^2.0.0" + object-assign "^4.1.0" + signal-exit "^3.0.0" + string-width "^1.0.1" + strip-ansi "^3.0.1" + wide-align "^1.1.0" + +gaze@~0.3.2: + version "0.3.4" + resolved "https://registry.yarnpkg.com/gaze/-/gaze-0.3.4.tgz#5f94bdda0afe53bc710969bcd6f282548d60c279" + dependencies: + fileset "~0.1.5" + minimatch "~0.2.9" + +generate-function@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/generate-function/-/generate-function-2.0.0.tgz#6858fe7c0969b7d4e9093337647ac79f60dfbe74" + +generate-object-property@^1.1.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/generate-object-property/-/generate-object-property-1.2.0.tgz#9c0e1c40308ce804f4783618b937fa88f99d50d0" + dependencies: + is-property "^1.0.0" + +getpass@^0.1.1: + version "0.1.6" + resolved "https://registry.yarnpkg.com/getpass/-/getpass-0.1.6.tgz#283ffd9fc1256840875311c1b60e8c40187110e6" + dependencies: + assert-plus "^1.0.0" + +glob-base@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/glob-base/-/glob-base-0.3.0.tgz#dbb164f6221b1c0b1ccf82aea328b497df0ea3c4" + dependencies: + glob-parent "^2.0.0" + is-glob "^2.0.0" + +glob-parent@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-2.0.0.tgz#81383d72db054fcccf5336daa902f182f6edbb28" + dependencies: + is-glob "^2.0.0" + +glob2base@^0.0.12: + version "0.0.12" + resolved "https://registry.yarnpkg.com/glob2base/-/glob2base-0.0.12.tgz#9d419b3e28f12e83a362164a277055922c9c0d56" + dependencies: + find-index "^0.1.1" + +glob@3.x: + version "3.2.11" + resolved "https://registry.yarnpkg.com/glob/-/glob-3.2.11.tgz#4a973f635b9190f715d10987d5c00fd2815ebe3d" + dependencies: + inherits "2" + minimatch "0.3" + +glob@^5.0.15: + version "5.0.15" + resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" + dependencies: + inflight "^1.0.4" + inherits "2" + minimatch "2 || 3" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.0.5: + version "7.1.1" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.1.tgz#805211df04faaf1c63a3600306cdf5ade50b2ec8" + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.2" + once "^1.3.0" + path-is-absolute "^1.0.0" + +graceful-fs@^4.1.2: + version "4.1.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" + +"graceful-readlink@>= 1.0.0": + version "1.0.1" + resolved "https://registry.yarnpkg.com/graceful-readlink/-/graceful-readlink-1.0.1.tgz#4cafad76bc62f02fa039b2f94e9a3dd3a391a725" + +growl@~1.7.0: + version "1.7.0" + resolved "https://registry.yarnpkg.com/growl/-/growl-1.7.0.tgz#de2d66136d002e112ba70f3f10c31cf7c350b2da" + +handlebars@^4.0.1: + version "4.0.6" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.6.tgz#2ce4484850537f9c97a8026d5399b935c4ed4ed7" + dependencies: + async "^1.4.0" + optimist "^0.6.1" + source-map "^0.4.4" + optionalDependencies: + uglify-js "^2.6" + +har-schema@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-1.0.5.tgz#d263135f43307c02c602afc8fe95970c0151369e" + +har-validator@~2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-2.0.6.tgz#cdcbc08188265ad119b6a5a7c8ab70eecfb5d27d" + dependencies: + chalk "^1.1.1" + commander "^2.9.0" + is-my-json-valid "^2.12.4" + pinkie-promise "^2.0.0" + +har-validator@~4.2.1: + version "4.2.1" + resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-4.2.1.tgz#33481d0f1bbff600dd203d75812a6a5fba002e2a" + dependencies: + ajv "^4.9.1" + har-schema "^1.0.5" + +has-ansi@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91" + dependencies: + ansi-regex "^2.0.0" + +has-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-1.0.0.tgz#9d9e793165ce017a00f00418c43f942a7b1d11fa" + +has-unicode@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" + +hawk@~3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/hawk/-/hawk-3.1.3.tgz#078444bd7c1640b0fe540d2c9b73d59678e8e1c4" + dependencies: + boom "2.x.x" + cryptiles "2.x.x" + hoek "2.x.x" + sntp "1.x.x" + +hoek@2.x.x: + version "2.16.3" + resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" + +htmlparser2@^3.9.1: + version "3.9.2" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" + dependencies: + domelementtype "^1.3.0" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^2.0.2" + +http-signature@~1.1.0: + version "1.1.1" + resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" + dependencies: + assert-plus "^0.2.0" + jsprim "^1.2.2" + sshpk "^1.7.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@^2.0.1, inherits@~2.0.0, inherits@~2.0.1: + version "2.0.3" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" + +ini@~1.3.0: + version "1.3.4" + resolved "https://registry.yarnpkg.com/ini/-/ini-1.3.4.tgz#0537cb79daf59b59a1a517dff706c86ec039162e" + +is-binary-path@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-1.0.1.tgz#75f16642b480f187a711c814161fd3a4a7655898" + dependencies: + binary-extensions "^1.0.0" + +is-buffer@^1.0.2: + version "1.1.5" + resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.5.tgz#1f3b26ef613b214b88cbca23cc6c01d87961eecc" + +is-dotfile@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-dotfile/-/is-dotfile-1.0.2.tgz#2c132383f39199f8edc268ca01b9b007d205cc4d" + +is-equal-shallow@^0.1.3: + version "0.1.3" + resolved "https://registry.yarnpkg.com/is-equal-shallow/-/is-equal-shallow-0.1.3.tgz#2238098fc221de0bcfa5d9eac4c45d638aa1c534" + dependencies: + is-primitive "^2.0.0" + +is-extendable@^0.1.1: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-0.1.1.tgz#62b110e289a471418e3ec36a617d472e301dfc89" + +is-extglob@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-1.0.0.tgz#ac468177c4943405a092fc8f29760c6ffc6206c0" + +is-fullwidth-code-point@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" + dependencies: + number-is-nan "^1.0.0" + +is-glob@^2.0.0, is-glob@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-2.0.1.tgz#d096f926a3ded5600f3fdfd91198cb0888c2d863" + dependencies: + is-extglob "^1.0.0" + +is-my-json-valid@^2.12.4: + version "2.16.0" + resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.16.0.tgz#f079dd9bfdae65ee2038aae8acbc86ab109e3693" + dependencies: + generate-function "^2.0.0" + generate-object-property "^1.1.0" + jsonpointer "^4.0.0" + xtend "^4.0.0" + +is-number@^2.0.2, is-number@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-2.1.0.tgz#01fcbbb393463a548f2f466cce16dece49db908f" + dependencies: + kind-of "^3.0.2" + +is-posix-bracket@^0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/is-posix-bracket/-/is-posix-bracket-0.1.1.tgz#3334dc79774368e92f016e6fbc0a88f5cd6e6bc4" + +is-primitive@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575" + +is-property@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/is-property/-/is-property-1.0.2.tgz#57fe1c4e48474edd65b09911f26b1cd4095dda84" + +is-typedarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" + +isarray@1.0.0, isarray@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/isarray/-/isarray-1.0.0.tgz#bb935d48582cba168c06834957a54a3e07124f11" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + +isobject@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-2.1.0.tgz#f065561096a3f1da2ef46272f815c840d87e0c89" + dependencies: + isarray "1.0.0" + +isstream@~0.1.2: + version "0.1.2" + resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" + +istanbul@^0.4.5: + version "0.4.5" + resolved "https://registry.yarnpkg.com/istanbul/-/istanbul-0.4.5.tgz#65c7d73d4c4da84d4f3ac310b918fb0b8033733b" + dependencies: + abbrev "1.0.x" + async "1.x" + escodegen "1.8.x" + esprima "2.7.x" + glob "^5.0.15" + handlebars "^4.0.1" + js-yaml "3.x" + mkdirp "0.5.x" + nopt "3.x" + once "1.x" + resolve "1.1.x" + supports-color "^3.1.0" + which "^1.1.1" + wordwrap "^1.0.0" + +jasmine-growl-reporter@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/jasmine-growl-reporter/-/jasmine-growl-reporter-0.0.3.tgz#b87ae551e359d28ad5217765eaef6c07b763f6c8" + dependencies: + growl "~1.7.0" + +jasmine-node@^1.14.5: + version "1.14.5" + resolved "https://registry.yarnpkg.com/jasmine-node/-/jasmine-node-1.14.5.tgz#18e8397b856924ee77003666c3731b5aea50c39d" + dependencies: + coffee-script ">=1.0.1" + gaze "~0.3.2" + jasmine-growl-reporter "~0.0.2" + jasmine-reporters "~1.0.0" + mkdirp "~0.3.5" + requirejs ">=0.27.1" + underscore ">= 1.3.1" + walkdir ">= 0.0.1" + +jasmine-reporters@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/jasmine-reporters/-/jasmine-reporters-1.0.2.tgz#ab613ed5977dc7487e85b3c12f6a8ea8db2ade31" + dependencies: + mkdirp "~0.3.5" + +jodid25519@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/jodid25519/-/jodid25519-1.0.2.tgz#06d4912255093419477d425633606e0e90782967" + dependencies: + jsbn "~0.1.0" + +js-yaml@3.6.1, js-yaml@3.x: + version "3.6.1" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.6.1.tgz#6e5fe67d8b205ce4d22fad05b7781e8dadcc4b30" + dependencies: + argparse "^1.0.7" + esprima "^2.6.0" + +jsbn@~0.1.0: + version "0.1.1" + resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" + +json-schema@0.2.3: + version "0.2.3" + resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" + +json-stable-stringify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" + dependencies: + jsonify "~0.0.0" + +json-stringify-safe@~5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" + +jsonify@~0.0.0: + version "0.0.0" + resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.0.tgz#2c74b6ee41d93ca51b7b5aaee8f503631d252a73" + +jsonpointer@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-4.0.1.tgz#4fd92cb34e0e9db3c89c8622ecf51f9b978c6cb9" + +jsprim@^1.2.2: + version "1.4.0" + resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.0.tgz#a3b87e40298d8c380552d8cc7628a0bb95a22918" + dependencies: + assert-plus "1.0.0" + extsprintf "1.0.2" + json-schema "0.2.3" + verror "1.3.6" + +kind-of@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.1.0.tgz#475d698a5e49ff5e53d14e3e732429dc8bf4cf47" + dependencies: + is-buffer "^1.0.2" + +lazy-cache@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/lazy-cache/-/lazy-cache-1.0.4.tgz#a1d78fc3a50474cb80845d3b3b6e1da49a446e8e" + +lcov-parse@0.0.10: + version "0.0.10" + resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-0.0.10.tgz#1b0b8ff9ac9c7889250582b70b71315d9da6d9a3" + +levn@~0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.3.0.tgz#3b09924edf9f083c0490fdd4c0bc4421e04764ee" + dependencies: + prelude-ls "~1.1.2" + type-check "~0.3.2" + +lodash.assignin@^4.0.9: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" + +lodash.bind@^4.1.4: + version "4.2.1" + resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35" + +lodash.defaults@^4.0.1: + version "4.2.0" + resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" + +lodash.filter@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" + +lodash.flatten@^4.2.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" + +lodash.foreach@^4.3.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" + +lodash.map@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" + +lodash.merge@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5" + +lodash.pick@^4.2.1: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" + +lodash.reduce@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" + +lodash.reject@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415" + +lodash.some@^4.4.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" + +log-driver@1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.5.tgz#7ae4ec257302fd790d557cb10c97100d857b0056" + +longest@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/longest/-/longest-1.0.1.tgz#30a0b2da38f73770e8294a0d22e6625ed77d0097" + +lru-cache@2: + version "2.7.3" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" + +micromatch@^2.1.5: + version "2.3.11" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-2.3.11.tgz#86677c97d1720b363431d04d0d15293bd38c1565" + dependencies: + arr-diff "^2.0.0" + array-unique "^0.2.1" + braces "^1.8.2" + expand-brackets "^0.1.4" + extglob "^0.3.1" + filename-regex "^2.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.1" + kind-of "^3.0.2" + normalize-path "^2.0.1" + object.omit "^2.0.0" + parse-glob "^3.0.4" + regex-cache "^0.4.2" + +mime-db@~1.27.0: + version "1.27.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.27.0.tgz#820f572296bbd20ec25ed55e5b5de869e5436eb1" + +mime-types@^2.1.12, mime-types@~2.1.7: + version "2.1.15" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.15.tgz#a4ebf5064094569237b8cf70046776d09fc92aed" + dependencies: + mime-db "~1.27.0" + +minimatch@0.3: + version "0.3.0" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.3.0.tgz#275d8edaac4f1bb3326472089e7949c8394699dd" + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +minimatch@0.x, minimatch@~0.2.9: + version "0.2.14" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a" + dependencies: + lru-cache "2" + sigmund "~1.0.0" + +"minimatch@2 || 3", minimatch@^3.0.0, minimatch@^3.0.2: + version "3.0.3" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.0.3.tgz#2a4e4090b96b2db06a9d7df01055a62a77c9b774" + dependencies: + brace-expansion "^1.0.0" + +minimist@0.0.8, minimist@~0.0.1: + version "0.0.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" + +minimist@1.2.0, minimist@^1.1.0, minimist@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" + +mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" + dependencies: + minimist "0.0.8" + +mkdirp@~0.3.5: + version "0.3.5" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.3.5.tgz#de3e5f8961c88c787ee1368df849ac4413eca8d7" + +ms@0.7.2: + version "0.7.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-0.7.2.tgz#ae25cf2512b3885a1d95d7f037868d8431124765" + +nan@^2.3.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.1.tgz#8c84f7b14c96b89f57fbc838012180ec8ca39a01" + +ngx-i18nsupport-lib@^0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/ngx-i18nsupport-lib/-/ngx-i18nsupport-lib-0.0.2.tgz#1dcb75c58bed075cec4791b43f3aec87e2842563" + dependencies: + cheerio "^0.22.0" + +node-pre-gyp@^0.6.29: + version "0.6.34" + resolved "https://registry.yarnpkg.com/node-pre-gyp/-/node-pre-gyp-0.6.34.tgz#94ad1c798a11d7fc67381b50d47f8cc18d9799f7" + dependencies: + mkdirp "^0.5.1" + nopt "^4.0.1" + npmlog "^4.0.2" + rc "^1.1.7" + request "^2.81.0" + rimraf "^2.6.1" + semver "^5.3.0" + tar "^2.2.1" + tar-pack "^3.4.0" + +nopt@3.x: + version "3.0.6" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-3.0.6.tgz#c6465dbf08abcd4db359317f79ac68a646b28ff9" + dependencies: + abbrev "1" + +nopt@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" + dependencies: + abbrev "1" + osenv "^0.1.4" + +normalize-path@^2.0.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-2.1.1.tgz#1ab28b556e198363a8c1a6f7e6fa20137fe6aed9" + dependencies: + remove-trailing-separator "^1.0.1" + +npmlog@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-4.0.2.tgz#d03950e0e78ce1527ba26d2a7592e9348ac3e75f" + dependencies: + are-we-there-yet "~1.1.2" + console-control-strings "~1.1.0" + gauge "~2.7.1" + set-blocking "~2.0.0" + +nth-check@~1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" + dependencies: + boolbase "~1.0.0" + +number-is-nan@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" + +oauth-sign@~0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" + +object-assign@^4.1.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + +object.omit@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-2.0.1.tgz#1a9c744829f39dbb858c76ca3579ae2a54ebd1fa" + dependencies: + for-own "^0.1.4" + is-extendable "^0.1.1" + +once@1.x, once@^1.3.0, once@^1.3.3: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + dependencies: + wrappy "1" + +optimist@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + dependencies: + minimist "~0.0.1" + wordwrap "~0.0.2" + +optionator@^0.8.1: + version "0.8.2" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.8.2.tgz#364c5e409d3f4d6301d6c0b4c05bba50180aeb64" + dependencies: + deep-is "~0.1.3" + fast-levenshtein "~2.0.4" + levn "~0.3.0" + prelude-ls "~1.1.2" + type-check "~0.3.2" + wordwrap "~1.0.0" + +os-homedir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" + +os-tmpdir@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + +osenv@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/osenv/-/osenv-0.1.4.tgz#42fe6d5953df06c8064be6f176c3d05aaaa34644" + dependencies: + os-homedir "^1.0.0" + os-tmpdir "^1.0.0" + +parse-glob@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/parse-glob/-/parse-glob-3.0.4.tgz#b2c376cfb11f35513badd173ef0bb6e3a388391c" + dependencies: + glob-base "^0.3.0" + is-dotfile "^1.0.0" + is-extglob "^1.0.0" + is-glob "^2.0.0" + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + +path-parse@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.5.tgz#3c1adf871ea9cd6c9431b6ea2bd74a0ff055c4c1" + +performance-now@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/performance-now/-/performance-now-0.2.0.tgz#33ef30c5c77d4ea21c5a53869d91b56d8f2555e5" + +pinkie-promise@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/pinkie-promise/-/pinkie-promise-2.0.1.tgz#2135d6dfa7a358c069ac9b178776288228450ffa" + dependencies: + pinkie "^2.0.0" + +pinkie@^2.0.0: + version "2.0.4" + resolved "https://registry.yarnpkg.com/pinkie/-/pinkie-2.0.4.tgz#72556b80cfa0d48a974e80e77248e80ed4f7f870" + +prelude-ls@~1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.1.2.tgz#21932a549f5e52ffd9a827f570e04be62a97da54" + +preserve@^0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" + +process-nextick-args@~1.0.6: + version "1.0.7" + resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-1.0.7.tgz#150e20b756590ad3f91093f25a4f2ad8bff30ba3" + +punycode@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e" + +qs@~6.3.0: + version "6.3.2" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.3.2.tgz#e75bd5f6e268122a2a0e0bda630b2550c166502c" + +qs@~6.4.0: + version "6.4.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.4.0.tgz#13e26d28ad6b0ffaa91312cd3bf708ed351e7233" + +randomatic@^1.1.3: + version "1.1.6" + resolved "https://registry.yarnpkg.com/randomatic/-/randomatic-1.1.6.tgz#110dcabff397e9dcff7c0789ccc0a49adf1ec5bb" + dependencies: + is-number "^2.0.2" + kind-of "^3.0.2" + +rc@^1.1.7: + version "1.2.1" + resolved "https://registry.yarnpkg.com/rc/-/rc-1.2.1.tgz#2e03e8e42ee450b8cb3dce65be1bf8974e1dfd95" + dependencies: + deep-extend "~0.4.0" + ini "~1.3.0" + minimist "^1.2.0" + strip-json-comments "~2.0.1" + +"readable-stream@^2.0.0 || ^1.1.13", readable-stream@^2.0.2, readable-stream@^2.1.4: + version "2.2.9" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.9.tgz#cf78ec6f4a6d1eb43d26488cac97f042e74b7fc8" + dependencies: + buffer-shims "~1.0.0" + core-util-is "~1.0.0" + inherits "~2.0.1" + isarray "~1.0.0" + process-nextick-args "~1.0.6" + string_decoder "~1.0.0" + util-deprecate "~1.0.1" + +readdirp@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" + dependencies: + graceful-fs "^4.1.2" + minimatch "^3.0.2" + readable-stream "^2.0.2" + set-immediate-shim "^1.0.1" + +regenerator-runtime@^0.10.0: + version "0.10.3" + resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.10.3.tgz#8c4367a904b51ea62a908ac310bf99ff90a82a3e" + +regex-cache@^0.4.2: + version "0.4.3" + resolved "https://registry.yarnpkg.com/regex-cache/-/regex-cache-0.4.3.tgz#9b1a6c35d4d0dfcef5711ae651e8e9d3d7114145" + dependencies: + is-equal-shallow "^0.1.3" + is-primitive "^2.0.0" + +remove-trailing-separator@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.0.1.tgz#615ebb96af559552d4bf4057c8436d486ab63cc4" + +repeat-element@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/repeat-element/-/repeat-element-1.1.2.tgz#ef089a178d1483baae4d93eb98b4f9e4e11d990a" + +repeat-string@^1.5.2: + version "1.6.1" + resolved "https://registry.yarnpkg.com/repeat-string/-/repeat-string-1.6.1.tgz#8dcae470e1c88abc2d600fff4a776286da75e637" + +request@2.79.0: + version "2.79.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.79.0.tgz#4dfe5bf6be8b8cdc37fcf93e04b65577722710de" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.11.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~2.0.6" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + qs "~6.3.0" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "~0.4.1" + uuid "^3.0.0" + +request@^2.81.0: + version "2.81.0" + resolved "https://registry.yarnpkg.com/request/-/request-2.81.0.tgz#c6928946a0e06c5f8d6f8a9333469ffda46298a0" + dependencies: + aws-sign2 "~0.6.0" + aws4 "^1.2.1" + caseless "~0.12.0" + combined-stream "~1.0.5" + extend "~3.0.0" + forever-agent "~0.6.1" + form-data "~2.1.1" + har-validator "~4.2.1" + hawk "~3.1.3" + http-signature "~1.1.0" + is-typedarray "~1.0.0" + isstream "~0.1.2" + json-stringify-safe "~5.0.1" + mime-types "~2.1.7" + oauth-sign "~0.8.1" + performance-now "^0.2.0" + qs "~6.4.0" + safe-buffer "^5.0.1" + stringstream "~0.0.4" + tough-cookie "~2.3.0" + tunnel-agent "^0.6.0" + uuid "^3.0.0" + +requirejs@>=0.27.1: + version "2.3.3" + resolved "https://registry.yarnpkg.com/requirejs/-/requirejs-2.3.3.tgz#aa59fd3a0287eaf407959a138228044b5dd6a6a3" + +resolve@1.1.x: + version "1.1.7" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.1.7.tgz#203114d82ad2c5ed9e8e0411b3932875e889e97b" + +resolve@^1.1.7: + version "1.3.2" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.3.2.tgz#1f0442c9e0cbb8136e87b9305f932f46c7f28235" + dependencies: + path-parse "^1.0.5" + +right-align@^0.1.1: + version "0.1.3" + resolved "https://registry.yarnpkg.com/right-align/-/right-align-0.1.3.tgz#61339b722fe6a3515689210d24e14c96148613ef" + dependencies: + align-text "^0.1.1" + +rimraf@2, rimraf@^2.5.1, rimraf@^2.6.1: + version "2.6.1" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.6.1.tgz#c2338ec643df7a1b7fe5c54fa86f57428a55f33d" + dependencies: + glob "^7.0.5" + +safe-buffer@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.0.1.tgz#d263ca54696cd8a306b5ca6551e92de57918fbe7" + +semver@^5.3.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/semver/-/semver-5.3.0.tgz#9b2ce5d3de02d17c6012ad326aa6b4d0cf54f94f" + +set-blocking@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" + +set-immediate-shim@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz#4b2b1b27eb808a9f8dcc481a58e5e56f599f3f61" + +shell-quote@^1.6.1: + version "1.6.1" + resolved "https://registry.yarnpkg.com/shell-quote/-/shell-quote-1.6.1.tgz#f4781949cce402697127430ea3b3c5476f481767" + dependencies: + array-filter "~0.0.0" + array-map "~0.0.0" + array-reduce "~0.0.0" + jsonify "~0.0.0" + +sigmund@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/sigmund/-/sigmund-1.0.1.tgz#3ff21f198cad2175f9f3b781853fd94d0d19b590" + +signal-exit@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.2.tgz#b5fdc08f1287ea1178628e415e25132b73646c6d" + +sntp@1.x.x: + version "1.0.9" + resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" + dependencies: + hoek "2.x.x" + +source-map@^0.4.4: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + dependencies: + amdefine ">=0.0.4" + +source-map@~0.2.0: + version "0.2.0" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.2.0.tgz#dab73fbcfc2ba819b4de03bd6f6eaa48164b3f9d" + dependencies: + amdefine ">=0.0.4" + +source-map@~0.5.1: + version "0.5.6" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" + +sprintf-js@~1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/sprintf-js/-/sprintf-js-1.0.3.tgz#04e6926f662895354f3dd015203633b857297e2c" + +sshpk@^1.7.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/sshpk/-/sshpk-1.11.0.tgz#2d8d5ebb4a6fab28ffba37fa62a90f4a3ea59d77" + dependencies: + asn1 "~0.2.3" + assert-plus "^1.0.0" + dashdash "^1.12.0" + getpass "^0.1.1" + optionalDependencies: + bcrypt-pbkdf "^1.0.0" + ecc-jsbn "~0.1.1" + jodid25519 "^1.0.0" + jsbn "~0.1.0" + tweetnacl "~0.14.0" + +string-width@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" + dependencies: + code-point-at "^1.0.0" + is-fullwidth-code-point "^1.0.0" + strip-ansi "^3.0.0" + +string_decoder@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.0.tgz#f06f41157b664d86069f84bdbdc9b0d8ab281667" + dependencies: + buffer-shims "~1.0.0" + +stringstream@~0.0.4: + version "0.0.5" + resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.5.tgz#4e484cd4de5a0bbbee18e46307710a8a81621878" + +strip-ansi@^3.0.0, strip-ansi@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" + dependencies: + ansi-regex "^2.0.0" + +strip-json-comments@~2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" + +subarg@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2" + dependencies: + minimist "^1.1.0" + +supports-color@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7" + +supports-color@^3.1.0: + version "3.2.3" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-3.2.3.tgz#65ac0504b3954171d8a64946b2ae3cbb8a5f54f6" + dependencies: + has-flag "^1.0.0" + +tar-pack@^3.4.0: + version "3.4.0" + resolved "https://registry.yarnpkg.com/tar-pack/-/tar-pack-3.4.0.tgz#23be2d7f671a8339376cbdb0b8fe3fdebf317984" + dependencies: + debug "^2.2.0" + fstream "^1.0.10" + fstream-ignore "^1.0.5" + once "^1.3.3" + readable-stream "^2.1.4" + rimraf "^2.5.1" + tar "^2.2.1" + uid-number "^0.0.6" + +tar@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tar/-/tar-2.2.1.tgz#8e4d2a256c0e2185c6b18ad694aec968b83cb1d1" + dependencies: + block-stream "*" + fstream "^1.0.2" + inherits "2" + +tough-cookie@~2.3.0: + version "2.3.2" + resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.2.tgz#f081f76e4c85720e6c37a5faced737150d84072a" + dependencies: + punycode "^1.4.1" + +tunnel-agent@^0.6.0: + version "0.6.0" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.6.0.tgz#27a5dea06b36b04a0a9966774b290868f0fc40fd" + dependencies: + safe-buffer "^5.0.1" + +tunnel-agent@~0.4.1: + version "0.4.3" + resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" + +tweetnacl@^0.14.3, tweetnacl@~0.14.0: + version "0.14.5" + resolved "https://registry.yarnpkg.com/tweetnacl/-/tweetnacl-0.14.5.tgz#5ae68177f192d4456269d108afa93ff8743f4f64" + +type-check@~0.3.2: + version "0.3.2" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.3.2.tgz#5884cab512cf1d355e3fb784f30804b2b520db72" + dependencies: + prelude-ls "~1.1.2" + +typescript@^2.2.1: + version "2.2.2" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-2.2.2.tgz#606022508479b55ffa368b58fee963a03dfd7b0c" + +uglify-js@^2.6: + version "2.8.22" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.22.tgz#d54934778a8da14903fa29a326fb24c0ab51a1a0" + dependencies: + source-map "~0.5.1" + yargs "~3.10.0" + optionalDependencies: + uglify-to-browserify "~1.0.0" + +uglify-to-browserify@~1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz#6e0924d6bda6b5afe349e39a6d632850a0f882b7" + +uid-number@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/uid-number/-/uid-number-0.0.6.tgz#0ea10e8035e8eb5b8e4449f06da1c730663baa81" + +"underscore@>= 1.3.1": + version "1.8.3" + resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.8.3.tgz#4f3fb53b106e6097fcf9cb4109f2a5e9bdfa5022" + +util-deprecate@~1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + +uuid@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.0.1.tgz#6544bba2dfda8c1cf17e629a3a305e2bb1fee6c1" + +verror@1.3.6: + version "1.3.6" + resolved "https://registry.yarnpkg.com/verror/-/verror-1.3.6.tgz#cff5df12946d297d2baaefaa2689e25be01c005c" + dependencies: + extsprintf "1.0.2" + +"walkdir@>= 0.0.1": + version "0.0.11" + resolved "https://registry.yarnpkg.com/walkdir/-/walkdir-0.0.11.tgz#a16d025eb931bd03b52f308caed0f40fcebe9532" + +which@^1.1.1: + version "1.2.14" + resolved "https://registry.yarnpkg.com/which/-/which-1.2.14.tgz#9a87c4378f03e827cecaf1acdf56c736c01c14e5" + dependencies: + isexe "^2.0.0" + +wide-align@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.0.tgz#40edde802a71fea1f070da3e62dcda2e7add96ad" + dependencies: + string-width "^1.0.1" + +window-size@0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" + +wordwrap@0.0.2: + version "0.0.2" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.2.tgz#b79669bb42ecb409f83d583cad52ca17eaa1643f" + +wordwrap@^1.0.0, wordwrap@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-1.0.0.tgz#27584810891456a4171c8d0226441ade90cbcaeb" + +wordwrap@~0.0.2: + version "0.0.3" + resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + +xtend@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" + +yargs@~3.10.0: + version "3.10.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1" + dependencies: + camelcase "^1.0.2" + cliui "^2.1.0" + decamelize "^1.0.0" + window-size "0.1.0" From b0e5b32f55b177d20e0bd869fb79ee7d68c08333 Mon Sep 17 00:00:00 2001 From: Martin Roob Date: Mon, 10 Apr 2017 15:02:32 +0200 Subject: [PATCH 4/9] using ngx-i18nsupport-lib v0.0.3 --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index de0385c..aac62a3 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,6 @@ "dependencies": { "chalk": "^1.1.3", "commander": "^2.9.0", - "ngx-i18nsupport-lib": "^0.0.2" + "ngx-i18nsupport-lib": "^0.0.3" } } diff --git a/yarn.lock b/yarn.lock index 95e0784..202afee 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1069,9 +1069,9 @@ nan@^2.3.0: version "2.6.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.1.tgz#8c84f7b14c96b89f57fbc838012180ec8ca39a01" -ngx-i18nsupport-lib@^0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/ngx-i18nsupport-lib/-/ngx-i18nsupport-lib-0.0.2.tgz#1dcb75c58bed075cec4791b43f3aec87e2842563" +ngx-i18nsupport-lib@^0.0.3: + version "0.0.3" + resolved "https://registry.yarnpkg.com/ngx-i18nsupport-lib/-/ngx-i18nsupport-lib-0.0.3.tgz#22c022fdf67300f51ea1c5228d7b2bd2c8fe404b" dependencies: cheerio "^0.22.0" From b86e5e55ebc8eaf5f6a457b42064386c9fbb805a Mon Sep 17 00:00:00 2001 From: Martin Roob Date: Tue, 2 May 2017 07:41:27 +0200 Subject: [PATCH 5/9] work in progress --- Changelog.md | 14 ++ README.md | 49 +++++-- package.json | 4 +- src/common/file-util.ts | 7 + src/common/writer-to-string.ts | 2 +- src/tsconfig.json | 5 +- src/xliffmerge/i-xliff-merge-options.ts | 1 + src/xliffmerge/ngx-translate-extractor.ts | 8 +- src/xliffmerge/xliff-merge-parameters.ts | 13 ++ src/xliffmerge/xliff-merge.spec.ts | 82 +++++++++++- src/xliffmerge/xliff-merge.ts | 6 +- test/testdata/i18n/ngExtractedMaster1.xmb | 11 +- test/testdata/i18n/ngExtractedMaster2.xmb | 11 +- test/testdata/i18n/ngxtranslate.xlf | 8 ++ test/testdata/i18n/ngxtranslate.xmb | 1 + yarn.lock | 152 ++-------------------- 16 files changed, 201 insertions(+), 173 deletions(-) diff --git a/Changelog.md b/Changelog.md index 6a5086f..a43adf8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,17 @@ + +# [0.3.1](https://github.com/martinroob/ngx-i18nsupport/compare/v0.3.0...v0.3.1) (2017-04-25) + +### Bug Fixes + +* **xliffmerge:** compilation problem in WriterToString ([#19](https://github.com/martinroob/ngx-i18nsupport/issues/19)) + + +# [0.3.0](https://github.com/martinroob/ngx-i18nsupport/compare/v0.2.3...v0.3.0) (2017-04-24) + +### Features + +* **xliffmerge:** Added useSourceAsTargetOption, allows empty translations if set to false. + # [0.2.1](https://github.com/martinroob/ngx-i18nsupport/compare/v0.2.0...v0.2.1) (2017-03-27) diff --git a/README.md b/README.md index 3364b5a..fd3d01a 100644 --- a/README.md +++ b/README.md @@ -82,21 +82,38 @@ Options: `json-configurationfile` is a json file with the following allowed content (every value is optional):
-"xliffmerge": {
-  "srcDir: "i18n", // directory, where the master file is expected
-  "genDir": "i18n", // directory, where files are written to (normally identical with srcDir)
-  "i18nFile": "messages.xlf", // master file (relativ to srcDir)
-  "i18nFormat": "xlf", // "xlf" for XLIFF or "xmb" for XML Message Bundles
-  "encoding": "UTF-8",  // expected encoding of xlf or xmb files
-  "defaultLanguage": "en",  // the native language used in your templates
-  "languages": ["en", "de"], // list of languages (if not spefified at command line)
-  "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
-  "quiet": false, // controls output
+{
+  "xliffmergeOptions": {
+    "srcDir": "i18n",
+    "genDir": "i18n",
+    "i18nFile": "messages.xlf",
+    "i18nFormat": "xlf",
+    "encoding": "UTF-8",
+    "defaultLanguage": "en",
+    "languages": ["en", "de"],
+    "removeUnusedIds": true,
+    "supportNgxTranslate": true,
+    "useSourceAsTarget": false,
+    "verbose": false,
+    "quiet": false,
+  }
 }
 
+The options are: +- `srcDir` (string, default "."): directory, where the master file is expected +- `genDir` (string, default "."): directory, where files are written to (normally identical with srcDir) +- `i18nFile` (string, default "messages.xlf"): master file (relativ to srcDir) +- `i18nFormat` (string, default "xlf"): "xlf" for XLIFF or "xmb" for XML Message Bundles +- `encoding` (string, default "UTF-8"): expected encoding of xlf or xmb files +- `defaultLanguage` (string, default "en"): the native language used in your templates +- `languages` (array of strings): list of languages (if not spefified at command line) +- `removeUnusedIds` (boolean, default `true`): flag, if unused IDs should be removed during merge +- `supportNgxTranslate` (boolean, default `false`): flag to active json translation files for ngx-translate +- `useSourceAsTarget` (boolean, default `true`): flag, if source should be copied to target for new trans-units +- `verbose` (boolean, default `false`): controls output +- `quiet` (boolean, default `false`): controls output + ### Generate (untranslated) language files, if not already there When you run `xliffmerge`, it will read the master xliff file **messages.xlf**. This is the file generated by the Angular extraction tool `ng-xi18n`. @@ -131,6 +148,9 @@ This is shown by the **state** `new`. The next step you have to do is to translate the file (or to let it translate). Depending on the software you use for translation you can filter for that state `new`. +>Have a look at my sister project [TinyTranslator](https://github.com/martinroob/tiny-translator). +It can filter for new untranslated entries and allows to edit xlf file very easily. + The file for English on the other hand is correct. So, due to the fact, that English is the **default language** here, the state is `translated`. @@ -161,6 +181,8 @@ it will remove it from the language file So after running it, you just have to translate the new parts. +>Once again: [TinyTranslator](https://github.com/martinroob/tiny-translator) might help you to do that. + ## Tests `npm test` @@ -179,7 +201,8 @@ But if you are interesting, send me an email, so that we can discuss it. * Phillippe Martin [Deploying an i18n Angular app with angular-cli](https://medium.com/@feloy/deploying-an-i18n-angular-app-with-angular-cli-fc788f17e358) * Roland Oldengarm: [Angular 2: Automated i18n workflow using gulp](http://rolandoldengarm.com/index.php/2016/10/17/angular-2-automated-i18n-workflow-using-gulp/) -* [XLIFF Spec](http://docs.oasis-open.org/xliff/xliff-core/xliff-core.html) +* XLIFF Specification: [XLIFF Spec](http://docs.oasis-open.org/xliff/xliff-core/xliff-core.html) +* My Tiny Translator Tool: [TinyTranslator](https://github.com/martinroob/tiny-translator) [travis-badge]: https://travis-ci.org/martinroob/ngx-i18nsupport.svg?branch=master [travis-badge-url]: https://travis-ci.org/martinroob/ngx-i18nsupport diff --git a/package.json b/package.json index aac62a3..123d61d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ngx-i18nsupport", - "version": "0.2.3", + "version": "0.3.1", "description": "Some tooling to be used with the Angular 2 i18n workflow", "main": "index.js", "module": "./src", @@ -49,6 +49,6 @@ "dependencies": { "chalk": "^1.1.3", "commander": "^2.9.0", - "ngx-i18nsupport-lib": "^0.0.3" + "ngx-i18nsupport-lib": "^0.0.6" } } diff --git a/src/common/file-util.ts b/src/common/file-util.ts index 050264a..a74dce2 100644 --- a/src/common/file-util.ts +++ b/src/common/file-util.ts @@ -93,4 +93,11 @@ export class FileUtil { } }; + /** + * Delete a file. + * @param path + */ + public static deleteFile(path: string) { + fs.unlinkSync(path); + } } \ No newline at end of file diff --git a/src/common/writer-to-string.ts b/src/common/writer-to-string.ts index 80b93c4..0d7c1d3 100644 --- a/src/common/writer-to-string.ts +++ b/src/common/writer-to-string.ts @@ -16,7 +16,7 @@ export class WriterToString extends Writable { this.resultString = ''; } - protected _write(chunk: any, encoding: string, callback: Function): void { + public _write(chunk: any, encoding: string, callback: Function): void { let chunkString; if (isString(chunk)) { chunkString = chunk; diff --git a/src/tsconfig.json b/src/tsconfig.json index db505ac..804e009 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -10,12 +10,15 @@ "moduleResolution": "node", "outDir": "../dist", "sourceMap": true, - "target": "es6", + "target": "es5", "typeRoots": [ "../node_modules/@types" ] }, "include": [ "**/*" + ], + "exclude": [ + "node_modules" ] } diff --git a/src/xliffmerge/i-xliff-merge-options.ts b/src/xliffmerge/i-xliff-merge-options.ts index ab87e21..c79b126 100644 --- a/src/xliffmerge/i-xliff-merge-options.ts +++ b/src/xliffmerge/i-xliff-merge-options.ts @@ -36,5 +36,6 @@ export interface IXliffMergeOptions { }; removeUnusedIds?: boolean; supportNgxTranslate?: boolean; // Flag, wether output for ngs-translate should be generated + useSourceAsTarget?: boolean; // Flag, whether source must be used as target for new trans-units } diff --git a/src/xliffmerge/ngx-translate-extractor.ts b/src/xliffmerge/ngx-translate-extractor.ts index 16c5a89..49c70a8 100644 --- a/src/xliffmerge/ngx-translate-extractor.ts +++ b/src/xliffmerge/ngx-translate-extractor.ts @@ -39,7 +39,13 @@ export class NgxTranslateExtractor { */ public extractTo(outputFile: string) { let translations: NgxTranslations = this.toNgxTranslations(this.extract()); - FileUtil.replaceContent(outputFile, JSON.stringify(translations, null, 4), 'UTF-8') + if (translations && Object.keys(translations).length > 0) { + FileUtil.replaceContent(outputFile, JSON.stringify(translations, null, 4), 'UTF-8') + } else { + if (FileUtil.exists(outputFile)) { + FileUtil.deleteFile(outputFile); + } + } } /** diff --git a/src/xliffmerge/xliff-merge-parameters.ts b/src/xliffmerge/xliff-merge-parameters.ts index f1b2732..80d2c44 100644 --- a/src/xliffmerge/xliff-merge-parameters.ts +++ b/src/xliffmerge/xliff-merge-parameters.ts @@ -24,6 +24,7 @@ export class XliffMergeParameters { private _languages: string[]; private _removeUnusedIds: boolean; private _supportNgxTranslate: boolean; + private _useSourceAsTarget: boolean; public errorsFound: XliffMergeError[]; public warningsFound: string[]; @@ -142,6 +143,9 @@ export class XliffMergeParameters { if (profile.supportNgxTranslate) { this._supportNgxTranslate = profile.supportNgxTranslate; } + if (!isNullOrUndefined(profile.useSourceAsTarget)) { + this._useSourceAsTarget = profile.useSourceAsTarget; + } } else { this.warningsFound.push('did not find "xliffmergeOptions" in profile, using defaults'); } @@ -225,6 +229,7 @@ export class XliffMergeParameters { commandOutput.debug('languages:\t%s', this.languages()); commandOutput.debug('removeUnusedIds:\t%s', this.removeUnusedIds()); commandOutput.debug('supportNgxTranslate:\t%s', this.supportNgxTranslate()); + commandOutput.debug('useSourceAsTarget:\t%s', this.useSourceAsTarget()); } /** @@ -310,4 +315,12 @@ export class XliffMergeParameters { public supportNgxTranslate(): boolean { return (isNullOrUndefined(this._supportNgxTranslate)) ? false : this._supportNgxTranslate; } + + /** + * Whether source must be used as target for new trans-units + * Default is true + */ + public useSourceAsTarget(): boolean { + return (isNullOrUndefined(this._useSourceAsTarget)) ? true : this._useSourceAsTarget; + } } \ No newline at end of file diff --git a/src/xliffmerge/xliff-merge.spec.ts b/src/xliffmerge/xliff-merge.spec.ts index 3833daf..681d331 100644 --- a/src/xliffmerge/xliff-merge.spec.ts +++ b/src/xliffmerge/xliff-merge.spec.ts @@ -354,6 +354,39 @@ describe('XliffMerge test spec', () => { done(); }); + it('should generate translated file for all languages with empty targets for non default languages', (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, + useSourceAsTarget: false + } + }; + let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de', 'en']}, profileContent); + xliffMergeCmd.run(); + expect(ws.writtenData()).not.toContain('ERROR'); + let langFileGerman: ITranslationMessagesFile = readXliff(xliffMergeCmd.generatedI18nFile('de')); + expect(langFileGerman.sourceLanguage()).toBe('de'); + expect(langFileGerman.targetLanguage()).toBe('de'); + langFileGerman.forEachTransUnit((tu: ITransUnit) => { + expect(tu.targetContent()).toBe(tu.sourceContent()); + expect(tu.targetState()).toBe('final'); + }); + let langFileEnglish: ITranslationMessagesFile = readXliff(xliffMergeCmd.generatedI18nFile('en')); + expect(langFileEnglish.sourceLanguage()).toBe('de'); + expect(langFileEnglish.targetLanguage()).toBe('en'); + langFileEnglish.forEachTransUnit((tu: ITransUnit) => { + expect(tu.targetContent()).toBe(''); + expect(tu.targetState()).toBe('new'); + }); + done(); + }); + it('should merge translated file for all languages', (done) => { FileUtil.copy(MASTER1SRC, MASTER); let ws: WriterToString = new WriterToString(); @@ -422,7 +455,7 @@ describe('XliffMerge test spec', () => { // look, that the new file contains the translation langFileEnglish = readXliff(xliffMergeCmd.generatedI18nFile('en')); - expect(langFileEnglish.transUnitWithId(ID_WITH_PLACEHOLDER).targetContent()).toBe('Item of added.'); + expect(langFileEnglish.transUnitWithId(ID_WITH_PLACEHOLDER).targetContent()).toBe('Item of added.'); done(); }); @@ -433,6 +466,7 @@ describe('XliffMerge test spec', () => { let MASTER1FILE = 'ngxtranslate.xlf'; let MASTER1SRC = SRCDIR + MASTER1FILE; + let MASTER_WITHOUT_NGX_TRANSLATE_STUFF = SRCDIR + 'ngExtractedMaster1.xlf'; let MASTERFILE = 'messages.xlf'; let MASTER = WORKDIR + MASTERFILE; @@ -567,6 +601,27 @@ describe('XliffMerge test spec', () => { done(); }); + it('should not write empty translation json file for ngx-translate, if there are no translation (issue #18)', (done) => { + FileUtil.copy(MASTER_WITHOUT_NGX_TRANSLATE_STUFF, 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)).toBeFalsy(); + done(); + }); + }); describe('Merge process checks for format xmb', () => { @@ -676,7 +731,7 @@ describe('XliffMerge test spec', () => { xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de', 'en']}, profileContent); xliffMergeCmd.run(); expect(ws.writtenData()).not.toContain('ERROR'); - expect(ws.writtenData()).toContain('merged 2 trans-units from master to "en"'); + expect(ws.writtenData()).toContain('merged 1 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 @@ -779,6 +834,7 @@ describe('XliffMerge test spec', () => { let MASTER1FILE = 'ngxtranslate.xmb'; let MASTER1SRC = SRCDIR + MASTER1FILE; + let MASTER_WITHOUT_NGX_TRANSLATE_STUFF = SRCDIR + 'ngExtractedMaster1.xmb'; let MASTERFILE = 'messages.xmb'; let MASTER = WORKDIR + MASTERFILE; @@ -917,5 +973,27 @@ describe('XliffMerge test spec', () => { done(); }); + it('should not write empty translation json file for ngx-translate, if there are no translation (issue #18)', (done) => { + FileUtil.copy(MASTER_WITHOUT_NGX_TRANSLATE_STUFF, 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)).toBeFalsy(); + done(); + }); + }); }); diff --git a/src/xliffmerge/xliff-merge.ts b/src/xliffmerge/xliff-merge.ts index 363a426..c732b49 100644 --- a/src/xliffmerge/xliff-merge.ts +++ b/src/xliffmerge/xliff-merge.ts @@ -209,10 +209,10 @@ export class XliffMerge { let languageSpecificMessagesFile: ITranslationMessagesFile = TranslationMessagesFileReader.fromFile(this.parameters.i18nFormat(), languageXliffFilePath, this.parameters.encoding()); languageSpecificMessagesFile.setTargetLanguage(lang); - // copy source to target + // copy source to target if necessary let isDefaultLang: boolean = (lang == this.parameters.defaultLanguage()); languageSpecificMessagesFile.forEachTransUnit((transUnit: ITransUnit) => { - languageSpecificMessagesFile.useSourceAsTarget(transUnit, isDefaultLang); + languageSpecificMessagesFile.useSourceAsTarget(transUnit, isDefaultLang, this.parameters.useSourceAsTarget()); }); // write it to file TranslationMessagesFileReader.save(languageSpecificMessagesFile); @@ -237,7 +237,7 @@ export class XliffMerge { let transUnit: ITransUnit = languageSpecificMessagesFile.transUnitWithId(masterTransUnit.id); if (!transUnit) { // oops, no translation, must be a new key, so add it - languageSpecificMessagesFile.useSourceAsTarget(masterTransUnit, isDefaultLang); + languageSpecificMessagesFile.useSourceAsTarget(masterTransUnit, isDefaultLang, this.parameters.useSourceAsTarget()); languageSpecificMessagesFile.addNewTransUnit(masterTransUnit); newCount++; } diff --git a/test/testdata/i18n/ngExtractedMaster1.xmb b/test/testdata/i18n/ngExtractedMaster1.xmb index b95add2..67f2a10 100644 --- a/test/testdata/i18n/ngExtractedMaster1.xmb +++ b/test/testdata/i18n/ngExtractedMaster1.xmb @@ -21,10 +21,11 @@ ]> - Meine erste I18N-Anwendung - Diese Anwendung ist eine reine Demonstration und hat keine wirklich nutzbare Funktion. - Diese Anwendung ist eine reine Demonstration und hat keine wirklich nutzbare Funktion. - Eintrag INTERPOLATION von INTERPOLATION_1 hinzugefügt. - Dieser Text + S:/experimente/sampleapp41/src/app/app.component.ts:1Meine erste I18N-Anwendung + S:/experimente/sampleapp41/src/app/app.component.ts:2S:/experimente/sampleapp41/src/app/app.component.ts:3Anwendung läuft! + S:/experimente/sampleapp41/src/app/app.component.ts:4Diese Anwendung ist eine reine Demonstration und hat keine wirklich nutzbare Funktion. + S:/experimente/sampleapp41/src/app/app.component.ts:5Diese Anwendung ist eine reine Demonstration und hat keine wirklich nutzbare Funktion. + S:/experimente/sampleapp41/src/app/app.component.ts:6Eintrag INTERPOLATION von INTERPOLATION_1 hinzugefügt. + S:/experimente/sampleapp41/src/app/app.component.ts:7,8Dieser Text enthält einen Zeilenumbruch. diff --git a/test/testdata/i18n/ngExtractedMaster2.xmb b/test/testdata/i18n/ngExtractedMaster2.xmb index d8c2171..9a94f95 100644 --- a/test/testdata/i18n/ngExtractedMaster2.xmb +++ b/test/testdata/i18n/ngExtractedMaster2.xmb @@ -21,11 +21,10 @@ ]> - Meine erste I18N-Anwendung - Anwendung läuft! - Eintrag INTERPOLATION von INTERPOLATION_1 hinzugefügt. - Dieser Text + S:/experimente/sampleapp41/src/app/app.component.ts:1Meine erste I18N-Anwendung + S:/experimente/sampleapp41/src/app/app.component.ts:2S:/experimente/sampleapp41/src/app/app.component.ts:3Anwendung läuft! + S:/experimente/sampleapp41/src/app/app.component.ts:6Eintrag INTERPOLATION von INTERPOLATION_1 hinzugefügt. + S:/experimente/sampleapp41/src/app/app.component.ts:7,8Dieser Text enthält einen Zeilenumbruch. - Dieser Text enthält <b>eingebettetes html</b> - Dieser Text enthält <b>eingebettetes html</b> + S:/experimente/sampleapp41/src/app/app.component.ts:9Dieser Text enthält <b>eingebettetes html</b> diff --git a/test/testdata/i18n/ngxtranslate.xlf b/test/testdata/i18n/ngxtranslate.xlf index 8935f53..1275a80 100644 --- a/test/testdata/i18n/ngxtranslate.xlf +++ b/test/testdata/i18n/ngxtranslate.xlf @@ -82,6 +82,14 @@ ngx-translate embeddedhtml.strange
+ + Explizit gesetzte ID + Explizit gesetzte ID + + S:/experimente/sampleapp41/src/app/ngx-translate-test-data/ngx-translate-test-data.component.ts + 18 + + \ No newline at end of file diff --git a/test/testdata/i18n/ngxtranslate.xmb b/test/testdata/i18n/ngxtranslate.xmb index 416e2ce..2787574 100644 --- a/test/testdata/i18n/ngxtranslate.xmb +++ b/test/testdata/i18n/ngxtranslate.xmb @@ -37,4 +37,5 @@ Diese Nachricht ist <b>WICHTIG</b> Diese Nachricht ist <b><strong>SEHR WICHTIG</strong></b> Diese Nachricht ist <strange>INTERPOLATION</strange> + S:/experimente/sampleapp41/src/app/ngx-translate-test-data/ngx-translate-test-data.component.ts:18Explizit gesetzte ID diff --git a/yarn.lock b/yarn.lock index 202afee..a19c2e3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10,6 +10,10 @@ version "7.0.12" resolved "https://registry.yarnpkg.com/@types/node/-/node-7.0.12.tgz#ae5f67a19c15f752148004db07cbbb372e69efc9" +"@types/xmldom@^0.1.29": + version "0.1.29" + resolved "https://registry.yarnpkg.com/@types/xmldom/-/xmldom-0.1.29.tgz#c4428b0ca86d3b881475726fd94980b38a27c381" + abbrev@1, abbrev@1.0.x: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" @@ -154,10 +158,6 @@ block-stream@*: dependencies: inherits "~2.0.0" -boolbase@~1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" - boom@2.x.x: version "2.10.1" resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" @@ -212,27 +212,6 @@ chalk@^1.1.1, chalk@^1.1.3: strip-ansi "^3.0.0" supports-color "^2.0.0" -cheerio@^0.22.0: - version "0.22.0" - resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-0.22.0.tgz#a9baa860a3f9b595a6b81b1a86873121ed3a269e" - dependencies: - css-select "~1.2.0" - dom-serializer "~0.1.0" - entities "~1.1.1" - htmlparser2 "^3.9.1" - lodash.assignin "^4.0.9" - lodash.bind "^4.1.4" - lodash.defaults "^4.0.1" - lodash.filter "^4.4.0" - lodash.flatten "^4.2.0" - lodash.foreach "^4.3.0" - lodash.map "^4.4.0" - lodash.merge "^4.4.0" - lodash.pick "^4.2.1" - lodash.reduce "^4.4.0" - lodash.reject "^4.4.0" - lodash.some "^4.4.0" - chokidar@^1.6.0: version "1.6.1" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-1.6.1.tgz#2f4447ab5e96e50fb3d789fd90d4c72e0e4c70c2" @@ -328,19 +307,6 @@ cryptiles@2.x.x: dependencies: boom "2.x.x" -css-select@~1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/css-select/-/css-select-1.2.0.tgz#2b3a110539c5355f1cd8d314623e870b121ec858" - dependencies: - boolbase "~1.0.0" - css-what "2.1" - domutils "1.5.1" - nth-check "~1.0.1" - -css-what@2.1: - version "2.1.0" - resolved "https://registry.yarnpkg.com/css-what/-/css-what-2.1.0.tgz#9467d032c38cfaefb9f2d79501253062f87fa1bd" - dashdash@^1.12.0: version "1.14.1" resolved "https://registry.yarnpkg.com/dashdash/-/dashdash-1.14.1.tgz#853cfa0f7cbe2fed5de20326b8dd581035f6e2f0" @@ -373,34 +339,6 @@ delegates@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" -dom-serializer@0, dom-serializer@~0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82" - dependencies: - domelementtype "~1.1.1" - entities "~1.1.1" - -domelementtype@1, domelementtype@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" - -domelementtype@~1.1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" - -domhandler@^2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" - dependencies: - domelementtype "1" - -domutils@1.5.1, domutils@^1.5.1: - version "1.5.1" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" - dependencies: - dom-serializer "0" - domelementtype "1" - duplexer@^0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1" @@ -411,10 +349,6 @@ ecc-jsbn@~0.1.1: dependencies: jsbn "~0.1.0" -entities@^1.1.1, entities@~1.1.1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.1.tgz#6e5c2d0a5621b5dadaecef80b90edfb5cd7772f0" - escape-string-regexp@^1.0.2: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" @@ -699,17 +633,6 @@ hoek@2.x.x: version "2.16.3" resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" -htmlparser2@^3.9.1: - version "3.9.2" - resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.9.2.tgz#1bdf87acca0f3f9e53fa4fcceb0f4b4cbb00b338" - dependencies: - domelementtype "^1.3.0" - domhandler "^2.3.0" - domutils "^1.5.1" - entities "^1.1.1" - inherits "^2.0.1" - readable-stream "^2.0.2" - http-signature@~1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.1.1.tgz#df72e267066cd0ac67fb76adf8e134a8fbcf91bf" @@ -935,54 +858,6 @@ levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -lodash.assignin@^4.0.9: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.assignin/-/lodash.assignin-4.2.0.tgz#ba8df5fb841eb0a3e8044232b0e263a8dc6a28a2" - -lodash.bind@^4.1.4: - version "4.2.1" - resolved "https://registry.yarnpkg.com/lodash.bind/-/lodash.bind-4.2.1.tgz#7ae3017e939622ac31b7d7d7dcb1b34db1690d35" - -lodash.defaults@^4.0.1: - version "4.2.0" - resolved "https://registry.yarnpkg.com/lodash.defaults/-/lodash.defaults-4.2.0.tgz#d09178716ffea4dde9e5fb7b37f6f0802274580c" - -lodash.filter@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.filter/-/lodash.filter-4.6.0.tgz#668b1d4981603ae1cc5a6fa760143e480b4c4ace" - -lodash.flatten@^4.2.0: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" - -lodash.foreach@^4.3.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.foreach/-/lodash.foreach-4.5.0.tgz#1a6a35eace401280c7f06dddec35165ab27e3e53" - -lodash.map@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.map/-/lodash.map-4.6.0.tgz#771ec7839e3473d9c4cde28b19394c3562f4f6d3" - -lodash.merge@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.0.tgz#69884ba144ac33fe699737a6086deffadd0f89c5" - -lodash.pick@^4.2.1: - version "4.4.0" - resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" - -lodash.reduce@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.reduce/-/lodash.reduce-4.6.0.tgz#f1ab6b839299ad48f784abbf476596f03b914d3b" - -lodash.reject@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.reject/-/lodash.reject-4.6.0.tgz#80d6492dc1470864bbf583533b651f42a9f52415" - -lodash.some@^4.4.0: - version "4.6.0" - resolved "https://registry.yarnpkg.com/lodash.some/-/lodash.some-4.6.0.tgz#1bb9f314ef6b8baded13b549169b2a945eb68e4d" - log-driver@1.2.5: version "1.2.5" resolved "https://registry.yarnpkg.com/log-driver/-/log-driver-1.2.5.tgz#7ae4ec257302fd790d557cb10c97100d857b0056" @@ -1069,11 +944,12 @@ nan@^2.3.0: version "2.6.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.1.tgz#8c84f7b14c96b89f57fbc838012180ec8ca39a01" -ngx-i18nsupport-lib@^0.0.3: - version "0.0.3" - resolved "https://registry.yarnpkg.com/ngx-i18nsupport-lib/-/ngx-i18nsupport-lib-0.0.3.tgz#22c022fdf67300f51ea1c5228d7b2bd2c8fe404b" +ngx-i18nsupport-lib@^0.0.6: + version "0.0.6" + resolved "https://registry.yarnpkg.com/ngx-i18nsupport-lib/-/ngx-i18nsupport-lib-0.0.6.tgz#e6e523719aae58f00e40f2df9e0aadd8af4c51af" dependencies: - cheerio "^0.22.0" + "@types/xmldom" "^0.1.29" + xmldom "^0.1.27" node-pre-gyp@^0.6.29: version "0.6.34" @@ -1117,12 +993,6 @@ npmlog@^4.0.2: gauge "~2.7.1" set-blocking "~2.0.0" -nth-check@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/nth-check/-/nth-check-1.0.1.tgz#9929acdf628fc2c41098deab82ac580cf149aae4" - dependencies: - boolbase "~1.0.0" - number-is-nan@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" @@ -1614,6 +1484,10 @@ wrappy@1: version "1.0.2" resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" +xmldom@^0.1.27: + version "0.1.27" + resolved "https://registry.yarnpkg.com/xmldom/-/xmldom-0.1.27.tgz#d501f97b3bdb403af8ef9ecc20573187aadac0e9" + xtend@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.1.tgz#a5c6d532be656e23db820efb943a1f04998d63af" From dfbe2fb025b6d2170f54d06934e55ee012e83d0c Mon Sep 17 00:00:00 2001 From: Martin Roob Date: Wed, 3 May 2017 07:40:28 +0200 Subject: [PATCH 6/9] lib updated due to some bugs (remove did not work) --- package.json | 2 +- yarn.lock | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index 123d61d..e66fc15 100644 --- a/package.json +++ b/package.json @@ -49,6 +49,6 @@ "dependencies": { "chalk": "^1.1.3", "commander": "^2.9.0", - "ngx-i18nsupport-lib": "^0.0.6" + "ngx-i18nsupport-lib": "^0.0.8" } } diff --git a/yarn.lock b/yarn.lock index a19c2e3..38a7eed 100644 --- a/yarn.lock +++ b/yarn.lock @@ -944,9 +944,9 @@ nan@^2.3.0: version "2.6.1" resolved "https://registry.yarnpkg.com/nan/-/nan-2.6.1.tgz#8c84f7b14c96b89f57fbc838012180ec8ca39a01" -ngx-i18nsupport-lib@^0.0.6: - version "0.0.6" - resolved "https://registry.yarnpkg.com/ngx-i18nsupport-lib/-/ngx-i18nsupport-lib-0.0.6.tgz#e6e523719aae58f00e40f2df9e0aadd8af4c51af" +ngx-i18nsupport-lib@^0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/ngx-i18nsupport-lib/-/ngx-i18nsupport-lib-0.0.8.tgz#ce8d527ea166f4e76ad0c2e90cdede78125e8979" dependencies: "@types/xmldom" "^0.1.29" xmldom "^0.1.27" From be19d10966b63761f9a543837d55ce39a8878e73 Mon Sep 17 00:00:00 2001 From: Martin Roob Date: Wed, 3 May 2017 16:31:00 +0200 Subject: [PATCH 7/9] ngx-translate explicit IDs --- ngx-i18nsupport.d.ts | 1 - public_api.d.ts | 6 ---- src/tsconfig.json | 5 +-- src/xliffmerge/ngx-translate-extractor.ts | 42 +++++++++++++++++++---- src/xliffmerge/xliff-merge.spec.ts | 3 ++ 5 files changed, 39 insertions(+), 18 deletions(-) delete mode 100644 ngx-i18nsupport.d.ts delete mode 100644 public_api.d.ts diff --git a/ngx-i18nsupport.d.ts b/ngx-i18nsupport.d.ts deleted file mode 100644 index 4aaf8f9..0000000 --- a/ngx-i18nsupport.d.ts +++ /dev/null @@ -1 +0,0 @@ -export * from './public_api'; diff --git a/public_api.d.ts b/public_api.d.ts deleted file mode 100644 index 7da5242..0000000 --- a/public_api.d.ts +++ /dev/null @@ -1,6 +0,0 @@ -/** - * @module - * @description - * Entry point for all public APIs of the package. - */ -export * from './src/index'; diff --git a/src/tsconfig.json b/src/tsconfig.json index 804e009..a0fac10 100644 --- a/src/tsconfig.json +++ b/src/tsconfig.json @@ -15,10 +15,7 @@ "../node_modules/@types" ] }, - "include": [ + "includes": [ "**/*" - ], - "exclude": [ - "node_modules" ] } diff --git a/src/xliffmerge/ngx-translate-extractor.ts b/src/xliffmerge/ngx-translate-extractor.ts index 49c70a8..a759443 100644 --- a/src/xliffmerge/ngx-translate-extractor.ts +++ b/src/xliffmerge/ngx-translate-extractor.ts @@ -55,18 +55,46 @@ export class NgxTranslateExtractor { private extract(): NgxMessage[] { let result: NgxMessage[] = []; this.messagesFile.forEachTransUnit((tu: ITransUnit) => { - let description = tu.description(); - let id = tu.meaning(); - if (description && description === 'ngx-translate') { - let messagetext = this.toTranslateString(tu.targetContentNormalized()); - result.push({id: id, message: messagetext}); + let ngxId = this.ngxTranslateIdFromTU(tu); + if (ngxId) { + let messagetext = tu.targetContentNormalized(); + result.push({id: ngxId, message: messagetext}); } }); return result; } - private toTranslateString(contentFromTU: string): string { - return contentFromTU; + /** + * Check, wether this tu should be extracted for ngx-translate usage, and return its id for ngx-translate. + * There are 2 possibilities: + * 1. description is set to "ngx-translate" and meaning contains the id. + * 2. id is explicitly set to a string. + * @param tu + * @return an id or null, if this tu should not be extracted. + */ + private ngxTranslateIdFromTU(tu: ITransUnit): string { + if (this.isExplicitlySetId(tu.id)) { + return tu.id; + } + let description = tu.description(); + if (description && description === 'ngx-translate') { + return tu.meaning(); + } + } + + /** + * Test, wether ID was explicitly set (via i18n="@myid). + * Just heuristic, an ID is explicitly that, if it does not look like a generated one. + * @param id + * @return {boolean} + */ + private isExplicitlySetId(id: string): boolean { + if (isNullOrUndefined(id)) { + return false; + } + // generated IDs are either decimal or sha1 hex + let reForGeneratedId = /^[0-9a-f]{11,}$/; + return !reForGeneratedId.test(id); } /** diff --git a/src/xliffmerge/xliff-merge.spec.ts b/src/xliffmerge/xliff-merge.spec.ts index 681d331..82069d1 100644 --- a/src/xliffmerge/xliff-merge.spec.ts +++ b/src/xliffmerge/xliff-merge.spec.ts @@ -542,6 +542,7 @@ describe('XliffMerge test spec', () => { expect(translation.myapp).toBeTruthy(); expect(translation.dateservice.monday).toBe("Montag"); expect(translation.dateservice.friday).toBe("Freitag"); + expect(translation.explicitlysetids.test1).toBe("Explizit gesetzte ID"); done(); }); @@ -912,6 +913,8 @@ describe('XliffMerge test spec', () => { expect(translation.myapp).toBeTruthy(); expect(translation.dateservice.monday).toBe("Montag"); expect(translation.dateservice.friday).toBe("Freitag"); + expect(translation.explicitlysetids.test1).toBe("Explizit gesetzte ID"); + expect(Object.keys(translation).length).toBe(5); done(); }); From 2a3771a4be80768aec1d9dda8f29e4b5300f0ac0 Mon Sep 17 00:00:00 2001 From: Martin Roob Date: Wed, 3 May 2017 21:35:37 +0200 Subject: [PATCH 8/9] Changelog for 0.4 --- Changelog.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Changelog.md b/Changelog.md index a43adf8..a0408bc 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,20 @@ + +# [0.4.0](https://github.com/martinroob/ngx-i18nsupport/compare/v0.3.1...v0.4.0) (2017-05-03) + +### Bug Fixes + +* **xliffmerge:** xliffmerge creates empty json files ([#18](https://github.com/martinroob/ngx-i18nsupport/issues/18)) + +### Features + +* **xliffmerge:** create an API to access parsing functionality. ([#11](https://github.com/martinroob/ngx-i18nsupport/issues/11)). +The API is in a separate npm package [ngx-i18nsupport-lib](https://github.com/martinroob/ngx-i18nsupport-lib). + +* **xliffmerge:** use explicitly set IDs for ngx-translate data generation. ([#15](https://github.com/martinroob/ngx-i18nsupport/issues/15)) +For detail have a look at the Wiki page [ngx translate usage](https://github.com/martinroob/ngx-i18nsupport/wiki/ngx-translate-usage). + +* **xliffmerge:** Handle new source element introduced with Angular 4. ([#21](https://github.com/martinroob/ngx-i18nsupport/issues/21)) + # [0.3.1](https://github.com/martinroob/ngx-i18nsupport/compare/v0.3.0...v0.3.1) (2017-04-25) From 0f015c7187f59c1adfc7e4dffaf7cfe44c946ca3 Mon Sep 17 00:00:00 2001 From: Martin Roob Date: Wed, 3 May 2017 21:36:03 +0200 Subject: [PATCH 9/9] v0.4 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index e66fc15..034c78c 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ngx-i18nsupport", - "version": "0.3.1", + "version": "0.4.0", "description": "Some tooling to be used with the Angular 2 i18n workflow", "main": "index.js", "module": "./src",