diff --git a/Changelog.md b/Changelog.md
index a0408bc..0590cf2 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,4 +1,11 @@
-
+
+# [0.5.0](https://github.com/martinroob/ngx-i18nsupport/compare/v0.4.0...v0.5.0) (2017-05-05)
+
+### Features
+
+* **xliffmerge:** added XLIFF 2.0 support. ([#20](https://github.com/martinroob/ngx-i18nsupport/issues/20))
+
+
# [0.4.0](https://github.com/martinroob/ngx-i18nsupport/compare/v0.3.1...v0.4.0) (2017-05-03)
### Bug Fixes
diff --git a/README.md b/README.md
index fd3d01a..aa53e6f 100644
--- a/README.md
+++ b/README.md
@@ -30,7 +30,7 @@ It is described in the official documentation Angular Cookbook [Internationaliza
Said in one sentence,
* markup your strings to translate in your templates with an attribute `i18n`
-* run the Amgular extraction tool (`ng-xi18n`) to extract the strings in an XML Format called [[XLIFF]]((http://docs.oasis-open.org/xliff/xliff-core/xliff-core.html))
+* run the Amgular extraction tool (`ng-xi18n`) to extract the strings in an XML Format called [[XLIFF-1.2]](http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html)
* copy and then translate the extracted file for every language you plan to support
* run the ng compiler to generate a special version of your app for the different languages
@@ -104,8 +104,8 @@ 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
+- `i18nFormat` (string, default "xlf"): `xlf` for XLIFF 1.2 or `xlf2` for XLIFF 2.0 or `xmb` for XML Message Bundles
+- `encoding` (string, default "UTF-8"): expected encoding of xlf, xlf2 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
@@ -201,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 Specification: [XLIFF Spec](http://docs.oasis-open.org/xliff/xliff-core/xliff-core.html)
+* XLIFF 1.2 Specification: [XLIFF-1.2](http://docs.oasis-open.org/xliff/v1.2/os/xliff-core.html)
+* XLIFF 2.0 Specification: [XLIFF-2.0](http://docs.oasis-open.org/xliff/xliff-core/v2.0/os/xliff-core-v2.0-os.html)
* My Tiny Translator Tool: [TinyTranslator](https://github.com/martinroob/tiny-translator)
[travis-badge]: https://travis-ci.org/martinroob/ngx-i18nsupport.svg?branch=master
diff --git a/package.json b/package.json
index 034c78c..b3d7c48 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "ngx-i18nsupport",
- "version": "0.4.0",
+ "version": "0.5.0",
"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.8"
+ "ngx-i18nsupport-lib": "^0.1.3"
}
}
diff --git a/src/xliffmerge/xliff-merge-parameters.ts b/src/xliffmerge/xliff-merge-parameters.ts
index aa9d511..ec5d9b7 100644
--- a/src/xliffmerge/xliff-merge-parameters.ts
+++ b/src/xliffmerge/xliff-merge-parameters.ts
@@ -189,9 +189,9 @@ export class XliffMergeParameters {
} catch (err) {
this.errorsFound.push(new XliffMergeError('i18nFile "' + this.i18nFile() + '" is not readable'));
}
- // i18nFormat must be xlf or xmb
- if (!(this.i18nFormat() === 'xlf' || this.i18nFormat() === 'xmb')) {
- this.errorsFound.push(new XliffMergeError('i18nFormat "' + this.i18nFormat() + '" invalid, must be "xlf" or "xmb"'));
+ // i18nFormat must be xlf xlf2 or xmb
+ if (!(this.i18nFormat() === 'xlf' || this.i18nFormat() === 'xlf2' || this.i18nFormat() === 'xmb')) {
+ this.errorsFound.push(new XliffMergeError('i18nFormat "' + this.i18nFormat() + '" invalid, must be "xlf" or "xlf2" or "xmb"'));
}
}
@@ -267,7 +267,7 @@ export class XliffMergeParameters {
/**
* Format of the master xlif file.
- * Default is "xlf", possible are "xlf" or "xmb".
+ * Default is "xlf", possible are "xlf" or "xlf2" or "xmb".
* @return {string}
*/
public i18nFormat(): string {
diff --git a/src/xliffmerge/xliff-merge.spec.ts b/src/xliffmerge/xliff-merge.spec.ts
index ac6bc87..3b45679 100644
--- a/src/xliffmerge/xliff-merge.spec.ts
+++ b/src/xliffmerge/xliff-merge.spec.ts
@@ -35,6 +35,14 @@ describe('XliffMerge test spec', () => {
return TranslationMessagesFileReader.fromFile('xlf', path, ENCODING);
}
+ /**
+ * Helper function to read XLIFF 2.0 from File
+ * @type {string}
+ */
+ function readXliff2(path: string): ITranslationMessagesFile {
+ return TranslationMessagesFileReader.fromFile('xlf2', path, ENCODING);
+ }
+
/**
* Helper function to read Xmb from File
* @type {string}
@@ -222,6 +230,20 @@ describe('XliffMerge test spec', () => {
done();
});
+ it('should accept i18n format xlf2', (done) => {
+ let ws: WriterToString = new WriterToString();
+ let commandOut = new CommandOutput(ws);
+ let profileContent: IConfigFile = {
+ xliffmergeOptions: {
+ i18nFormat: 'xlf2',
+ }
+ };
+ let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {}, profileContent);
+ xliffMergeCmd.run();
+ expect(ws.writtenData()).not.toContain('i18nFormat');
+ done();
+ });
+
it('should accept i18n format xmb', (done) => {
let ws: WriterToString = new WriterToString();
let commandOut = new CommandOutput(ws);
@@ -457,6 +479,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).targetContentNormalized()).toBe('Item {{0}} of {{1}} added.');
done();
});
@@ -626,6 +649,370 @@ describe('XliffMerge test spec', () => {
});
+ describe('Merge process checks for format XLIFF 2.0', () => {
+ let MASTER1FILE = 'ngExtractedMaster1.xlf2';
+ let MASTER2FILE = 'ngExtractedMaster2.xlf2';
+ let MASTER1SRC = SRCDIR + MASTER1FILE;
+ let MASTER2SRC = SRCDIR + MASTER2FILE;
+ let MASTERFILE = 'messages.xlf2';
+ let MASTER = WORKDIR + MASTERFILE;
+
+ let ID_APP_RUNS = "4371668001355139802"; // an ID from ngExtractedMaster1.xlf
+ let ID_REMOVED_MYFIRST = "2047558209369508311"; // an ID that will be removed in master2
+ let ID_REMOVED_APPDESCRIPTION = "7499557905529977371"; // another removed ID
+ let ID_WITH_PLACEHOLDER = "9030312858648510700";
+
+ beforeEach(() => {
+ if (!fs.existsSync(WORKDIR)){
+ fs.mkdirSync(WORKDIR);
+ }
+ // cleanup workdir
+ FileUtil.deleteFolderContentRecursive(WORKDIR);
+ });
+
+ it('should fix source language, if the masters lang is not the default', (done) => {
+ FileUtil.copy(MASTER1SRC, MASTER);
+ let master: ITranslationMessagesFile = readXliff2(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);
+ let profileContent: IConfigFile = {
+ xliffmergeOptions: {
+ defaultLanguage: 'de',
+ srcDir: WORKDIR,
+ genDir: WORKDIR,
+ i18nFormat: 'xlf2',
+ i18nFile: MASTERFILE,
+ }
+ };
+ let xliffMergeCmd = 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"');
+ let newmaster: ITranslationMessagesFile = readXliff2(MASTER);
+ expect(newmaster.sourceLanguage()).toBe('de'); // master is german
+ done();
+ });
+
+ it('should generate translated file for default language de from master', (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,
+ i18nFormat: 'xlf2',
+ i18nFile: MASTERFILE,
+ useSourceAsTarget: false
+ }
+ };
+ let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent);
+ xliffMergeCmd.run();
+ expect(ws.writtenData()).not.toContain('ERROR');
+ let langFile: ITranslationMessagesFile = readXliff2(xliffMergeCmd.generatedI18nFile('de'));
+ expect(langFile.sourceLanguage()).toBe('de');
+ expect(langFile.targetLanguage()).toBe('de');
+ langFile.forEachTransUnit((tu: ITransUnit) => {
+ expect(tu.targetContent()).toBe(tu.sourceContent());
+ expect(tu.targetState()).toBe('final');
+ });
+ done();
+ });
+
+ it('should generate translated file for all 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,
+ i18nFormat: 'xlf2',
+ i18nFile: MASTERFILE
+ }
+ };
+ let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de', 'en']}, profileContent);
+ xliffMergeCmd.run();
+ expect(ws.writtenData()).not.toContain('ERROR');
+ let langFileGerman: ITranslationMessagesFile = readXliff2(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 = readXliff2(xliffMergeCmd.generatedI18nFile('en'));
+ expect(langFileEnglish.sourceLanguage()).toBe('de');
+ expect(langFileEnglish.targetLanguage()).toBe('en');
+ langFileEnglish.forEachTransUnit((tu: ITransUnit) => {
+ expect(tu.targetContent()).toBe(tu.sourceContent());
+ expect(tu.targetState()).toBe('new');
+ });
+ 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,
+ i18nFormat: 'xlf2',
+ i18nFile: MASTERFILE,
+ useSourceAsTarget: false
+ }
+ };
+ let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de', 'en']}, profileContent);
+ xliffMergeCmd.run();
+ expect(ws.writtenData()).not.toContain('ERROR');
+ let langFileGerman: ITranslationMessagesFile = readXliff2(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 = readXliff2(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();
+ let commandOut = new CommandOutput(ws);
+ let profileContent: IConfigFile = {
+ xliffmergeOptions: {
+ defaultLanguage: 'de',
+ srcDir: WORKDIR,
+ genDir: WORKDIR,
+ i18nFormat: 'xlf2',
+ i18nFile: MASTERFILE
+ }
+ };
+ let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de', 'en']}, profileContent);
+ xliffMergeCmd.run();
+ expect(ws.writtenData()).not.toContain('ERROR');
+
+ // now translate some texts in the English version
+ let langFileEnglish: ITranslationMessagesFile = readXliff2(xliffMergeCmd.generatedI18nFile('en'));
+ let tu: ITransUnit = langFileEnglish.transUnitWithId(ID_APP_RUNS);
+ expect(tu).toBeTruthy();
+ langFileEnglish.translate(tu, 'App runs');
+ TranslationMessagesFileReader.save(langFileEnglish);
+
+ // next step, use another master
+ FileUtil.copy(MASTER2SRC, MASTER);
+ ws = new WriterToString();
+ commandOut = new CommandOutput(ws);
+ xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de', 'en']}, profileContent);
+ xliffMergeCmd.run();
+ expect(ws.writtenData()).not.toContain('ERROR');
+ 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
+ langFileEnglish = readXliff2(xliffMergeCmd.generatedI18nFile('en'));
+ expect(langFileEnglish.transUnitWithId(ID_APP_RUNS).targetContent()).toBe('App runs');
+
+ // look, that the removed IDs are really removed.
+ expect(langFileEnglish.transUnitWithId(ID_REMOVED_MYFIRST)).toBeFalsy();
+ expect(langFileEnglish.transUnitWithId(ID_REMOVED_APPDESCRIPTION)).toBeFalsy();
+ done();
+ });
+
+ it('should translate messages with placeholder', (done) => {
+ FileUtil.copy(MASTER2SRC, MASTER);
+ let ws: WriterToString = new WriterToString();
+ let commandOut = new CommandOutput(ws);
+ let profileContent: IConfigFile = {
+ xliffmergeOptions: {
+ defaultLanguage: 'de',
+ srcDir: WORKDIR,
+ genDir: WORKDIR,
+ i18nFormat: 'xlf2',
+ i18nFile: MASTERFILE
+ }
+ };
+ let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de', 'en']}, profileContent);
+ xliffMergeCmd.run();
+ expect(ws.writtenData()).not.toContain('ERROR');
+
+ // now translate some texts in the English version
+ let langFileEnglish: ITranslationMessagesFile = readXliff2(xliffMergeCmd.generatedI18nFile('en'));
+ let tu: ITransUnit = langFileEnglish.transUnitWithId(ID_WITH_PLACEHOLDER);
+ expect(tu).toBeTruthy();
+ langFileEnglish.translate(tu, 'Item of added.');
+ TranslationMessagesFileReader.save(langFileEnglish);
+
+ // look, that the new file contains the translation
+ langFileEnglish = readXliff2(xliffMergeCmd.generatedI18nFile('en'));
+ expect(langFileEnglish.transUnitWithId(ID_WITH_PLACEHOLDER).targetContent()).toBe('Item of added.');
+ expect(langFileEnglish.transUnitWithId(ID_WITH_PLACEHOLDER).targetContentNormalized()).toBe('Item {{0}} of {{1}} added.');
+
+ done();
+ });
+
+ });
+
+ describe('ngx-translate processing for format XLIFF 2.0', () => {
+
+ let MASTER1FILE = 'ngxtranslate.xlf2';
+ let MASTER1SRC = SRCDIR + MASTER1FILE;
+ let MASTER_WITHOUT_NGX_TRANSLATE_STUFF = SRCDIR + 'ngExtractedMaster1.xlf2';
+ let MASTERFILE = 'messages.xlf2';
+ let MASTER = WORKDIR + MASTERFILE;
+
+ let ID_NODESC_NOMEANING = "2047558209369508311"; // an ID without set meaning and description
+ let ID_MONDAY = "6830980354990918030"; // an ID from ngxtranslate.xlf with meaning "x.y" and description "ngx-translate"
+
+ beforeEach(() => {
+ if (!fs.existsSync(WORKDIR)){
+ fs.mkdirSync(WORKDIR);
+ }
+ // cleanup workdir
+ FileUtil.deleteFolderContentRecursive(WORKDIR);
+ });
+
+ it('should return null for unset description and meaning in master xlf2 file', (done) => {
+ FileUtil.copy(MASTER1SRC, MASTER);
+ let master: ITranslationMessagesFile = readXliff2(MASTER);
+ expect(master.transUnitWithId(ID_NODESC_NOMEANING).description()).toBeFalsy();
+ expect(master.transUnitWithId(ID_NODESC_NOMEANING).meaning()).toBeFalsy();
+ done();
+ });
+
+ it('should find description and meaning in master xlf2 file', (done) => {
+ FileUtil.copy(MASTER1SRC, MASTER);
+ let master: ITranslationMessagesFile = readXliff2(MASTER);
+ expect(master.transUnitWithId(ID_MONDAY).description()).toBe('ngx-translate');
+ expect(master.transUnitWithId(ID_MONDAY).meaning()).toBe('dateservice.monday');
+ done();
+ });
+
+ it('should find description and meaning in translated xlf2 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,
+ i18nFormat: 'xlf2',
+ i18nFile: MASTERFILE
+ }
+ };
+ let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent);
+ xliffMergeCmd.run();
+ expect(ws.writtenData()).not.toContain('ERROR');
+ let langFile: ITranslationMessagesFile = readXliff2(xliffMergeCmd.generatedI18nFile('de'));
+ expect(langFile.transUnitWithId(ID_MONDAY).description()).toBe('ngx-translate');
+ expect(langFile.transUnitWithId(ID_MONDAY).meaning()).toBe('dateservice.monday');
+ done();
+ });
+
+ it('should write translation json file for ngx-translate', (done) => {
+ FileUtil.copy(MASTER1SRC, MASTER);
+ let ws: WriterToString = new WriterToString();
+ let commandOut = new CommandOutput(ws);
+ let profileContent: IConfigFile = {
+ xliffmergeOptions: {
+ defaultLanguage: 'de',
+ srcDir: WORKDIR,
+ genDir: WORKDIR,
+ i18nFormat: 'xlf2',
+ i18nFile: MASTERFILE,
+ supportNgxTranslate: true
+ }
+ };
+ let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent);
+ xliffMergeCmd.run();
+ expect(ws.writtenData()).not.toContain('ERROR');
+ let translationJsonFilename = xliffMergeCmd.generatedNgxTranslateFile('de');
+ expect(FileUtil.exists(translationJsonFilename)).toBeTruthy();
+ let fileContent = FileUtil.read(translationJsonFilename, 'UTF-8');
+ let translation: any = JSON.parse(fileContent);
+ expect(translation).toBeTruthy();
+ expect(translation.myapp).toBeTruthy();
+ expect(translation.dateservice.monday).toBe("Montag");
+ expect(translation.dateservice.friday).toBe("Freitag");
+ expect(translation.explicitlysetids.test1).toBe("Explizit gesetzte ID");
+ done();
+ });
+
+ it('should handle placeholders in json file for ngx-translate', (done) => {
+ FileUtil.copy(MASTER1SRC, MASTER);
+ let ws: WriterToString = new WriterToString();
+ let commandOut = new CommandOutput(ws);
+ let profileContent: IConfigFile = {
+ xliffmergeOptions: {
+ defaultLanguage: 'de',
+ srcDir: WORKDIR,
+ genDir: WORKDIR,
+ i18nFormat: 'xlf2',
+ i18nFile: MASTERFILE,
+ supportNgxTranslate: true
+ }
+ };
+ let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent);
+ xliffMergeCmd.run();
+ expect(ws.writtenData()).not.toContain('ERROR');
+ let translationJsonFilename = xliffMergeCmd.generatedNgxTranslateFile('de');
+ expect(FileUtil.exists(translationJsonFilename)).toBeTruthy();
+ let fileContent = FileUtil.read(translationJsonFilename, 'UTF-8');
+ let translation: any = JSON.parse(fileContent);
+ expect(translation).toBeTruthy();
+ expect(translation.placeholders).toBeTruthy();
+ expect(translation.placeholders.test1placeholder).toBe('{{0}}: Eine Nachricht mit einem Platzhalter');
+ expect(translation.placeholders.test2placeholder).toBe('{{0}}: Eine Nachricht mit 2 Platzhaltern: {{1}}');
+ expect(translation.placeholders.test2placeholderRepeated).toBe('{{0}}: Eine Nachricht mit 2 Platzhaltern: {{0}} {{1}}');
+ done();
+ });
+
+ it('should handle embedded html markup in json file for ngx-translate', (done) => {
+ FileUtil.copy(MASTER1SRC, MASTER);
+ let ws: WriterToString = new WriterToString();
+ let commandOut = new CommandOutput(ws);
+ let profileContent: IConfigFile = {
+ xliffmergeOptions: {
+ defaultLanguage: 'de',
+ srcDir: WORKDIR,
+ genDir: WORKDIR,
+ i18nFormat: 'xlf2',
+ i18nFile: MASTERFILE,
+ supportNgxTranslate: true
+ }
+ };
+ let xliffMergeCmd = XliffMerge.createFromOptions(commandOut, {languages: ['de']}, profileContent);
+ xliffMergeCmd.run();
+ expect(ws.writtenData()).not.toContain('ERROR');
+ let translationJsonFilename = xliffMergeCmd.generatedNgxTranslateFile('de');
+ expect(FileUtil.exists(translationJsonFilename)).toBeTruthy();
+ let fileContent = FileUtil.read(translationJsonFilename, 'UTF-8');
+ let translation: any = JSON.parse(fileContent);
+ expect(translation).toBeTruthy();
+ expect(translation.embeddedhtml).toBeTruthy();
+ expect(translation.embeddedhtml.bold).toBe('Diese Nachricht ist WICHTIG');
+ expect(translation.embeddedhtml.boldstrong).toBe('Diese Nachricht ist SEHR WICHTIG');
+ expect(translation.embeddedhtml.strange).toBe('Diese Nachricht ist {{0}}');
+ done();
+ });
+
+ });
+
describe('Merge process checks for format xmb', () => {
let MASTER1FILE = 'ngExtractedMaster1.xmb';
diff --git a/src/xliffmerge/xliff-merge.ts b/src/xliffmerge/xliff-merge.ts
index 834d2ed..1b29de2 100644
--- a/src/xliffmerge/xliff-merge.ts
+++ b/src/xliffmerge/xliff-merge.ts
@@ -73,7 +73,7 @@ export class XliffMerge {
/**
* The read master xlf file.
*/
- private master: ITranslationMessagesFile; // XliffFile or XmbFile
+ private master: ITranslationMessagesFile; // XliffFile or Xliff2File or XmbFile
/**
* For Tests, create instance with given profile
diff --git a/test/testdata/i18n/ngExtractedMaster1.xlf2 b/test/testdata/i18n/ngExtractedMaster1.xlf2
new file mode 100644
index 0000000..ac6ad34
--- /dev/null
+++ b/test/testdata/i18n/ngExtractedMaster1.xlf2
@@ -0,0 +1,216 @@
+
+
+
+
+
+ Meine erste I18N-Anwendung
+
+
+
+
+ Anwendung läuft!
+
+
+
+
+ Beschreibung der Anwendung
+
+
+ Diese Anwendung ist eine reine Demonstration und hat keine wirklich nutzbare Funktion.
+
+
+
+
+ sampleapp.appdescription
+ ngx-translate
+
+
+ Diese Anwendung ist eine reine Demonstration und hat keine wirklich nutzbare Funktion.
+
+
+
+
+ placeholder sample
+
+
+ Eintrag von hinzugefügt.
+
+
+
+
+ linebreak sample
+
+
+ Dieser Text
+ enthält einen Zeilenumbruch.
+
+
+
+
+ embedded html sample
+
+
+ Dieser Text enthält eingebettetes html
+
+
+
+
+ Eine Nachricht per ngx-translate
+
+
+
+
+ ngx-translate
+ appTitle
+
+
+ Sample App Title
+
+
+
+
+ ngx-translate
+ messages.example
+
+
+ : A message from a service
+
+
+
+
+ ngx-translate
+ myapp
+
+
+ Meine Anwendung
+
+
+
+
+ ngx-translate
+ dateservice.monday
+
+
+ Montag
+
+
+
+
+ ngx-translate
+ dateservice.tuesday
+
+
+ Dienstag
+
+
+
+
+ ngx-translate
+ dateservice.wednesday
+
+
+ Mittwoch
+
+
+
+
+ ngx-translate
+ dateservice.thursday
+
+
+ Donnerstag
+
+
+
+
+ ngx-translate
+ dateservice.friday
+
+
+ Freitag
+
+
+
+
+ ngx-translate
+ dateservice.saturday
+
+
+ Samstag
+
+
+
+
+ ngx-translate
+ dateservice.sunday
+
+
+ Sonntag
+
+
+
+
+ ngx-translate
+ placeholders.test1placeholder
+
+
+ : Eine Nachricht mit einem Platzhalter
+
+
+
+
+ ngx-translate
+ placeholders.test2placeholder
+
+
+ : Eine Nachricht mit 2 Platzhaltern:
+
+
+
+
+ ngx-translate
+ placeholders.test2placeholderRepeated
+
+
+ : Eine Nachricht mit 2 Platzhaltern:
+
+
+
+
+ ngx-translate
+ embeddedhtml.bold
+
+
+ Diese Nachricht ist WICHTIG
+
+
+
+
+ ngx-translate
+ embeddedhtml.boldstrong
+
+
+ Diese Nachricht ist SEHR WICHTIG
+
+
+
+
+ ngx-translate
+ embeddedhtml.strange
+
+
+ Diese Nachricht ist
+
+
+
+
+ Explizit gesetzte ID
+
+
+
+
+ unit without id
+
+
+
+
diff --git a/test/testdata/i18n/ngExtractedMaster2.xlf2 b/test/testdata/i18n/ngExtractedMaster2.xlf2
new file mode 100644
index 0000000..c3cad31
--- /dev/null
+++ b/test/testdata/i18n/ngExtractedMaster2.xlf2
@@ -0,0 +1,211 @@
+
+
+
+
+
+ Anwendung läuft!
+
+
+
+
+ Added unit
+
+
+ this message is a new one.
+
+
+
+
+ sampleapp.appdescription
+ ngx-translate
+
+
+ Diese Anwendung ist eine reine Demonstration und hat keine wirklich nutzbare Funktion.
+
+
+
+
+ placeholder sample
+
+
+ Eintrag von hinzugefügt.
+
+
+
+
+ linebreak sample
+
+
+ Dieser Text
+ enthält einen Zeilenumbruch.
+
+
+
+
+ embedded html sample
+
+
+ Dieser Text enthält eingebettetes html
+
+
+
+
+ Eine Nachricht per ngx-translate
+
+
+
+
+ ngx-translate
+ appTitle
+
+
+ Sample App Title
+
+
+
+
+ ngx-translate
+ messages.example
+
+
+ : A message from a service
+
+
+
+
+ ngx-translate
+ myapp
+
+
+ Meine Anwendung
+
+
+
+
+ ngx-translate
+ dateservice.monday
+
+
+ Montag
+
+
+
+
+ ngx-translate
+ dateservice.tuesday
+
+
+ Dienstag
+
+
+
+
+ ngx-translate
+ dateservice.wednesday
+
+
+ Mittwoch
+
+
+
+
+ ngx-translate
+ dateservice.thursday
+
+
+ Donnerstag
+
+
+
+
+ ngx-translate
+ dateservice.friday
+
+
+ Freitag
+
+
+
+
+ ngx-translate
+ dateservice.saturday
+
+
+ Samstag
+
+
+
+
+ ngx-translate
+ dateservice.sunday
+
+
+ Sonntag
+
+
+
+
+ ngx-translate
+ placeholders.test1placeholder
+
+
+ : Eine Nachricht mit einem Platzhalter
+
+
+
+
+ ngx-translate
+ placeholders.test2placeholder
+
+
+ : Eine Nachricht mit 2 Platzhaltern:
+
+
+
+
+ ngx-translate
+ placeholders.test2placeholderRepeated
+
+
+ : Eine Nachricht mit 2 Platzhaltern:
+
+
+
+
+ ngx-translate
+ embeddedhtml.bold
+
+
+ Diese Nachricht ist WICHTIG
+
+
+
+
+ ngx-translate
+ embeddedhtml.boldstrong
+
+
+ Diese Nachricht ist SEHR WICHTIG
+
+
+
+
+ ngx-translate
+ embeddedhtml.strange
+
+
+ Diese Nachricht ist
+
+
+
+
+ Explizit gesetzte ID
+
+
+
+
+ unit without id
+
+
+
+
diff --git a/test/testdata/i18n/ngxtranslate.xlf2 b/test/testdata/i18n/ngxtranslate.xlf2
new file mode 100644
index 0000000..260c71d
--- /dev/null
+++ b/test/testdata/i18n/ngxtranslate.xlf2
@@ -0,0 +1,237 @@
+
+
+
+
+
+ Meine erste I18N-Anwendung
+ My first i18n application
+
+
+
+
+ Anwendung läuft!
+ app workst!
+
+
+
+
+ Beschreibung der Anwendung
+
+
+ Diese Anwendung ist eine reine Demonstration und hat keine wirklich nutzbare Funktion.
+ This app is for demonstration purposes only.
+
+
+
+
+ sampleapp.appdescription
+ ngx-translate
+
+
+ Diese Anwendung ist eine reine Demonstration und hat keine wirklich nutzbare Funktion.
+ This app is for demonstration purposes only.
+
+
+
+
+ placeholder sample
+
+
+ Eintrag von hinzugefügt.
+ Entry of total added.
+
+
+
+
+ linebreak sample
+
+
+ Dieser Text
+ enthält einen Zeilenumbruch.
+ This text
+ contains a line break.
+
+
+
+
+ embedded html sample
+
+
+ Dieser Text enthält eingebettetes html
+ This text contains embedded html
+
+
+
+
+ Eine Nachricht per ngx-translate
+ A message via ngx-translate
+
+
+
+
+ ngx-translate
+ appTitle
+
+
+ Sample App Title
+ Sample App Title
+
+
+
+
+ ngx-translate
+ messages.example
+
+
+ : A message from a service
+ : A message from a service
+
+
+
+
+ ngx-translate
+ myapp
+
+
+ Meine Anwendung
+ My app
+
+
+
+
+ ngx-translate
+ dateservice.monday
+
+
+ Montag
+ Monday
+
+
+
+
+ ngx-translate
+ dateservice.tuesday
+
+
+ Dienstag
+ Tuesday
+
+
+
+
+ ngx-translate
+ dateservice.wednesday
+
+
+ Mittwoch
+ Wednesday
+
+
+
+
+ ngx-translate
+ dateservice.thursday
+
+
+ Donnerstag
+ Thursday
+
+
+
+
+ ngx-translate
+ dateservice.friday
+
+
+ Freitag
+ Fryday
+
+
+
+
+ ngx-translate
+ dateservice.saturday
+
+
+ Samstag
+ Saturday
+
+
+
+
+ ngx-translate
+ dateservice.sunday
+
+
+ Sonntag
+ Sunday
+
+
+
+
+ ngx-translate
+ placeholders.test1placeholder
+
+
+ : Eine Nachricht mit einem Platzhalter
+ : A message with a placeholder
+
+
+
+
+ ngx-translate
+ placeholders.test2placeholder
+
+
+ : Eine Nachricht mit 2 Platzhaltern:
+ : A message with 2 placeholders:
+
+
+
+
+ ngx-translate
+ placeholders.test2placeholderRepeated
+
+
+ : Eine Nachricht mit 2 Platzhaltern:
+ : A message with 2 placeholders:
+
+
+
+
+ ngx-translate
+ embeddedhtml.bold
+
+
+ Diese Nachricht ist WICHTIG
+ This message is IMPORTANT
+
+
+
+
+ ngx-translate
+ embeddedhtml.boldstrong
+
+
+ Diese Nachricht ist SEHR WICHTIG
+ This message is VERY IMPORTANT
+
+
+
+
+ ngx-translate
+ embeddedhtml.strange
+
+
+ Diese Nachricht ist
+ This message is
+
+
+
+
+ Explizit gesetzte ID
+ Explicitly set ID
+
+
+
+
diff --git a/test/testdata/i18n/readme.txt b/test/testdata/i18n/readme.txt
index d63138f..a3f5eff 100644
--- a/test/testdata/i18n/readme.txt
+++ b/test/testdata/i18n/readme.txt
@@ -9,4 +9,5 @@ It contains the same IDs as Master1, but some additional ones.
ngttranslate.xlf is an extracted master with some messages with description and meaning to be used with ngx-translate.
-The xmb files are the same in principle, but for format xmb.
\ No newline at end of file
+The xmb files are the same in principle, but for format xmb.
+And the xlf2 files are for XLIFF 2.0
\ No newline at end of file
diff --git a/yarn.lock b/yarn.lock
index 38a7eed..64ae12a 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.8:
- version "0.0.8"
- resolved "https://registry.yarnpkg.com/ngx-i18nsupport-lib/-/ngx-i18nsupport-lib-0.0.8.tgz#ce8d527ea166f4e76ad0c2e90cdede78125e8979"
+ngx-i18nsupport-lib@^0.1.3:
+ version "0.1.3"
+ resolved "https://registry.yarnpkg.com/ngx-i18nsupport-lib/-/ngx-i18nsupport-lib-0.1.3.tgz#c1b00e5e43c0a2310679c27621e0e76a6b971ee5"
dependencies:
"@types/xmldom" "^0.1.29"
xmldom "^0.1.27"