|
@@ -43,6 +43,8 @@
|
|
-record(dtl_context, {
|
|
-record(dtl_context, {
|
|
local_scopes = [],
|
|
local_scopes = [],
|
|
block_dict = dict:new(),
|
|
block_dict = dict:new(),
|
|
|
|
+ blocktrans_fun = none,
|
|
|
|
+ blocktrans_locales = [],
|
|
auto_escape = off,
|
|
auto_escape = off,
|
|
doc_root = "",
|
|
doc_root = "",
|
|
parse_trail = [],
|
|
parse_trail = [],
|
|
@@ -111,14 +113,11 @@ compile_dir(Dir, Module) ->
|
|
|
|
|
|
compile_dir(Dir, Module, Options) ->
|
|
compile_dir(Dir, Module, Options) ->
|
|
Context = init_dtl_context_dir(Dir, Module, Options),
|
|
Context = init_dtl_context_dir(Dir, Module, Options),
|
|
- Files = case file:list_dir(Dir) of
|
|
|
|
- {ok, FileList} -> FileList;
|
|
|
|
- _ -> []
|
|
|
|
- end,
|
|
|
|
|
|
+ Files = filelib:fold_files(Dir, ".*", true, fun(F1,Acc1) -> [F1 | Acc1] end, []),
|
|
{ParserResults, ParserErrors} = lists:foldl(fun
|
|
{ParserResults, ParserErrors} = lists:foldl(fun
|
|
("."++_, Acc) -> Acc;
|
|
("."++_, Acc) -> Acc;
|
|
- (File, {ResultAcc, ErrorAcc}) ->
|
|
|
|
- FilePath = filename:join([Dir, File]),
|
|
|
|
|
|
+ (FilePath, {ResultAcc, ErrorAcc}) ->
|
|
|
|
+ File = filename:basename(FilePath),
|
|
case parse(FilePath, Context) of
|
|
case parse(FilePath, Context) of
|
|
ok -> {ResultAcc, ErrorAcc};
|
|
ok -> {ResultAcc, ErrorAcc};
|
|
{ok, DjangoParseTree, CheckSum} -> {[{File, DjangoParseTree, CheckSum}|ResultAcc], ErrorAcc};
|
|
{ok, DjangoParseTree, CheckSum} -> {[{File, DjangoParseTree, CheckSum}|ResultAcc], ErrorAcc};
|
|
@@ -210,6 +209,8 @@ init_dtl_context(File, Module, Options) ->
|
|
doc_root = proplists:get_value(doc_root, Options, filename:dirname(File)),
|
|
doc_root = proplists:get_value(doc_root, Options, filename:dirname(File)),
|
|
custom_tags_dir = proplists:get_value(custom_tags_dir, Options, filename:join([erlydtl_deps:get_base_dir(), "priv", "custom_tags"])),
|
|
custom_tags_dir = proplists:get_value(custom_tags_dir, Options, filename:join([erlydtl_deps:get_base_dir(), "priv", "custom_tags"])),
|
|
custom_tags_module = proplists:get_value(custom_tags_module, Options, Ctx#dtl_context.custom_tags_module),
|
|
custom_tags_module = proplists:get_value(custom_tags_module, Options, Ctx#dtl_context.custom_tags_module),
|
|
|
|
+ blocktrans_fun = proplists:get_value(blocktrans_fun, Options, Ctx#dtl_context.blocktrans_fun),
|
|
|
|
+ blocktrans_locales = proplists:get_value(blocktrans_locales, Options, Ctx#dtl_context.blocktrans_locales),
|
|
vars = proplists:get_value(vars, Options, Ctx#dtl_context.vars),
|
|
vars = proplists:get_value(vars, Options, Ctx#dtl_context.vars),
|
|
reader = proplists:get_value(reader, Options, Ctx#dtl_context.reader),
|
|
reader = proplists:get_value(reader, Options, Ctx#dtl_context.reader),
|
|
compiler_options = proplists:get_value(compiler_options, Options, Ctx#dtl_context.compiler_options),
|
|
compiler_options = proplists:get_value(compiler_options, Options, Ctx#dtl_context.compiler_options),
|
|
@@ -226,6 +227,8 @@ init_dtl_context_dir(Dir, Module, Options) ->
|
|
doc_root = proplists:get_value(doc_root, Options, Dir),
|
|
doc_root = proplists:get_value(doc_root, Options, Dir),
|
|
custom_tags_dir = proplists:get_value(custom_tags_dir, Options, filename:join([erlydtl_deps:get_base_dir(), "priv", "custom_tags"])),
|
|
custom_tags_dir = proplists:get_value(custom_tags_dir, Options, filename:join([erlydtl_deps:get_base_dir(), "priv", "custom_tags"])),
|
|
custom_tags_module = proplists:get_value(custom_tags_module, Options, Module),
|
|
custom_tags_module = proplists:get_value(custom_tags_module, Options, Module),
|
|
|
|
+ blocktrans_fun = proplists:get_value(blocktrans_fun, Options, Ctx#dtl_context.blocktrans_fun),
|
|
|
|
+ blocktrans_locales = proplists:get_value(blocktrans_locales, Options, Ctx#dtl_context.blocktrans_locales),
|
|
vars = proplists:get_value(vars, Options, Ctx#dtl_context.vars),
|
|
vars = proplists:get_value(vars, Options, Ctx#dtl_context.vars),
|
|
reader = proplists:get_value(reader, Options, Ctx#dtl_context.reader),
|
|
reader = proplists:get_value(reader, Options, Ctx#dtl_context.reader),
|
|
compiler_options = proplists:get_value(compiler_options, Options, Ctx#dtl_context.compiler_options),
|
|
compiler_options = proplists:get_value(compiler_options, Options, Ctx#dtl_context.compiler_options),
|
|
@@ -299,7 +302,7 @@ parse(CheckSum, Data, Context) ->
|
|
end.
|
|
end.
|
|
|
|
|
|
parse(Data) ->
|
|
parse(Data) ->
|
|
- case erlydtl_scanner:scan(binary_to_list(Data)) of
|
|
|
|
|
|
+ case erlydtl_scanner:scan(unicode:characters_to_list(Data)) of
|
|
{ok, Tokens} ->
|
|
{ok, Tokens} ->
|
|
erlydtl_parser:parse(Tokens);
|
|
erlydtl_parser:parse(Tokens);
|
|
Err ->
|
|
Err ->
|
|
@@ -369,7 +372,7 @@ custom_forms(Dir, Module, Functions, AstInfo) ->
|
|
FunctionAsts = lists:foldl(fun({_, Function1, Function2}, Acc) -> [Function1, Function2 | Acc] end, [], Functions),
|
|
FunctionAsts = lists:foldl(fun({_, Function1, Function2}, Acc) -> [Function1, Function2 | Acc] end, [], Functions),
|
|
|
|
|
|
[erl_syntax:revert(X) || X <- [ModuleAst, ExportAst, SourceFunctionAst, DependenciesFunctionAst, TranslatableStringsFunctionAst
|
|
[erl_syntax:revert(X) || X <- [ModuleAst, ExportAst, SourceFunctionAst, DependenciesFunctionAst, TranslatableStringsFunctionAst
|
|
- | FunctionAsts]].
|
|
|
|
|
|
+ | FunctionAsts] ++ AstInfo#ast_info.pre_render_asts].
|
|
|
|
|
|
forms(File, Module, {BodyAst, BodyInfo}, {CustomTagsFunctionAst, CustomTagsInfo}, CheckSum) ->
|
|
forms(File, Module, {BodyAst, BodyInfo}, {CustomTagsFunctionAst, CustomTagsInfo}, CheckSum) ->
|
|
MergedInfo = merge_info(BodyInfo, CustomTagsInfo),
|
|
MergedInfo = merge_info(BodyInfo, CustomTagsInfo),
|
|
@@ -380,16 +383,25 @@ forms(File, Module, {BodyAst, BodyInfo}, {CustomTagsFunctionAst, CustomTagsInfo}
|
|
[erl_syntax:clause([erl_syntax:variable("Variables")], none,
|
|
[erl_syntax:clause([erl_syntax:variable("Variables")], none,
|
|
[erl_syntax:application(none,
|
|
[erl_syntax:application(none,
|
|
erl_syntax:atom(render),
|
|
erl_syntax:atom(render),
|
|
- [erl_syntax:variable("Variables"), erl_syntax:atom(none)])])]),
|
|
|
|
|
|
+ [erl_syntax:variable("Variables"), erl_syntax:list([])])])]),
|
|
Function2 = erl_syntax:application(none, erl_syntax:atom(render_internal),
|
|
Function2 = erl_syntax:application(none, erl_syntax:atom(render_internal),
|
|
- [erl_syntax:variable("Variables"), erl_syntax:variable("TranslationFun")]),
|
|
|
|
|
|
+ [erl_syntax:variable("Variables"),
|
|
|
|
+ erl_syntax:application(
|
|
|
|
+ erl_syntax:atom(proplists),
|
|
|
|
+ erl_syntax:atom(get_value),
|
|
|
|
+ [erl_syntax:atom(translation_fun), erl_syntax:variable("Options"), erl_syntax:atom(none)]),
|
|
|
|
+ erl_syntax:application(
|
|
|
|
+ erl_syntax:atom(proplists),
|
|
|
|
+ erl_syntax:atom(get_value),
|
|
|
|
+ [erl_syntax:atom(locale), erl_syntax:variable("Options"), erl_syntax:atom(none)])
|
|
|
|
+ ]),
|
|
ClauseOk = erl_syntax:clause([erl_syntax:variable("Val")], none,
|
|
ClauseOk = erl_syntax:clause([erl_syntax:variable("Val")], none,
|
|
[erl_syntax:tuple([erl_syntax:atom(ok), erl_syntax:variable("Val")])]),
|
|
[erl_syntax:tuple([erl_syntax:atom(ok), erl_syntax:variable("Val")])]),
|
|
ClauseCatch = erl_syntax:clause([erl_syntax:variable("Err")], none,
|
|
ClauseCatch = erl_syntax:clause([erl_syntax:variable("Err")], none,
|
|
[erl_syntax:tuple([erl_syntax:atom(error), erl_syntax:variable("Err")])]),
|
|
[erl_syntax:tuple([erl_syntax:atom(error), erl_syntax:variable("Err")])]),
|
|
Render2FunctionAst = erl_syntax:function(erl_syntax:atom(render),
|
|
Render2FunctionAst = erl_syntax:function(erl_syntax:atom(render),
|
|
[erl_syntax:clause([erl_syntax:variable("Variables"),
|
|
[erl_syntax:clause([erl_syntax:variable("Variables"),
|
|
- erl_syntax:variable("TranslationFun")], none,
|
|
|
|
|
|
+ erl_syntax:variable("Options")], none,
|
|
[erl_syntax:try_expr([Function2], [ClauseOk], [ClauseCatch])])]),
|
|
[erl_syntax:try_expr([Function2], [ClauseOk], [ClauseCatch])])]),
|
|
|
|
|
|
SourceFunctionTuple = erl_syntax:tuple(
|
|
SourceFunctionTuple = erl_syntax:tuple(
|
|
@@ -409,7 +421,7 @@ forms(File, Module, {BodyAst, BodyInfo}, {CustomTagsFunctionAst, CustomTagsInfo}
|
|
|
|
|
|
RenderInternalFunctionAst = erl_syntax:function(
|
|
RenderInternalFunctionAst = erl_syntax:function(
|
|
erl_syntax:atom(render_internal),
|
|
erl_syntax:atom(render_internal),
|
|
- [erl_syntax:clause([erl_syntax:variable("Variables"), erl_syntax:variable("TranslationFun")], none,
|
|
|
|
|
|
+ [erl_syntax:clause([erl_syntax:variable("Variables"), erl_syntax:variable("TranslationFun"), erl_syntax:variable("CurrentLocale")], none,
|
|
[BodyAstTmp])]),
|
|
[BodyAstTmp])]),
|
|
|
|
|
|
ModuleAst = erl_syntax:attribute(erl_syntax:atom(module), [erl_syntax:atom(Module)]),
|
|
ModuleAst = erl_syntax:attribute(erl_syntax:atom(module), [erl_syntax:atom(Module)]),
|
|
@@ -465,6 +477,8 @@ body_ast(DjangoParseTree, Context, TreeWalker) ->
|
|
_ -> Contents
|
|
_ -> Contents
|
|
end,
|
|
end,
|
|
body_ast(Block, Context, TreeWalkerAcc);
|
|
body_ast(Block, Context, TreeWalkerAcc);
|
|
|
|
+ ({'blocktrans', {identifier, _, Name}, Contents}, TreeWalkerAcc) ->
|
|
|
|
+ blocktrans_ast(Name, Contents, Context, TreeWalkerAcc);
|
|
({'call', {'identifier', _, Name}}, TreeWalkerAcc) ->
|
|
({'call', {'identifier', _, Name}}, TreeWalkerAcc) ->
|
|
call_ast(Name, TreeWalkerAcc);
|
|
call_ast(Name, TreeWalkerAcc);
|
|
({'call', {'identifier', _, Name}, With}, TreeWalkerAcc) ->
|
|
({'call', {'identifier', _, Name}, With}, TreeWalkerAcc) ->
|
|
@@ -634,6 +648,27 @@ with_dependency(FilePath, {{Ast, Info}, TreeWalker}) ->
|
|
empty_ast(TreeWalker) ->
|
|
empty_ast(TreeWalker) ->
|
|
{{erl_syntax:list([]), #ast_info{}}, TreeWalker}.
|
|
{{erl_syntax:list([]), #ast_info{}}, TreeWalker}.
|
|
|
|
|
|
|
|
+blocktrans_ast(Name, Contents, Context, TreeWalker) ->
|
|
|
|
+ case Context#dtl_context.blocktrans_fun of
|
|
|
|
+ none ->
|
|
|
|
+ body_ast(Contents, Context, TreeWalker);
|
|
|
|
+ BlockTransFun when is_function(BlockTransFun) ->
|
|
|
|
+ {{DefaultAst, AstInfo}, TreeWalker1} = body_ast(Contents, Context, TreeWalker),
|
|
|
|
+ {FinalAstInfo, FinalTreeWalker, Clauses} = lists:foldr(fun(Locale, {AstInfoAcc, ThisTreeWalker, ClauseAcc}) ->
|
|
|
|
+ case BlockTransFun(Name, Locale) of
|
|
|
|
+ default ->
|
|
|
|
+ {AstInfoAcc, ThisTreeWalker, ClauseAcc};
|
|
|
|
+ Body ->
|
|
|
|
+ {ok, DjangoParseTree} = parse(Body),
|
|
|
|
+ {{ThisAst, ThisAstInfo}, TreeWalker2} = body_ast(DjangoParseTree, Context, ThisTreeWalker),
|
|
|
|
+ {merge_info(ThisAstInfo, AstInfoAcc), TreeWalker2,
|
|
|
|
+ [erl_syntax:clause([erl_syntax:string(Locale)], none, [ThisAst])|ClauseAcc]}
|
|
|
|
+ end
|
|
|
|
+ end, {AstInfo, TreeWalker1, []}, Context#dtl_context.blocktrans_locales),
|
|
|
|
+ Ast = erl_syntax:case_expr(erl_syntax:variable("CurrentLocale"),
|
|
|
|
+ Clauses ++ [erl_syntax:clause([erl_syntax:underscore()], none, [DefaultAst])]),
|
|
|
|
+ {{Ast, FinalAstInfo}, FinalTreeWalker}
|
|
|
|
+ end.
|
|
|
|
|
|
translated_ast({string_literal, _, String}, Context, TreeWalker) ->
|
|
translated_ast({string_literal, _, String}, Context, TreeWalker) ->
|
|
NewStr = unescape_string_literal(String),
|
|
NewStr = unescape_string_literal(String),
|
|
@@ -1060,7 +1095,10 @@ call_ast(Module, Variable, AstInfo, TreeWalker) ->
|
|
AppAst = erl_syntax:application(
|
|
AppAst = erl_syntax:application(
|
|
erl_syntax:atom(Module),
|
|
erl_syntax:atom(Module),
|
|
erl_syntax:atom(render),
|
|
erl_syntax:atom(render),
|
|
- [Variable, erl_syntax:variable("TranslationFun")]),
|
|
|
|
|
|
+ [Variable, erl_syntax:list([
|
|
|
|
+ erl_syntax:tuple([erl_syntax:atom(translation_fun), erl_syntax:variable("TranslationFun")]),
|
|
|
|
+ erl_syntax:tuple([erl_syntax:atom(locale), erl_syntax:variable("CurrentLocale")])
|
|
|
|
+ ])]),
|
|
RenderedAst = erl_syntax:variable("Rendered"),
|
|
RenderedAst = erl_syntax:variable("Rendered"),
|
|
OkAst = erl_syntax:clause(
|
|
OkAst = erl_syntax:clause(
|
|
[erl_syntax:tuple([erl_syntax:atom(ok), RenderedAst])],
|
|
[erl_syntax:tuple([erl_syntax:atom(ok), RenderedAst])],
|