license import * as html from "../ast/ast" ; import * as i18n from "../ast/i18n_ast" ; import { getHtmlTagDefinition } from "../ast/html_tags" ; import { I18nPluralPipe , I18nSelectPipe , NgLocaleLocalization } from "@angular/common" ; import { Parser } from "../ast/parser" ; import { getXmlTagDefinition } from "../ast/xml_tags" ; import { I18nError } from "../ast/parse_util" ; import * as xml from "./xml_helper" ; export interface I18nMessagesById { [ msgId ] : i18n . Node [ ] ; } export interface XmlMessagesById { [ id ] : xml . Node ; } export interface IcuContent { cases : { [ value ] : html . Node [ ] ; } ; expression : string ; type : string ; } export interface IcuContentStr { cases : { [ value ] : string ; } ; expression : string ; type : string ; } /**
 * A `PlaceholderMapper` converts placeholder names from internal to serialized representation and
 * back.
 *
 * It should be used for serialization format that put constraints on the placeholder names.
 */ export interface PlaceholderMapper { toPublicName ( internalName ) : string | null ; toInternalName ( publicName ) : string | null ; } /**
 * A simple mapper that take a function to transform an internal name to a public name
 */ export class SimplePlaceholderMapper extends i18n . RecurseVisitor implements PlaceholderMapper { private internalToPublic : { [ k ] : string ; } = { } ; private publicToNextId : { [ k ] : number ; } = { } ; private publicToInternal : { [ k ] : string ; } = { } ; constructor ( message , private mapName ) { super ( ) ; message . nodes . forEach ( node => node . visit ( this ) ) ; } toPublicName ( internalName ) { return this . internalToPublic . hasOwnProperty ( internalName ) ? this . internalToPublic [ internalName ] : null ; } toInternalName ( publicName ) { return this . publicToInternal . hasOwnProperty ( publicName ) ? this . publicToInternal [ publicName ] : null ; } visitText ( text , context ? ) { return null ; } visitTagPlaceholder ( ph , context ? ) { this . visitPlaceholderName ( ph . startName ) ; super . visitTagPlaceholder ( ph , context ) ; this . visitPlaceholderName ( ph . closeName ) ; } visitPlaceholder ( ph , context ? ) { this . visitPlaceholderName ( ph . name ) ; } visitIcuPlaceholder ( ph , context ? ) { this . visitPlaceholderName ( ph . name ) ; } private visitPlaceholderName ( internalName ) { if ( ! internalName || this . internalToPublic . hasOwnProperty ( internalName ) ) { return ; } let publicName = this . mapName ( internalName ) ; if ( this . publicToInternal . hasOwnProperty ( publicName ) ) { const nextId = this . publicToNextId [ publicName ] ; this . publicToNextId [ publicName ] = nextId + 1 ; publicName = `${ publicName }_${ nextId }` ; } else { this . publicToNextId [ publicName ] = 1 ; } this . internalToPublic [ internalName ] = publicName ; this . publicToInternal [ publicName ] = internalName ; } } const i18nSelectPipe = new I18nSelectPipe ( ) ; class SerializerVisitor implements html . Visitor { private i18nPluralPipe : I18nPluralPipe ; constructor ( locale , private params ) { this . i18nPluralPipe = new I18nPluralPipe ( new NgLocaleLocalization ( locale ) ) ; } visitElement ( element , context ) { if ( getHtmlTagDefinition ( element . name ) . isVoid ) { return `<${ element . name }${ this . serializeNodes ( element . attrs , " " ) }/>` ; } return `<${ element . name }${ this . serializeNodes ( element . attrs , " " ) }>${ this . serializeNodes ( element . children ) }</${ element . name }>` ; } visitAttribute ( attribute , context ) { return `${ attribute . name }="${ attribute . value }"` ; } visitText ( text , context ) { return text . value ; } visitComment ( comment , context ) { return `<!--${ comment . value }-->` ; } visitExpansion ( expansion , context ) { const cases = { } ; expansion . cases . forEach ( c => ( cases [ c . value ] = this . serializeNodes ( c . expression ) ) ) ; switch ( expansion . type ) { case "select" : return i18nSelectPipe . transform ( this . params [ expansion . switchValue ] || "" , cases ) ; case "plural" : return this . i18nPluralPipe . transform ( this . params [ expansion . switchValue ] , cases ) ; } throw new Error ( `Unknown expansion type "${ expansion . type }"` ) ; } visitExpansionCase ( expansionCase , context ) { return ` ${ expansionCase . value } {${ this . serializeNodes ( expansionCase . expression ) }}` ; } private serializeNodes ( nodes , join = "" ) { if ( nodes . length === 0 ) { return "" ; } return join + nodes . map ( a => a . visit ( this , null ) ) . join ( join ) ; } } export function serializeNodes ( nodes , locale , params ) { return nodes . map ( node => node . visit ( new SerializerVisitor ( locale , params ) , null ) ) ; } export class HtmlToXmlParser implements html . Visitor { private errors : I18nError [ ] ; private xmlMessagesById : { [ id ] : xml . Node ; } ; constructor ( private MESSAGE_TAG ) { } parse ( content ) { this . xmlMessagesById = { } ; const parser = new Parser ( getXmlTagDefinition ) . parse ( content , "" , false ) ; this . errors = parser . errors ; html . visitAll ( this , parser . rootNodes , null ) ; return { xmlMessagesById : this . xmlMessagesById , errors : this . errors } ; } visitElement ( element , context ) { switch ( element . name ) { case this . MESSAGE_TAG : const id = element . attrs . find ( attr => attr . name === "id" ) ; if ( id ) { this . xmlMessagesById [ id . value ] = ( element as any ) as xml . Node ; } break ; default : html . visitAll ( this , element . children , null ) ; } } visitAttribute ( attribute , context ) { } visitText ( text , context ) { } visitComment ( comment , context ) { } visitExpansion ( expansion , context ) { } visitExpansionCase ( expansionCase , context ) { } }