fix(compiler): ensure that i18n message-parts have the correct source-span (#39486)

In an i18n message, two placeholders next to each other must have
an "empty" message-part to separate them. Previously, the source-span
for this message-part was pointing to the wrong original location.
This caused problems in the generated source-maps and lead to extracted
i18n messages from being rendered incorrectly.

PR Close #39486
This commit is contained in:
Pete Bacon Darwin 2020-10-28 21:01:31 +00:00 committed by Misko Hevery
parent 27358eb60f
commit 3f4fe45277
2 changed files with 26 additions and 4 deletions

View File

@ -120,7 +120,7 @@ function processMessagePieces(pieces: o.MessagePiece[]):
placeHolders.push(part); placeHolders.push(part);
if (pieces[i - 1] instanceof o.PlaceholderPiece) { if (pieces[i - 1] instanceof o.PlaceholderPiece) {
// There were two placeholders in a row, so we need to add an empty message part. // There were two placeholders in a row, so we need to add an empty message part.
messageParts.push(createEmptyMessagePart(part.sourceSpan.end)); messageParts.push(createEmptyMessagePart(pieces[i - 1].sourceSpan.end));
} }
} }
} }

View File

@ -489,6 +489,26 @@ describe('serializeI18nMessageForLocalize', () => {
expect(placeHolders[3].sourceSpan.toString()).toEqual('</span>'); expect(placeHolders[3].sourceSpan.toString()).toEqual('</span>');
}); });
it('should create the correct source-spans when there are two placeholders next to each other',
() => {
const {messageParts, placeHolders} = serialize('<b>{{value}}</b>');
expect(messageParts[0].text).toEqual('');
expect(humanizeSourceSpan(messageParts[0].sourceSpan)).toEqual('"" (10-10)');
expect(messageParts[1].text).toEqual('');
expect(humanizeSourceSpan(messageParts[1].sourceSpan)).toEqual('"" (13-13)');
expect(messageParts[2].text).toEqual('');
expect(humanizeSourceSpan(messageParts[2].sourceSpan)).toEqual('"" (22-22)');
expect(messageParts[3].text).toEqual('');
expect(humanizeSourceSpan(messageParts[3].sourceSpan)).toEqual('"" (26-26)');
expect(placeHolders[0].text).toEqual('START_BOLD_TEXT');
expect(humanizeSourceSpan(placeHolders[0].sourceSpan)).toEqual('"<b>" (10-13)');
expect(placeHolders[1].text).toEqual('INTERPOLATION');
expect(humanizeSourceSpan(placeHolders[1].sourceSpan)).toEqual('"{{value}}" (13-22)');
expect(placeHolders[2].text).toEqual('CLOSE_BOLD_TEXT');
expect(humanizeSourceSpan(placeHolders[2].sourceSpan)).toEqual('"</b>" (22-26)');
});
it('should serialize simple ICU for `$localize()`', () => { it('should serialize simple ICU for `$localize()`', () => {
expect(serialize('{age, plural, 10 {ten} other {other}}')).toEqual({ expect(serialize('{age, plural, 10 {ten} other {other}}')).toEqual({
messageParts: [literal('{VAR_PLURAL, plural, 10 {ten} other {other}}')], messageParts: [literal('{VAR_PLURAL, plural, 10 {ten} other {other}}')],
@ -496,7 +516,6 @@ describe('serializeI18nMessageForLocalize', () => {
}); });
}); });
it('should serialize nested ICUs for `$localize()`', () => { it('should serialize nested ICUs for `$localize()`', () => {
expect(serialize( expect(serialize(
'{age, plural, 10 {ten {size, select, 1 {one} 2 {two} other {2+}}} other {other}}')) '{age, plural, 10 {ten {size, select, 1 {one} 2 {two} other {2+}}} other {other}}'))
@ -509,7 +528,6 @@ describe('serializeI18nMessageForLocalize', () => {
}); });
}); });
it('should serialize ICU with embedded HTML for `$localize()`', () => { it('should serialize ICU with embedded HTML for `$localize()`', () => {
expect(serialize('{age, plural, 10 {<b>ten</b>} other {<div class="A">other</div>}}')).toEqual({ expect(serialize('{age, plural, 10 {<b>ten</b>} other {<div class="A">other</div>}}')).toEqual({
messageParts: [ messageParts: [
@ -595,3 +613,7 @@ function literal(text: string, span: any = jasmine.any(ParseSourceSpan)): o.Lite
function placeholder(name: string, span: any = jasmine.any(ParseSourceSpan)): o.PlaceholderPiece { function placeholder(name: string, span: any = jasmine.any(ParseSourceSpan)): o.PlaceholderPiece {
return new o.PlaceholderPiece(name, span); return new o.PlaceholderPiece(name, span);
} }
function humanizeSourceSpan(span: ParseSourceSpan): string {
return `"${span.toString()}" (${span.start.offset}-${span.end.offset})`;
}