Просмотр исходного кода

blocktrans trimmed issues (fixes #212)

Andreas Stenius 9 лет назад
Родитель
Сommit
802c1a197d
4 измененных файлов с 143 добавлено и 130 удалено
  1. 8 7
      src/erlydtl_beam_compiler.erl
  2. 126 102
      src/erlydtl_unparser.erl
  3. 4 20
      src/i18n/sources_parser.erl
  4. 5 1
      test/erlydtl_test_defs.erl

+ 8 - 7
src/erlydtl_beam_compiler.erl

@@ -815,6 +815,7 @@ blocktrans_ast(Args, Contents, PluralContents, TreeWalker) ->
                   {string_literal, _, S} ->
                   {string_literal, _, S} ->
                       unescape_string_literal(S)
                       unescape_string_literal(S)
               end,
               end,
+    Trimmed = proplists:get_value(trimmed, Args),
 
 
     %% add new scope using 'with' values
     %% add new scope using 'with' values
     {NewScope, {ArgInfo, TreeWalker1}} =
     {NewScope, {ArgInfo, TreeWalker1}} =
@@ -834,7 +835,7 @@ blocktrans_ast(Args, Contents, PluralContents, TreeWalker) ->
     TreeWalker2 = push_scope(NewScope, TreeWalker1),
     TreeWalker2 = push_scope(NewScope, TreeWalker1),
 
 
     %% key for translation lookup
     %% key for translation lookup
-    SourceText = erlydtl_unparser:unparse(Contents),
+    SourceText = erlydtl_unparser:unparse(Contents, Trimmed),
     {{DefaultAst, AstInfo}, TreeWalker3} = body_ast(Contents, TreeWalker2),
     {{DefaultAst, AstInfo}, TreeWalker3} = body_ast(Contents, TreeWalker2),
     MergedInfo = merge_info(AstInfo, ArgInfo),
     MergedInfo = merge_info(AstInfo, ArgInfo),
 
 
@@ -845,7 +846,7 @@ blocktrans_ast(Args, Contents, PluralContents, TreeWalker) ->
             %% translate in runtime
             %% translate in runtime
             {FinalAst, FinalTW} = blocktrans_runtime_ast(
             {FinalAst, FinalTW} = blocktrans_runtime_ast(
                                     {DefaultAst, MergedInfo}, SourceText, Contents, Context, AutoEscape,
                                     {DefaultAst, MergedInfo}, SourceText, Contents, Context, AutoEscape,
-                                    plural_contents(PluralContents, Count, TreeWalker3)),
+                                    plural_contents(PluralContents, Count, TreeWalker3), Trimmed),
             {FinalAst, restore_scope(TreeWalker1, FinalTW)};
             {FinalAst, restore_scope(TreeWalker1, FinalTW)};
        is_function(TFun, 2) ->
        is_function(TFun, 2) ->
             %% translate in compile-time
             %% translate in compile-time
@@ -870,7 +871,7 @@ blocktrans_ast(Args, Contents, PluralContents, TreeWalker) ->
             empty_ast(?ERR({translation_fun, TFun}, TreeWalker3))
             empty_ast(?ERR({translation_fun, TFun}, TreeWalker3))
     end.
     end.
 
 
-blocktrans_runtime_ast({DefaultAst, Info}, SourceText, Contents, Context, AutoEscape, {Plural, TreeWalker}) ->
+blocktrans_runtime_ast({DefaultAst, Info}, SourceText, Contents, Context, AutoEscape, {Plural, TreeWalker}, Trimmed) ->
     %% Contents is flat - only strings and '{{var}}' allowed.
     %% Contents is flat - only strings and '{{var}}' allowed.
     %% build sorted list (orddict) of pre-resolved variables to pass to runtime translation function
     %% build sorted list (orddict) of pre-resolved variables to pass to runtime translation function
     USortedVariables = lists:usort(fun({variable, {identifier, _, A}},
     USortedVariables = lists:usort(fun({variable, {identifier, _, A}},
@@ -892,7 +893,7 @@ blocktrans_runtime_ast({DefaultAst, Info}, SourceText, Contents, Context, AutoEs
                         "    Text -> Text",
                         "    Text -> Text",
                         "  end",
                         "  end",
                         "end"],
                         "end"],
-                       [{phrase, phrase_ast(SourceText, Plural)},
+                       [{phrase, phrase_ast(SourceText, Plural, Trimmed)},
                         {auto_escape, autoescape_ast(AutoEscape)},
                         {auto_escape, autoescape_ast(AutoEscape)},
                         {locale, phrase_locale_ast(Context)}]),
                         {locale, phrase_locale_ast(Context)}]),
     {{BlockTransAst, merge_count_info(Info, Plural)}, TreeWalker1}.
     {{BlockTransAst, merge_count_info(Info, Plural)}, TreeWalker1}.
@@ -909,12 +910,12 @@ plural_contents(Contents, {_CountVarName, Value}, TreeWalker) ->
     {CountAst, TW} = value_ast(Value, false, false, TreeWalker),
     {CountAst, TW} = value_ast(Value, false, false, TreeWalker),
     {{Contents, CountAst}, TW}.
     {{Contents, CountAst}, TW}.
 
 
-phrase_ast(Text, undefined) -> merl:term(Text);
-phrase_ast(Text, {Contents, {CountAst, _CountInfo}}) ->
+phrase_ast(Text, undefined, _) -> merl:term(Text);
+phrase_ast(Text, {Contents, {CountAst, _CountInfo}}, Trimmed) ->
     erl_syntax:tuple(
     erl_syntax:tuple(
       [merl:term(Text),
       [merl:term(Text),
        erl_syntax:tuple(
        erl_syntax:tuple(
-         [merl:term(erlydtl_unparser:unparse(Contents)),
+         [merl:term(erlydtl_unparser:unparse(Contents, Trimmed)),
           CountAst])
           CountAst])
       ]).
       ]).
 autoescape_ast([]) -> autoescape_ast([on]);
 autoescape_ast([]) -> autoescape_ast([on]);

+ 126 - 102
src/erlydtl_unparser.erl

@@ -1,129 +1,153 @@
 -module(erlydtl_unparser).
 -module(erlydtl_unparser).
--export([unparse/1]).
+-export([unparse/1, unparse/2]).
+
+unparse(DjangoParseTree, undefined) ->
+    unparse(DjangoParseTree);
+unparse(DjangoParseTree, true) ->
+    Text = unparse(DjangoParseTree),
+    Trimmed = re:replace(Text, <<"(^\\s+)|(\\s+$)|\n">>, <<"">>, [global, multiline]),
+    Joined = join_iolist(Trimmed, " "),
+    binary_to_list(iolist_to_binary(Joined)).
+
+join_iolist(IOList, Sep) ->
+    join_iolist(IOList, Sep, []).
+
+join_iolist([[]|IOList], Sep, Acc) ->
+    join_iolist(IOList, Sep, Acc);
+join_iolist([Data|IOList], Sep, Acc) ->
+    join_iolist(IOList, Sep, [Sep, Data|Acc]);
+join_iolist([], _, [_|Acc]) ->
+    lists:reverse(Acc);
+join_iolist(IOList, _, Acc) ->
+    lists:reverse([IOList|Acc]).
+
 
 
 unparse(DjangoParseTree) ->
 unparse(DjangoParseTree) ->
-    unparse(DjangoParseTree, []).
+    do_unparse(DjangoParseTree).
+
+do_unparse(DjangoParseTree) ->
+    do_unparse(DjangoParseTree, []).
 
 
-unparse([], Acc) ->
+do_unparse([], Acc) ->
     lists:flatten(lists:reverse(Acc));
     lists:flatten(lists:reverse(Acc));
-unparse([{'extends', Value}|Rest], Acc) ->
-    unparse(Rest, [["{% extends ", unparse_value(Value), " %}"]|Acc]);
-unparse([{'autoescape', OnOrOff, Contents}|Rest], Acc) ->
-    unparse(Rest, [["{% autoescape ", unparse_identifier(OnOrOff), " %}", unparse(Contents), "{% endautoescape %}"]|Acc]);
-unparse([{'block', Identifier, Contents}|Rest], Acc) ->
-    unparse(Rest, [["{% block ", unparse_identifier(Identifier), " %}", unparse(Contents), "{% endblock %}"]|Acc]);
-unparse([{'blocktrans', Args, Contents, undefined}|Rest], Acc) ->
-    unparse(Rest, [["{% blocktrans ", unparse_blocktrans_args(Args), " %}", unparse(Contents), "{% endblocktrans %}"]|Acc]);
-unparse([{'blocktrans', Args, Contents, PluralContents}|Rest], Acc) ->
-    unparse(Rest, [["{% blocktrans ", unparse_blocktrans_args(Args), " %}",
-                    unparse(Contents),
+do_unparse([{'extends', Value}|Rest], Acc) ->
+    do_unparse(Rest, [["{% extends ", unparse_value(Value), " %}"]|Acc]);
+do_unparse([{'autoescape', OnOrOff, Contents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% autoescape ", unparse_identifier(OnOrOff), " %}", do_unparse(Contents), "{% endautoescape %}"]|Acc]);
+do_unparse([{'block', Identifier, Contents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% block ", unparse_identifier(Identifier), " %}", do_unparse(Contents), "{% endblock %}"]|Acc]);
+do_unparse([{'blocktrans', Args, Contents, undefined}|Rest], Acc) ->
+    do_unparse(Rest, [["{% blocktrans ", unparse_blocktrans_args(Args), " %}", do_unparse(Contents), "{% endblocktrans %}"]|Acc]);
+do_unparse([{'blocktrans', Args, Contents, PluralContents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% blocktrans ", unparse_blocktrans_args(Args), " %}",
+                    do_unparse(Contents),
                     "{% plural %}",
                     "{% plural %}",
-                    unparse(PluralContents),
+                    do_unparse(PluralContents),
                     "{% endblocktrans %}"]|Acc]);
                     "{% endblocktrans %}"]|Acc]);
-unparse([{'call', Identifier}|Rest], Acc) ->
-    unparse(Rest, [["{% call ", unparse_identifier(Identifier), " %}"]|Acc]);
-unparse([{'call', Identifier, With}|Rest], Acc) ->
-    unparse(Rest, [["{% call ", unparse_identifier(Identifier), " with ", unparse_args(With), " %}"]|Acc]);
-unparse([{'comment', Contents}|Rest], Acc) ->
-    unparse(Rest, [["{% comment %}", unparse(Contents), "{% endcomment %}"]|Acc]);
-unparse([{'comment_tag', _Pos, Text}|Rest], Acc) ->
-    unparse(Rest, [["{#", Text, "#}"]|Acc]);
-unparse([{'cycle', Names}|Rest], Acc) ->
-    unparse(Rest, [["{% cycle ", unparse(Names), " %}"]|Acc]);
-unparse([{'cycle_compat', Names}|Rest], Acc) ->
-    unparse(Rest, [["{% cycle ", unparse_cycle_compat_names(Names), " %}"]|Acc]);
-unparse([{'date', 'now', Value}|Rest], Acc) ->
-    unparse(Rest, [["{% now ", unparse_value(Value), " %}"]|Acc]);
-unparse([{'filter', FilterList, Contents}|Rest], Acc) ->
-    unparse(Rest, [["{% filter ", unparse_filters(FilterList), " %}", unparse(Contents), "{% endfilter %}"]|Acc]);
-unparse([{'firstof', Vars}|Rest], Acc) ->
-    unparse(Rest, [["{% firstof ", unparse(Vars), " %}"]|Acc]);
-unparse([{'for', {'in', IteratorList, Identifier}, Contents}|Rest], Acc) ->
-    unparse(Rest, [["{% for ", unparse_identifier(Identifier), " in ", unparse(IteratorList), " %}",
-                    unparse(Contents),
+do_unparse([{'call', Identifier}|Rest], Acc) ->
+    do_unparse(Rest, [["{% call ", unparse_identifier(Identifier), " %}"]|Acc]);
+do_unparse([{'call', Identifier, With}|Rest], Acc) ->
+    do_unparse(Rest, [["{% call ", unparse_identifier(Identifier), " with ", unparse_args(With), " %}"]|Acc]);
+do_unparse([{'comment', Contents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% comment %}", do_unparse(Contents), "{% endcomment %}"]|Acc]);
+do_unparse([{'comment_tag', _Pos, Text}|Rest], Acc) ->
+    do_unparse(Rest, [["{#", Text, "#}"]|Acc]);
+do_unparse([{'cycle', Names}|Rest], Acc) ->
+    do_unparse(Rest, [["{% cycle ", do_unparse(Names), " %}"]|Acc]);
+do_unparse([{'cycle_compat', Names}|Rest], Acc) ->
+    do_unparse(Rest, [["{% cycle ", unparse_cycle_compat_names(Names), " %}"]|Acc]);
+do_unparse([{'date', 'now', Value}|Rest], Acc) ->
+    do_unparse(Rest, [["{% now ", unparse_value(Value), " %}"]|Acc]);
+do_unparse([{'filter', FilterList, Contents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% filter ", unparse_filters(FilterList), " %}", do_unparse(Contents), "{% endfilter %}"]|Acc]);
+do_unparse([{'firstof', Vars}|Rest], Acc) ->
+    do_unparse(Rest, [["{% firstof ", do_unparse(Vars), " %}"]|Acc]);
+do_unparse([{'for', {'in', IteratorList, Identifier}, Contents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% for ", unparse_identifier(Identifier), " in ", do_unparse(IteratorList), " %}",
+                    do_unparse(Contents),
                     "{% endfor %}"]|Acc]);
                     "{% endfor %}"]|Acc]);
-unparse([{'for', {'in', IteratorList, Identifier}, Contents, EmptyPartsContents}|Rest], Acc) ->
-    unparse(Rest, [["{% for ", unparse_identifier(Identifier), " in ", unparse(IteratorList), " %}",
-                    unparse(Contents),
+do_unparse([{'for', {'in', IteratorList, Identifier}, Contents, EmptyPartsContents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% for ", unparse_identifier(Identifier), " in ", do_unparse(IteratorList), " %}",
+                    do_unparse(Contents),
                     "{% empty %}",
                     "{% empty %}",
-                    unparse(EmptyPartsContents),
+                    do_unparse(EmptyPartsContents),
                     "{% endfor %}"]|Acc]);
                     "{% endfor %}"]|Acc]);
-unparse([{'if', Expression, Contents}|Rest], Acc) ->
-    unparse(Rest, [["{% if ", unparse_expression(Expression), " %}",
-                    unparse(Contents),
+do_unparse([{'if', Expression, Contents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% if ", unparse_expression(Expression), " %}",
+                    do_unparse(Contents),
                     "{% endif %}"]|Acc]);
                     "{% endif %}"]|Acc]);
-unparse([{'ifchanged', Expression, IfContents}|Rest], Acc) ->
-    unparse(Rest, [["{% ifchanged ", unparse_expression(Expression), " %}",
-                    unparse(IfContents),
+do_unparse([{'ifchanged', Expression, IfContents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% ifchanged ", unparse_expression(Expression), " %}",
+                    do_unparse(IfContents),
                     "{% endifchanged %}"]|Acc]);
                     "{% endifchanged %}"]|Acc]);
-unparse([{'ifchangedelse', Expression, IfContents, ElseContents}|Rest], Acc) ->
-    unparse(Rest, [["{% ifchanged ", unparse_expression(Expression), " %}",
-                    unparse(IfContents),
+do_unparse([{'ifchangedelse', Expression, IfContents, ElseContents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% ifchanged ", unparse_expression(Expression), " %}",
+                    do_unparse(IfContents),
                     "{% else %}",
                     "{% else %}",
-                    unparse(ElseContents),
+                    do_unparse(ElseContents),
                     "{% endifchanged %}"]|Acc]);
                     "{% endifchanged %}"]|Acc]);
-unparse([{'ifelse', Expression, IfContents, ElseContents}|Rest], Acc) ->
-    unparse(Rest, [["{% if ", unparse_expression(Expression), " %}",
-                    unparse(IfContents),
+do_unparse([{'ifelse', Expression, IfContents, ElseContents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% if ", unparse_expression(Expression), " %}",
+                    do_unparse(IfContents),
                     "{% else %}",
                     "{% else %}",
-                    unparse(ElseContents),
+                    do_unparse(ElseContents),
                     "{% endif %}"]|Acc]);
                     "{% endif %}"]|Acc]);
-unparse([{'ifequal', [Arg1, Arg2], Contents}|Rest], Acc) ->
-    unparse(Rest, [["{% ifequal ", unparse_value(Arg1), " ", unparse_value(Arg2), " %}",
-                    unparse(Contents),
+do_unparse([{'ifequal', [Arg1, Arg2], Contents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% ifequal ", unparse_value(Arg1), " ", unparse_value(Arg2), " %}",
+                    do_unparse(Contents),
                     "{% endifequal %}"]|Acc]);
                     "{% endifequal %}"]|Acc]);
-unparse([{'ifequalelse', [Arg1, Arg2], IfContents, ElseContents}|Rest], Acc) ->
-    unparse(Rest, [["{% ifequal ", unparse_value(Arg1), " ", unparse_value(Arg2), " %}",
-                    unparse(IfContents),
+do_unparse([{'ifequalelse', [Arg1, Arg2], IfContents, ElseContents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% ifequal ", unparse_value(Arg1), " ", unparse_value(Arg2), " %}",
+                    do_unparse(IfContents),
                     "{% else %}",
                     "{% else %}",
-                    unparse(ElseContents),
+                    do_unparse(ElseContents),
                     "{% endifequal %}"]|Acc]);
                     "{% endifequal %}"]|Acc]);
-unparse([{'ifnotequal', [Arg1, Arg2], Contents}|Rest], Acc) ->
-    unparse(Rest, [["{% ifnotequal ", unparse_value(Arg1), " ", unparse_value(Arg2), " %}",
-                    unparse(Contents),
+do_unparse([{'ifnotequal', [Arg1, Arg2], Contents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% ifnotequal ", unparse_value(Arg1), " ", unparse_value(Arg2), " %}",
+                    do_unparse(Contents),
                     "{% endifnotequal %}"]|Acc]);
                     "{% endifnotequal %}"]|Acc]);
