Skip to content

Commit efaa015

Browse files
ocombefilipesilva
authored andcommitted
feat(@ngtools/webpack): auto-load i18n locale data files
1 parent 3b53403 commit efaa015

File tree

4 files changed

+147
-2
lines changed

4 files changed

+147
-2
lines changed

packages/@ngtools/webpack/src/angular_compiler_plugin.ts

+14-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,8 @@ import {
2020
makeTransform,
2121
replaceBootstrap,
2222
exportNgFactory,
23-
exportLazyModuleMap
23+
exportLazyModuleMap,
24+
registerLocaleData
2425
} from './transformers';
2526

2627
// These imports do not exist on Angular versions lower than 5.
@@ -549,7 +550,18 @@ export class AngularCompilerPlugin implements Tapable {
549550
const sourceFile = this._program.getTsProgram().getSourceFile(fileName);
550551
let transformOps;
551552
if (this._platform === PLATFORM.Browser) {
552-
transformOps = replaceBootstrap(sourceFile, this.entryModule);
553+
transformOps = [
554+
...replaceBootstrap(sourceFile, this.entryModule)
555+
];
556+
557+
// if we have a locale, auto import the locale data file
558+
if (this._angularCompilerOptions.i18nInLocale) {
559+
transformOps.push(...registerLocaleData(
560+
sourceFile,
561+
this.entryModule,
562+
this._angularCompilerOptions.i18nInLocale
563+
));
564+
}
553565
} else if (this._platform === PLATFORM.Server) {
554566
// export_module_map
555567
transformOps = [

packages/@ngtools/webpack/src/transformers/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -5,3 +5,4 @@ export * from './remove_import';
55
export * from './replace_bootstrap';
66
export * from './export_ngfactory';
77
export * from './export_lazy_module_map';
8+
export * from './register_locale_data';
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
import * as path from 'path';
2+
import * as fs from 'fs';
3+
import * as ts from 'typescript';
4+
5+
import { findAstNodes, getFirstNode } from './ast_helpers';
6+
import { AddNodeOperation, TransformOperation } from './make_transform';
7+
8+
9+
export function registerLocaleData(
10+
sourceFile: ts.SourceFile,
11+
entryModule: { path: string, className: string },
12+
locale: string
13+
): TransformOperation[] {
14+
const ops: TransformOperation[] = [];
15+
16+
// Find all identifiers using the entry module class name.
17+
const entryModuleIdentifiers = findAstNodes<ts.Identifier>(null, sourceFile,
18+
ts.SyntaxKind.Identifier, true)
19+
.filter(identifier => identifier.getText() === entryModule.className);
20+
21+
if (entryModuleIdentifiers.length === 0) {
22+
return [];
23+
}
24+
25+
// Find the bootstrap call
26+
entryModuleIdentifiers.forEach(entryModuleIdentifier => {
27+
// Figure out if it's a `platformBrowserDynamic().bootstrapModule(AppModule)` call.
28+
if (!(
29+
entryModuleIdentifier.parent
30+
&& entryModuleIdentifier.parent.kind === ts.SyntaxKind.CallExpression
31+
)) {
32+
return;
33+
}
34+
35+
const callExpr = entryModuleIdentifier.parent as ts.CallExpression;
36+
37+
if (callExpr.expression.kind !== ts.SyntaxKind.PropertyAccessExpression) {
38+
return;
39+
}
40+
41+
const propAccessExpr = callExpr.expression as ts.PropertyAccessExpression;
42+
43+
if (propAccessExpr.name.text !== 'bootstrapModule'
44+
|| propAccessExpr.expression.kind !== ts.SyntaxKind.CallExpression) {
45+
return;
46+
}
47+
48+
// get the path of the common module
49+
const commonPath = path.dirname(require.resolve('@angular/common/package.json'));
50+
// check if the locale file exists
51+
if (!fs.existsSync(path.resolve(commonPath, 'locales', `${locale}.js`))) {
52+
// check for an alternative locale (if the locale id was badly formatted)
53+
const locales = fs.readdirSync(path.resolve(commonPath, 'locales'))
54+
.filter(file => file.endsWith('.js'))
55+
.map(file => file.replace('.js', ''));
56+
57+
let newLocale;
58+
const normalizedLocale = locale.toLowerCase().replace(/_/g, '-');
59+
for (const l of locales) {
60+
if (l.toLowerCase() === normalizedLocale) {
61+
newLocale = l;
62+
break;
63+
}
64+
}
65+
66+
if (newLocale) {
67+
locale = newLocale;
68+
} else {
69+
// check for a parent locale
70+
const parentLocale = normalizedLocale.split('-')[0];
71+
if (locales.indexOf(parentLocale) !== -1) {
72+
locale = parentLocale;
73+
} else {
74+
throw new Error(
75+
`Unable to load the locale data file "@angular/common/locales/${locale}", ` +
76+
`please check that "${locale}" is a valid locale id.`);
77+
}
78+
}
79+
}
80+
81+
// Create the import node for the locale.
82+
const localeIdentifier = ts.createIdentifier(`__locale_${locale.replace(/-/g, '')}__`);
83+
const localeImportClause = ts.createImportClause(localeIdentifier, undefined);
84+
const localeNewImport = ts.createImportDeclaration(undefined, undefined, localeImportClause,
85+
ts.createLiteral(`@angular/common/locales/${locale}`));
86+
87+
ops.push(new AddNodeOperation(
88+
sourceFile,
89+
getFirstNode(sourceFile),
90+
localeNewImport
91+
));
92+
93+
// Create the import node for the registerLocaleData function.
94+
const regIdentifier = ts.createIdentifier(`registerLocaleData`);
95+
const regImportSpecifier = ts.createImportSpecifier(regIdentifier, regIdentifier);
96+
const regNamedImport = ts.createNamedImports([regImportSpecifier]);
97+
const regImportClause = ts.createImportClause(undefined, regNamedImport);
98+
const regNewImport = ts.createImportDeclaration(undefined, undefined, regImportClause,
99+
ts.createLiteral('@angular/common'));
100+
101+
ops.push(new AddNodeOperation(
102+
sourceFile,
103+
getFirstNode(sourceFile),
104+
regNewImport
105+
));
106+
107+
// Create the register function call
108+
const registerFunctionCall = ts.createCall(regIdentifier, undefined, [localeIdentifier]);
109+
const registerFunctionStatement = ts.createStatement(registerFunctionCall);
110+
111+
ops.push(new AddNodeOperation(
112+
sourceFile,
113+
getFirstNode(sourceFile),
114+
registerFunctionStatement
115+
));
116+
});
117+
118+
return ops;
119+
}

tests/e2e/tests/build/aot/angular-compiler.ts

+13
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { ng, npm } from '../../../utils/process';
22
import { updateJsonFile } from '../../../utils/project';
33
import { expectFileToMatch, rimraf, moveFile } from '../../../utils/fs';
44
import { getGlobalVariable } from '../../../utils/env';
5+
import { expectToFail } from '../../../utils/utils';
56

67

78
// THIS TEST REQUIRES TO MOVE NODE_MODULES AND MOVE IT BACK.
@@ -34,6 +35,18 @@ export default function () {
3435
.then(() => ng('build', '--aot'))
3536
.then(() => expectFileToMatch('dist/main.bundle.js',
3637
/bootstrapModuleFactory.*\/\* AppModuleNgFactory \*\//))
38+
39+
// tests for register_locale_data transformer
40+
.then(() => rimraf('dist'))
41+
.then(() => ng('build', '--aot', '--locale=fr'))
42+
.then(() => expectFileToMatch('dist/main.bundle.js',
43+
/registerLocaleData/))
44+
.then(() => expectFileToMatch('dist/main.bundle.js',
45+
/angular_common_locales_fr/))
46+
.then(() => rimraf('dist'))
47+
.then(() => expectToFail(() =>
48+
ng('build', '--aot', '--locale=no-locale')))
49+
3750
// Cleanup
3851
.then(() => {
3952
return rimraf('node_modules')

0 commit comments

Comments
 (0)