@@ -17,19 +17,19 @@ import {Message, id} from './message';
1717import {
1818 messageFromAttribute ,
1919 I18nError ,
20- isI18nAttr ,
20+ I18N_ATTR_PREFIX ,
21+ I18N_ATTR ,
2122 partition ,
2223 Part ,
2324 stringifyNodes ,
2425 meaning
2526} from './shared' ;
2627
27- const I18N_ATTR = "i18n" ;
28- const PLACEHOLDER_ELEMENT = "ph" ;
29- const NAME_ATTR = "name" ;
30- const I18N_ATTR_PREFIX = "i18n-" ;
31- let PLACEHOLDER_REGEXP = RegExpWrapper . create ( `\\<ph(\\s)+name=("(\\d)+")\\/\\>` ) ;
32- let PLACEHOLDER_EXPANDED_REGEXP = RegExpWrapper . create ( `\\<ph(\\s)+name=("(\\d)+")\\>\\<\\/ph\\>` ) ;
28+ const _I18N_ATTR = "i18n" ;
29+ const _PLACEHOLDER_ELEMENT = "ph" ;
30+ const _NAME_ATTR = "name" ;
31+ const _I18N_ATTR_PREFIX = "i18n-" ;
32+ let _PLACEHOLDER_EXPANDED_REGEXP = RegExpWrapper . create ( `\\<ph(\\s)+name=("(\\d)+")\\>\\<\\/ph\\>` ) ;
3333
3434/**
3535 * Creates an i18n-ed version of the parsed template.
@@ -94,7 +94,7 @@ let PLACEHOLDER_EXPANDED_REGEXP = RegExpWrapper.create(`\\<ph(\\s)+name=("(\\d)+
9494 * This is how the merging works:
9595 *
9696 * 1. Use the stringify function to get the message id. Look up the message in the map.
97- * 2. Parse the translated message. At this point we have two trees: the original tree
97+ * 2. Get the translated message. At this point we have two trees: the original tree
9898 * and the translated tree, where all the elements are replaced with placeholders.
9999 * 3. Use the original tree to create a mapping Index:number -> HtmlAst.
100100 * 4. Walk the translated tree.
@@ -115,7 +115,7 @@ export class I18nHtmlParser implements HtmlParser {
115115 errors : ParseError [ ] ;
116116
117117 constructor ( private _htmlParser : HtmlParser , private _parser : Parser ,
118- private _messages : { [ key : string ] : string } ) { }
118+ private _messagesContent : string , private _messages : { [ key : string ] : HtmlAst [ ] } ) { }
119119
120120 parse ( sourceContent : string , sourceUrl : string ) : HtmlParseTreeResult {
121121 this . errors = [ ] ;
@@ -149,17 +149,8 @@ export class I18nHtmlParser implements HtmlParser {
149149 throw new I18nError ( p . sourceSpan , `Cannot find message for id '${ messageId } '` ) ;
150150 }
151151
152- // get the message and expand a placeholder so <ph/> becomes <ph></ph>
153- // we need to do it cause we use HtmlParser to parse the message
154- let message = _expandPlaceholder ( this . _messages [ messageId ] ) ;
155- let parsedMessage = this . _htmlParser . parse ( message , "source" ) ;
156-
157- if ( parsedMessage . errors . length > 0 ) {
158- this . errors = this . errors . concat ( parsedMessage . errors ) ;
159- return [ ] ;
160- } else {
161- return this . _mergeTrees ( p , message , parsedMessage . rootNodes , p . children ) ;
162- }
152+ let parsedMessage = this . _messages [ messageId ] ;
153+ return this . _mergeTrees ( p , parsedMessage , p . children ) ;
163154 }
164155
165156 private _recurseIntoI18nPart ( p : Part ) : HtmlAst [ ] {
@@ -189,14 +180,13 @@ export class I18nHtmlParser implements HtmlParser {
189180 return ListWrapper . flatten ( ps . map ( p => this . _processI18nPart ( p ) ) ) ;
190181 }
191182
192- private _mergeTrees ( p : Part , translatedSource : string , translated : HtmlAst [ ] ,
193- original : HtmlAst [ ] ) : HtmlAst [ ] {
183+ private _mergeTrees ( p : Part , translated : HtmlAst [ ] , original : HtmlAst [ ] ) : HtmlAst [ ] {
194184 let l = new _CreateNodeMapping ( ) ;
195185 htmlVisitAll ( l , original ) ;
196186
197187 // merge the translated tree with the original tree.
198188 // we do it by preserving the source code position of the original tree
199- let merged = this . _mergeTreesHelper ( translatedSource , translated , l . mapping ) ;
189+ let merged = this . _mergeTreesHelper ( translated , l . mapping ) ;
200190
201191 // if the root element is present, we need to create a new root element with its attributes
202192 // translated
@@ -217,11 +207,10 @@ export class I18nHtmlParser implements HtmlParser {
217207 }
218208 }
219209
220- private _mergeTreesHelper ( translatedSource : string , translated : HtmlAst [ ] ,
221- mapping : HtmlAst [ ] ) : HtmlAst [ ] {
210+ private _mergeTreesHelper ( translated : HtmlAst [ ] , mapping : HtmlAst [ ] ) : HtmlAst [ ] {
222211 return translated . map ( t => {
223212 if ( t instanceof HtmlElementAst ) {
224- return this . _mergeElementOrInterpolation ( t , translatedSource , translated , mapping ) ;
213+ return this . _mergeElementOrInterpolation ( t , translated , mapping ) ;
225214
226215 } else if ( t instanceof HtmlTextAst ) {
227216 return t ;
@@ -232,52 +221,51 @@ export class I18nHtmlParser implements HtmlParser {
232221 } ) ;
233222 }
234223
235- private _mergeElementOrInterpolation ( t : HtmlElementAst , translatedSource : string ,
236- translated : HtmlAst [ ] , mapping : HtmlAst [ ] ) : HtmlAst {
224+ private _mergeElementOrInterpolation ( t : HtmlElementAst , translated : HtmlAst [ ] ,
225+ mapping : HtmlAst [ ] ) : HtmlAst {
237226 let name = this . _getName ( t ) ;
238227 let type = name [ 0 ] ;
239228 let index = NumberWrapper . parseInt ( name . substring ( 1 ) , 10 ) ;
240229 let originalNode = mapping [ index ] ;
241230
242231 if ( type == "t" ) {
243- return this . _mergeTextInterpolation ( t , < HtmlTextAst > originalNode , translatedSource ) ;
232+ return this . _mergeTextInterpolation ( t , < HtmlTextAst > originalNode ) ;
244233 } else if ( type == "e" ) {
245- return this . _mergeElement ( t , < HtmlElementAst > originalNode , mapping , translatedSource ) ;
234+ return this . _mergeElement ( t , < HtmlElementAst > originalNode , mapping ) ;
246235 } else {
247236 throw new BaseException ( "should not be reached" ) ;
248237 }
249238 }
250239
251240 private _getName ( t : HtmlElementAst ) : string {
252- if ( t . name != PLACEHOLDER_ELEMENT ) {
241+ if ( t . name != _PLACEHOLDER_ELEMENT ) {
253242 throw new I18nError (
254243 t . sourceSpan ,
255- `Unexpected tag "${ t . name } ". Only "${ PLACEHOLDER_ELEMENT } " tags are allowed.` ) ;
244+ `Unexpected tag "${ t . name } ". Only "${ _PLACEHOLDER_ELEMENT } " tags are allowed.` ) ;
256245 }
257- let names = t . attrs . filter ( a => a . name == NAME_ATTR ) ;
246+ let names = t . attrs . filter ( a => a . name == _NAME_ATTR ) ;
258247 if ( names . length == 0 ) {
259- throw new I18nError ( t . sourceSpan , `Missing "${ NAME_ATTR } " attribute.` ) ;
248+ throw new I18nError ( t . sourceSpan , `Missing "${ _NAME_ATTR } " attribute.` ) ;
260249 }
261250 return names [ 0 ] . value ;
262251 }
263252
264- private _mergeTextInterpolation ( t : HtmlElementAst , originalNode : HtmlTextAst ,
265- translatedSource : string ) : HtmlTextAst {
253+ private _mergeTextInterpolation ( t : HtmlElementAst , originalNode : HtmlTextAst ) : HtmlTextAst {
266254 let split =
267255 this . _parser . splitInterpolation ( originalNode . value , originalNode . sourceSpan . toString ( ) ) ;
268256 let exps = isPresent ( split ) ? split . expressions : [ ] ;
269257
270258 let messageSubstring =
271- translatedSource . substring ( t . startSourceSpan . end . offset , t . endSourceSpan . start . offset ) ;
259+ this . _messagesContent . substring ( t . startSourceSpan . end . offset , t . endSourceSpan . start . offset ) ;
272260 let translated =
273261 this . _replacePlaceholdersWithExpressions ( messageSubstring , exps , originalNode . sourceSpan ) ;
274262
275263 return new HtmlTextAst ( translated , originalNode . sourceSpan ) ;
276264 }
277265
278- private _mergeElement ( t : HtmlElementAst , originalNode : HtmlElementAst , mapping : HtmlAst [ ] ,
279- translatedSource : string ) : HtmlElementAst {
280- let children = this . _mergeTreesHelper ( translatedSource , t . children , mapping ) ;
266+ private _mergeElement ( t : HtmlElementAst , originalNode : HtmlElementAst ,
267+ mapping : HtmlAst [ ] ) : HtmlElementAst {
268+ let children = this . _mergeTreesHelper ( t . children , mapping ) ;
281269 return new HtmlElementAst ( originalNode . name , this . _i18nAttributes ( originalNode ) , children ,
282270 originalNode . sourceSpan , originalNode . startSourceSpan ,
283271 originalNode . endSourceSpan ) ;
@@ -286,30 +274,46 @@ export class I18nHtmlParser implements HtmlParser {
286274 private _i18nAttributes ( el : HtmlElementAst ) : HtmlAttrAst [ ] {
287275 let res = [ ] ;
288276 el . attrs . forEach ( attr => {
289- if ( isI18nAttr ( attr . name ) ) {
290- let messageId = id ( messageFromAttribute ( this . _parser , el , attr ) ) ;
291- let expectedName = attr . name . substring ( 5 ) ;
292- let m = el . attrs . filter ( a => a . name == expectedName ) [ 0 ] ;
293-
294- if ( StringMapWrapper . contains ( this . _messages , messageId ) ) {
295- let split = this . _parser . splitInterpolation ( m . value , m . sourceSpan . toString ( ) ) ;
296- let exps = isPresent ( split ) ? split . expressions : [ ] ;
297- let message = this . _replacePlaceholdersWithExpressions (
298- _expandPlaceholder ( this . _messages [ messageId ] ) , exps , m . sourceSpan ) ;
299- res . push ( new HtmlAttrAst ( m . name , message , m . sourceSpan ) ) ;
300-
301- } else {
302- throw new I18nError ( m . sourceSpan , `Cannot find message for id '${ messageId } '` ) ;
303- }
277+ if ( attr . name . startsWith ( I18N_ATTR_PREFIX ) || attr . name == I18N_ATTR ) return ;
278+
279+ let i18ns = el . attrs . filter ( a => a . name == `i18n-${ attr . name } ` ) ;
280+ if ( i18ns . length == 0 ) {
281+ res . push ( attr ) ;
282+ return ;
304283 }
305284
285+ let i18n = i18ns [ 0 ] ;
286+ let messageId = id ( messageFromAttribute ( this . _parser , el , i18n ) ) ;
287+
288+ if ( StringMapWrapper . contains ( this . _messages , messageId ) ) {
289+ let updatedMessage = this . _replaceInterpolationInAttr ( attr , this . _messages [ messageId ] ) ;
290+ res . push ( new HtmlAttrAst ( attr . name , updatedMessage , attr . sourceSpan ) ) ;
291+
292+ } else {
293+ throw new I18nError ( attr . sourceSpan , `Cannot find message for id '${ messageId } '` ) ;
294+ }
306295 } ) ;
307296 return res ;
308297 }
309298
299+ private _replaceInterpolationInAttr ( attr : HtmlAttrAst , msg : HtmlAst [ ] ) : string {
300+ let split = this . _parser . splitInterpolation ( attr . value , attr . sourceSpan . toString ( ) ) ;
301+ let exps = isPresent ( split ) ? split . expressions : [ ] ;
302+
303+ let first = msg [ 0 ] ;
304+ let last = msg [ msg . length - 1 ] ;
305+
306+ let start = first . sourceSpan . start . offset ;
307+ let end =
308+ last instanceof HtmlElementAst ? last . endSourceSpan . end . offset : last . sourceSpan . end . offset ;
309+ let messageSubstring = this . _messagesContent . substring ( start , end ) ;
310+
311+ return this . _replacePlaceholdersWithExpressions ( messageSubstring , exps , attr . sourceSpan ) ;
312+ } ;
313+
310314 private _replacePlaceholdersWithExpressions ( message : string , exps : string [ ] ,
311315 sourceSpan : ParseSourceSpan ) : string {
312- return RegExpWrapper . replaceAll ( PLACEHOLDER_EXPANDED_REGEXP , message , ( match ) => {
316+ return RegExpWrapper . replaceAll ( _PLACEHOLDER_EXPANDED_REGEXP , message , ( match ) => {
313317 let nameWithQuotes = match [ 2 ] ;
314318 let name = nameWithQuotes . substring ( 1 , nameWithQuotes . length - 1 ) ;
315319 let index = NumberWrapper . parseInt ( name , 10 ) ;
@@ -343,11 +347,4 @@ class _CreateNodeMapping implements HtmlAstVisitor {
343347 }
344348
345349 visitComment ( ast : HtmlCommentAst , context : any ) : any { return "" ; }
346- }
347-
348- function _expandPlaceholder ( input : string ) : string {
349- return RegExpWrapper . replaceAll ( PLACEHOLDER_REGEXP , input , ( match ) => {
350- let nameWithQuotes = match [ 2 ] ;
351- return `<ph name=${ nameWithQuotes } ></ph>` ;
352- } ) ;
353350}
0 commit comments