| 
									
										
										
										
											2016-07-15 09:42:33 -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-08-01 12:19:09 -07:00
										 |  |  | import {HtmlParser} from '../ml_parser/html_parser'; | 
					
						
							|  |  |  | import {InterpolationConfig} from '../ml_parser/interpolation_config'; | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | import {ParseError} from '../parse_util'; | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-05 12:08:43 -07:00
										 |  |  | import {extractMessages} from './extractor_merger'; | 
					
						
							| 
									
										
										
										
											2017-01-19 14:42:25 -08:00
										 |  |  | import * as i18n from './i18n_ast'; | 
					
						
							|  |  |  | import {PlaceholderMapper, Serializer} from './serializers/serializer'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  | /** | 
					
						
							|  |  |  |  * A container for message extracted from the templates. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | export class MessageBundle { | 
					
						
							| 
									
										
										
										
											2017-01-19 14:42:25 -08:00
										 |  |  |   private _messages: i18n.Message[] = []; | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |   constructor( | 
					
						
							|  |  |  |       private _htmlParser: HtmlParser, private _implicitTags: string[], | 
					
						
							|  |  |  |       private _implicitAttrs: {[k: string]: string[]}) {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |   updateFromTemplate(html: string, url: string, interpolationConfig: InterpolationConfig): | 
					
						
							|  |  |  |       ParseError[] { | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  |     const htmlParserResult = this._htmlParser.parse(html, url, true, interpolationConfig); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     if (htmlParserResult.errors.length) { | 
					
						
							| 
									
										
										
										
											2016-07-21 13:56:58 -07:00
										 |  |  |       return htmlParserResult.errors; | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-05 12:08:43 -07:00
										 |  |  |     const i18nParserResult = extractMessages( | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  |         htmlParserResult.rootNodes, interpolationConfig, this._implicitTags, this._implicitAttrs); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-08-04 17:46:31 -07:00
										 |  |  |     if (i18nParserResult.errors.length) { | 
					
						
							|  |  |  |       return i18nParserResult.errors; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2016-10-28 19:53:42 -07:00
										 |  |  |     this._messages.push(...i18nParserResult.messages); | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-19 14:42:25 -08:00
										 |  |  |   // Return the message in the internal format
 | 
					
						
							|  |  |  |   // The public (serialized) format might be different, see the `write` method.
 | 
					
						
							|  |  |  |   getMessages(): i18n.Message[] { return this._messages; } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   write(serializer: Serializer): string { | 
					
						
							|  |  |  |     const messages: {[id: string]: i18n.Message} = {}; | 
					
						
							|  |  |  |     const mapperVisitor = new MapPlaceholderNames(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     // Deduplicate messages based on their ID
 | 
					
						
							|  |  |  |     this._messages.forEach(message => { | 
					
						
							|  |  |  |       const id = serializer.digest(message); | 
					
						
							|  |  |  |       if (!messages.hasOwnProperty(id)) { | 
					
						
							|  |  |  |         messages[id] = message; | 
					
						
							|  |  |  |       } | 
					
						
							|  |  |  |     }); | 
					
						
							| 
									
										
										
										
											2016-08-09 21:05:04 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-19 14:42:25 -08:00
										 |  |  |     // Transform placeholder names using the serializer mapping
 | 
					
						
							|  |  |  |     const msgList = Object.keys(messages).map(id => { | 
					
						
							|  |  |  |       const mapper = serializer.createNameMapper(messages[id]); | 
					
						
							|  |  |  |       const src = messages[id]; | 
					
						
							|  |  |  |       const nodes = mapper ? mapperVisitor.convert(src.nodes, mapper) : src.nodes; | 
					
						
							|  |  |  |       return new i18n.Message(nodes, {}, {}, src.meaning, src.description, id); | 
					
						
							|  |  |  |     }); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     return serializer.write(msgList); | 
					
						
							|  |  |  |   } | 
					
						
							| 
									
										
										
										
											2016-07-15 09:42:33 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2017-01-19 14:42:25 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | // Transform an i18n AST by renaming the placeholder nodes with the given mapper
 | 
					
						
							|  |  |  | class MapPlaceholderNames implements i18n.Visitor { | 
					
						
							|  |  |  |   convert(nodes: i18n.Node[], mapper: PlaceholderMapper): i18n.Node[] { | 
					
						
							|  |  |  |     return mapper ? nodes.map(n => n.visit(this, mapper)) : nodes; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   visitText(text: i18n.Text, mapper: PlaceholderMapper): i18n.Text { | 
					
						
							|  |  |  |     return new i18n.Text(text.value, text.sourceSpan); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   visitContainer(container: i18n.Container, mapper: PlaceholderMapper): i18n.Container { | 
					
						
							|  |  |  |     const children = container.children.map(n => n.visit(this, mapper)); | 
					
						
							|  |  |  |     return new i18n.Container(children, container.sourceSpan); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   visitIcu(icu: i18n.Icu, mapper: PlaceholderMapper): i18n.Icu { | 
					
						
							|  |  |  |     const cases: {[k: string]: i18n.Node} = {}; | 
					
						
							|  |  |  |     Object.keys(icu.cases).forEach(key => cases[key] = icu.cases[key].visit(this, mapper)); | 
					
						
							|  |  |  |     const msg = new i18n.Icu(icu.expression, icu.type, cases, icu.sourceSpan); | 
					
						
							|  |  |  |     msg.expressionPlaceholder = icu.expressionPlaceholder; | 
					
						
							|  |  |  |     return msg; | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   visitTagPlaceholder(ph: i18n.TagPlaceholder, mapper: PlaceholderMapper): i18n.TagPlaceholder { | 
					
						
							|  |  |  |     const startName = mapper.toPublicName(ph.startName); | 
					
						
							|  |  |  |     const closeName = ph.closeName ? mapper.toPublicName(ph.closeName) : ph.closeName; | 
					
						
							|  |  |  |     const children = ph.children.map(n => n.visit(this, mapper)); | 
					
						
							|  |  |  |     return new i18n.TagPlaceholder( | 
					
						
							|  |  |  |         ph.tag, ph.attrs, startName, closeName, children, ph.isVoid, ph.sourceSpan); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   visitPlaceholder(ph: i18n.Placeholder, mapper: PlaceholderMapper): i18n.Placeholder { | 
					
						
							|  |  |  |     return new i18n.Placeholder(ph.value, mapper.toPublicName(ph.name), ph.sourceSpan); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   visitIcuPlaceholder(ph: i18n.IcuPlaceholder, mapper: PlaceholderMapper): i18n.IcuPlaceholder { | 
					
						
							|  |  |  |     return new i18n.IcuPlaceholder(ph.value, mapper.toPublicName(ph.name), ph.sourceSpan); | 
					
						
							|  |  |  |   } | 
					
						
							|  |  |  | } |