fix(ivy): support bindings in ICUs in nested contexts (#27914)

Prior to this change, ICU extraction logic was not taking into account nested bindings (that look like this: �0:1�) and only accounted for top level bindings (like this �0�). As a result, ICUs were not parsed and remained as text in the output. Now the extraction logic (regular expressions) take into account the nested bindings format as well.

PR Close #27914
This commit is contained in:
Andrew Kushnir 2019-01-03 11:18:09 -08:00 committed by Kara Erickson
parent 38b4d15227
commit 3f2ebbd7ab
2 changed files with 62 additions and 3 deletions

View File

@ -25,11 +25,11 @@ import {NO_CHANGE} from './tokens';
import {addAllToArray, getNativeByIndex, getNativeByTNode, getTNode, isLContainer, stringify} from './util';
const MARKER = `<EFBFBD>`;
const ICU_BLOCK_REGEX = /^\s*(<28>\d+<EFBFBD>)\s*,\s*(select|plural)\s*,/;
const ICU_BLOCK_REGEX = /^\s*(<28>\d+:?\d*<EFBFBD>)\s*,\s*(select|plural)\s*,/;
const SUBTEMPLATE_REGEXP = /<2F>\/?\*(\d+:\d+)<29>/gi;
const PH_REGEXP = /<2F>(\/?[#*]\d+):?\d*<2A>/gi;
const BINDING_REGEXP = /<2F>(\d+):?\d*<2A>/gi;
const ICU_REGEXP = /({\s*<2A>\d+<EFBFBD>\s*,\s*\S{6}\s*,[\s\S]*})/gi;
const ICU_REGEXP = /({\s*<2A>\d+:?\d*<EFBFBD>\s*,\s*\S{6}\s*,[\s\S]*})/gi;
// i18nPostproocess regexps
const PP_PLACEHOLDERS = /\[(<28>.+?<3F>?)\]/g;

View File

@ -15,7 +15,7 @@ import {getNativeByIndex} from '../../src/render3/util';
import {NgIf} from './common_with_def';
import {element, elementEnd, elementStart, template, text, bind, elementProperty, projectionDef, projection} from '../../src/render3/instructions';
import {element, elementEnd, elementStart, template, text, nextContext, bind, elementProperty, projectionDef, projection} from '../../src/render3/instructions';
import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nUpdateOpCode, I18nUpdateOpCodes, TI18n} from '../../src/render3/interfaces/i18n';
import {HEADER_OFFSET, LView, TVIEW} from '../../src/render3/interfaces/view';
import {ComponentFixture, TemplateFixture} from './render_util';
@ -656,6 +656,65 @@ describe('Runtime i18n', () => {
expect(fixture.html).toEqual('<div><!--ICU 2--></div>');
});
it('for ICU expressions inside templates', () => {
const MSG_DIV = `<EFBFBD>*2:1<><31>#1:1<>{<7B>0:1<>, plural,
=0 {no <b title="none">emails</b>!}
=1 {one <i>email</i>}
other {<EFBFBD>0:1<EFBFBD> <span title="<22>1:1<>">emails</span>}
}<EFBFBD>/#1:1<EFBFBD><EFBFBD>/*2:1<EFBFBD>`;
function subTemplate_1(rf: RenderFlags, ctx: any) {
if (rf & RenderFlags.Create) {
i18nStart(0, MSG_DIV, 1);
element(1, 'span');
i18nEnd();
}
if (rf & RenderFlags.Update) {
const ctx = nextContext();
i18nExp(bind(ctx.value0));
i18nExp(bind(ctx.value1));
i18nApply(0);
}
}
class MyApp {
value0 = 0;
value1 = 'emails label';
static ngComponentDef = defineComponent({
type: MyApp,
selectors: [['my-app']],
directives: [NgIf],
factory: () => new MyApp(),
consts: 3,
vars: 1,
template: (rf: RenderFlags, ctx: MyApp) => {
if (rf & RenderFlags.Create) {
elementStart(0, 'div');
i18nStart(1, MSG_DIV);
template(2, subTemplate_1, 2, 2, 'span', [3, 'ngIf']);
i18nEnd();
elementEnd();
}
if (rf & RenderFlags.Update) {
elementProperty(2, 'ngIf', true);
}
}
});
}
const fixture = new ComponentFixture(MyApp);
expect(fixture.html)
.toEqual('<div><span>no <b title="none">emails</b>!<!--ICU 4--></span></div>');
// Update the value
fixture.component.value0 = 3;
fixture.update();
expect(fixture.html)
.toEqual(
'<div><span>3 <span title="emails label">emails</span><!--ICU 4--></span></div>');
});
it('for nested ICU expressions', () => {
const MSG_DIV = `{<7B>0<EFBFBD>, plural,
=0 {zero}