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:
parent
38b4d15227
commit
3f2ebbd7ab
|
@ -25,11 +25,11 @@ import {NO_CHANGE} from './tokens';
|
||||||
import {addAllToArray, getNativeByIndex, getNativeByTNode, getTNode, isLContainer, stringify} from './util';
|
import {addAllToArray, getNativeByIndex, getNativeByTNode, getTNode, isLContainer, stringify} from './util';
|
||||||
|
|
||||||
const MARKER = `<EFBFBD>`;
|
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 SUBTEMPLATE_REGEXP = /<2F>\/?\*(\d+:\d+)<29>/gi;
|
||||||
const PH_REGEXP = /<2F>(\/?[#*]\d+):?\d*<2A>/gi;
|
const PH_REGEXP = /<2F>(\/?[#*]\d+):?\d*<2A>/gi;
|
||||||
const BINDING_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
|
// i18nPostproocess regexps
|
||||||
const PP_PLACEHOLDERS = /\[(<28>.+?<3F>?)\]/g;
|
const PP_PLACEHOLDERS = /\[(<28>.+?<3F>?)\]/g;
|
||||||
|
|
|
@ -15,7 +15,7 @@ import {getNativeByIndex} from '../../src/render3/util';
|
||||||
|
|
||||||
import {NgIf} from './common_with_def';
|
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 {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nUpdateOpCode, I18nUpdateOpCodes, TI18n} from '../../src/render3/interfaces/i18n';
|
||||||
import {HEADER_OFFSET, LView, TVIEW} from '../../src/render3/interfaces/view';
|
import {HEADER_OFFSET, LView, TVIEW} from '../../src/render3/interfaces/view';
|
||||||
import {ComponentFixture, TemplateFixture} from './render_util';
|
import {ComponentFixture, TemplateFixture} from './render_util';
|
||||||
|
@ -656,6 +656,65 @@ describe('Runtime i18n', () => {
|
||||||
expect(fixture.html).toEqual('<div><!--ICU 2--></div>');
|
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', () => {
|
it('for nested ICU expressions', () => {
|
||||||
const MSG_DIV = `{<7B>0<EFBFBD>, plural,
|
const MSG_DIV = `{<7B>0<EFBFBD>, plural,
|
||||||
=0 {zero}
|
=0 {zero}
|
||||||
|
|
Loading…
Reference in New Issue