2016-07-01 17:29:54 -07:00
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
2016-09-27 17:12:25 -07:00
import { describe , expect , it } from '@angular/core/testing/testing_internal' ;
2016-07-01 17:29:54 -07:00
2016-08-05 12:08:43 -07:00
import { digestMessage , serializeNodes as serializeI18nNodes } from '../../src/i18n/digest' ;
2016-08-04 17:46:31 -07:00
import { extractMessages , mergeTranslations } from '../../src/i18n/extractor_merger' ;
import * as i18n from '../../src/i18n/i18n_ast' ;
2016-08-01 18:10:34 -07:00
import { TranslationBundle } from '../../src/i18n/translation_bundle' ;
import * as html from '../../src/ml_parser/ast' ;
2016-08-01 12:19:09 -07:00
import { HtmlParser } from '../../src/ml_parser/html_parser' ;
2016-08-01 18:10:34 -07:00
import { DEFAULT_INTERPOLATION_CONFIG } from '../../src/ml_parser/interpolation_config' ;
2016-08-04 17:46:31 -07:00
import { serializeNodes as serializeHtmlNodes } from '../ml_parser/ast_serializer_spec' ;
2016-07-01 17:29:54 -07:00
export function main() {
2016-08-01 18:10:34 -07:00
describe ( 'Extractor' , () => {
2016-07-21 13:56:58 -07:00
describe ( 'elements' , () => {
it ( 'should extract from elements' , () => {
2016-08-01 11:33:35 -07:00
expect ( extract ( '<div i18n="m|d|e">text<span>nested</span></div>' )). toEqual ([
2016-08-04 17:46:31 -07:00
[[ 'text' , '<ph tag name="START_TAG_SPAN">nested</ph name="CLOSE_TAG_SPAN">' ], 'm' , 'd|e' ],
2016-07-21 13:56:58 -07:00
]);
2016-08-01 13:11:48 -07:00
});
2016-08-04 17:46:31 -07:00
it ( 'should extract from attributes' , () => {
2016-08-01 14:43:20 -07:00
expect (
extract (
2016-08-04 17:46:31 -07:00
'<div i18n="m1|d1"><span i18n-title="m2|d2" title="single child">nested</span></div>' ))
2016-08-01 14:43:20 -07:00
. toEqual ([
2016-08-04 17:46:31 -07:00
[[ '<ph tag name="START_TAG_SPAN">nested</ph name="CLOSE_TAG_SPAN">' ], 'm1' , 'd1' ],
[[ 'single child' ], 'm2' , 'd2' ],
2016-08-01 14:43:20 -07:00
]);
});
2016-08-04 17:46:31 -07:00
it ( 'should extract from ICU messages' , () => {
2016-08-01 13:11:48 -07:00
expect (
extract (
'<div i18n="m|d">{count, plural, =0 { <p i18n-title i18n-desc title="title" desc="desc"></p>}}</div>' ))
. toEqual ([
[
[
2016-08-04 17:46:31 -07:00
'{count, plural, =0 {[<ph tag name="START_PARAGRAPH"></ph name="CLOSE_PARAGRAPH">]}}'
2016-08-01 13:11:48 -07:00
],
'm' , 'd'
],
2016-08-04 17:46:31 -07:00
[[ 'title' ], '' , '' ],
[[ 'desc' ], '' , '' ],
2016-08-01 13:11:48 -07:00
]);
2016-07-21 13:56:58 -07:00
});
2016-07-01 17:29:54 -07:00
2016-07-21 13:56:58 -07:00
it ( 'should not create a message for empty elements' ,
() => { expect ( extract ( '<div i18n="m|d"></div>' )). toEqual ([]); });
2016-08-05 14:45:47 -07:00
it ( 'should ignore implicit elements in translatable elements' , () => {
expect ( extract ( '<div i18n="m|d"><p></p></div>' , [ 'p' ])). toEqual ([
[[ '<ph tag name="START_PARAGRAPH"></ph name="CLOSE_PARAGRAPH">' ], 'm' , 'd' ]
]);
});
2016-07-21 13:56:58 -07:00
});
2016-07-01 17:29:54 -07:00
2016-07-21 13:56:58 -07:00
describe ( 'blocks' , () => {
it ( 'should extract from blocks' , () => {
expect ( extract ( `<!-- i18n: meaning1|desc1 -->message1<!-- /i18n -->
2016-08-01 11:33:35 -07:00
<!-- i18n: desc2 -->message2<!-- /i18n -->
2016-07-01 17:29:54 -07:00
<!-- i18n -->message3<!-- /i18n -->` ))
2016-07-21 13:56:58 -07:00
. toEqual ([
[[ 'message1' ], 'meaning1' , 'desc1' ],
2016-08-01 11:33:35 -07:00
[[ 'message2' ], '' , 'desc2' ],
2016-07-21 13:56:58 -07:00
[[ 'message3' ], '' , '' ],
2016-07-01 17:29:54 -07:00
]);
2016-07-21 13:56:58 -07:00
});
2016-07-01 17:29:54 -07:00
2016-08-05 14:45:47 -07:00
it ( 'should ignore implicit elements in blocks' , () => {
expect ( extract ( '<!-- i18n:m|d --><p></p><!-- /i18n -->' , [ 'p' ])). toEqual ([
[[ '<ph tag name="START_PARAGRAPH"></ph name="CLOSE_PARAGRAPH">' ], 'm' , 'd' ]
]);
});
2016-07-21 13:56:58 -07:00
it ( 'should extract siblings' , () => {
expect (
extract (
`<!-- i18n -->text<p>html<b>nested</b></p>{count, plural, =0 {<span>html</span>}}{{interp}}<!-- /i18n -->` ))
. toEqual ([
[
[
2016-08-04 17:46:31 -07:00
'{count, plural, =0 {[<ph tag name="START_TAG_SPAN">html</ph name="CLOSE_TAG_SPAN">]}}'
],
'' , ''
],
[
[
'text' ,
'<ph tag name="START_PARAGRAPH">html, <ph tag name="START_BOLD_TEXT">nested</ph name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">' ,
'<ph icu name="ICU">{count, plural, =0 {[<ph tag name="START_TAG_SPAN">html</ph name="CLOSE_TAG_SPAN">]}}</ph>' ,
'[<ph name="INTERPOLATION">interp</ph>]'
2016-07-21 13:56:58 -07:00
],
'' , ''
],
]);
});
it ( 'should ignore other comments' , () => {
expect ( extract ( `<!-- i18n: meaning1|desc1 --><!-- other -->message1<!-- /i18n -->` ))
. toEqual ([
[[ 'message1' ], 'meaning1' , 'desc1' ],
]);
});
it ( 'should not create a message for empty blocks' ,
() => { expect ( extract ( `<!-- i18n: meaning1|desc1 --><!-- /i18n -->` )). toEqual ([]); });
});
describe ( 'ICU messages' , () => {
it ( 'should extract ICU messages from translatable elements' , () => {
// single message when ICU is the only children
expect ( extract ( '<div i18n="m|d">{count, plural, =0 {text}}</div>' )). toEqual ([
2016-08-04 17:46:31 -07:00
[[ '{count, plural, =0 {[text]}}' ], 'm' , 'd' ],
2016-07-21 13:56:58 -07:00
]);
2016-08-01 14:43:20 -07:00
// single message when ICU is the only (implicit) children
expect ( extract ( '<div>{count, plural, =0 {text}}</div>' , [ 'div' ])). toEqual ([
2016-08-04 17:46:31 -07:00
[[ '{count, plural, =0 {[text]}}' ], '' , '' ],
2016-08-01 14:43:20 -07:00
]);
2016-07-21 13:56:58 -07:00
// one message for the element content and one message for the ICU
expect ( extract ( '<div i18n="m|d">before{count, plural, =0 {text}}after</div>' )). toEqual ([
2016-08-04 17:46:31 -07:00
[[ 'before' , '<ph icu name="ICU">{count, plural, =0 {[text]}}</ph>' , 'after' ], 'm' , 'd' ],
[[ '{count, plural, =0 {[text]}}' ], '' , '' ],
2016-07-21 13:56:58 -07:00
]);
});
it ( 'should extract ICU messages from translatable block' , () => {
// single message when ICU is the only children
expect ( extract ( '<!-- i18n:m|d -->{count, plural, =0 {text}}<!-- /i18n -->' )). toEqual ([
2016-08-04 17:46:31 -07:00
[[ '{count, plural, =0 {[text]}}' ], 'm' , 'd' ],
2016-07-21 13:56:58 -07:00
]);
// one message for the block content and one message for the ICU
expect ( extract ( '<!-- i18n:m|d -->before{count, plural, =0 {text}}after<!-- /i18n -->' ))
. toEqual ([
2016-08-04 17:46:31 -07:00
[[ '{count, plural, =0 {[text]}}' ], '' , '' ],
[
[ 'before' , '<ph icu name="ICU">{count, plural, =0 {[text]}}</ph>' , 'after' ], 'm' ,
'd'
],
2016-07-01 17:29:54 -07:00
]);
2016-07-21 13:56:58 -07:00
});
2016-07-01 17:29:54 -07:00
2016-07-21 13:56:58 -07:00
it ( 'should not extract ICU messages outside of i18n sections' ,
() => { expect ( extract ( '{count, plural, =0 {text}}' )). toEqual ([]); });
2016-08-05 14:45:47 -07:00
it ( 'should ignore nested ICU messages' , () => {
2016-07-21 13:56:58 -07:00
expect ( extract ( '<div i18n="m|d">{count, plural, =0 { {sex, gender, =m {m}} }}</div>' ))
. toEqual ([
2016-08-04 17:46:31 -07:00
[[ '{count, plural, =0 {[{sex, gender, =m {[m]}}, ]}}' ], 'm' , 'd' ],
2016-07-01 17:29:54 -07:00
]);
2016-07-21 13:56:58 -07:00
});
2016-08-05 14:45:47 -07:00
it ( 'should ignore implicit elements in non translatable ICU messages' , () => {
expect (
extract (
'<div i18n="m|d">{count, plural, =0 { {sex, gender, =m {<p>ignore</p>}} }}</div>' ,
[ 'p' ]))
. toEqual ([[
[
'{count, plural, =0 {[{sex, gender, =m {[<ph tag name="START_PARAGRAPH">ignore</ph name="CLOSE_PARAGRAPH">]}}, ]}}'
],
'm' , 'd'
]]);
});
it ( 'should ignore implicit elements in non translatable ICU messages' , () => {
expect ( extract ( '{count, plural, =0 { {sex, gender, =m {<p>ignore</p>}} }}' , [ 'p' ]))
. toEqual ([]);
});
2016-07-21 13:56:58 -07:00
});
2016-07-01 17:29:54 -07:00
2016-07-21 13:56:58 -07:00
describe ( 'attributes' , () => {
2016-08-01 18:10:34 -07:00
it ( 'should extract from attributes outside of translatable sections' , () => {
2016-07-21 13:56:58 -07:00
expect ( extract ( '<div i18n-title="m|d" title="msg"></div>' )). toEqual ([
2016-08-04 17:46:31 -07:00
[[ 'msg' ], 'm' , 'd' ],
2016-07-21 13:56:58 -07:00
]);
});
2016-07-01 17:29:54 -07:00
2016-08-01 18:10:34 -07:00
it ( 'should extract from attributes in translatable elements' , () => {
2016-07-21 13:56:58 -07:00
expect ( extract ( '<div i18n><p><b i18n-title="m|d" title="msg"></b></p></div>' )). toEqual ([
2016-08-04 17:46:31 -07:00
[
[
'<ph tag name="START_PARAGRAPH"><ph tag name="START_BOLD_TEXT"></ph name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">'
],
'' , ''
],
[[ 'msg' ], 'm' , 'd' ],
2016-07-21 13:56:58 -07:00
]);
});
2016-07-01 17:29:54 -07:00
2016-08-01 18:10:34 -07:00
it ( 'should extract from attributes in translatable blocks' , () => {
2016-07-21 13:56:58 -07:00
expect ( extract ( '<!-- i18n --><p><b i18n-title="m|d" title="msg"></b></p><!-- /i18n -->' ))
. toEqual ([
2016-08-04 17:46:31 -07:00
[[ 'msg' ], 'm' , 'd' ],
[
[
'<ph tag name="START_PARAGRAPH"><ph tag name="START_BOLD_TEXT"></ph name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">'
],
'' , ''
],
2016-07-21 13:56:58 -07:00
]);
});
2016-08-01 18:10:34 -07:00
it ( 'should extract from attributes in translatable ICUs' , () => {
2016-07-21 13:56:58 -07:00
expect (
extract (
'<!-- i18n -->{count, plural, =0 {<p><b i18n-title="m|d" title="msg"></b></p>}}<!-- /i18n -->' ))
. toEqual ([
2016-08-04 17:46:31 -07:00
[[ 'msg' ], 'm' , 'd' ],
[
[
'{count, plural, =0 {[<ph tag name="START_PARAGRAPH"><ph tag name="START_BOLD_TEXT"></ph name="CLOSE_BOLD_TEXT"></ph name="CLOSE_PARAGRAPH">]}}'
],
'' , ''
],
2016-07-21 13:56:58 -07:00
]);
});
2016-08-01 18:10:34 -07:00
it ( 'should extract from attributes in non translatable ICUs' , () => {
2016-07-21 13:56:58 -07:00
expect ( extract ( '{count, plural, =0 {<p><b i18n-title="m|d" title="msg"></b></p>}}' ))
. toEqual ([
2016-08-04 17:46:31 -07:00
[[ 'msg' ], 'm' , 'd' ],
2016-07-01 17:29:54 -07:00
]);
2016-07-21 13:56:58 -07:00
});
2016-07-01 17:29:54 -07:00
2016-07-21 13:56:58 -07:00
it ( 'should not create a message for empty attributes' ,
() => { expect ( extract ( '<div i18n-title="m|d" title></div>' )). toEqual ([]); });
});
2016-07-01 17:29:54 -07:00
2016-07-21 13:56:58 -07:00
describe ( 'implicit elements' , () => {
it ( 'should extract from implicit elements' , () => {
expect ( extract ( '<b>bold</b><i>italic</i>' , [ 'b' ])). toEqual ([
[[ 'bold' ], '' , '' ],
]);
});
2016-08-05 14:14:31 -07:00
it ( 'should allow nested implicit elements' , () => {
let result : any [];
2016-08-08 12:44:48 -07:00
expect (() => {
result = extract ( '<div>outer<div>inner</div></div>' , [ 'div' ]);
}). not . toThrow ();
2016-08-05 14:14:31 -07:00
expect ( result ). toEqual ([
[[ 'outer' , '<ph tag name="START_TAG_DIV">inner</ph name="CLOSE_TAG_DIV">' ], '' , '' ],
]);
});
2016-07-21 13:56:58 -07:00
});
2016-07-01 17:29:54 -07:00
2016-07-21 13:56:58 -07:00
describe ( 'implicit attributes' , () => {
it ( 'should extract implicit attributes' , () => {
expect ( extract ( '<b title="bb">bold</b><i title="ii">italic</i>' , [], { 'b' : [ 'title' ]}))
. toEqual ([
2016-08-04 17:46:31 -07:00
[[ 'bb' ], '' , '' ],
2016-07-01 17:29:54 -07:00
]);
2016-07-21 13:56:58 -07:00
});
});
describe ( 'errors' , () => {
describe ( 'elements' , () => {
it ( 'should report nested translatable elements' , () => {
expect ( extractErrors ( `<p i18n><b i18n></b></p>` )). toEqual ([
[ 'Could not mark an element as translatable inside a translatable section' , '<b i18n>' ],
]);
2016-07-01 17:29:54 -07:00
});
2016-07-21 13:56:58 -07:00
it ( 'should report translatable elements in implicit elements' , () => {
expect ( extractErrors ( `<p><b i18n></b></p>` , [ 'p' ])). toEqual ([
[ 'Could not mark an element as translatable inside a translatable section' , '<b i18n>' ],
]);
2016-07-01 17:29:54 -07:00
});
2016-07-21 13:56:58 -07:00
it ( 'should report translatable elements in translatable blocks' , () => {
expect ( extractErrors ( `<!-- i18n --><b i18n></b><!-- /i18n -->` )). toEqual ([
[ 'Could not mark an element as translatable inside a translatable section' , '<b i18n>' ],
]);
2016-07-01 17:29:54 -07:00
});
});
2016-07-21 13:56:58 -07:00
describe ( 'blocks' , () => {
it ( 'should report nested blocks' , () => {
expect ( extractErrors ( `<!-- i18n --><!-- i18n --><!-- /i18n --><!-- /i18n -->` )). toEqual ([
[ 'Could not start a block inside a translatable section' , '<!--' ],
[ 'Trying to close an unopened block' , '<!--' ],
]);
});
it ( 'should report unclosed blocks' , () => {
expect ( extractErrors ( `<!-- i18n -->` )). toEqual ([
[ 'Unclosed block' , '<!--' ],
]);
});
it ( 'should report translatable blocks in translatable elements' , () => {
expect ( extractErrors ( `<p i18n><!-- i18n --><!-- /i18n --></p>` )). toEqual ([
[ 'Could not start a block inside a translatable section' , '<!--' ],
[ 'Trying to close an unopened block' , '<!--' ],
]);
});
it ( 'should report translatable blocks in implicit elements' , () => {
expect ( extractErrors ( `<p><!-- i18n --><!-- /i18n --></p>` , [ 'p' ])). toEqual ([
[ 'Could not start a block inside a translatable section' , '<!--' ],
[ 'Trying to close an unopened block' , '<!--' ],
]);
});
it ( 'should report when start and end of a block are not at the same level' , () => {
expect ( extractErrors ( `<!-- i18n --><p><!-- /i18n --></p>` )). toEqual ([
[ 'I18N blocks should not cross element boundaries' , '<!--' ],
[ 'Unclosed block' , '<p>' ],
]);
expect ( extractErrors ( `<p><!-- i18n --></p><!-- /i18n -->` )). toEqual ([
[ 'I18N blocks should not cross element boundaries' , '<!--' ],
[ 'Unclosed block' , '<!--' ],
]);
});
});
});
});
2016-08-01 18:10:34 -07:00
describe ( 'Merger' , () => {
describe ( 'elements' , () => {
it ( 'should merge elements' , () => {
const HTML = `<p i18n="m|d">foo</p>` ;
2016-08-04 17:46:31 -07:00
expect ( fakeTranslate ( HTML )). toEqual ( '<p>**foo**</p>' );
2016-08-01 18:10:34 -07:00
});
it ( 'should merge nested elements' , () => {
const HTML = `<div>before<p i18n="m|d">foo</p><!-- comment --></div>` ;
2016-08-04 17:46:31 -07:00
expect ( fakeTranslate ( HTML )). toEqual ( '<div>before<p>**foo**</p></div>' );
2016-08-01 18:10:34 -07:00
});
});
describe ( 'blocks' , () => {
it ( 'should merge blocks' , () => {
const HTML = `before<!-- i18n --><p>foo</p><span><i>bar</i></span><!-- /i18n -->after` ;
2016-08-04 17:46:31 -07:00
expect ( fakeTranslate ( HTML ))
. toEqual (
'before**<ph tag name="START_PARAGRAPH">foo</ph name="CLOSE_PARAGRAPH"><ph tag name="START_TAG_SPAN"><ph tag name="START_ITALIC_TEXT">bar</ph name="CLOSE_ITALIC_TEXT"></ph name="CLOSE_TAG_SPAN">**after' );
2016-08-01 18:10:34 -07:00
});
it ( 'should merge nested blocks' , () => {
const HTML =
`<div>before<!-- i18n --><p>foo</p><span><i>bar</i></span><!-- /i18n -->after</div>` ;
expect ( fakeTranslate ( HTML ))
2016-08-04 17:46:31 -07:00
. toEqual (
'<div>before**<ph tag name="START_PARAGRAPH">foo</ph name="CLOSE_PARAGRAPH"><ph tag name="START_TAG_SPAN"><ph tag name="START_ITALIC_TEXT">bar</ph name="CLOSE_ITALIC_TEXT"></ph name="CLOSE_TAG_SPAN">**after</div>' );
2016-08-01 18:10:34 -07:00
});
});
describe ( 'attributes' , () => {
it ( 'should merge attributes' , () => {
const HTML = `<p i18n-title="m|d" title="foo"></p>` ;
2016-08-04 17:46:31 -07:00
expect ( fakeTranslate ( HTML )). toEqual ( '<p title="**foo**"></p>' );
2016-08-01 18:10:34 -07:00
});
2016-08-11 21:00:35 -07:00
it ( 'should merge nested attributes' , () => {
2016-08-01 18:10:34 -07:00
const HTML = `<div>{count, plural, =0 {<p i18n-title title="foo"></p>}}</div>` ;
2016-08-04 17:46:31 -07:00
expect ( fakeTranslate ( HTML ))
. toEqual ( '<div>{count, plural, =0 {<p title="**foo**"></p>}}</div>' );
2016-08-01 18:10:34 -07:00
});
2016-08-11 21:00:35 -07:00
it ( 'should merge attributes without values' , () => {
const HTML = `<p i18n-title="m|d" title=""></p>` ;
expect ( fakeTranslate ( HTML )). toEqual ( '<p title=""></p>' );
});
2016-08-01 18:10:34 -07:00
});
});
2016-07-01 17:29:54 -07:00
}
2016-07-08 16:46:49 -07:00
2016-08-01 18:10:34 -07:00
function parseHtml ( html : string ) : html . Node [] {
2016-07-08 16:46:49 -07:00
const htmlParser = new HtmlParser ();
const parseResult = htmlParser . parse ( html , 'extractor spec' , true );
if ( parseResult . errors . length > 1 ) {
2016-08-05 14:14:31 -07:00
throw new Error ( `unexpected parse errors: ${ parseResult . errors . join ( '\n' ) } ` );
2016-07-08 16:46:49 -07:00
}
2016-08-01 18:10:34 -07:00
return parseResult . rootNodes ;
}
2016-07-08 16:46:49 -07:00
2016-08-01 18:10:34 -07:00
function fakeTranslate (
content : string , implicitTags : string [] = [],
implicitAttrs : {[ k : string ] : string []} = {}) : string {
const htmlNodes : html.Node [] = parseHtml ( content );
2016-08-04 17:46:31 -07:00
const messages : i18n.Message [] =
extractMessages ( htmlNodes , DEFAULT_INTERPOLATION_CONFIG , implicitTags , implicitAttrs )
. messages ;
2016-08-01 18:10:34 -07:00
const i18nMsgMap : {[ id : string ] : html . Node []} = {};
2016-08-05 11:53:37 -07:00
messages . forEach ( message => {
const id = digestMessage ( message );
const text = serializeI18nNodes ( message . nodes ). join ( '' );
2016-08-04 17:46:31 -07:00
i18nMsgMap [ id ] = [ new html . Text ( `** ${ text } **` , null )];
2016-08-01 18:10:34 -07:00
});
const translations = new TranslationBundle ( i18nMsgMap );
2016-08-11 15:31:36 -07:00
const translatedNodes =
mergeTranslations (
htmlNodes , translations , DEFAULT_INTERPOLATION_CONFIG , implicitTags , implicitAttrs )
. rootNodes ;
2016-08-01 18:10:34 -07:00
2016-08-11 15:31:36 -07:00
return serializeHtmlNodes ( translatedNodes ). join ( '' );
2016-07-08 16:46:49 -07:00
}
function extract (
2016-07-21 13:56:58 -07:00
html : string , implicitTags : string [] = [],
implicitAttrs : {[ k : string ] : string []} = {}) : [ string [], string , string ][] {
2016-08-05 14:14:31 -07:00
const result =
extractMessages ( parseHtml ( html ), DEFAULT_INTERPOLATION_CONFIG , implicitTags , implicitAttrs );
if ( result . errors . length > 0 ) {
throw new Error ( `unexpected errors: ${ result . errors . join ( '\n' ) } ` );
}
2016-07-08 16:46:49 -07:00
// clang-format off
// https://github.com/angular/clang-format/issues/35
2016-08-05 14:14:31 -07:00
return result . messages . map (
2016-08-04 17:46:31 -07:00
message => [ serializeI18nNodes ( message . nodes ), message . meaning , message . description , ]) as [ string [], string , string ][];
2016-07-08 16:46:49 -07:00
// clang-format on
}
function extractErrors (
2016-07-21 13:56:58 -07:00
html : string , implicitTags : string [] = [], implicitAttrs : {[ k : string ] : string []} = {}) : any [] {
2016-08-04 17:46:31 -07:00
const errors =
extractMessages ( parseHtml ( html ), DEFAULT_INTERPOLATION_CONFIG , implicitTags , implicitAttrs )
. errors ;
2016-07-08 16:46:49 -07:00
return errors . map (( e ) : [ string , string ] => [ e . msg , e . span . toString ()]);
}