-
Notifications
You must be signed in to change notification settings - Fork 12k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(@angular-devkit/build-angular): support i18n message extraction …
…with Ivy This change adds support for extracting i18n translation messages with an Ivy enabled application. This is accomplished by using the new extraction capabilities present in the `@angular/localize` package and will require version 10.1 or later of the package. Since this change uses an new extraction method, it currently must be enabled during extraction by using the `--ivy` flag. The flag is a precaution to prevent unintentional breakage for existing applications but will become the default behavior for all Ivy enabled applications in a future release. Closes #18275
- Loading branch information
1 parent
b12de7a
commit a5293fe
Showing
6 changed files
with
298 additions
and
24 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
85 changes: 85 additions & 0 deletions
85
packages/angular_devkit/build_angular/src/extract-i18n/ivy-extract-loader.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,85 @@ | ||
/** | ||
* @license | ||
* Copyright Google Inc. All Rights Reserved. | ||
* | ||
* Use of this source code is governed by an MIT-style license that can be | ||
* found in the LICENSE file at https://angular.io/license | ||
*/ | ||
import { MessageExtractor } from '@angular/localize/src/tools/src/extract/extraction'; | ||
import { getOptions } from 'loader-utils'; | ||
import * as nodePath from 'path'; | ||
|
||
interface LocalizeExtractLoaderOptions { | ||
messageHandler: (messages: import('@angular/localize').ɵParsedMessage[]) => void; | ||
} | ||
|
||
export default function localizeExtractLoader( | ||
this: import('webpack').loader.LoaderContext, | ||
content: string, | ||
// Source map types are broken in the webpack type definitions | ||
// tslint:disable-next-line: no-any | ||
map: any, | ||
) { | ||
const loaderContext = this; | ||
|
||
// Casts are needed to workaround the loader-utils typings limited support for option values | ||
const options = (getOptions(this) as unknown) as LocalizeExtractLoaderOptions | undefined; | ||
|
||
// Setup a Webpack-based logger instance | ||
const logger = { | ||
// level 2 is warnings | ||
level: 2, | ||
debug(...args: string[]): void { | ||
// tslint:disable-next-line: no-console | ||
console.debug(...args); | ||
}, | ||
info(...args: string[]): void { | ||
loaderContext.emitWarning(args.join('')); | ||
}, | ||
warn(...args: string[]): void { | ||
loaderContext.emitWarning(args.join('')); | ||
}, | ||
error(...args: string[]): void { | ||
loaderContext.emitError(args.join('')); | ||
}, | ||
}; | ||
|
||
// Setup a virtual file system instance for the extractor | ||
// * MessageExtractor itself uses readFile and resolve | ||
// * Internal SourceFileLoader (sourcemap support) uses dirname, exists, readFile, and resolve | ||
const filesystem = { | ||
readFile(path: string): string { | ||
if (path === loaderContext.resourcePath) { | ||
return content; | ||
} else if (path === loaderContext.resourcePath + '.map') { | ||
return typeof map === 'string' ? map : JSON.stringify(map); | ||
} else { | ||
throw new Error('Unknown file requested.'); | ||
} | ||
}, | ||
resolve(...paths: string[]): string { | ||
return nodePath.resolve(...paths); | ||
}, | ||
exists(path: string): boolean { | ||
return path === loaderContext.resourcePath || path === loaderContext.resourcePath + '.map'; | ||
}, | ||
dirname(path: string): string { | ||
return nodePath.dirname(path); | ||
}, | ||
}; | ||
|
||
// tslint:disable-next-line: no-any | ||
const extractor = new MessageExtractor(filesystem as any, logger, { | ||
// tslint:disable-next-line: no-any | ||
basePath: this.rootContext as any, | ||
useSourceMaps: !!map, | ||
}); | ||
|
||
const messages = extractor.extractMessages(loaderContext.resourcePath); | ||
if (messages.length > 0) { | ||
options?.messageHandler(messages); | ||
} | ||
|
||
// Pass through the original content now that messages have been extracted | ||
this.callback(undefined, content, map); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
import { join } from 'path'; | ||
import { getGlobalVariable } from '../../utils/env'; | ||
import { writeFile } from '../../utils/fs'; | ||
import { ng, npm } from '../../utils/process'; | ||
import { updateJsonFile } from '../../utils/project'; | ||
import { expectToFail } from '../../utils/utils'; | ||
import { readNgVersion } from '../../utils/version'; | ||
|
||
export default async function() { | ||
// Ivy only test | ||
if (getGlobalVariable('argv')['ve']) { | ||
return; | ||
} | ||
|
||
// Setup an i18n enabled component | ||
await ng('generate', 'component', 'i18n-test'); | ||
await writeFile( | ||
join('src/app/i18n-test', 'i18n-test.component.html'), | ||
'<p i18n>Hello world</p>', | ||
); | ||
|
||
// Should fail with --ivy flag if `@angular/localize` is missing | ||
const { message: message1 } = await expectToFail(() => ng('xi18n', '--ivy')); | ||
if (!message1.includes(`Ivy extraction requires the '@angular/localize' package version 10.1.0 or higher.`)) { | ||
throw new Error('Expected localize package error message when missing'); | ||
} | ||
|
||
// Should fail with --ivy flag if `@angular/localize` is wrong version | ||
await npm('install', '@angular/localize@9'); | ||
const { message: message2 } = await expectToFail(() => ng('xi18n', '--ivy')); | ||
if (!message2.includes(`Ivy extraction requires the '@angular/localize' package version 10.1.0 or higher.`)) { | ||
throw new Error('Expected localize package error message when wrong version'); | ||
} | ||
|
||
// Install correct version | ||
let localizeVersion = '@angular/localize@' + readNgVersion(); | ||
if (getGlobalVariable('argv')['ng-snapshots']) { | ||
localizeVersion = require('../../ng-snapshot/package.json').dependencies['@angular/localize']; | ||
} | ||
await npm('install', `${localizeVersion}`); | ||
|
||
// Should show ivy enabled application warning without --ivy flag | ||
const { stderr: message3 } = await ng('xi18n'); | ||
if (!message3.includes(`Ivy extraction not enabled but application is Ivy enabled.`)) { | ||
throw new Error('Expected ivy enabled application warning'); | ||
} | ||
|
||
// Disable Ivy | ||
await updateJsonFile('tsconfig.json', config => { | ||
const { angularCompilerOptions = {} } = config; | ||
angularCompilerOptions.enableIvy = false; | ||
config.angularCompilerOptions = angularCompilerOptions; | ||
}); | ||
|
||
// Should show ivy disabled application warning with --ivy flag and enableIvy false | ||
const { message: message4 } = await expectToFail(() => ng('xi18n', '--ivy')); | ||
if (!message4.includes(`Ivy extraction enabled but application is not Ivy enabled.`)) { | ||
throw new Error('Expected ivy disabled application warning'); | ||
} | ||
} |
Oops, something went wrong.