Skip to content

Commit

Permalink
Merge pull request #46 from martinroob/feature-1.7.0
Browse files Browse the repository at this point in the history
Feature 1.7.0
  • Loading branch information
martinroob committed Feb 2, 2018
2 parents 437ab3e + ea0a460 commit 1572b3a
Show file tree
Hide file tree
Showing 13 changed files with 518 additions and 251 deletions.
13 changes: 13 additions & 0 deletions Changelog.md
@@ -1,3 +1,16 @@
<a name="1.7.0"></a>
# [1.7.0](https://github.com/martinroob/ngx-i18nsupport-lib/compare/v1.7.0...v1.6.2) (2018-02-02)

### Bug Fixes

* **API** Method ITranslationMessagesFile>>importNewTransUnit now returns newly create trans unit (internally needed for xliffmerge).

### Features

* **all formats** library now preserves EOL at end of XML files. ([xliffmerge #66](https://github.com/martinroob/ngx-i18nsupport/issues/66)).

* **internal** updated out of date dependencies (webpack, coveralls, codelyzer, @types/node) to latest versions

<a name="1.6.2"></a>
# [1.6.2](https://github.com/martinroob/ngx-i18nsupport-lib/compare/v1.6.2...v1.6.1) (2017-10-19)

Expand Down
10 changes: 5 additions & 5 deletions package.json
@@ -1,6 +1,6 @@
{
"name": "ngx-i18nsupport-lib",
"version": "1.6.2",
"version": "1.7.0",
"description": "A Typescript library to work with Angular generated i18n files (xliff, xmb)",
"main": "bundles/ngx-i18nsupport-lib.umd.js",
"module": "./dist/index.js",
Expand Down Expand Up @@ -36,18 +36,18 @@
"homepage": "https://github.com/martinroob/ngx-i18nsupport-lib#readme",
"devDependencies": {
"@types/jasmine": "^2.5.43",
"@types/node": "^7.0.5",
"@types/node": "^9.4.0",
"awesome-typescript-loader": "^3.1.2",
"clean-webpack-plugin": "^0.1.16",
"codelyzer": "^2.1.1",
"coveralls": "^2.11.16",
"codelyzer": "^4.1.0",
"coveralls": "^3.0.0",
"cpx": "^1.5.0",
"istanbul": "0.4.5",
"jasmine-node": "^1.14.5",
"tslint": "^5.0.0",
"tslint-loader": "^3.5.2",
"typescript": "^2.2.1",
"webpack": "^2.3.3"
"webpack": "^3.10.0"
},
"dependencies": {
"@types/xmldom": "^0.1.29",
Expand Down
3 changes: 2 additions & 1 deletion src/api/i-translation-messages-file.ts
Expand Up @@ -106,9 +106,10 @@ export interface ITranslationMessagesFile {
* @param copyContent Flag, wether to copy content or leave it empty.
* Wben true, content will be copied from source.
* When false, content will be left empty (if it is not the default language).
* @return the newly imported trans unit (since version 1.7.0)
* @throws an error if trans-unit with same id already is in the file.
*/
importNewTransUnit(foreignTransUnit: ITransUnit, isDefaultLang: boolean, copyContent: boolean);
importNewTransUnit(foreignTransUnit: ITransUnit, isDefaultLang: boolean, copyContent: boolean): ITransUnit;

/**
* Remove the trans-unit with the given id.
Expand Down
28 changes: 26 additions & 2 deletions src/impl/abstract-translation-messages-file.ts
@@ -1,7 +1,8 @@
import {ITranslationMessagesFile, ITransUnit, STATE_NEW, STATE_TRANSLATED} from '../api';
import {isNullOrUndefined} from 'util';
import {XMLSerializer} from 'xmldom';
import {DOMParser, XMLSerializer} from 'xmldom';
import {AbstractTransUnit} from './abstract-trans-unit';
import {XtbFile} from './xtb-file';
/**
* Created by roobm on 09.05.2017.
* Abstract superclass for all implementations of ITranslationMessagesFile.
Expand All @@ -15,6 +16,8 @@ export abstract class AbstractTranslationMessagesFile implements ITranslationMes

protected _parsedDocument: Document;

protected _fileEndsWithEOL: boolean;

// trans-unit elements and their id from the file
protected transUnits: ITransUnit[];

Expand All @@ -31,6 +34,21 @@ export abstract class AbstractTranslationMessagesFile implements ITranslationMes
this._warnings = [];
}

/**
* Parse file content.
* Sets _parsedDocument, line ending, encoding, etc.
* @param {string} xmlString
* @param {string} path
* @param {string} encoding
* @param {{xmlContent: string; path: string; encoding: string}} optionalMaster
*/
protected parseContent(xmlString: string, path: string, encoding: string, optionalMaster?: { xmlContent: string, path: string, encoding: string }): void {
this._filename = path;
this._encoding = encoding;
this._parsedDocument = new DOMParser().parseFromString(xmlString, 'text/xml');
this._fileEndsWithEOL = xmlString.endsWith('\n');
}

abstract i18nFormat(): string;

abstract fileType(): string;
Expand Down Expand Up @@ -207,7 +225,13 @@ export abstract class AbstractTranslationMessagesFile implements ITranslationMes
* The xml content to be saved after changes are made.
*/
public editedContent(): string {
return new XMLSerializer().serializeToString(this._parsedDocument);
const result = new XMLSerializer().serializeToString(this._parsedDocument);
if (this._fileEndsWithEOL) {
// add eol if there was eol in original source
return result + '\n';
} else {
return result;
}
}

/**
Expand Down
16 changes: 15 additions & 1 deletion src/impl/xliff-file.spec.ts
Expand Up @@ -426,13 +426,27 @@ describe('ngx-i18nsupport-lib xliff 1.2 test spec', () => {
expect(tu).toBeTruthy();
const targetFile: ITranslationMessagesFile = readFile(TRANSLATED_FILE_SRC);
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toBeFalsy();
targetFile.importNewTransUnit(tu, false, true);
const newTu = targetFile.importNewTransUnit(tu, false, true);
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toBeTruthy();
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toEqual(newTu);
let changedTargetFile = TranslationMessagesFileFactory.fromUnknownFormatFileContent(targetFile.editedContent(), null, null);
let targetTu = changedTargetFile.transUnitWithId(ID_TO_MERGE);
expect(targetTu.sourceContent()).toBe('Test for merging units');
});

it ('should preserve line end at end of file while editing', () => {
const content = fs.readFileSync(MASTER1SRC, ENCODING);
expect(content.endsWith('\n')).toBeTruthy('Master should end with EOL');
const file: ITranslationMessagesFile = readFile(MASTER1SRC);
const tu: ITransUnit = file.transUnitWithId(ID_TO_MERGE);
expect(tu).toBeTruthy();
const targetFile: ITranslationMessagesFile = readFile(TRANSLATED_FILE_SRC);
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toBeFalsy();
targetFile.importNewTransUnit(tu, false, true);
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toBeTruthy();
expect(targetFile.editedContent().endsWith('\n')).toBeTruthy('Edited content should end with EOL');
});

it ('should translate using NormalizedMessage (plain text case, no placeholders, no markup)', () => {
const file: ITranslationMessagesFile = readFile(MASTER1SRC);
const tu: ITransUnit = file.transUnitWithId(ID_TRANSLATED_SCHLIESSEN);
Expand Down
8 changes: 4 additions & 4 deletions src/impl/xliff-file.ts
Expand Up @@ -29,9 +29,7 @@ export class XliffFile extends AbstractTranslationMessagesFile implements ITrans
}

private initializeFromContent(xmlString: string, path: string, encoding: string): XliffFile {
this._filename = path;
this._encoding = encoding;
this._parsedDocument = new DOMParser().parseFromString(xmlString, 'text/xml');
this.parseContent(xmlString, path, encoding);
const xliffList = this._parsedDocument.getElementsByTagName('xliff');
if (xliffList.length !== 1) {
throw new Error(format('File "%s" seems to be no xliff file (should contain an xliff element)', path));
Expand Down Expand Up @@ -138,9 +136,10 @@ export class XliffFile extends AbstractTranslationMessagesFile implements ITrans
* @param copyContent Flag, wether to copy content or leave it empty.
* Wben true, content will be copied from source.
* When false, content will be left empty (if it is not the default language).
* @return the newly imported trans unit (since version 1.7.0)
* @throws an error if trans-unit with same id already is in the file.
*/
public importNewTransUnit(transUnit: ITransUnit, isDefaultLang: boolean, copyContent: boolean) {
public importNewTransUnit(transUnit: ITransUnit, isDefaultLang: boolean, copyContent: boolean): ITransUnit {
if (this.transUnitWithId(transUnit.id)) {
throw new Error(format('tu with id %s already exists in file, cannot import it', transUnit.id));
}
Expand All @@ -152,6 +151,7 @@ export class XliffFile extends AbstractTranslationMessagesFile implements ITrans
this.transUnits.push(newTu);
this.countNumbers();
}
return newTu;
}

/**
Expand Down
16 changes: 15 additions & 1 deletion src/impl/xliff2-file.spec.ts
Expand Up @@ -418,13 +418,27 @@ describe('ngx-i18nsupport-lib XLIFF 2.0 test spec', () => {
expect(tu).toBeTruthy();
const targetFile: ITranslationMessagesFile = readFile(TRANSLATED_FILE_SRC);
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toBeFalsy();
targetFile.importNewTransUnit(tu, false, true);
const newTu = targetFile.importNewTransUnit(tu, false, true);
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toBeTruthy();
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toEqual(newTu);
let changedTargetFile = TranslationMessagesFileFactory.fromUnknownFormatFileContent(targetFile.editedContent(), null, null);
let targetTu = changedTargetFile.transUnitWithId(ID_TO_MERGE);
expect(targetTu.sourceContent()).toBe('Test for merging units');
});

it ('should preserve line end at end of file while editing', () => {
const content = fs.readFileSync(MASTER1SRC, ENCODING);
expect(content.endsWith('\n')).toBeTruthy('Master should end with EOL');
const file: ITranslationMessagesFile = readFile(MASTER1SRC);
const tu: ITransUnit = file.transUnitWithId(ID_TO_MERGE);
expect(tu).toBeTruthy();
const targetFile: ITranslationMessagesFile = readFile(TRANSLATED_FILE_SRC);
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toBeFalsy();
targetFile.importNewTransUnit(tu, false, true);
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toBeTruthy();
expect(targetFile.editedContent().endsWith('\n')).toBeTruthy('Edited content should end with EOL');
});

it ('should translate using NormalizedMessage (plain text case, no placeholders, no markup)', () => {
const file: ITranslationMessagesFile = readFile(MASTER1SRC);
const tu: ITransUnit = file.transUnitWithId(ID_APP_RUNS);
Expand Down
8 changes: 4 additions & 4 deletions src/impl/xliff2-file.ts
Expand Up @@ -31,9 +31,7 @@ export class Xliff2File extends AbstractTranslationMessagesFile implements ITran
}

private initializeFromContent(xmlString: string, path: string, encoding: string): Xliff2File {
this._filename = path;
this._encoding = encoding;
this._parsedDocument = new DOMParser().parseFromString(xmlString, 'text/xml');
this.parseContent(xmlString, path, encoding);
const xliffList = this._parsedDocument.getElementsByTagName('xliff');
if (xliffList.length !== 1) {
throw new Error(format('File "%s" seems to be no xliff file (should contain an xliff element)', path));
Expand Down Expand Up @@ -140,9 +138,10 @@ export class Xliff2File extends AbstractTranslationMessagesFile implements ITran
* @param copyContent Flag, wether to copy content or leave it empty.
* Wben true, content will be copied from source.
* When false, content will be left empty (if it is not the default language).
* @return the newly imported trans unit (since version 1.7.0)
* @throws an error if trans-unit with same id already is in the file.
*/
public importNewTransUnit(transUnit: ITransUnit, isDefaultLang: boolean, copyContent: boolean) {
public importNewTransUnit(transUnit: ITransUnit, isDefaultLang: boolean, copyContent: boolean): ITransUnit {
if (this.transUnitWithId(transUnit.id)) {
throw new Error(format('tu with id %s already exists in file, cannot import it', transUnit.id));
}
Expand All @@ -156,6 +155,7 @@ export class Xliff2File extends AbstractTranslationMessagesFile implements ITran
} else {
throw new Error(format('File "%s" seems to be no xliff 2.0 file (should contain a file element)', this._filename));
}
return newTu;
}

/**
Expand Down
16 changes: 15 additions & 1 deletion src/impl/xmb-file.spec.ts
Expand Up @@ -239,13 +239,27 @@ describe('ngx-i18nsupport-lib xmb test spec', () => {
expect(targetFile.fileType()).toBe(FILETYPE_XTB);
targetFile.removeTransUnitWithId(ID_TO_MERGE);
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toBeFalsy();
targetFile.importNewTransUnit(tu, false, true);
const newTu = targetFile.importNewTransUnit(tu, false, true);
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toBeTruthy();
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toEqual(newTu);
let changedTargetFile = TranslationMessagesFileFactory.fromUnknownFormatFileContent(targetFile.editedContent(), null, null);
let targetTu = changedTargetFile.transUnitWithId(ID_TO_MERGE);
expect(targetTu.targetContent()).toBe('Test for merging units');
});

it ('should preserve line end at end of file while editing', () => {
const content = fs.readFileSync(MASTER1SRC, ENCODING);
expect(content.endsWith('\n')).toBeTruthy('Master should end with EOL');
const file: ITranslationMessagesFile = readFile(MASTER1SRC);
const tu: ITransUnit = file.transUnitWithId(ID_TO_MERGE);
expect(tu).toBeTruthy();
const targetFile: ITranslationMessagesFile = file.createTranslationFileForLang('de', null, false, true);
targetFile.removeTransUnitWithId(ID_TO_MERGE);
targetFile.importNewTransUnit(tu, false, true);
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toBeTruthy();
expect(targetFile.editedContent().endsWith('\n')).toBeTruthy('Edited content should end with EOL');
});

it ('should not be translatable at all', () => {
const file: ITranslationMessagesFile = readFile(MASTER1SRC);
const tu: ITransUnit = file.transUnitWithId(ID_MY_FIRST);
Expand Down
9 changes: 4 additions & 5 deletions src/impl/xmb-file.ts
Expand Up @@ -27,9 +27,7 @@ export class XmbFile extends AbstractTranslationMessagesFile implements ITransla
}

private initializeFromContent(xmlString: string, path: string, encoding: string): XmbFile {
this._filename = path;
this._encoding = encoding;
this._parsedDocument = new DOMParser().parseFromString(xmlString, 'text/xml');
this.parseContent(xmlString, path, encoding);
if (this._parsedDocument.getElementsByTagName('messagebundle').length !== 1) {
throw new Error(format('File "%s" seems to be no xmb file (should contain a messagebundle element)', path));
}
Expand Down Expand Up @@ -134,9 +132,10 @@ export class XmbFile extends AbstractTranslationMessagesFile implements ITransla
* @param copyContent Flag, wether to copy content or leave it empty.
* Wben true, content will be copied from source.
* When false, content will be left empty (if it is not the default language).
* @return the newly imported trans unit (since version 1.7.0)
* @throws an error if trans-unit with same id already is in the file.
*/
public importNewTransUnit(transUnit: ITransUnit, isDefaultLang: boolean, copyContent: boolean) {
public importNewTransUnit(transUnit: ITransUnit, isDefaultLang: boolean, copyContent: boolean): ITransUnit {
throw Error('xmb file cannot be used to store translations, use xtb file');
}

Expand All @@ -155,7 +154,7 @@ export class XmbFile extends AbstractTranslationMessagesFile implements ITransla
* When false, content will be left empty (if it is not the default language).
*/
public createTranslationFileForLang(lang: string, filename: string, isDefaultLang: boolean, copyContent: boolean): ITranslationMessagesFile {
let translationbundleXMLSource = '<?xml version="1.0" encoding="UTF-8"?>\n' + XTB_DOCTYPE + '\n<translationbundle>\n</translationbundle>';
let translationbundleXMLSource = '<?xml version="1.0" encoding="UTF-8"?>\n' + XTB_DOCTYPE + '\n<translationbundle>\n</translationbundle>\n';
let translationFile = new XtbFile(translationbundleXMLSource, filename, this.encoding(), {xmlContent: this.editedContent(), path: this.filename(), encoding: this.encoding()});
translationFile.setTargetLanguage(lang);
this.forEachTransUnit((tu) => {
Expand Down
3 changes: 2 additions & 1 deletion src/impl/xtb-file.spec.ts
Expand Up @@ -305,8 +305,9 @@ describe('ngx-i18nsupport-lib xtb test spec', () => {
expect(tu).toBeTruthy();
const targetFile: ITranslationMessagesFile = readFile(TRANSLATION_EN_XTB, MASTER_1_XMB);
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toBeFalsy();
targetFile.importNewTransUnit(tu, false, true);
const newTu = targetFile.importNewTransUnit(tu, false, true);
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toBeTruthy();
expect(targetFile.transUnitWithId(ID_TO_MERGE)).toEqual(newTu);
let changedTargetFile = TranslationMessagesFileFactory.fromUnknownFormatFileContent(targetFile.editedContent(), null, null);
let targetTu = changedTargetFile.transUnitWithId(ID_TO_MERGE);
expect(targetTu.targetContent()).toBe('Test for merging units');
Expand Down
11 changes: 7 additions & 4 deletions src/impl/xtb-file.ts
Expand Up @@ -45,9 +45,7 @@ export class XtbFile extends AbstractTranslationMessagesFile implements ITransla
}

private initializeFromContent(xmlString: string, path: string, encoding: string, optionalMaster?: { xmlContent: string, path: string, encoding: string }): XtbFile {
this._filename = path;
this._encoding = encoding;
this._parsedDocument = new DOMParser().parseFromString(xmlString, 'text/xml');
this.parseContent(xmlString, path, encoding);
if (this._parsedDocument.getElementsByTagName('translationbundle').length !== 1) {
throw new Error(format('File "%s" seems to be no xtb file (should contain a translationbundle element)', path));
}
Expand Down Expand Up @@ -163,8 +161,10 @@ export class XtbFile extends AbstractTranslationMessagesFile implements ITransla
* @param copyContent Flag, wether to copy content or leave it empty.
* Wben true, content will be copied from source.
* When false, content will be left empty (if it is not the default language).
* @return the newly imported trans unit (since version 1.7.0)
* @throws an error if trans-unit with same id already is in the file.
*/
public importNewTransUnit(transUnit: ITransUnit, isDefaultLang: boolean, copyContent: boolean) {
public importNewTransUnit(transUnit: ITransUnit, isDefaultLang: boolean, copyContent: boolean): ITransUnit {
if (this.transUnitWithId(transUnit.id)) {
throw new Error(format('tu with id %s already exists in file, cannot import it', transUnit.id));
}
Expand All @@ -179,6 +179,9 @@ export class XtbFile extends AbstractTranslationMessagesFile implements ITransla
this.lazyInitializeTransUnits();
this.transUnits.push(newTransUnit);
this.countNumbers();
return newTransUnit;
} else {
return null;
}
}

Expand Down

0 comments on commit 1572b3a

Please sign in to comment.