Skip to content

Commit 21b1fcf

Browse files
committed
feature(i18n): implement i18n command
Implement i18n messages extractor. Contrary to @angular/complier-cli's command it will not throw an error if a resource is not found.
1 parent 0365118 commit 21b1fcf

File tree

4 files changed

+142
-0
lines changed

4 files changed

+142
-0
lines changed

packages/angular-cli/addon/index.js

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ module.exports = {
3232
'completion': require('../commands/completion').default,
3333
'doc': require('../commands/doc').default,
3434
'github-pages-deploy': require('../commands/github-pages-deploy').default,
35+
'xi18n': require('../commands/xi18n').default,
3536

3637
// Easter eggs.
3738
'make-this-awesome': require('../commands/easter-egg').default,
+30
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
const Command = require('../ember-cli/lib/models/command');
2+
3+
import {Extracti18nTask} from '../tasks/extract-i18n';
4+
5+
const Xi18nCommand = Command.extend({
6+
name: 'xi18n',
7+
description: 'Extracts i18n messages from source code.',
8+
works: 'insideProject',
9+
availableOptions: [
10+
{
11+
name: 'format',
12+
type: String,
13+
default: 'xliff',
14+
aliases: ['f', {'xmb': 'xmb'}, {'xlf': 'xlf'}, {'xliff': 'xliff'}]}
15+
],
16+
run: function (commandOptions: any) {
17+
18+
const xi18nTask = new Extracti18nTask({
19+
ui: this.ui,
20+
project: this.project,
21+
i18nFormat: commandOptions.format
22+
});
23+
24+
return xi18nTask.run();
25+
}
26+
});
27+
28+
29+
export default Xi18nCommand;
30+
+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
const Task = require('../ember-cli/lib/models/task');
2+
3+
import * as compiler from '@angular/compiler';
4+
import {Extractor} from '@angular/compiler-cli';
5+
import * as tsc from '@angular/tsc-wrapped';
6+
import * as ts from 'typescript';
7+
import * as path from 'path';
8+
9+
export const Extracti18nTask = Task.extend({
10+
run: function () {
11+
const project = path.resolve(this.project.root, 'src');
12+
const cliOptions = new tsc.I18nExtractionCliOptions({
13+
i18nFormat: this.i18nFormat
14+
});
15+
16+
function extract (
17+
ngOptions: tsc.AngularCompilerOptions, cliOptions: tsc.I18nExtractionCliOptions,
18+
program: ts.Program, host: ts.CompilerHost) {
19+
20+
const resourceLoader: compiler.ResourceLoader = {
21+
get: (s: string) => {
22+
if (!host.fileExists(s)) {
23+
// Return empty string to avoid extractor stop processing
24+
return Promise.resolve('');
25+
}
26+
return Promise.resolve(host.readFile(s));
27+
}
28+
};
29+
const extractor =
30+
Extractor.create(ngOptions, cliOptions.i18nFormat, program, host, resourceLoader);
31+
32+
const bundlePromise: Promise<compiler.MessageBundle> = extractor.extract();
33+
34+
return (bundlePromise).then(messageBundle => {
35+
let ext: string;
36+
let serializer: compiler.Serializer;
37+
const format = (cliOptions.i18nFormat || 'xlf').toLowerCase();
38+
39+
switch (format) {
40+
case 'xmb':
41+
ext = 'xmb';
42+
serializer = new compiler.Xmb();
43+
break;
44+
case 'xliff':
45+
case 'xlf':
46+
default:
47+
const htmlParser = new compiler.I18NHtmlParser(new compiler.HtmlParser());
48+
ext = 'xlf';
49+
serializer = new compiler.Xliff(htmlParser, compiler.DEFAULT_INTERPOLATION_CONFIG);
50+
break;
51+
}
52+
53+
const dstPath = path.join(ngOptions.genDir, `messages.${ext}`);
54+
host.writeFile(dstPath, messageBundle.write(serializer), false);
55+
});
56+
}
57+
58+
return tsc.main(project, cliOptions, extract);
59+
}
60+
});

tests/acceptance/extract-i18n.spec.js

+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
const tmp = require('../helpers/tmp');
2+
const ng = require('../helpers/ng');
3+
const existsSync = require('exists-sync');
4+
const path = require('path');
5+
const root = process.cwd();
6+
const expect = require('chai').expect;
7+
8+
// TODO: Enable when tests get validated
9+
describe.skip('Acceptance: ng xi18n', function() {
10+
beforeEach(function() {
11+
this.timeout(180000);
12+
return tmp.setup('./tmp').then(function() {
13+
process.chdir('./tmp');
14+
}).then(function() {
15+
return ng(['new', 'foo', '--link-cli']);
16+
});
17+
});
18+
19+
afterEach(function() {
20+
this.timeout(10000);
21+
22+
return tmp.teardown('./tmp');
23+
});
24+
25+
it('ng xi18n', function() {
26+
this.timeout(10000);
27+
28+
const appRoot = path.join(root, 'tmp/foo');
29+
const messagesPath = path.join(appRoot, 'src/messages.xlf');
30+
31+
return ng(['xi18n'])
32+
.then(() => {
33+
expect(existsSync(messagesPath)).to.equal(true);
34+
});
35+
36+
});
37+
38+
it('ng xi18n --format=xmb', function() {
39+
this.timeout(10000);
40+
41+
const appRoot = path.join(root, 'tmp/foo');
42+
const messagesPath = path.join(appRoot, 'src/messages.xmb');
43+
44+
return ng(['xi18n', '--format=xmb'])
45+
.then(() => {
46+
expect(existsSync(messagesPath)).to.equal(true);
47+
});
48+
49+
});
50+
51+
});

0 commit comments

Comments
 (0)