-unparse([{'ifnotequalelse', [Arg1, Arg2], IfContents, ElseContents}|Rest], Acc) ->
-    unparse(Rest, [["{% ifnotequal ", unparse_value(Arg1), " ", unparse_value(Arg2), " %}",
-                    unparse(IfContents),
+do_unparse([{'ifnotequalelse', [Arg1, Arg2], IfContents, ElseContents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% ifnotequal ", unparse_value(Arg1), " ", unparse_value(Arg2), " %}",
+                    do_unparse(IfContents),
                     "{% else %}",
                     "{% else %}",
-                    unparse(ElseContents),
+                    do_unparse(ElseContents),
                     "{% endifnotequal %}"]|Acc]);
                     "{% endifnotequal %}"]|Acc]);
-unparse([{'include', Value, []}|Rest], Acc) ->
-    unparse(Rest, [["{% include ", unparse_value(Value), " %}"]|Acc]);
-unparse([{'include', Value, Args}|Rest], Acc) ->
-    unparse(Rest, [["{% include ", unparse_value(Value), " with ", unparse_args(Args)]|Acc]);
-unparse([{'include_only', Value, []}|Rest], Acc) ->
-    unparse(Rest, [["{% include ", unparse_value(Value), " only %}"]|Acc]);
-unparse([{'include_only', Value, Args}|Rest], Acc) ->
-    unparse(Rest, [["{% include ", unparse_value(Value), " with ", unparse_args(Args), " only %}"]|Acc]);
-unparse([{'regroup', {Variable, Identifier1, Identifier2}, Contents}|Rest], Acc) ->
-    unparse(Rest, [["{% regroup ", unparse_value(Variable), " by ", unparse_identifier(Identifier1), " as ", unparse_identifier(Identifier2), " %}",
-                    unparse(Contents),
+do_unparse([{'include', Value, []}|Rest], Acc) ->
+    do_unparse(Rest, [["{% include ", unparse_value(Value), " %}"]|Acc]);
+do_unparse([{'include', Value, Args}|Rest], Acc) ->
+    do_unparse(Rest, [["{% include ", unparse_value(Value), " with ", unparse_args(Args)]|Acc]);
+do_unparse([{'include_only', Value, []}|Rest], Acc) ->
+    do_unparse(Rest, [["{% include ", unparse_value(Value), " only %}"]|Acc]);
+do_unparse([{'include_only', Value, Args}|Rest], Acc) ->
+    do_unparse(Rest, [["{% include ", unparse_value(Value), " with ", unparse_args(Args), " only %}"]|Acc]);
+do_unparse([{'regroup', {Variable, Identifier1, Identifier2}, Contents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% regroup ", unparse_value(Variable), " by ", unparse_identifier(Identifier1), " as ", unparse_identifier(Identifier2), " %}",
+                    do_unparse(Contents),
                     "{% endregroup %}"]|Acc]);
                     "{% endregroup %}"]|Acc]);
