Skip to content
Permalink
Browse files

fix(ngcc): render legacy i18n message ids by default (#34135)

By ensuring that legacy i18n message ids are rendered into the templates
of components for packages processed by ngcc, we ensure that these packages
can be used in an application that may provide translations in a legacy
format.

Fixes #34056

PR Close #34135
  • Loading branch information
petebacondarwin authored and mhevery committed Dec 3, 2019
1 parent 62d384d commit 93ac362848b929daa54a64a4b1d85a5b21a3d5ba
@@ -52,6 +52,17 @@ if (require.main === module) {
'The Angular CLI does this already, so it is safe to use this option if the project is being built via the CLI.',
type: 'boolean',
})
.option('legacy-message-ids', {
describe: 'Render `$localize` messages with legacy format ids.\n' +
'The default value is `true`. Only set this to `false` if you do not want legacy message ids to\n' +
'be rendered. For example, if you are not using legacy message ids in your translation files\n' +
'AND are not doing compile-time inlining of translations, in which case the extra message ids\n' +
'would add unwanted size to the final source bundle.\n' +
'It is safe to leave this set to true if you are doing compile-time inlining because the extra\n' +
'legacy message ids will all be stripped during translation.',
type: 'boolean',
default: true,
})
.option('async', {
describe:
'Whether to compile asynchronously. This is enabled by default as it allows compilations to be parallelized.\n' +
@@ -81,6 +92,7 @@ if (require.main === module) {
const compileAllFormats = !options['first-only'];
const createNewEntryPointFormats = options['create-ivy-entry-points'];
const logLevel = options['l'] as keyof typeof LogLevel | undefined;
const enableI18nLegacyMessageIdFormat = options['legacy-message-ids'];

(async() => {
try {
@@ -93,6 +105,7 @@ if (require.main === module) {
compileAllFormats,
createNewEntryPointFormats,
logger,
enableI18nLegacyMessageIdFormat,
async: options['async'],
});

@@ -97,6 +97,19 @@ export interface SyncNgccOptions {
* Default: `false` (i.e. run synchronously)
*/
async?: false;

/**
* Render `$localize` messages with legacy format ids.
*
* The default value is `true`. Only set this to `false` if you do not want legacy message ids to
* be rendered. For example, if you are not using legacy message ids in your translation files
* AND are not doing compile-time inlining of translations, in which case the extra message ids
* would add unwanted size to the final source bundle.
*
* It is safe to leave this set to true if you are doing compile-time inlining because the extra
* legacy message ids will all be stripped during translation.
*/
enableI18nLegacyMessageIdFormat?: boolean;
}

/**
@@ -124,8 +137,8 @@ export function mainNgcc(options: SyncNgccOptions): void;
export function mainNgcc(
{basePath, targetEntryPointPath, propertiesToConsider = SUPPORTED_FORMAT_PROPERTIES,
compileAllFormats = true, createNewEntryPointFormats = false,
logger = new ConsoleLogger(LogLevel.info), pathMappings, async = false}: NgccOptions): void|
Promise<void> {
logger = new ConsoleLogger(LogLevel.info), pathMappings, async = false,
enableI18nLegacyMessageIdFormat = true}: NgccOptions): void|Promise<void> {
// Execute in parallel, if async execution is acceptable and there are more than 1 CPU cores.
const inParallel = async && (os.cpus().length > 1);

@@ -242,7 +255,7 @@ export function mainNgcc(

const bundle = makeEntryPointBundle(
fileSystem, entryPoint, formatPath, isCore, format, processDts, pathMappings, true,
false);
enableI18nLegacyMessageIdFormat);

logger.info(`Compiling ${entryPoint.name} : ${formatProperty} as ${format}`);

@@ -312,7 +312,7 @@ runInEachFileSystem(() => {
expect(jsContents).not.toMatch(/\$localize\s*`/);
expect(jsContents)
.toMatch(
/\$localize\(ɵngcc\d+\.__makeTemplateObject\(\[":some:`description`:A message"], \[":some\\\\:\\\\`description\\\\`:A message"]\)\);/);
/\$localize\(ɵngcc\d+\.__makeTemplateObject\(\[":some:`description`\\u241Fefc92f285b3c24b083a8a594f62c7fccf3118766\\u241F3806630072763809030:A message"], \[":some\\\\:\\\\`description\\\\`\\u241Fefc92f285b3c24b083a8a594f62c7fccf3118766\\u241F3806630072763809030:A message"]\)\);/);
});

describe('in async mode', () => {
@@ -1219,6 +1219,62 @@ runInEachFileSystem(() => {
});
});

describe('legacy message ids', () => {
it('should render legacy message ids when compiling i18n tags in component templates', () => {
compileIntoApf('test-package', {
'/index.ts': `
import {Component} from '@angular/core';
@Component({
selector: '[base]',
template: '<div i18n>Some message</div>'
})
export class AppComponent {}
`,
});

mainNgcc({
basePath: '/node_modules',
targetEntryPointPath: 'test-package',
propertiesToConsider: ['esm2015'],
});


const jsContents = fs.readFile(_(`/node_modules/test-package/esm2015/src/index.js`));
expect(jsContents)
.toContain(
'$localize `:␟888aea0e46f7e9dddbd95fc1ef380a3ff70ada9d␟1812794354835616626:Some message');
});

it('should not render legacy message ids when compiling i18n tags in component templates if `enableI18nLegacyMessageIdFormat` is false',
() => {
compileIntoApf('test-package', {
'/index.ts': `
import {Component} from '@angular/core';
@Component({
selector: '[base]',
template: '<div i18n>Some message</div>'
})
export class AppComponent {}
`,
});

mainNgcc({
basePath: '/node_modules',
targetEntryPointPath: 'test-package',
propertiesToConsider: ['esm2015'],
enableI18nLegacyMessageIdFormat: false,
});


const jsContents = fs.readFile(_(`/node_modules/test-package/esm2015/src/index.js`));
expect(jsContents).not.toContain('␟888aea0e46f7e9dddbd95fc1ef380a3ff70ada9d');
expect(jsContents).not.toContain('␟1812794354835616626');
expect(jsContents).not.toContain('␟');
});
});

function loadPackage(
packageName: string, basePath: AbsoluteFsPath = _('/node_modules')): EntryPointPackageJson {
return JSON.parse(fs.readFile(fs.resolve(basePath, packageName, 'package.json')));

0 comments on commit 93ac362

Please sign in to comment.
You can’t perform that action at this time.