fix(ivy): sanitize external i18n ids before generating const names (#28522)

Prior to this change there was no i18n id sanitization before we output goog.getMsg calls. Due to the fact that message ids are used as a part of const names, some characters were bcausing issues while executing generated code. This commit adds sanitization to i18n ids used to generate i18n-related consts.

PR Close #28522
This commit is contained in:
Andrew Kushnir 2019-01-11 17:23:51 -08:00 committed by Matias Niemelä
parent 7c5c1fae62
commit 3f73dfa151
2 changed files with 39 additions and 16 deletions

View File

@ -129,23 +129,25 @@ const verify = (input: string, output: string, extra: any = {}): void => {
({i18nUseExternalIds, ...(extra.compilerOptions || {})});
// invoke with file-based prefix translation names
let result = compile(files, angularFiles, opts(false));
maybePrint(result.source, extra.verbose);
expect(verifyPlaceholdersIntegrity(result.source)).toBe(true);
expectEmit(result.source, output, 'Incorrect template');
if (extra.skipIdBasedCheck) return;
if (!extra.skipPathBasedCheck) {
const result = compile(files, angularFiles, opts(false));
maybePrint(result.source, extra.verbose);
expect(verifyPlaceholdersIntegrity(result.source)).toBe(true);
expectEmit(result.source, output, 'Incorrect template');
}
// invoke with translation names based on external ids
result = compile(files, angularFiles, opts(true));
maybePrint(result.source, extra.verbose);
const interpolationConfig = extra.inputArgs && extra.inputArgs.interpolation ?
InterpolationConfig.fromArray(extra.inputArgs.interpolation) :
undefined;
expect(verifyTranslationIds(input, result.source, extra.exceptions, interpolationConfig))
.toBe(true);
expect(verifyPlaceholdersIntegrity(result.source)).toBe(true);
expectEmit(result.source, output, 'Incorrect template');
if (!extra.skipIdBasedCheck) {
const result = compile(files, angularFiles, opts(true));
maybePrint(result.source, extra.verbose);
const interpolationConfig = extra.inputArgs && extra.inputArgs.interpolation ?
InterpolationConfig.fromArray(extra.inputArgs.interpolation) :
undefined;
expect(verifyTranslationIds(input, result.source, extra.exceptions, interpolationConfig))
.toBe(true);
expect(verifyPlaceholdersIntegrity(result.source)).toBe(true);
expectEmit(result.source, output, 'Incorrect template');
}
};
describe('i18n support in the view compiler', () => {
@ -589,6 +591,27 @@ describe('i18n support in the view compiler', () => {
verify(input, output);
});
it('should sanitize ids and generate proper const names', () => {
const input = `
<div i18n="@@ID.WITH.INVALID.CHARS.2" i18n-title="@@ID.WITH.INVALID.CHARS" title="Element title">
Some content
</div>
`;
const output = String.raw `
const MSG_EXTERNAL_ID_WITH_INVALID_CHARS$$APP_SPEC_TS_0 = goog.getMsg("Element title");
const $_c1$ = ["title", MSG_EXTERNAL_ID_WITH_INVALID_CHARS$$APP_SPEC_TS_0];
const MSG_EXTERNAL_ID_WITH_INVALID_CHARS_2$$APP_SPEC_TS_2 = goog.getMsg(" Some content ");
`;
const exceptions = {
'ID.WITH.INVALID.CHARS': 'Verify const name generation only',
'ID.WITH.INVALID.CHARS.2': 'Verify const name generation only'
};
verify(input, output, {exceptions, skipPathBasedCheck: true});
});
});
describe('nested nodes', () => {

View File

@ -362,7 +362,7 @@ export class TemplateDefinitionBuilder implements t.Visitor<void>, LocalResolver
if (this.i18nUseExternalIds) {
const prefix = getTranslationConstPrefix(`EXTERNAL_`);
const uniqueSuffix = this.constantPool.uniqueName(suffix);
name = `${prefix}${messageId}$$${uniqueSuffix}`;
name = `${prefix}${sanitizeIdentifier(messageId)}$$${uniqueSuffix}`;
} else {
const prefix = getTranslationConstPrefix(suffix);
name = this.constantPool.uniqueName(prefix);