77 */
88
99import { ArrayType , AssertNotNull , BinaryOperator , BinaryOperatorExpr , BuiltinType , BuiltinTypeName , CastExpr , ClassStmt , CommaExpr , CommentStmt , ConditionalExpr , DeclareFunctionStmt , DeclareVarStmt , Expression , ExpressionStatement , ExpressionType , ExpressionVisitor , ExternalExpr , ExternalReference , FunctionExpr , IfStmt , InstantiateExpr , InvokeFunctionExpr , InvokeMethodExpr , JSDocCommentStmt , LiteralArrayExpr , LiteralExpr , LiteralMapExpr , MapType , NotExpr , ReadKeyExpr , ReadPropExpr , ReadVarExpr , ReturnStatement , Statement , StatementVisitor , StmtModifier , ThrowStmt , TryCatchStmt , Type , TypeVisitor , TypeofExpr , WrappedNodeExpr , WriteKeyExpr , WritePropExpr , WriteVarExpr } from '@angular/compiler' ;
10+ import { LocalizedString } from '@angular/compiler/src/output/output_ast' ;
1011import * as ts from 'typescript' ;
1112
1213import { DefaultImportRecorder , ImportRewriter , NOOP_DEFAULT_IMPORT_RECORDER , NoopImportRewriter } from '../../imports' ;
@@ -249,6 +250,10 @@ class ExpressionTranslatorVisitor implements ExpressionVisitor, StatementVisitor
249250 return expr ;
250251 }
251252
253+ visitLocalizedString ( ast : LocalizedString , context : Context ) : ts . Expression {
254+ return visitLocalizedString ( ast , context , this ) ;
255+ }
256+
252257 visitExternalExpr ( ast : ExternalExpr , context : Context ) : ts . PropertyAccessExpression
253258 | ts . Identifier {
254259 if ( ast . value . moduleName === null || ast . value . name === null ) {
@@ -435,6 +440,10 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor {
435440 return ts . createLiteral ( ast . value as string ) ;
436441 }
437442
443+ visitLocalizedString ( ast : LocalizedString , context : Context ) : ts . Expression {
444+ return visitLocalizedString ( ast , context , this ) ;
445+ }
446+
438447 visitExternalExpr ( ast : ExternalExpr , context : Context ) : ts . TypeNode {
439448 if ( ast . value . moduleName === null || ast . value . name === null ) {
440449 throw new Error ( `Import unknown module or symbol` ) ;
@@ -512,3 +521,44 @@ export class TypeTranslatorVisitor implements ExpressionVisitor, TypeVisitor {
512521 return ts . createTypeQueryNode ( expr as ts . Identifier ) ;
513522 }
514523}
524+
525+ /**
526+ * A helper to reduce duplication, since this functionality is required in both
527+ * `ExpressionTranslatorVisitor` and `TypeTranslatorVisitor`.
528+ */
529+ function visitLocalizedString ( ast : LocalizedString , context : Context , visitor : ExpressionVisitor ) {
530+ let template : ts . TemplateLiteral ;
531+ if ( ast . messageParts . length === 1 ) {
532+ template = ts . createNoSubstitutionTemplateLiteral ( ast . messageParts [ 0 ] ) ;
533+ } else {
534+ const head = ts . createTemplateHead ( ast . messageParts [ 0 ] ) ;
535+ const spans : ts . TemplateSpan [ ] = [ ] ;
536+ for ( let i = 1 ; i < ast . messageParts . length ; i ++ ) {
537+ const resolvedExpression = ast . expressions [ i - 1 ] . visitExpression ( visitor , context ) ;
538+ spans . push ( ts . createTemplateSpan (
539+ resolvedExpression , ts . createTemplateMiddle ( prefixWithPlaceholderMarker (
540+ ast . messageParts [ i ] , ast . placeHolderNames [ i - 1 ] ) ) ) ) ;
541+ }
542+ if ( spans . length > 0 ) {
543+ // The last span is supposed to have a tail rather than a middle
544+ spans [ spans . length - 1 ] . literal . kind = ts . SyntaxKind . TemplateTail ;
545+ }
546+ template = ts . createTemplateExpression ( head , spans ) ;
547+ }
548+ return ts . createTaggedTemplate ( ts . createIdentifier ( '$localize' ) , template ) ;
549+ }
550+
551+ /**
552+ * We want our tagged literals to include placeholder name information to aid runtime translation.
553+ *
554+ * The expressions are marked with placeholder names by postfixing the expression with
555+ * `:placeHolderName:`. To achieve this, we actually "prefix" the message part that follows the
556+ * expression.
557+ *
558+ * @param messagePart the message part that follows the current expression.
559+ * @param placeHolderName the name of the placeholder for the current expression.
560+ * @returns the prefixed message part.
561+ */
562+ function prefixWithPlaceholderMarker ( messagePart : string , placeHolderName : string ) {
563+ return `:${ placeHolderName } :${ messagePart } ` ;
564+ }
0 commit comments