Skip to content

Commit

Permalink
work in progress
Browse files Browse the repository at this point in the history
  • Loading branch information
martinroob committed May 2, 2017
1 parent b0e5b32 commit b86e5e5
Show file tree
Hide file tree
Showing 16 changed files with 201 additions and 173 deletions.
14 changes: 14 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
<a name="0.3.1"></a>
# [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))

<a name="0.3.0"></a>
# [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.

<a name="0.2.1"></a>
# [0.2.1](https://github.com/martinroob/ngx-i18nsupport/compare/v0.2.0...v0.2.1) (2017-03-27)

Expand Down
49 changes: 36 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -82,21 +82,38 @@ Options:

`json-configurationfile` is a json file with the following allowed content (every value is optional):
<pre>
"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,
}
}
</pre>

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`.
Expand Down Expand Up @@ -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`.

Expand Down Expand Up @@ -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`
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -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",
Expand Down Expand Up @@ -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"
}
}
7 changes: 7 additions & 0 deletions src/common/file-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -93,4 +93,11 @@ export class FileUtil {
}
};

/**
* Delete a file.
* @param path
*/
public static deleteFile(path: string) {
fs.unlinkSync(path);
}
}
2 changes: 1 addition & 1 deletion src/common/writer-to-string.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
5 changes: 4 additions & 1 deletion src/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,15 @@
"moduleResolution": "node",
"outDir": "../dist",
"sourceMap": true,
"target": "es6",
"target": "es5",
"typeRoots": [
"../node_modules/@types"
]
},
"include": [
"**/*"
],
"exclude": [
"node_modules"
]
}
1 change: 1 addition & 0 deletions src/xliffmerge/i-xliff-merge-options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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
}

8 changes: 7 additions & 1 deletion src/xliffmerge/ngx-translate-extractor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}

/**
Expand Down
13 changes: 13 additions & 0 deletions src/xliffmerge/xliff-merge-parameters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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[];
Expand Down Expand Up @@ -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');
}
Expand Down Expand Up @@ -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());
}

/**
Expand Down Expand Up @@ -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;
}
}
82 changes: 80 additions & 2 deletions src/xliffmerge/xliff-merge.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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 <x id="INTERPOLATION"></x> of <x id="INTERPOLATION_1"></x> added.');
expect(langFileEnglish.transUnitWithId(ID_WITH_PLACEHOLDER).targetContent()).toBe('Item <x id="INTERPOLATION"/> of <x id="INTERPOLATION_1"/> added.');

done();
});
Expand All @@ -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;

Expand Down Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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();
});

});
});
6 changes: 3 additions & 3 deletions src/xliffmerge/xliff-merge.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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++;
}
Expand Down
11 changes: 6 additions & 5 deletions test/testdata/i18n/ngExtractedMaster1.xmb
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,11 @@
<!ELEMENT ex (#PCDATA)>
]>
<messagebundle>
<msg id="2047558209369508311">Meine erste I18N-Anwendung</msg>
<msg id="7499557905529977371" desc="Beschreibung der Anwendung">Diese Anwendung ist eine reine Demonstration und hat keine wirklich nutzbare Funktion.</msg>
<msg id="3274258156935474372" desc="sampleapp.appdescription" meaning="ngx-translate">Diese Anwendung ist eine reine Demonstration und hat keine wirklich nutzbare Funktion.</msg>
<msg id="9030312858648510700" desc="placeholder sample">Eintrag <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> von <ph name="INTERPOLATION_1"><ex>INTERPOLATION_1</ex></ph> hinzugefügt.</msg>
<msg id="7149517499881679376" desc="linebreak sample">Dieser Text
<msg id="2047558209369508311"><source>S:/experimente/sampleapp41/src/app/app.component.ts:1</source>Meine erste I18N-Anwendung</msg>
<msg id="4371668001355139802"><source>S:/experimente/sampleapp41/src/app/app.component.ts:2</source><source>S:/experimente/sampleapp41/src/app/app.component.ts:3</source>Anwendung läuft!</msg>
<msg id="7499557905529977371" desc="Beschreibung der Anwendung"><source>S:/experimente/sampleapp41/src/app/app.component.ts:4</source>Diese Anwendung ist eine reine Demonstration und hat keine wirklich nutzbare Funktion.</msg>
<msg id="3274258156935474372" desc="sampleapp.appdescription" meaning="ngx-translate"><source>S:/experimente/sampleapp41/src/app/app.component.ts:5</source>Diese Anwendung ist eine reine Demonstration und hat keine wirklich nutzbare Funktion.</msg>
<msg id="9030312858648510700" desc="placeholder sample"><source>S:/experimente/sampleapp41/src/app/app.component.ts:6</source>Eintrag <ph name="INTERPOLATION"><ex>INTERPOLATION</ex></ph> von <ph name="INTERPOLATION_1"><ex>INTERPOLATION_1</ex></ph> hinzugefügt.</msg>
<msg id="7149517499881679376" desc="linebreak sample"><source>S:/experimente/sampleapp41/src/app/app.component.ts:7,8</source>Dieser Text
enthält einen Zeilenumbruch.</msg>
</messagebundle>
Loading

0 comments on commit b86e5e5

Please sign in to comment.