-unparse([{'spaceless', Contents}|Rest], Acc) ->
-    unparse(Rest, [["{% spaceless %}", unparse(Contents), "{% endspaceless %}"]|Acc]);
-unparse([{'ssi', Arg}|Rest], Acc) ->
-    unparse(Rest, [["{% ssi ", unparse_value(Arg), " %}"]|Acc]);
-unparse([{'ssi_parsed', Arg}|Rest], Acc) ->
-    unparse(Rest, [["{% ssi ", unparse_value(Arg), " parsed %}"]|Acc]);
-unparse([{'string', _, String}|Rest], Acc) ->
-    unparse(Rest, [[String]|Acc]);
-unparse([{'tag', Identifier, []}|Rest], Acc) ->
-    unparse(Rest, [["{% ", unparse_identifier(Identifier), " %}"]|Acc]);
-unparse([{'tag', Identifier, Args}|Rest], Acc) ->
-    unparse(Rest, [["{% ", unparse_identifier(Identifier), " ", unparse_args(Args), " %}"]|Acc]);
-unparse([{'templatetag', Identifier}|Rest], Acc) ->
-    unparse(Rest, [["{% templatetag ", unparse_identifier(Identifier), " %}"]|Acc]);
-unparse([{'trans', Value}|Rest], Acc) ->
-    unparse(Rest, [["{% trans ", unparse_value(Value), " %}"]|Acc]);
-unparse([{'widthratio', Numerator, Denominator, Scale}|Rest], Acc) ->
-    unparse(Rest, [["{% widthratio ", unparse_value(Numerator), " ", unparse_value(Denominator), " ", unparse_value(Scale), " %}"]|Acc]);
-unparse([{'with', Args, Contents}|Rest], Acc) ->
-    unparse(Rest, [["{% with ", unparse_args(Args), " %}",
-                    unparse(Contents),
+do_unparse([{'spaceless', Contents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% spaceless %}", do_unparse(Contents), "{% endspaceless %}"]|Acc]);
+do_unparse([{'ssi', Arg}|Rest], Acc) ->
+    do_unparse(Rest, [["{% ssi ", unparse_value(Arg), " %}"]|Acc]);
+do_unparse([{'ssi_parsed', Arg}|Rest], Acc) ->
+    do_unparse(Rest, [["{% ssi ", unparse_value(Arg), " parsed %}"]|Acc]);
+do_unparse([{'string', _, String}|Rest], Acc) ->
+    do_unparse(Rest, [[String]|Acc]);
+do_unparse([{'tag', Identifier, []}|Rest], Acc) ->
+    do_unparse(Rest, [["{% ", unparse_identifier(Identifier), " %}"]|Acc]);
+do_unparse([{'tag', Identifier, Args}|Rest], Acc) ->
+    do_unparse(Rest, [["{% ", unparse_identifier(Identifier), " ", unparse_args(Args), " %}"]|Acc]);
+do_unparse([{'templatetag', Identifier}|Rest], Acc) ->
+    do_unparse(Rest, [["{% templatetag ", unparse_identifier(Identifier), " %}"]|Acc]);
+do_unparse([{'trans', Value}|Rest], Acc) ->
+    do_unparse(Rest, [["{% trans ", unparse_value(Value), " %}"]|Acc]);
+do_unparse([{'widthratio', Numerator, Denominator, Scale}|Rest], Acc) ->
+    do_unparse(Rest, [["{% widthratio ", unparse_value(Numerator), " ", unparse_value(Denominator), " ", unparse_value(Scale), " %}"]|Acc]);
+do_unparse([{'with', Args, Contents}|Rest], Acc) ->
+    do_unparse(Rest, [["{% with ", unparse_args(Args), " %}",
+                    do_unparse(Contents),
                     "{% endwidth %}"]|Acc]);
                     "{% endwidth %}"]|Acc]);
-unparse([ValueToken|Rest], Acc) ->
-    unparse(Rest, [["{{ ", unparse_value(ValueToken), " }}"]|Acc]).
+do_unparse([ValueToken|Rest], Acc) ->
+    do_unparse(Rest, [["{{ ", unparse_value(ValueToken), " }}"]|Acc]).
 
 
 
 
 unparse_identifier({identifier, _, Name}) ->
 unparse_identifier({identifier, _, Name}) ->

+ 4 - 20
src/i18n/sources_parser.erl

@@ -174,8 +174,8 @@ process_token(Fname,
 process_token(Fname, {blocktrans, Args, Contents, PluralContents}, #state{acc=Acc, translators_comment=Comment}=St) ->
 process_token(Fname, {blocktrans, Args, Contents, PluralContents}, #state{acc=Acc, translators_comment=Comment}=St) ->
     {Fname, Line, Col} = guess_blocktrans_lc(Fname, Args, Contents),
     {Fname, Line, Col} = guess_blocktrans_lc(Fname, Args, Contents),
     Trim = proplists:get_value(trimmed, Args),
     Trim = proplists:get_value(trimmed, Args),
-    Phrase = #phrase{msgid=maybe_trim(unparse(Contents), Trim),
-                     msgid_plural=maybe_trim(unparse(PluralContents), Trim),
+    Phrase = #phrase{msgid=unparse(Contents, Trim),
+                     msgid_plural=unparse(PluralContents, Trim),
                      context=case proplists:get_value(context, Args) of
                      context=case proplists:get_value(context, Args) of
                                  {string_literal, _, String} ->
                                  {string_literal, _, String} ->
                                      erlydtl_compiler_utils:unescape_string_literal(String);
                                      erlydtl_compiler_utils:unescape_string_literal(String);
@@ -202,24 +202,8 @@ trans({string_literal,Pos,String}) -> {Pos, String}.
 
 
 unescape(String) -> string:sub_string(String, 2, string:len(String) -1).
 unescape(String) -> string:sub_string(String, 2, string:len(String) -1).
 
 
-unparse(undefined) -> undefined;
-unparse(Contents) -> erlydtl_unparser:unparse(Contents).
-
-maybe_trim(undefined, _) -> undefined;
-maybe_trim(Text, undefined) -> Text;
-maybe_trim(Text, true) ->
-    binary_to_list(
-      iolist_to_binary(
-        tl(
-          lists:foldr(
-            fun (L, Ls) -> [" ", L|Ls] end, [],
-            lists:flatten(
-              re:replace(Text, <<"(^\\s+)|(\\s+$)|\n">>, <<"">>, [global, multiline])
-             )
-           )
-         )
-       )
-     ).
+unparse(undefined, _) -> undefined;
+unparse(Contents, Trim) -> erlydtl_unparser:unparse(Contents, Trim).
 
 
 %% hack to guess ~position of blocktrans
 %% hack to guess ~position of blocktrans
 guess_blocktrans_lc(Fname, [{{identifier, {L, C}, _}, _} | _], _) ->
 guess_blocktrans_lc(Fname, [{{identifier, {L, C}, _}, _} | _], _) ->

+ 5 - 1
test/erlydtl_test_defs.erl

@@ -1453,7 +1453,11 @@ all_test_defs() ->
         <<"{%blocktrans with v1=foo%}Hello, {{ name }}! See {{v1}}.{%endblocktrans%}">>,
         <<"{%blocktrans with v1=foo%}Hello, {{ name }}! See {{v1}}.{%endblocktrans%}">>,
         [{name, "Mr. President"}, {foo, <<"rubber-duck">>}],
         [{name, "Mr. President"}, {foo, <<"rubber-duck">>}],
         [{translation_fun, fun("Hello, {{ name }}! See {{ v1 }}.") -> <<"Guten tag, {{name}}! Sehen {{    v1   }}.">> end}],
         [{translation_fun, fun("Hello, {{ name }}! See {{ v1 }}.") -> <<"Guten tag, {{name}}! Sehen {{    v1   }}.">> end}],
-        [], <<"Guten tag, Mr. President! Sehen rubber-duck.">>}
+        [], <<"Guten tag, Mr. President! Sehen rubber-duck.">>},
+       {"trimmed",
+        <<"{% blocktrans trimmed %}\n  foo  \n   bar   here .\n \n   \n baz{% endblocktrans %}">>,
+        [], [{translation_fun, fun ("foo bar   here . baz") -> "ok" end}],
+        <<"ok">>}
       ]},
       ]},
      {"extended translation features (#131)",
      {"extended translation features (#131)",
       [{"trans default locale",
       [{"trans default locale",