Browse Source

support translation fun initialization.

Andreas Stenius 11 years ago
parent
commit
fc5fb603b1
5 changed files with 57 additions and 10 deletions
  1. 19 0
      README.markdown
  2. 5 2
      src/erlydtl_beam_compiler.erl
  3. 2 1
      src/erlydtl_compiler.erl
  4. 20 5
      src/erlydtl_runtime.erl
  5. 11 2
      test/erlydtl_test_defs.erl

+ 19 - 0
README.markdown

@@ -244,6 +244,21 @@ Options is a proplist possibly containing:
   See description of the `translation_fun` render option for more
   details on the translation `context`.
 
+  Notice, you may instead pass a `fun/0`, `{Module, Function}` or
+  `{Module, Function, Args}` which will be called recursively until it
+  yields a valid translation function, at which time any needed
+  translation setup actions can be carried out prior to returning the
+  next step (either another setup function/tuple, or the translation
+  function).
+
+  ```erlang
+  %% sample translation setup
+  fun () ->
+      translation_engine:init(),
+      fun translation_engine:translate/2
+  end
+  ```
+
 * `tuples_0_based` - **Compatibility warning** Defaults to `false`,
   giving 1-based tuple access, as is common practice in Erlang. Set it
   to `true` to get 1-based access as in Django, or to `defer` to not
@@ -351,6 +366,10 @@ Same as `render/1`, but with the following options:
   {% endblocktrans %}
   ```
 
+  Notice, the translation fun can also be a `fun/0` or a MFA-tuple to
+  setup the translation prior to rendering. See the `translation_fun`
+  compile option for more details.
+
 * `lists_0_based` - If the compile option `lists_0_based` was set to
   `defer`, pass this option (or set it to true, `{lists_0_based,
   true}`) to get 0-based list indexing when rendering the

+ 5 - 2
src/erlydtl_beam_compiler.erl

@@ -506,7 +506,8 @@ options_match_ast(Context) ->
 
 options_match_ast(Context, TreeWalker) ->
     [
-     ?Q("_TranslationFun = proplists:get_value(translation_fun, RenderOptions, none)"),
+     ?Q(["_TranslationFun = erlydtl_runtime:init_translation(",
+         "  proplists:get_value(translation_fun, RenderOptions, none))"]),
      ?Q("_CurrentLocale = proplists:get_value(locale, RenderOptions, default)"),
      ?Q("_RecordInfo = _@info", [{info, merl:term(Context#dtl_context.record_info)}])
      | case call_extension(Context, setup_render_ast, [Context, TreeWalker]) of
@@ -887,7 +888,9 @@ translated_ast({string_literal, _, String}, Context, TreeWalker) ->
                     compiletime_trans_ast(Fun, Text, Context, TreeWalker);
                 Fun when is_function(Fun, 1) ->
                     compiletime_trans_ast(fun (T, _) -> Fun(T) end,
-                                          Text, Context, TreeWalker)
+                                          Text, Context, TreeWalker);
+                Fun ->
+                    empty_ast(?ERR({translation_fun, Fun}, TreeWalker))
             end;
         TranslatedAst ->
             TranslatedAst

+ 2 - 1
src/erlydtl_compiler.erl

@@ -260,7 +260,8 @@ init_context(ParseTrail, DefDir, Module, Options) ->
            custom_tags_dir = proplists:get_value(
                                custom_tags_dir, Options,
                                filename:join([erlydtl_deps:get_base_dir(), "priv", "custom_tags"])),
-           trans_fun = proplists:get_value(translation_fun, Options, Ctx#dtl_context.trans_fun),
+           trans_fun = erlydtl_runtime:init_translation(
+                         proplists:get_value(translation_fun, Options, Ctx#dtl_context.trans_fun)),
            trans_locales = Locales,
            vars = proplists:get_value(default_vars, Options, Ctx#dtl_context.vars),
            const = proplists:get_value(constants, Options, Ctx#dtl_context.const),

+ 20 - 5
src/erlydtl_runtime.erl

@@ -10,6 +10,12 @@
 -type new_translate_fun() :: fun((phrase(), locale()) -> iodata() | default).
 -type translate_fun() :: new_translate_fun() | old_translate_fun().
 
+-type init_translation() :: none
+                          | fun (() -> init_translation())
+                          | {M::atom(), F::atom()}
+                          | {M::atom(), F::atom(), A::list()}
+                          | translate_fun().
+
 -define(IFCHANGED_CONTEXT_VARIABLE, erlydtl_ifchanged_context).
 
 find_value(Key, Data, Options) when is_atom(Key), is_tuple(Data) ->
@@ -136,6 +142,19 @@ regroup([Item|Rest], Attribute, [[{grouper, PrevGrouper}, {list, PrevList}]|Acc]
             regroup(Rest, Attribute, [[{grouper, Value}, {list, [Item]}], [{grouper, PrevGrouper}, {list, lists:reverse(PrevList)}]|Acc])
     end.
 
+-spec init_translation(init_translation()) -> none | translate_fun().
+init_translation(none) -> none;
+init_translation(Fun) when is_function(Fun, 0) ->
+    init_translation(Fun());
+init_translation({M, F}) ->
+    init_translation({M, F, []});
+init_translation({M, F, A}) ->
+    init_translation(apply(M, F, A));
+init_translation(Fun)
+  when is_function(Fun, 1); is_function(Fun, 2) -> Fun;
+init_translation(Other) ->
+    throw({translation_fun, Other}).
+
 -spec translate(Phrase, Locale, Fun) -> iodata() | default when
       Phrase :: phrase(),
       Locale :: locale(),
@@ -163,7 +182,6 @@ do_translate(Phrase, Locale, TranslationFun)
   when is_function(TranslationFun, 2) ->
     TranslationFun(Phrase, Locale).
 
-
 %% @doc Translate and interpolate 'blocktrans' content.
 %% Pre-requisites:
 %%  * `Variables' should be sorted
@@ -178,10 +196,7 @@ translate_block(Phrase, Locale, Variables, TranslationFun) ->
             try interpolate_variables(Translated, Variables)
             catch
                 {no_close_var, T} ->
-                    io:format(
-                      standard_error,
-                      "Warning: template translation: variable not closed: \"~s\"~n",
-                      [T]),
+                    io:format(standard_error, "Warning: template translation: variable not closed: \"~s\"~n", [T]),
                     default;
                 _:_ -> default
             end

+ 11 - 2
test/erlydtl_test_defs.erl

@@ -1393,12 +1393,21 @@ all_test_defs() ->
 
        %% This does work, but always prints a warning to std err.. :/
        %% Warning: template translation: variable not closed: "bar {{ 123"
-
-       %% {"interpolate error",
+       %% {"variable error",
        %%  <<"{% blocktrans %}foo{{ bar }}{% endblocktrans %}">>,
        %%  [], [{translation_fun, fun (_) -> "bar {{ 123" end}],
        %%  <<"foo">>}
       ]},
+     {"i18n",
+      [{"setup translation context, using fun, at render time",
+        <<"{% trans 'foo' %}">>, [],
+        [{translation_fun, fun () -> fun (Msg) -> string:to_upper(Msg) end end}],
+        <<"FOO">>},
+       {"setup translation context, using fun, at compile time",
+        <<"{% trans 'foo' %}">>, [], [],
+        [{locale, default}, {translation_fun, fun () -> fun lists:reverse/1 end}],
+        <<"oof">>}
+      ]},
      {"verbatim",
       [{"Plain verbatim",
         <<"{% verbatim %}{{ oh no{% foobar %}{% endverbatim %}">>, [],