Compare commits

...

4 Commits

Author SHA1 Message Date
Kim Biesbjerg
97e8937709 (feat) add argument --null-as-default-value to use null as default value for extracted translations 2019-09-16 17:52:41 +02:00
Kim Biesbjerg
eb7f3f603e (test) (thanks, @adrienverge) 2019-09-16 16:58:21 +02:00
Kim Biesbjerg
ab2b78eaec (chore) rename function 2019-09-16 16:54:24 +02:00
Kim Biesbjerg
75ee2bdfda fix return type 2019-09-16 16:49:47 +02:00
10 changed files with 92 additions and 9 deletions

View File

@@ -103,5 +103,9 @@ Options:
[boolean] [default: false]
--clean, -c Remove obsolete strings when merging
[boolean] [default: false]
--key-as-default-value, -k Use key as default value for translations
[boolean] [default: false]
--key-as-default-value, -k Use key as default value for translations
[boolean] [default: false]
--null-as-default-value, -n Use null as default value for translations
[boolean] [default: false]
Arguments key-as-default-value and null-as-default-value are mutually exclusive

View File

@@ -1,6 +1,6 @@
{
"name": "@biesbjerg/ngx-translate-extract",
"version": "4.0.0",
"version": "4.1.0",
"description": "Extract strings from projects using ngx-translate",
"main": "dist/index.js",
"typings": "dist/index.d.ts",

View File

@@ -10,6 +10,7 @@ import { MarkerParser } from '../parsers/marker.parser';
import { PostProcessorInterface } from '../post-processors/post-processor.interface';
import { SortByKeyPostProcessor } from '../post-processors/sort-by-key.post-processor';
import { KeyAsDefaultValuePostProcessor } from '../post-processors/key-as-default-value.post-processor';
import { NullAsDefaultValuePostProcessor } from '../post-processors/null-as-default-value.post-processor';
import { PurgeObsoleteKeysPostProcessor } from '../post-processors/purge-obsolete-keys.post-processor';
import { CompilerInterface } from '../compilers/compiler.interface';
import { CompilerFactory } from '../compilers/compiler.factory';
@@ -87,6 +88,13 @@ export const cli = yargs
default: false,
type: 'boolean'
})
.option('null-as-default-value', {
alias: 'n',
describe: 'Use null as default value for translations',
default: false,
type: 'boolean'
})
.conflicts('key-as-default-value', 'null-as-default-value')
.exitProcess(true)
.parse(process.argv);
@@ -111,6 +119,8 @@ if (cli.clean) {
}
if (cli.keyAsDefaultValue) {
postProcessors.push(new KeyAsDefaultValuePostProcessor());
} else if (cli.nullAsDefaultValue) {
postProcessors.push(new NullAsDefaultValuePostProcessor());
}
if (cli.sort) {
postProcessors.push(new SortByKeyPostProcessor());

View File

@@ -9,7 +9,7 @@ const MARKER_PACKAGE_IMPORT_NAME = 'marker';
export class MarkerParser implements ParserInterface {
public extract(contents: string, filePath: string): TranslationCollection {
public extract(contents: string, filePath: string): TranslationCollection | null {
const sourceFile = tsquery.ast(contents, filePath);
const markerFnName = getNamedImportAlias(sourceFile, MARKER_PACKAGE_MODULE_NAME, MARKER_PACKAGE_IMPORT_NAME);

View File

@@ -2,6 +2,6 @@ import { TranslationCollection } from '../utils/translation.collection';
export interface ParserInterface {
extract(template: string, path: string): TranslationCollection;
extract(source: string, filePath: string): TranslationCollection | null;
}

View File

@@ -2,14 +2,14 @@ import { tsquery } from '@phenomnomnominal/tsquery';
import { ParserInterface } from './parser.interface';
import { TranslationCollection } from '../utils/translation.collection';
import { findClasses, findClassPropertyByType, findMethodCallExpression, getStringsFromExpression } from '../utils/ast-helpers';
import { findClasses, findClassPropertyByType, findMethodCallExpressions, getStringsFromExpression } from '../utils/ast-helpers';
const TRANSLATE_SERVICE_TYPE_REFERENCE = 'TranslateService';
const TRANSLATE_SERVICE_METHOD_NAMES = ['get', 'instant', 'stream'];
export class ServiceParser implements ParserInterface {
public extract(source: string, filePath: string): TranslationCollection {
public extract(source: string, filePath: string): TranslationCollection | null {
const sourceFile = tsquery.ast(source, filePath);
const classNodes = findClasses(sourceFile);
@@ -25,7 +25,7 @@ export class ServiceParser implements ParserInterface {
return;
}
const callNodes = findMethodCallExpression(classNode, propName, TRANSLATE_SERVICE_METHOD_NAMES);
const callNodes = findMethodCallExpressions(classNode, propName, TRANSLATE_SERVICE_METHOD_NAMES);
callNodes.forEach(callNode => {
const [firstArgNode] = callNode.arguments;
if (!firstArgNode) {

View File

@@ -0,0 +1,12 @@
import { TranslationCollection } from '../utils/translation.collection';
import { PostProcessorInterface } from './post-processor.interface';
export class NullAsDefaultValuePostProcessor implements PostProcessorInterface {
public name: string = 'NullAsDefaultValue';
public process(draft: TranslationCollection, extracted: TranslationCollection, existing: TranslationCollection): TranslationCollection {
return draft.map((key, val) => existing.get(key) === undefined ? null : val);
}
}

View File

@@ -72,7 +72,7 @@ export function findFunctionCallExpressions(node: Node, fnName: string | string[
return nodes;
}
export function findMethodCallExpression(node: Node, prop: string, fnName: string | string[]): CallExpression[] {
export function findMethodCallExpressions(node: Node, prop: string, fnName: string | string[]): CallExpression[] {
if (Array.isArray(fnName)) {
fnName = fnName.join('|');
}

View File

@@ -27,4 +27,19 @@ describe('MarkerParser', () => {
expect(keys).to.deep.equal(['Hello world', 'I', 'am', 'extracted', 'binary expression', 'conditional operator', 'FOO.bar']);
});
it('should extract split strings', () => {
const contents = `
import { marker as _ } from '@biesbjerg/ngx-translate-extract-marker';
_('Hello ' + 'world');
_('This is a ' + 'very ' + 'very ' + 'very ' + 'very ' + 'long line.');
_('Mix ' + \`of \` + 'different ' + \`types\`);
`;
const keys = parser.extract(contents, componentFilename).keys();
expect(keys).to.deep.equal([
'Hello world',
'This is a very very very very long line.',
'Mix of different types'
]);
});
});

View File

@@ -0,0 +1,42 @@
import { expect } from 'chai';
import { PostProcessorInterface } from '../../src/post-processors/post-processor.interface';
import { NullAsDefaultValuePostProcessor } from '../../src/post-processors/null-as-default-value.post-processor';
import { TranslationCollection } from '../../src/utils/translation.collection';
describe('NullAsDefaultValuePostProcessor', () => {
let processor: PostProcessorInterface;
beforeEach(() => {
processor = new NullAsDefaultValuePostProcessor();
});
it('should use null as default value', () => {
const draft = new TranslationCollection({ 'String A': '' });
const extracted = new TranslationCollection({ 'String A': '' });
const existing = new TranslationCollection();
expect(processor.process(draft, extracted, existing).values).to.deep.equal({
'String A': null
});
});
it('should keep existing value even if it is an empty string', () => {
const draft = new TranslationCollection({ 'String A': '' });
const extracted = new TranslationCollection({ 'String A': '' });
const existing = new TranslationCollection({ 'String A': '' });
expect(processor.process(draft, extracted, existing).values).to.deep.equal({
'String A': ''
});
});
it('should keep existing value', () => {
const draft = new TranslationCollection({ 'String A': 'Streng A' });
const extracted = new TranslationCollection({ 'String A': 'Streng A' });
const existing = new TranslationCollection({ 'String A': 'Streng A' });
expect(processor.process(draft, extracted, existing).values).to.deep.equal({
'String A': 'Streng A'
});
});
});