feat(ivy): i18n - render legacy message ids in $localize
if requested (#32937)
The `$localize` library uses a new message digest function for computing message ids. This means that translations in legacy translation files will no longer match the message ids in the code and so will not be translated. This commit adds the ability to specify the format of your legacy translation files, so that the appropriate message id can be rendered in the `$localize` tagged strings. This results in larger code size and requires that all translations are in the legacy format. Going forward the developer should migrate their translation files to use the new message id format. PR Close #32937
This commit is contained in:

committed by
atscott

parent
fc28b266cd
commit
bcbf3e4123
@ -76,8 +76,8 @@ export class DecorationAnalyzer {
|
||||
this.reflectionHost, this.evaluator, this.fullRegistry, this.fullMetaReader,
|
||||
this.scopeRegistry, this.scopeRegistry, this.isCore, this.resourceManager, this.rootDirs,
|
||||
/* defaultPreserveWhitespaces */ false,
|
||||
/* i18nUseExternalIds */ true, this.moduleResolver, this.cycleAnalyzer, this.refEmitter,
|
||||
NOOP_DEFAULT_IMPORT_RECORDER),
|
||||
/* i18nUseExternalIds */ true, /* i18nLegacyMessageIdFormat */ '', this.moduleResolver,
|
||||
this.cycleAnalyzer, this.refEmitter, NOOP_DEFAULT_IMPORT_RECORDER),
|
||||
new DirectiveDecoratorHandler(
|
||||
this.reflectionHost, this.evaluator, this.fullRegistry, NOOP_DEFAULT_IMPORT_RECORDER,
|
||||
this.isCore),
|
||||
|
@ -32,6 +32,7 @@ export function main(
|
||||
if (configErrors.length) {
|
||||
return reportErrorsAndExit(configErrors, /*options*/ undefined, consoleError);
|
||||
}
|
||||
warnForDeprecatedOptions(options);
|
||||
if (watch) {
|
||||
const result = watchMode(project, options, consoleError);
|
||||
return reportErrorsAndExit(result.firstCompileResult, options, consoleError);
|
||||
@ -226,6 +227,14 @@ export function watchMode(
|
||||
}, options, options => createEmitCallback(options)));
|
||||
}
|
||||
|
||||
function warnForDeprecatedOptions(options: api.CompilerOptions) {
|
||||
if (options.i18nLegacyMessageIdFormat !== undefined) {
|
||||
console.warn(
|
||||
'The `i18nLegacyMessageIdFormat` option is deprecated.\n' +
|
||||
'Migrate your legacy translation files to the new `$localize` message id format and remove this option.');
|
||||
}
|
||||
}
|
||||
|
||||
// CLI entry point
|
||||
if (require.main === module) {
|
||||
const args = process.argv.slice(2);
|
||||
|
@ -52,8 +52,9 @@ export class ComponentDecoratorHandler implements
|
||||
private scopeReader: ComponentScopeReader, private scopeRegistry: LocalModuleScopeRegistry,
|
||||
private isCore: boolean, private resourceLoader: ResourceLoader, private rootDirs: string[],
|
||||
private defaultPreserveWhitespaces: boolean, private i18nUseExternalIds: boolean,
|
||||
private moduleResolver: ModuleResolver, private cycleAnalyzer: CycleAnalyzer,
|
||||
private refEmitter: ReferenceEmitter, private defaultImportRecorder: DefaultImportRecorder,
|
||||
private i18nLegacyMessageIdFormat: string, private moduleResolver: ModuleResolver,
|
||||
private cycleAnalyzer: CycleAnalyzer, private refEmitter: ReferenceEmitter,
|
||||
private defaultImportRecorder: DefaultImportRecorder,
|
||||
private resourceDependencies:
|
||||
ResourceDependencyRecorder = new NoopResourceDependencyRecorder()) {}
|
||||
|
||||
@ -697,7 +698,8 @@ export class ComponentDecoratorHandler implements
|
||||
...parseTemplate(templateStr, templateUrl, {
|
||||
preserveWhitespaces,
|
||||
interpolationConfig: interpolation,
|
||||
range: templateRange, escapedString, ...options,
|
||||
range: templateRange, escapedString,
|
||||
i18nLegacyMessageIdFormat: this.i18nLegacyMessageIdFormat, ...options,
|
||||
}),
|
||||
template: templateStr, templateUrl,
|
||||
isInline: component.has('template'),
|
||||
|
@ -61,8 +61,8 @@ runInEachFileSystem(() => {
|
||||
|
||||
const handler = new ComponentDecoratorHandler(
|
||||
reflectionHost, evaluator, metaRegistry, metaReader, scopeRegistry, scopeRegistry, false,
|
||||
new NoopResourceLoader(), [''], false, true, moduleResolver, cycleAnalyzer, refEmitter,
|
||||
NOOP_DEFAULT_IMPORT_RECORDER);
|
||||
new NoopResourceLoader(), [''], false, true, '', moduleResolver, cycleAnalyzer,
|
||||
refEmitter, NOOP_DEFAULT_IMPORT_RECORDER);
|
||||
const TestCmp = getDeclaration(program, _('/entry.ts'), 'TestCmp', isNamedClassDeclaration);
|
||||
const detected = handler.detect(TestCmp, reflectionHost.getDecoratorsOfDeclaration(TestCmp));
|
||||
if (detected === undefined) {
|
||||
|
@ -509,8 +509,8 @@ export class NgtscProgram implements api.Program {
|
||||
this.reflector, evaluator, metaRegistry, this.metaReader !, scopeReader, scopeRegistry,
|
||||
this.isCore, this.resourceManager, this.rootDirs,
|
||||
this.options.preserveWhitespaces || false, this.options.i18nUseExternalIds !== false,
|
||||
this.moduleResolver, this.cycleAnalyzer, this.refEmitter, this.defaultImportTracker,
|
||||
this.incrementalState),
|
||||
this.options.i18nLegacyMessageIdFormat || '', this.moduleResolver, this.cycleAnalyzer,
|
||||
this.refEmitter, this.defaultImportTracker, this.incrementalState),
|
||||
new DirectiveDecoratorHandler(
|
||||
this.reflector, evaluator, metaRegistry, this.defaultImportTracker, this.isCore),
|
||||
new InjectableDecoratorHandler(
|
||||
|
@ -161,6 +161,19 @@ export interface CompilerOptions extends ts.CompilerOptions {
|
||||
// (used by Closure Compiler's output of `goog.getMsg` for transition period)
|
||||
i18nUseExternalIds?: boolean;
|
||||
|
||||
/**
|
||||
* Render `$localize` message ids with the specified legacy format (xlf, xlf2 or xmb).
|
||||
*
|
||||
* Use this option when use are using the `$localize` based localization messages but
|
||||
* have not migrated the translation files to use the new `$localize` message id format.
|
||||
*
|
||||
* @deprecated
|
||||
* `i18nLegacyMessageIdFormat` should only be used while migrating from legacy message id
|
||||
* formatted translation files and will be removed at the same time as ViewEngine support is
|
||||
* removed.
|
||||
*/
|
||||
i18nLegacyMessageIdFormat?: string;
|
||||
|
||||
// Whether to remove blank text nodes from compiled templates. It is `false` by default starting
|
||||
// from Angular 6.
|
||||
preserveWhitespaces?: boolean;
|
||||
|
@ -2052,6 +2052,62 @@ runInEachFileSystem(os => {
|
||||
expect(jsContents).not.toContain('MSG_EXTERNAL_');
|
||||
});
|
||||
|
||||
it('should render legacy id when i18nLegacyMessageIdFormat config is set to xlf', () => {
|
||||
env.tsconfig({i18nLegacyMessageIdFormat: 'xlf'});
|
||||
env.write(`test.ts`, `
|
||||
import {Component} from '@angular/core';
|
||||
@Component({
|
||||
selector: 'test',
|
||||
template: '<div i18n>Some text</div>'
|
||||
})
|
||||
class FooCmp {}`);
|
||||
env.driveMain();
|
||||
const jsContents = env.getContents('test.js');
|
||||
expect(jsContents).toContain(':@@5dbba0a3da8dff890e20cf76eb075d58900fbcd3:Some text');
|
||||
});
|
||||
|
||||
it('should render legacy id when i18nLegacyMessageIdFormat config is set to xlf2', () => {
|
||||
env.tsconfig({i18nLegacyMessageIdFormat: 'xlf2'});
|
||||
env.write(`test.ts`, `
|
||||
import {Component} from '@angular/core';
|
||||
@Component({
|
||||
selector: 'test',
|
||||
template: '<div i18n>Some text</div>'
|
||||
})
|
||||
class FooCmp {}`);
|
||||
env.driveMain();
|
||||
const jsContents = env.getContents('test.js');
|
||||
expect(jsContents).toContain(':@@8321000940098097247:Some text');
|
||||
});
|
||||
|
||||
it('should render legacy id when i18nLegacyMessageIdFormat config is set to xmb', () => {
|
||||
env.tsconfig({i18nLegacyMessageIdFormat: 'xmb'});
|
||||
env.write(`test.ts`, `
|
||||
import {Component} from '@angular/core';
|
||||
@Component({
|
||||
selector: 'test',
|
||||
template: '<div i18n>Some text</div>'
|
||||
})
|
||||
class FooCmp {}`);
|
||||
env.driveMain();
|
||||
const jsContents = env.getContents('test.js');
|
||||
expect(jsContents).toContain(':@@8321000940098097247:Some text');
|
||||
});
|
||||
|
||||
it('should render custom id even if i18nLegacyMessageIdFormat config is set', () => {
|
||||
env.tsconfig({i18nLegacyMessageIdFormat: 'xlf'});
|
||||
env.write(`test.ts`, `
|
||||
import {Component} from '@angular/core';
|
||||
@Component({
|
||||
selector: 'test',
|
||||
template: '<div i18n="@@custom">Some text</div>'
|
||||
})
|
||||
class FooCmp {}`);
|
||||
env.driveMain();
|
||||
const jsContents = env.getContents('test.js');
|
||||
expect(jsContents).toContain(':@@custom:Some text');
|
||||
});
|
||||
|
||||
it('@Component\'s `interpolation` should override default interpolation config', () => {
|
||||
env.write(`test.ts`, `
|
||||
import {Component} from '@angular/core';
|
||||
|
Reference in New Issue
Block a user