import * as html from "../ast/ast" ; import * as i18n from "../ast/i18n_ast" ; import { InterpolationConfig } from "../ast/interpolation_config" ; import { Parser } from "./parser" ; import { Lexer } from "./lexer" ; import { PlaceholderRegistry } from "../serializers/placeholder" ; import { getHtmlTagDefinition } from "../ast/html_tags" ; import { ParseSourceSpan } from "../ast/parse_util" ; const _expParser = new Parser ( new Lexer ( ) ) ; /**
 * Returns a function converting html nodes to an i18n Message given an interpolationConfig
 */ export function createI18nMessageFactory ( interpolationConfig ) { const visitor = new I18nVisitor ( _expParser , interpolationConfig ) ; return ( nodes , meaning , description , id ) => visitor . toI18nMessage ( nodes , meaning , description , id ) ; } class I18nVisitor implements html . Visitor { private _isIcu : boolean ; private _icuDepth : number ; private _placeholderRegistry : PlaceholderRegistry ; private _placeholderToContent : { [ phName ] : string ; } ; private _placeholderToMessage : { [ phName ] : i18n . Message ; } ; constructor ( private _expressionParser , private _interpolationConfig ) { } public toI18nMessage ( nodes , meaning , description , id ) { this . _isIcu = nodes . length === 1 && nodes [ 0 ] instanceof html . Expansion ; this . _icuDepth = 0 ; this . _placeholderRegistry = new PlaceholderRegistry ( ) ; this . _placeholderToContent = { } ; this . _placeholderToMessage = { } ; const i18nodes = html . visitAll ( this , nodes , { } ) ; return new i18n . Message ( i18nodes , this . _placeholderToContent , this . _placeholderToMessage , meaning , description , id ) ; } visitElement ( el , context ) { const children = html . visitAll ( this , el . children ) ; const attrs = { } ; el . attrs . forEach ( attr => { attrs [ attr . name ] = attr . value ; } ) ; const isVoid = getHtmlTagDefinition ( el . name ) . isVoid ; const startPhName = this . _placeholderRegistry . getStartTagPlaceholderName ( el . name , attrs , isVoid ) ; this . _placeholderToContent [ startPhName ] = el . sourceSpan ? el . sourceSpan ! . toString ( ) : "" ; let closePhName = "" ; if ( ! isVoid ) { closePhName = this . _placeholderRegistry . getCloseTagPlaceholderName ( el . name ) ; this . _placeholderToContent [ closePhName ] = `</${ el . name }>` ; } return new i18n . TagPlaceholder ( el . name , attrs , startPhName , closePhName , children , isVoid , el . sourceSpan ! ) ; } visitAttribute ( attribute , context ) { return this . _visitTextWithInterpolation ( attribute . value , attribute . sourceSpan ) ; } visitText ( text , context ) { return this . _visitTextWithInterpolation ( text . value , text . sourceSpan ! ) ; } visitComment ( comment , context ) { return null ; } visitExpansion ( icu , context ) { this . _icuDepth ++ ; const i18nIcuCases = { } ; const i18nIcu = new i18n . Icu ( icu . switchValue , icu . type , i18nIcuCases , icu . sourceSpan ) ; icu . cases . forEach ( ( caze ) : void => { i18nIcuCases [ caze . value ] = new i18n . Container ( caze . expression . map ( node => node . visit ( this , { } ) ) , caze . expSourceSpan ) ; } ) ; this . _icuDepth -- ; if ( this . _isIcu || this . _icuDepth > 0 ) { const expPh = this . _placeholderRegistry . getUniquePlaceholder ( `VAR_${ icu . type }` ) ; i18nIcu . expressionPlaceholder = expPh ; this . _placeholderToContent [ expPh ] = icu . switchValue ; return i18nIcu ; } const phName = this . _placeholderRegistry . getPlaceholderName ( "ICU" , icu . sourceSpan . toString ( ) ) ; const visitor = new I18nVisitor ( this . _expressionParser , this . _interpolationConfig ) ; this . _placeholderToMessage [ phName ] = visitor . toI18nMessage ( [ icu ] , "" , "" , "" ) ; return new i18n . IcuPlaceholder ( i18nIcu , phName , icu . sourceSpan ) ; } visitExpansionCase ( icuCase , context ) { throw new Error ( "Unreachable code" ) ; } private _visitTextWithInterpolation ( text , sourceSpan ) { const splitInterpolation = this . _expressionParser . splitInterpolation ( text , sourceSpan . start . toString ( ) , this . _interpolationConfig ) ; if ( ! splitInterpolation ) { return new i18n . Text ( text , sourceSpan ) ; } const nodes = [ ] ; const container = new i18n . Container ( nodes , sourceSpan ) ; const { start : sDelimiter , end : eDelimiter } = this . _interpolationConfig ; for ( let i = 0 ; i < splitInterpolation . strings . length - 1 ; i ++ ) { const expression = splitInterpolation . expressions [ i ] ; const baseName = extractPlaceholderName ( expression ) || "INTERPOLATION" ; const phName = this . _placeholderRegistry . getPlaceholderName ( baseName , expression ) ; if ( splitInterpolation . strings [ i ] . length ) { nodes . push ( new i18n . Text ( splitInterpolation . strings [ i ] , sourceSpan ) ) ; } nodes . push ( new i18n . Placeholder ( expression , phName , sourceSpan ) ) ; this . _placeholderToContent [ phName ] = sDelimiter + expression + eDelimiter ; } const lastStringIdx = splitInterpolation . strings . length - 1 ; if ( splitInterpolation . strings [ lastStringIdx ] . length ) { nodes . push ( new i18n . Text ( splitInterpolation . strings [ lastStringIdx ] , sourceSpan ) ) ; } return container ; } } const _CUSTOM_PH_EXP = /\/\/[\s\S]*i18n[\s\S]*\([\s\S]*ph[\s\S]*=[\s\S]*("|')([\s\S]*?)\1[\s\S]*\)/g ; function extractPlaceholderName ( input ) { return input . split ( _CUSTOM_PH_EXP ) [ 2 ] ; }