Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Add parse_transform handler.

  • Loading branch information...
commit fd137ff1b58d0bcbbc4f18e6587f4dd9013815a6 1 parent dcbee1b
@arcusfelis arcusfelis authored
Showing with 2,146 additions and 29 deletions.
  1. +60 −0 .eunit/TEST-i18n_sup.xml
  2. BIN  .eunit/ct_expand.beam
  3. +272 −0 .eunit/ct_expand.erl
  4. BIN  .eunit/i18n.beam
  5. +20 −0 .eunit/i18n.erl
  6. BIN  .eunit/i18n_app.beam
  7. +39 −0 .eunit/i18n_app.erl
  8. BIN  .eunit/i18n_collation.beam
  9. +176 −0 .eunit/i18n_collation.erl
  10. BIN  .eunit/i18n_collation_tests.beam
  11. +169 −0 .eunit/i18n_collation_tests.erl
  12. BIN  .eunit/i18n_expand.beam
  13. +6 −0 .eunit/i18n_expand.erl
  14. BIN  .eunit/i18n_locale.beam
  15. +69 −0 .eunit/i18n_locale.erl
  16. BIN  .eunit/i18n_locale_server.beam
  17. +102 −0 .eunit/i18n_locale_server.erl
  18. BIN  .eunit/i18n_message.beam
  19. +58 −0 .eunit/i18n_message.erl
  20. BIN  .eunit/i18n_nif.beam
  21. +151 −0 .eunit/i18n_nif.erl
  22. BIN  .eunit/i18n_search.beam
  23. +63 −0 .eunit/i18n_search.erl
  24. BIN  .eunit/i18n_string.beam
  25. +147 −0 .eunit/i18n_string.erl
  26. BIN  .eunit/i18n_string_tests.beam
  27. +113 −0 .eunit/i18n_string_tests.erl
  28. BIN  .eunit/i18n_sup.beam
  29. +57 −0 .eunit/i18n_sup.erl
  30. +4 −6 .gitignore
  31. +273 −6 c_src/i18n_nif.cpp
  32. BIN  fprof.trace
  33. +11 −1 include/i18n.hrl
  34. +1 −0  priv/README.md
  35. BIN  priv/i18n_nif.so
  36. +272 −0 src/ct_expand.erl
  37. +0 −4 src/i18n.erl
  38. +2 −1  src/i18n_collation_tests.erl
  39. +6 −0 src/i18n_expand.erl
  40. +11 −3 src/i18n_locale.erl
  41. +4 −4 src/i18n_locale_server.erl
  42. +38 −1 src/i18n_nif.erl
  43. +22 −3 src/i18n_string.erl
View
60 .eunit/TEST-i18n_sup.xml
@@ -0,0 +1,60 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<testsuite tests="13" failures="0" errors="0" skipped="0" time="0.000" name="module 'i18n_sup'">
+ <testcase time="0.033" name="i18n_string_tests:from_utf8_test/0_0"/>
+ <testcase time="0.001" name="i18n_string_tests:simple_open_test/0_0"/>
+ <testcase time="1.012" name="i18n_string_tests:prop_from_char_test_/0_0" description="1 char conversation prop testing.">
+ <system-out>Failed!
+
+Failed after 1 tests with false
+Simplified:
+ { Xs } = {0}
+
+ </system-out>
+ </testcase>
+ <testcase time="6.700" name="i18n_string_tests:prop_from_utf8_test_/0_0" description="from_utf8 and to_utf8 prop testing.">
+ <system-out>.....Failed!
+
+Failed after 6 tests with false
+Simplified:
+ { Xs } = {&lt;&lt;243,157,182,164,243,182,149,153,240,156,170,179,240,184,143,149,242,156,164,173,243,173,143,159,242,128,169,128,241,149,163,162,242,184,130,146,240,152,146,135,243,175,167,149,243,180,132,145,242,145,164,159,243,165,191,153,237,133,145,243,163,186,174,243,140,135,134,0,242,191,162,162,240,157,134,187,239,131,128,242,157,188,184,241,148,160,164,242,134,146,141,243,176,185,152,242,164,138,179,241,182,178,182,241,180,132,137,240,165,146,146,242,141,154,161,241,135,135,140,240,175,184,137,242,181,167,154,242,135,178,170,241,143,148,182,243,179,146,171,243,188,136,172,243,129,176,133,242,182,176,182,238,143,158,239,184,129,243,155,133,153,242,178,146,151,241,189,156,164,243,153,187,156,244,141,163,175,244,139,170,181,243,180,185,150,241,153,138,175,241,162,189,138,240,187,162,154,227,157,190,243,137,151,141,242,134,133,135,241,156,145,178,225,128,161,241,165,165,188,242,184,189,190,240,164,141,188,242,172,165,165,241,185,179,154,244,139,135,151,243,155,190,162,242,175,133,191,242,176,146,140,242,173,129,154,241,182,155,129,243,184,161,188,242,134,164,135,242,142,134,148,243,149,144,142,243,167,190,161,242,163,132,133,241,133,191,131,239,151,128,242,185,145,186,241,132,144,128,234,137,173,244,141,145,130,242,161,164,160,240,188,146,141,243,175,163,168,241,167,132,176,241,187,152,152,242,154,159,158,244,136,165,181,243,159,185,191,243,169,134,134,242,147,162,184,240,157,177,188,242,181,182,185,242,188,169,167,235,187,143,240,155,179,155,242,129,160,148,241,156,176,186,243,143,138,160,241,135,152,172,242,143,191,147,241,154,163,167&gt;&gt;}
+
+ </system-out>
+ </testcase>
+ <testcase time="5.617" name="i18n_string_tests:prop_case_test_/0_0" description="to_lower and to_upper prop testing.">
+ <system-out>..............................................................................Failed!
+
+Failed after 79 tests with false
+Simplified:
+ { Xs } = {&lt;&lt;241,181,130,130,241,135,128,146,226,153,174,242,135,144,167,242,128,181,142,235,160,157,240,188,162,181,242,131,186,173,243,146,170,129,243,145,177,136,243,136,133,151,243,167,172,187,241,175,157,178,244,131,155,182,241,182,190,161,242,185,174,190,243,141,152,191,240,187,168,141,240,167,157,176,215,149,240,145,158,129,243,138,155,153,233,145,160,241,160,141,186,240,149,153,173,242,149,183,184,232,176,138,240,186,156,174,240,182,145,164,242,137,144,143,240,183,148,191,241,158,132,182,243,136,186,178,242,146,129,145,243,175,174,167,243,173,150,171,242,136,168,129,242,129,174,190,240,152,131,165,240,172,130,158,240,150,139,136,233,183,142,241,191,130,142,243,186,154,174,244,135,188,132,242,146,183,166,242,154,182,157,242,147,178,140,244,141,175,164,240,169,128,160,243,141,140,130,235,137,136,242,130,158,148,243,185,136,165,241,161,144,184,242,157,134,132,225,191,130,243,180,159,132,241,157,189,138,241,164,158,172,241,145,131,182,242,173,136,135,240,152,137,129,242,133,128,147,241,153,183,128,240,176,163,186,242,174,161,145,241,153,144,128,243,166,190,178,242,155,184,161,241,157,142,138,241,166,131,163,243,147,167,131,241,188,130,161,241,137,189,160,244,142,129,160,243,189,167,160,243,144,147,191,242,165,166,179,243,142,189,164,240,144,179,163,243,138,165,183,240,151,172,153,241,152,140,129,241,141,181,156,240,149,158,182,240,177,172,150,241,157,175,172,243,135,180,174,243,160,132,183,242,172,147,147,240,187,146,163,241,170,176,150,242,172,191,135,243,132,166,162,243,166,182,179,240,155,187,158,244,135,175,131,243,189,175,137,243,168,130,188&gt;&gt;}
+
+ </system-out>
+ </testcase>
+ <testcase time="0.017" name="i18n_string_tests:prop_len_test_/0_0" description="len prop testing.">
+ <system-out>....................................................................................................
+Ran 100 tests
+
+ </system-out>
+ </testcase>
+ <testcase time="0.034" name="i18n_string_tests:prop_get_iterator_parallel_test_/0_0" description="Cloner for the iterator type prop testing.">
+ <system-out>....................................................................................................
+Ran 100 tests
+
+ </system-out>
+ </testcase>
+ <testcase time="0.000" name="i18n_collation_tests:simple_open_test/0_0"/>
+ <testcase time="0.000" name="i18n_collation_tests:simple_sort_key_test/0_0"/>
+ <testcase time="0.000" name="i18n_collation_tests:simple_sort_test_/0_45" description="Simple input data."/>
+ <testcase time="0.016" name="i18n_collation_tests:natural_sort_test_/0_136" description="Using official test strings from Dave Koelle"/>
+ <testcase time="0.035" name="i18n_collation_tests:prop_sort_key_test_/0_0" description="sort_key prop testing.">
+ <system-out>....................................................................................................
+Ran 100 tests
+
+ </system-out>
+ </testcase>
+ <testcase time="0.023" name="i18n_collation_tests:prop_compare_test_/0_0" description="compare prop testing.">
+ <system-out>....................................................................................................
+Ran 100 tests
+
+ </system-out>
+ </testcase>
+</testsuite>
View
BIN  .eunit/ct_expand.beam
Binary file not shown
View
272 .eunit/ct_expand.erl
@@ -0,0 +1,272 @@
+%%% The contents of this file are subject to the Erlang Public License,
+%%% Version 1.0, (the "License"); you may not use this file except in
+%%% compliance with the License. You may obtain a copy of the License at
+%%% http://www.erlang.org/license/EPL1_0.txt
+%%%
+%%% Software distributed under the License is distributed on an "AS IS"
+%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%%% the License for the specific language governing rights and limitations
+%%% under the License.
+%%%
+%%% The Original Code is ___
+%%%
+%%% The Initial Developer of the Original Code is Ericsson Telecom
+%%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson
+%%% Telecom AB. All Rights Reserved.
+%%%
+%%% Contributor(s): ______________________________________.
+%%%
+%%% ------------------------------------------------------------------
+-module(ct_expand).
+-id('').
+-vsn('').
+-date('2006-08-22').
+-author('ulf.wiger@ericsson.com').
+
+%%% This module is a quick hack to allow for compile-time expansion of
+%%% complex expressions. To trigger the expansion, wrap the expression
+%%% inside a call to ct_expand:term/1.
+%%%
+%%% A simple example:
+%%%
+%%% f() ->
+%%% ct_expand:term(
+%%% ordsets:from_list([this, is, a, list, 'of', atoms])
+%%% ).
+%%%
+%%% The parse_transform evaluates this into:
+%%%
+%%% f() -> [a, atoms, is, list, 'of', this].
+%%%
+
+
+-export([function/4,
+ format_error/1]).
+-export([parse_transform/2]).
+-export([term/1]).
+
+
+-define(ERROR(R, T, F, I),
+ begin
+ rpt_error(R, T, F, I),
+ throw({error,erl_syntax:get_pos(
+ proplists:get_value(form,I)),{unknown,R}})
+ end).
+
+-import(erl_syntax, [clause/3,
+ clause_patterns/1,
+ clause_body/1,
+ clause_guard/1,
+ match_expr/2,
+ function_clauses/1,
+ get_pos/1,
+ add_ann/2,
+ get_ann/1]).
+
+term(X) -> X.
+
+
+%%% ==================================================================
+%%% This is the custom code that's been added. The rest of the module
+%%% is 'library code' (or should be...)
+%%% Any expression wrapped inside a call to ct_expand:term(Expr) is
+%%% replaced at compile-time with the results from erl_eval([Expr], [])
+%%% (note: no bindings)
+%%%
+parse_transform(Forms, Options) ->
+ function({?MODULE, term, 1},
+ fun(Form, _Context) ->
+ io:format("expanding...~n", []),
+ case erl_syntax:application_arguments(Form) of
+ [Expr] ->
+ case erl_eval:exprs(revert_tree([Expr]), []) of
+ {value, Value, _} ->
+ erl_syntax:abstract(Value);
+ Other ->
+ erlang:error({cannot_evaluate,
+ [Expr, Other]})
+ end;
+ _Args ->
+ erlang:error(illegal_form)
+ end
+ end, Forms, Options).
+
+%%% End custom code.
+%%% ==================================================================
+
+
+%%% API: function({Module, Function, Arity}, Fun, Forms, Options) ->
+%%% NewForms
+%%%
+%%% Forms and Options are the arguments passed to the parse_transform/2
+%%% function.
+%%% {Module, Function, Arity} is the function call to transform
+%%% Fun(Form, Context) -> NewForm is the fun provided by the caller.
+%%%
+%%% Context is a property list, containing the following properties:
+%%% - {file, Filename}
+%%% - {module, ModuleName}
+%%% - {function, FunctionName} % name of the enclosing function
+%%% - {arity, Arity :: integer()} % arity of same
+%%% - {var_names, Vars :: [atom()]} % generated variables binding the
+%%% % function arguments.
+%%% % length(Vars) == Arity
+%%%
+function({_Module, _Function, _Arity} = MFA, F,
+ Forms, Options) when is_function(F) ->
+ parse_transform(MFA, F, Forms, Options).
+
+parse_transform(MFA, Fun, Forms, _Options) ->
+ [File|_] = [F || {attribute,_,file,{F,_}} <- Forms],
+ try begin
+ NewTree = xform(MFA, Fun, Forms, [{file, File}]),
+ revert_tree(NewTree)
+ end
+ catch
+ throw:{error,Ln,What} ->
+ {error, [{File, [{Ln,?MODULE,What}]}], []}
+ end.
+
+revert_tree(Tree) ->
+ [erl_syntax:revert(T) || T <- lists:flatten(Tree)].
+
+
+format_error(Other) ->
+ lists:flatten(
+ io_lib:format("unknown error in parse_transform: ~p", [Other])).
+
+
+
+
+xform({M,F,A}, Fun, Forms, Context0) ->
+ Bef = fun(function, Form, Ctxt) ->
+ {Fname, Arity} = erl_syntax_lib:analyze_function(Form),
+ VarNames = erl_syntax_lib:new_variable_names(
+ Arity,
+ erl_syntax_lib:variables(Form)),
+ {Form, [{function, Fname},
+ {arity, Arity},
+ {var_names, VarNames}|Ctxt]};
+ (_, Form, Context) ->
+ {Form, Context}
+ end,
+ Aft = fun(application, Form, Context) ->
+ case erl_syntax_lib:analyze_application(Form) of
+ {M, {F, A}} ->
+ add_ann(
+ bind_state,
+ Fun(Form, Context));
+ _ ->
+ Form
+ end;
+ (function, Form, Context) ->
+ Form1 =
+ erl_syntax_lib:map_subtrees(
+ fun(Clause) ->
+ case should_i_bind(Clause) of
+ true ->
+ Pats = clause_patterns(Clause),
+ CBod = clause_body(Clause),
+ CGd = clause_guard(Clause),
+ Pats1 =
+ lists:zipwith(
+ fun(V, P) ->
+ match_expr(v(V), P)
+ end,
+ proplists:get_value(
+ var_names, Context),
+ Pats),
+ clause(Pats1, CGd, CBod);
+ false ->
+ Clause
+ end
+ end, Form),
+ Form1;
+ (_, Form, _Context) ->
+ Form
+ end,
+ [Module] = [Mx || {attribute, _, module, Mx} <- Forms],
+ transform(Forms, Bef, Aft, [{module, Module}|Context0]).
+
+
+transform(Forms, Before, After, Context) ->
+ F1 =
+ fun(Form) ->
+ Type = erl_syntax:type(Form),
+ {Form1, Context1} =
+ try Before(Type, Form, Context)
+ catch
+ error:Reason ->
+ ?ERROR(Reason, 'before', Before,
+ [{type, Type},
+ {context, Context},
+ {form, Form}])
+ end,
+ Form2 =
+ case erl_syntax:subtrees(Form1) of
+ [] ->
+ Form1;
+ List ->
+ NewList =
+ transform(
+ List, Before, After, Context1),
+ erl_syntax:update_tree(Form, NewList)
+ end,
+ Type2 = erl_syntax:type(Form2),
+ try After(Type2, Form2, Context1)
+ catch
+ error:Reason2 ->
+ ?ERROR(Reason2, 'after', After,
+ [{type, Type2},
+ {context, Context1},
+ {form, Form2}])
+ end
+ end,
+ F2 = fun(List) when is_list(List) ->
+ map(F1, List);
+ (Form) ->
+ F1(Form)
+ end,
+ map(F2, Forms).
+
+%%% Slightly modified version of lists:mapfoldl/3
+%%% Here, F/2 is able to insert forms before and after the form
+%%% in question. The inserted forms are not transformed afterwards.
+map(F, [Hd|Tail]) ->
+ {Before, Res, After} =
+ case F(Hd) of
+ {Be, _, Af} = Result when is_list(Be), is_list(Af) ->
+ Result;
+ R1 ->
+ {[], R1, []}
+ end,
+ Rs = map(F, Tail),
+ Before ++ [Res| After ++ Rs];
+map(F, []) when is_function(F, 1) -> [].
+
+
+
+rpt_error(Reason, BeforeOrAfter, Fun, Info) ->
+ Fmt = lists:flatten(
+ ["*** ERROR in parse_transform function:~n"
+ "*** Reason = ~p~n"
+ "*** applying ~w fun (~p)~n",
+ ["*** ~10w = ~p~n" || _ <- Info]]),
+ Args = [Reason, BeforeOrAfter, Fun |
+ lists:foldr(
+ fun({K,V}, Acc) ->
+ [K, V | Acc]
+ end, [], Info)],
+ io:format(Fmt, Args).
+
+
+should_i_bind(Tree) ->
+ erl_syntax_lib:fold(
+ fun(T, Flag) ->
+ lists:member(bind_state, get_ann(T)) or Flag
+ end, false, Tree).
+
+
+
+v(V) ->
+ erl_syntax:variable(V).
View
BIN  .eunit/i18n.beam
Binary file not shown
View
20 .eunit/i18n.erl
@@ -0,0 +1,20 @@
+%% @doc i18n.
+%% @private
+
+-module(i18n).
+-export([start/0, stop/0]).
+-export([repeat/2]).
+
+%% @spec start() -> ok
+%% @doc Start the ux server.
+start() ->
+ application:start(i18n).
+
+repeat(N, F) when N>0 ->
+F(), repeat(N-1, F);
+repeat(N, F) -> ok.
+
+%% @spec stop() -> ok
+%% @doc Stop the ux server.
+stop() ->
+ application:stop(i18n).
View
BIN  .eunit/i18n_app.beam
Binary file not shown
View
39 .eunit/i18n_app.erl
@@ -0,0 +1,39 @@
+% vim: set filetype=erlang shiftwidth=4 tabstop=4 expandtab tw=80:
+
+%%% =====================================================================
+%%% Copyright 2011 Uvarov Michael
+%%%
+%%% Licensed under the Apache License, Version 2.0 (the "License");
+%%% you may not use this file except in compliance with the License.
+%%% You may obtain a copy of the License at
+%%%
+%%% http://www.apache.org/licenses/LICENSE-2.0
+%%%
+%%% Unless required by applicable law or agreed to in writing, software
+%%% distributed under the License is distributed on an "AS IS" BASIS,
+%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%%% See the License for the specific language governing permissions and
+%%% limitations under the License.
+%%%
+%%% $Id$
+%%%
+%%% @copyright 2010-2011 Michael Uvarov
+%%% @author Michael Uvarov <freeakk@gmail.com>
+%%% =====================================================================
+
+-module(i18n_app).
+
+-behaviour(application).
+
+%% Application callbacks
+-export([start/2, stop/1]).
+
+%% ===================================================================
+%% Application callbacks
+%% ===================================================================
+
+start(_StartType, _StartArgs) ->
+ i18n_sup:start_link().
+
+stop(_State) ->
+ ok.
View
BIN  .eunit/i18n_collation.beam
Binary file not shown
View
176 .eunit/i18n_collation.erl
@@ -0,0 +1,176 @@
+% vim: set filetype=erlang shiftwidth=4 tabstop=4 expandtab tw=80:
+
+%%% =====================================================================
+%%% Copyright 2011 Uvarov Michael
+%%%
+%%% Licensed under the Apache License, Version 2.0 (the "License");
+%%% you may not use this file except in compliance with the License.
+%%% You may obtain a copy of the License at
+%%%
+%%% http://www.apache.org/licenses/LICENSE-2.0
+%%%
+%%% Unless required by applicable law or agreed to in writing, software
+%%% distributed under the License is distributed on an "AS IS" BASIS,
+%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%%% See the License for the specific language governing permissions and
+%%% limitations under the License.
+%%%
+%%% $Id$
+%%%
+%%% @copyright 2010-2011 Michael Uvarov
+%%% @author Michael Uvarov <freeakk@gmail.com>
+%%% =====================================================================
+
+-module(i18n_collation).
+-include_lib("i18n.hrl").
+
+
+% NIFs
+-export([open/0, open/1, open/2]).
+-export([sort_key/2, compare/3, sort/2]).
+
+-export([map_sort/2]).
+
+
+%%
+%% Types
+%%
+
+-type i18n_locale_id() :: atom().
+-type i18n_string() :: binary().
+
+
+-type i18n_collation_option() ::
+ 'primary'
+ | 'secondary'
+ | 'tertiary'
+ | 'quantiary'
+ | 'identical'
+ | 'shifted'
+ | 'non_ignorable'
+ | 'lower_first'
+ | 'upper_first'
+ | 'numeric'
+ | 'french_accents'
+ | 'hiragana'
+ | 'normalization'.
+
+-type resource() :: <<>>.
+-type i18n_collator() :: resource().
+-type i18n_sort_key() :: binary().
+-type i18n_compare_result() ::'less' | 'equal' | 'greater'.
+
+
+
+%%
+%% API
+%%
+
+-spec open() -> i18n_collator().
+open() ->
+ L = i18n_locale:get_locale(),
+ open(L).
+
+-spec open(i18n_locale_id()) -> i18n_collator().
+open(L) when is_atom(L) ->
+ ?TRY_RES(?IM:get_collator(L)).
+
+-spec open(i18n_locale_id(), [i18n_collation_option()]) -> i18n_collator().
+open(L, Options) ->
+ C = open(L),
+ do_options(C, Options).
+
+-spec sort_key(i18n_collator(), i18n_string()) -> i18n_sort_key().
+sort_key(C, S) ->
+ ?TRY_BIN(?IM:sort_key(C, S)).
+
+-spec compare(i18n_collator(), i18n_string(), i18n_string()) ->
+ i18n_compare_result().
+compare(C, S1, S2) ->
+ ?TRY_ATOM(?IM:compare(C, S1, S2)).
+
+
+-spec sort(i18n_collator(),[i18n_string()]) ->
+ [i18n_string()].
+sort(C, Ss) ->
+ % Step 1: produce array of sort keys
+ F = fun(S) ->
+ sort_key(C, S)
+ end,
+ map_sort(F, Ss).
+
+
+%% @doc Xx list of strings to sort.
+%% F produces a sort keys.
+-spec map_sort(fun(), [any()]) -> [any()].
+map_sort(F, Xx)
+ when is_function(F) ->
+ % Step 1: produce array of sort keys
+ PairFn = fun(X) ->
+ Key = F(X),
+ {Key, X}
+ end,
+ Keys = lists:map(PairFn, Xx),
+
+ % Step 2: sort array
+ SortedKeys = lists:keysort(1, Keys),
+
+ % Step 3: Return result
+ RetFn = fun({_Key, X}) -> X end,
+ lists:map(RetFn, SortedKeys).
+
+
+
+
+%%
+%% Helpers
+%%
+
+do_options(C, [H|T])
+ when H=:='primary'
+ orelse H=:='secondary'
+ orelse H=:='tertiary'
+ orelse H=:='quanternary'
+ orelse H=:='identical' ->
+ set_strength(C, H),
+ do_options(C, T);
+do_options(C, [H|T])
+ when H=:='shifted'
+ orelse H=:='non_ignorable' ->
+ set_alternate(C, H),
+ do_options(C, T);
+do_options(C, [H|T])
+ when H=:='lower_first'
+ orelse H=:='upper_first' ->
+ set_case_first(C, H),
+ do_options(C, T);
+do_options(C, [H|T])
+ when H=:='numeric' % natural sort
+ orelse H=:='french_accents' % bachwards on level 2
+ orelse H=:='hiragana' % on level 4
+ orelse H=:='normalization' ->
+ on(C, H),
+ do_options(C, T);
+do_options(C, []) ->
+ C.
+
+on(C, K) ->
+ set_attr(C, K, 'on').
+
+off(C, K) ->
+ set_attr(C, K, 'off').
+
+
+
+set_strength(C, V) ->
+ set_attr(C, 'strength', V).
+
+set_alternate(C, V) ->
+ set_attr(C, 'alternate', V).
+
+set_case_first(C, V) ->
+ set_attr(C, 'case_first', V).
+
+set_attr(C, K, V) ->
+ ?TRY_OK(?IM:collator_set_attr(C, K, V)).
+
View
BIN  .eunit/i18n_collation_tests.beam
Binary file not shown
View
169 .eunit/i18n_collation_tests.erl
@@ -0,0 +1,169 @@
+% vim: set filetype=erlang shiftwidth=4 tabstop=4 expandtab tw=80:
+
+%%% =====================================================================
+%%% Copyright 2011 Uvarov Michael
+%%%
+%%% Licensed under the Apache License, Version 2.0 (the "License");
+%%% you may not use this file except in compliance with the License.
+%%% You may obtain a copy of the License at
+%%%
+%%% http://www.apache.org/licenses/LICENSE-2.0
+%%%
+%%% Unless required by applicable law or agreed to in writing, software
+%%% distributed under the License is distributed on an "AS IS" BASIS,
+%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%%% See the License for the specific language governing permissions and
+%%% limitations under the License.
+%%%
+%%% $Id$
+%%%
+%%% @copyright 2010-2011 Michael Uvarov
+%%% @author Michael Uvarov <freeakk@gmail.com>
+%%% =====================================================================
+
+%%% @private
+-module(i18n_collation_tests).
+-include_lib("i18n/include/i18n.hrl").
+
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+-include_lib("triq/include/triq.hrl").
+
+
+simple_open_test() ->
+ i18n_collation:open().
+
+simple_sort_key_test() ->
+ C = i18n_collation:open(),
+ F = i18n_collation:sort_key(C, ?ISTR("A")).
+
+simple_sort_test_() ->
+ C = i18n_collation:open(),
+ F = fun(X) -> i18n_collation:sort(C,X) end,
+ {"Simple input data.",
+ ?_assertEqual(F([?ISTR("A"), ?ISTR("C"), ?ISTR("B")]), [?ISTR("A"), ?ISTR("B"), ?ISTR("C")])}.
+
+natural_sort_test_() ->
+ Unsorted = ["1000X Radonius Maximus",
+ "10X Radonius",
+ "200X Radonius",
+ "20X Radonius",
+ "20X Radonius Prime",
+ "30X Radonius",
+ "40X Radonius",
+ "Allegia 50 Clasteron",
+ "Allegia 500 Clasteron",
+ "Allegia 51 Clasteron",
+ "Allegia 51B Clasteron",
+ "Allegia 52 Clasteron",
+ "Allegia 60 Clasteron",
+ "Alpha 100",
+ "Alpha 2",
+ "Alpha 200",
+ "Alpha 2A",
+ "Alpha 2A-8000",
+ "Alpha 2A-900",
+ "Callisto Morphamax",
+ "Callisto Morphamax 500",
+ "Callisto Morphamax 5000",
+ "Callisto Morphamax 600",
+ "Callisto Morphamax 700",
+ "Callisto Morphamax 7000",
+ "Callisto Morphamax 7000 SE",
+ "Callisto Morphamax 7000 SE2",
+ "QRS-60 Intrinsia Machine",
+ "QRS-60F Intrinsia Machine",
+ "QRS-62 Intrinsia Machine",
+ "QRS-62F Intrinsia Machine",
+ "Xiph Xlater 10000",
+ "Xiph Xlater 2000",
+ "Xiph Xlater 300",
+ "Xiph Xlater 40",
+ "Xiph Xlater 5",
+ "Xiph Xlater 50",
+ "Xiph Xlater 500",
+ "Xiph Xlater 5000",
+ "Xiph Xlater 58"],
+
+ Sorted = ["10X Radonius",
+ "20X Radonius",
+ "20X Radonius Prime",
+ "30X Radonius",
+ "40X Radonius",
+ "200X Radonius",
+ "1000X Radonius Maximus",
+ "Allegia 50 Clasteron",
+ "Allegia 51 Clasteron",
+ "Allegia 51B Clasteron",
+ "Allegia 52 Clasteron",
+ "Allegia 60 Clasteron",
+ "Allegia 500 Clasteron",
+ "Alpha 2",
+ "Alpha 2A",
+ "Alpha 2A-900",
+ "Alpha 2A-8000",
+ "Alpha 100",
+ "Alpha 200",
+ "Callisto Morphamax",
+ "Callisto Morphamax 500",
+ "Callisto Morphamax 600",
+ "Callisto Morphamax 700",
+ "Callisto Morphamax 5000",
+ "Callisto Morphamax 7000",
+ "Callisto Morphamax 7000 SE",
+ "Callisto Morphamax 7000 SE2",
+ "QRS-60 Intrinsia Machine",
+ "QRS-60F Intrinsia Machine",
+ "QRS-62 Intrinsia Machine",
+ "QRS-62F Intrinsia Machine",
+ "Xiph Xlater 5",
+ "Xiph Xlater 40",
+ "Xiph Xlater 50",
+ "Xiph Xlater 58",
+ "Xiph Xlater 300",
+ "Xiph Xlater 500",
+ "Xiph Xlater 2000",
+ "Xiph Xlater 5000",
+ "Xiph Xlater 10000"],
+
+ C = i18n_collation:open('root', [numeric]),
+ KeyFn = fun(X) -> i18n_collation:sort_key(C, ?_ISTR(X)) end,
+ F = fun(X) -> i18n_collation:map_sort(KeyFn, X) end,
+
+
+ [{"Using official test strings from Dave Koelle",
+ ?_assertEqual(F(Unsorted), Sorted)}
+ ].
+
+
+
+prop_sort_key_test_() ->
+ {"sort_key prop testing.",
+ {timeout, 60,
+ fun() -> triq:check(prop_sort_key()) end}}.
+
+prop_compare_test_() ->
+ {"compare prop testing.",
+ {timeout, 60,
+ fun() -> triq:check(prop_compare()) end}}.
+
+
+prop_sort_key() ->
+ C = i18n_collation:open(),
+ F = fun(Xs) ->
+ S = i18n_string:from_utf8(Xs),
+ Key = i18n_collation:sort_key(C, S),
+ is_binary(Key)
+ end,
+ ?FORALL({Xs},{unicode_binary(100)}, F(Xs)).
+
+prop_compare() ->
+ C = i18n_collation:open(),
+ F = fun(Xs) ->
+ S = i18n_string:from_utf8(Xs),
+ equal =:= i18n_collation:compare(C, S, S)
+ end,
+ ?FORALL({Xs},{unicode_binary(100)}, F(Xs)).
+
+-endif.
View
BIN  .eunit/i18n_expand.beam
Binary file not shown
View
6 .eunit/i18n_expand.erl
@@ -0,0 +1,6 @@
+-module(i18n_expand).
+
+-export([parse_transform/2]).
+
+parse_transform(AST, Options) ->
+ ct_expand:parse_transform(AST, Options).
View
BIN  .eunit/i18n_locale.beam
Binary file not shown
View
69 .eunit/i18n_locale.erl
@@ -0,0 +1,69 @@
+% vim: set filetype=erlang shiftwidth=4 tabstop=4 expandtab tw=80:
+
+%%% =====================================================================
+%%% Copyright 2011 Uvarov Michael
+%%%
+%%% Licensed under the Apache License, Version 2.0 (the "License");
+%%% you may not use this file except in compliance with the License.
+%%% You may obtain a copy of the License at
+%%%
+%%% http://www.apache.org/licenses/LICENSE-2.0
+%%%
+%%% Unless required by applicable law or agreed to in writing, software
+%%% distributed under the License is distributed on an "AS IS" BASIS,
+%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%%% See the License for the specific language governing permissions and
+%%% limitations under the License.
+%%%
+%%% $Id$
+%%%
+%%% @copyright 2010-2011 Michael Uvarov
+%%% @author Michael Uvarov <freeakk@gmail.com>
+%%% =====================================================================
+
+-module(i18n_locale).
+-include("i18n.hrl").
+-export([set_locale/1, set_default_locale/1, get_locale/0]).
+-export([base_name/1]).
+
+-define(SERVER, 'i18n_locale_server').
+
+set_locale(Value) ->
+ LName = ?TRY_ATOM(?IM:locale_name(Value)),
+ set_value('i18n_locale', LName).
+
+set_default_locale(Value) ->
+ set_default_value('i18n_locale', Value).
+
+get_locale() ->
+ get_value('i18n_locale').
+
+
+
+
+
+
+
+set_value(Key, Value) ->
+ erlang:put(Key, Value),
+ Value.
+
+set_default_value(Key, Value) ->
+ ?SERVER:set_default(Key, Value),
+ Value.
+
+
+
+get_value(Key) ->
+ case erlang:get(Key) of
+ 'undefined' ->
+ % Get a global value
+ Value = ?SERVER:get_default(Key),
+ erlang:put(Key, Value),
+ Value;
+ Value ->
+ Value
+ end.
+
+base_name(LocaleId) ->
+ ?TRY_ATOM(?IM:locale_base_name(LocaleId)).
View
BIN  .eunit/i18n_locale_server.beam
Binary file not shown
View
102 .eunit/i18n_locale_server.erl
@@ -0,0 +1,102 @@
+% vim: set filetype=erlang shiftwidth=4 tabstop=4 expandtab tw=80:
+
+%%% =====================================================================
+%%% Copyright 2011 Uvarov Michael
+%%%
+%%% Licensed under the Apache License, Version 2.0 (the "License");
+%%% you may not use this file except in compliance with the License.
+%%% You may obtain a copy of the License at
+%%%
+%%% http://www.apache.org/licenses/LICENSE-2.0
+%%%
+%%% Unless required by applicable law or agreed to in writing, software
+%%% distributed under the License is distributed on an "AS IS" BASIS,
+%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%%% See the License for the specific language governing permissions and
+%%% limitations under the License.
+%%%
+%%% $Id$
+%%%
+%%% @copyright 2010-2011 Michael Uvarov
+%%% @author Michael Uvarov <freeakk@gmail.com>
+%%% =====================================================================
+
+%%% @private
+-module(i18n_locale_server).
+
+-export([start_link/0]).
+-export([init/1, terminate/2,
+ handle_call/3]).
+-export([set_default/2, get_default/1]).
+
+-behavior(gen_server).
+
+-define(CFG_TABLE_NAME, 'i18n_settings').
+
+%% You can set a default locale as a compiler's parameter
+-ifdef(I18N_LOCALE).
+-else.
+-define(I18N_LOCALE, 'root').
+-endif.
+
+%% Exported Client Functions
+%% Operation & Maintenance API
+start_link() ->
+ Arguments = [],
+ Opts = [],
+ gen_server:start_link({local, ?MODULE}, ?MODULE, Arguments, Opts).
+
+init([]) ->
+ E = ets:new(?CFG_TABLE_NAME, [{read_concurrency, true}, named_table]),
+ true = ets:insert(E, {'i18n_locale', ?I18N_LOCALE}),
+ {ok, []}.
+
+terminate(_Reason, _LoopData) ->
+ ok.
+
+
+handle_call({set_default, Key, Value}, _From, LoopData) ->
+ % Update inherited values
+ Reply = ets:insert(?CFG_TABLE_NAME, {Key, Value}),
+ {reply, Reply, LoopData}.
+
+
+%%
+%% API
+%%
+
+get_default(Key) ->
+ lookup(Key).
+
+set_default(Key, Value) ->
+ call({set_default, Key, Value}).
+
+
+%%
+%% Helpers
+%%
+
+call(Cmd) ->
+ try
+ gen_server:call(?MODULE, Cmd)
+ catch
+ exit:{noproc, _Stack} ->
+ i18n:start(),
+ gen_server:call(?MODULE, Cmd)
+ end.
+
+lookup(Key) ->
+ try
+ [{Key, Value}] = ets:lookup(?CFG_TABLE_NAME, Key),
+ Value
+ catch
+ error:badarg ->
+ % Server is not running.
+ i18n:start(),
+ lookup(Key);
+
+ error:{badmatch, _V} ->
+ % Key is not found.
+ 'undefined'
+ end.
+
View
BIN  .eunit/i18n_message.beam
Binary file not shown
View
58 .eunit/i18n_message.erl
@@ -0,0 +1,58 @@
+% vim: set filetype=erlang shiftwidth=4 tabstop=4 expandtab tw=80:
+
+%%% =====================================================================
+%%% Copyright 2011 Uvarov Michael
+%%%
+%%% Licensed under the Apache License, Version 2.0 (the "License");
+%%% you may not use this file except in compliance with the License.
+%%% You may obtain a copy of the License at
+%%%
+%%% http://www.apache.org/licenses/LICENSE-2.0
+%%%
+%%% Unless required by applicable law or agreed to in writing, software
+%%% distributed under the License is distributed on an "AS IS" BASIS,
+%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%%% See the License for the specific language governing permissions and
+%%% limitations under the License.
+%%%
+%%% $Id$
+%%%
+%%% @copyright 2010-2011 Michael Uvarov
+%%% @author Michael Uvarov <freeakk@gmail.com>
+%%% =====================================================================
+
+-module(i18n_message).
+-include("i18n.hrl").
+
+-type i18n_string() :: binary().
+-type i18n_locale_id() :: atom().
+
+-type resource() :: <<>>.
+-type i18n_msg_format() :: resource().
+-type i18n_msg_param() :: {atom(), i18n_msg_arg()}.
+-type i18n_msg_arg() :: any().
+
+% NIFs
+-export([open/1, open/2]).
+-export([format/2, format/3]).
+
+
+
+-spec open(i18n_locale_id(), i18n_string()) -> i18n_msg_format().
+%% @doc Parse a message to a resourse.
+open(L, S) ->
+ ?TRY_RES(?IM:open_format(L, S)).
+
+-spec open(i18n_string()) -> i18n_msg_format().
+open(S) ->
+ L = i18n_locale:get_locale(),
+ open(L, S).
+
+-spec format(i18n_msg_format(), [i18n_msg_param()]) -> i18n_string().
+format(M, P) ->
+ ?TRY_STR(?IM:format(M, P)).
+
+-spec format(i18n_msg_format(), [i18n_msg_param()], i18n_string()) -> i18n_string().
+format(M, P, A) ->
+ ?TRY_STR(?IM:format(M, P, A)).
+
View
BIN  .eunit/i18n_nif.beam
Binary file not shown
View
151 .eunit/i18n_nif.erl
@@ -0,0 +1,151 @@
+% vim: set filetype=erlang shiftwidth=4 tabstop=4 expandtab tw=80:
+
+%%% =====================================================================
+%%% Copyright 2011 Uvarov Michael
+%%%
+%%% Licensed under the Apache License, Version 2.0 (the "License");
+%%% you may not use this file except in compliance with the License.
+%%% You may obtain a copy of the License at
+%%%
+%%% http://www.apache.org/licenses/LICENSE-2.0
+%%%
+%%% Unless required by applicable law or agreed to in writing, software
+%%% distributed under the License is distributed on an "AS IS" BASIS,
+%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%%% See the License for the specific language governing permissions and
+%%% limitations under the License.
+%%%
+%%% $Id$
+%%%
+%%% @copyright 2010-2011 Michael Uvarov
+%%% @author Michael Uvarov <freeakk@gmail.com>
+%%% =====================================================================
+
+%%% @private
+%%% Private functions. Use API functions from modules instead.
+
+-module(i18n_nif).
+-include("i18n.hrl").
+-on_load(init/0).
+
+-export([init/0]).
+
+
+% NIFs
+-export([from_utf8/1, to_utf8/1]).
+-export([concat/2]).
+
+-export([to_lower/2, to_upper/2, to_title/2]).
+-export([get_iterator/2, len/2]).
+
+
+-export([get_collator/1]).
+-export([sort_key/2, compare/3]).
+% Bad function
+-export([collator_set_attr/3]).
+
+-export([open_format/2, format/2, format/3]).
+-export([open_regex/1, regex_replace/3]).
+
+-export([locale_name/1, locale_parent/1, locale_language_tag/1,
+ locale_base_name/1]).
+
+init() ->
+ i18n:start(),
+ Nif = ?I18N_NIF_PATH(?MODULE),
+ erlang:load_nif(Nif, 0).
+
+
+
+from_utf8(_BinUTF8) ->
+ ?I18N_NIF_NOT_LOADED.
+
+to_utf8(_BinUTF16) ->
+ ?I18N_NIF_NOT_LOADED.
+
+concat(_BinUTF16Beginning, _BinUTF16Ending) ->
+ ?I18N_NIF_NOT_LOADED.
+
+len(_IterResource, _String) ->
+ ?I18N_NIF_NOT_LOADED.
+
+to_lower(_Locale, _String) ->
+ ?I18N_NIF_NOT_LOADED.
+
+to_upper(_Locale, _String) ->
+ ?I18N_NIF_NOT_LOADED.
+
+to_title(_Locale, _String) ->
+ ?I18N_NIF_NOT_LOADED.
+
+get_iterator(_Locale, _Type) ->
+ ?I18N_NIF_NOT_LOADED.
+
+
+
+%%
+%% Collation
+%%
+
+get_collator(_Locale) ->
+ ?I18N_NIF_NOT_LOADED.
+
+sort_key(_CollatorResource, _String) ->
+ ?I18N_NIF_NOT_LOADED.
+
+compare(_CollatorResource, _String, _String2) ->
+ ?I18N_NIF_NOT_LOADED.
+
+%% Destructive function!
+collator_set_attr(_CollationResource, _Key, _Value) ->
+ ?I18N_NIF_NOT_LOADED.
+
+
+
+
+
+%%
+%% Message Format
+%%
+
+open_format(_L, _S) ->
+ ?I18N_NIF_NOT_LOADED.
+
+format(_Mesage, _Parameters) ->
+ ?I18N_NIF_NOT_LOADED.
+
+format(_Mesage, _Parameters, _AppendTo) ->
+ ?I18N_NIF_NOT_LOADED.
+
+
+
+
+
+%%
+%% Regular Expressions
+%%
+
+open_regex(_FormatString) ->
+ ?I18N_NIF_NOT_LOADED.
+
+regex_replace(_Regex, _SrcS, _RepS) ->
+ ?I18N_NIF_NOT_LOADED.
+
+
+
+
+%%
+%% Locale
+%%
+
+locale_name(_L) ->
+ ?I18N_NIF_NOT_LOADED.
+
+locale_parent(_L) ->
+ ?I18N_NIF_NOT_LOADED.
+
+locale_language_tag(_L) ->
+ ?I18N_NIF_NOT_LOADED.
+
+locale_base_name(_L) ->
+ ?I18N_NIF_NOT_LOADED.
View
BIN  .eunit/i18n_search.beam
Binary file not shown
View
63 .eunit/i18n_search.erl
@@ -0,0 +1,63 @@
+% vim: set filetype=erlang shiftwidth=4 tabstop=4 expandtab tw=80:
+
+%%% =====================================================================
+%%% Copyright 2011 Uvarov Michael
+%%%
+%%% Licensed under the Apache License, Version 2.0 (the "License");
+%%% you may not use this file except in compliance with the License.
+%%% You may obtain a copy of the License at
+%%%
+%%% http://www.apache.org/licenses/LICENSE-2.0
+%%%
+%%% Unless required by applicable law or agreed to in writing, software
+%%% distributed under the License is distributed on an "AS IS" BASIS,
+%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%%% See the License for the specific language governing permissions and
+%%% limitations under the License.
+%%%
+%%% $Id$
+%%%
+%%% @copyright 2010-2011 Michael Uvarov
+%%% @author Michael Uvarov <freeakk@gmail.com>
+%%% =====================================================================
+
+-module(i18n_search).
+-include_lib("i18n.hrl").
+
+% NIFs
+-export([open/0, open/1, open/2]).
+
+
+
+
+%%
+%% Types
+%%
+
+-type i18n_locale_id() :: atom().
+-type i18n_string() :: binary().
+
+-type resource() :: <<>>.
+-type i18n_collator() :: resource().
+-type i18n_search_option() :: atom().
+
+
+%%
+%% API
+%%
+
+
+-spec open() -> i18n_collator().
+open() ->
+ L = i18n_locale:get_locale(),
+ open(L).
+
+-spec open(i18n_locale_id()) -> i18n_collator().
+open(_Locale) ->
+ ?I18N_NIF_NOT_LOADED.
+
+-spec open(i18n_locale_id(), [i18n_search_option()]) -> i18n_collator().
+open(L, Options) ->
+ C = open(L).
+% do_options(C, Options).
+
View
BIN  .eunit/i18n_string.beam
Binary file not shown
View
147 .eunit/i18n_string.erl
@@ -0,0 +1,147 @@
+% vim: set filetype=erlang shiftwidth=4 tabstop=4 expandtab tw=80:
+
+%%% =====================================================================
+%%% Copyright 2011 Uvarov Michael
+%%%
+%%% Licensed under the Apache License, Version 2.0 (the "License");
+%%% you may not use this file except in compliance with the License.
+%%% You may obtain a copy of the License at
+%%%
+%%% http://www.apache.org/licenses/LICENSE-2.0
+%%%
+%%% Unless required by applicable law or agreed to in writing, software
+%%% distributed under the License is distributed on an "AS IS" BASIS,
+%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%%% See the License for the specific language governing permissions and
+%%% limitations under the License.
+%%%
+%%% $Id$
+%%%
+%%% @copyright 2010-2011 Michael Uvarov
+%%% @author Michael Uvarov <freeakk@gmail.com>
+%%% =====================================================================
+
+-module(i18n_string).
+-include("i18n.hrl").
+
+-type i18n_locale_id() :: atom().
+
+%% UTF-8 string
+-type unicode_binary() :: binary().
+
+%% UTF-16 string
+-type i18n_string() :: binary().
+
+-type resource() :: <<>>.
+-type i18n_iterator() :: resource().
+
+-type i18n_string_iterator_type() :: 'grapheme' | 'word' | 'sentence' | 'line'.
+
+% NIFs
+-export([from/1, native_from/1]).
+-export([from_utf8/1, to_utf8/1]).
+-export([concat/2]).
+
+
+-export([to_lower/2, to_upper/2, to_title/2]).
+-export([to_lower/1, to_upper/1, to_title/1]).
+-export([get_iterator/1, get_iterator/2]).
+-export([len/1, len/2]).
+
+
+from(B)
+ when is_binary(B) ->
+ from_utf8(B);
+from(L)
+ when is_list(L) ->
+ B = unicode:characters_to_binary(L),
+ from_utf8(B);
+from(A)
+ when is_atom(A) ->
+ L = erlang:atom_to_list(A),
+ B = erlang:atom_to_binary(L),
+ from_utf8(B).
+
+
+%% Slow from/1.
+native_from(B)
+ when is_binary(B) ->
+ L = unicode:characters_to_list(B),
+ list_to_i18n_string(L);
+native_from(L)
+ when is_list(L) ->
+ list_to_i18n_string(L);
+native_from(A)
+ when is_atom(A) ->
+ L = erlang:atom_to_list(A),
+ list_to_i18n_string(L).
+
+
+list_to_i18n_string(L) ->
+ erlang:list_to_binary(xmerl_ucs:to_utf16be(L)).
+
+
+-spec from_utf8(unicode_binary()) -> i18n_string().
+from_utf8(B) ->
+ ?TRY_STR(?IM:from_utf8(B)).
+
+-spec to_utf8(i18n_string()) -> unicode_binary().
+to_utf8(B) ->
+ ?TRY_BIN(?IM:to_utf8(B)).
+
+-spec concat(i18n_string(), i18n_string()) -> i18n_string().
+concat(B1, B2) ->
+ ?TRY_STR(?IM:concat(B1, B2)).
+
+
+-spec to_lower(i18n_string()) -> i18n_string().
+to_lower(S) ->
+ L = i18n_locale:get_locale(),
+ to_lower(L, S).
+
+-spec to_upper(i18n_string()) -> i18n_string().
+to_upper(S) ->
+ L = i18n_locale:get_locale(),
+ to_upper(L, S).
+
+-spec to_title(i18n_string()) -> i18n_string().
+to_title(S) ->
+ L = i18n_locale:get_locale(),
+ to_title(L, S).
+
+-spec get_iterator(i18n_string_iterator_type()) -> i18n_iterator().
+get_iterator(T) ->
+ L = i18n_locale:get_locale(),
+ get_iterator(L, T).
+
+%% @doc Count of code paints.
+-spec len(i18n_string()) -> non_neg_integer().
+len(S) when is_integer(S) ->
+ byte_size(S) div 2.
+
+%% @doc Count the length og the string with help of an iterator.
+%%
+%% ```
+%% i18n_string:len(i18n_string:get_iterator('grapheme'), ?ISTR("Example"));
+%% '''
+-spec len(i18n_iterator(), i18n_string()) -> non_neg_integer().
+len(I, S) ->
+ ?TRY_INT(?IM:len(I, S)).
+
+-spec to_lower(i18n_locale_id(), i18n_string()) -> i18n_string().
+to_lower(L, S) when is_atom(L) ->
+ ?TRY_STR(?IM:to_lower(L, S)).
+
+-spec to_upper(i18n_locale_id(), i18n_string()) -> i18n_string().
+to_upper(L, S) when is_atom(L) ->
+ ?TRY_STR(?IM:to_upper(L, S)).
+
+-spec to_title(i18n_locale_id() | i18n_iterator(), i18n_string()) -> i18n_string().
+to_title(L, S) ->
+ ?TRY_STR(?IM:to_title(L, S)).
+
+-spec get_iterator(i18n_locale_id(), i18n_string_iterator_type()) -> i18n_iterator().
+get_iterator(S, T) when is_atom(T) ->
+ ?TRY_RES(?IM:get_iterator(S, T)).
+
+
View
BIN  .eunit/i18n_string_tests.beam
Binary file not shown
View
113 .eunit/i18n_string_tests.erl
@@ -0,0 +1,113 @@
+% vim: set filetype=erlang shiftwidth=4 tabstop=4 expandtab tw=80:
+
+%%% =====================================================================
+%%% Copyright 2011 Uvarov Michael
+%%%
+%%% Licensed under the Apache License, Version 2.0 (the "License");
+%%% you may not use this file except in compliance with the License.
+%%% You may obtain a copy of the License at
+%%%
+%%% http://www.apache.org/licenses/LICENSE-2.0
+%%%
+%%% Unless required by applicable law or agreed to in writing, software
+%%% distributed under the License is distributed on an "AS IS" BASIS,
+%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%%% See the License for the specific language governing permissions and
+%%% limitations under the License.
+%%%
+%%% $Id$
+%%%
+%%% @copyright 2010-2011 Michael Uvarov
+%%% @author Michael Uvarov <freeakk@gmail.com>
+%%% =====================================================================
+
+%%% @private
+-module(i18n_string_tests).
+-include_lib("i18n/include/i18n.hrl").
+
+
+-ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+-include_lib("triq/include/triq.hrl").
+
+from_utf8_test() ->
+ i18n_string:from_utf8(<<"F">>),
+ ok.
+
+simple_open_test() ->
+ i18n_string:get_iterator('grapheme'),
+ i18n_string:get_iterator('word'),
+ i18n_string:get_iterator('line'),
+ i18n_string:get_iterator('sentence'),
+ ok.
+
+prop_from_char_test_() ->
+ {"1 char conversation prop testing.",
+ {timeout, 60,
+ fun() -> triq:check(prop_from_char()) end}}.
+
+prop_from_utf8_test_() ->
+ {"from_utf8 and to_utf8 prop testing.",
+ {timeout, 60,
+ fun() -> triq:check(prop_from_utf8()) end}}.
+
+prop_case_test_() ->
+ {"to_lower and to_upper prop testing.",
+ {timeout, 60,
+ fun() -> triq:check(prop_case()) end}}.
+
+prop_len_test_() ->
+ {"len prop testing.",
+ {timeout, 60,
+ fun() -> triq:check(prop_len()) end}}.
+
+prop_get_iterator_parallel_test_() ->
+ {"Cloner for the iterator type prop testing.",
+ {timeout, 60,
+ fun() -> triq:check(prop_get_iterator_parallel()) end}}.
+
+
+
+prop_from_char() ->
+ ?FORALL({Xs},{unicode_char()},
+ i18n_string:to_utf8(i18n_string:from([Xs])) =:= [Xs]).
+
+prop_from_utf8() ->
+ ?FORALL({Xs},{unicode_binary(100)},
+ i18n_string:to_utf8(i18n_string:from_utf8(Xs)) =:= Xs).
+
+prop_case() ->
+ I = i18n_string:get_iterator('grapheme'),
+ F = fun(Xs) ->
+ S = i18n_string:from_utf8(Xs),
+ i18n_string:to_lower(S) =:= i18n_string:to_lower(i18n_string:to_upper(S))
+ andalso
+ i18n_string:to_upper(S) =:= i18n_string:to_upper(i18n_string:to_upper(S))
+ andalso
+ i18n_string:to_title(S) =:= i18n_string:to_title(i18n_string:to_upper(S))
+ andalso
+ i18n_string:to_title(I, S) =:= i18n_string:to_title(I, i18n_string:to_upper(S))
+ end,
+ ?FORALL({Xs},{unicode_binary(100)}, F(Xs)).
+
+prop_len() ->
+ I = i18n_string:get_iterator('grapheme'),
+ F = fun(Xs) ->
+ S = i18n_string:from_utf8(Xs),
+ is_integer(i18n_string:len(I, S))
+ end,
+ ?FORALL({Xs},{unicode_binary(100)}, F(Xs)).
+
+prop_get_iterator_parallel() ->
+ I = i18n_string:get_iterator('grapheme'),
+ F = fun(Xs) ->
+ S = i18n_string:from_utf8(Xs),
+ is_integer(i18n_string:len(I, S))
+ end,
+ FF = fun(F, Xs) ->
+ spawn(fun() -> F(Xs) end),
+ true
+ end,
+ ?FORALL({Xs},{unicode_binary(100)}, FF(F, Xs)).
+
+-endif.
View
BIN  .eunit/i18n_sup.beam
Binary file not shown
View
57 .eunit/i18n_sup.erl
@@ -0,0 +1,57 @@
+% vim: set filetype=erlang shiftwidth=4 tabstop=4 expandtab tw=80:
+
+%%% =====================================================================
+%%% Copyright 2011 Uvarov Michael
+%%%
+%%% Licensed under the Apache License, Version 2.0 (the "License");
+%%% you may not use this file except in compliance with the License.
+%%% You may obtain a copy of the License at
+%%%
+%%% http://www.apache.org/licenses/LICENSE-2.0
+%%%
+%%% Unless required by applicable law or agreed to in writing, software
+%%% distributed under the License is distributed on an "AS IS" BASIS,
+%%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+%%% See the License for the specific language governing permissions and
+%%% limitations under the License.
+%%%
+%%% $Id$
+%%%
+%%% @copyright 2010-2011 Michael Uvarov
+%%% @author Michael Uvarov <freeakk@gmail.com>
+%%% =====================================================================
+
+%%% @private
+
+-module(i18n_sup).
+
+-behaviour(supervisor).
+
+%% API
+-export([start_link/0]).
+
+%% Supervisor callbacks
+-export([init/1]).
+
+%% Helper macro for declaring children of supervisor
+-define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
+
+%% ===================================================================
+%% API functions
+%% ===================================================================
+
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+%% ===================================================================
+%% Supervisor callbacks
+%% ===================================================================
+
+init([]) ->
+ % Provide a global dictionary for this node.
+ DefaultWorker = {i18n_locale_server,
+ {i18n_locale_server, start_link, []},
+ permanent, 10000, worker, [i18n_locale_server]},
+
+ {ok, { {one_for_one, 5, 10}, [DefaultWorker]} }.
+
View
10 .gitignore
@@ -1,9 +1,7 @@
ebin
*.swp
*.dump
-fprof.trace
-erl_crash.dump
-
-c_src/system
-c_src/icu4c
-c_src/*.o
+*.trace
+*.o
+*.so
+*.dll
View
279 c_src/i18n_nif.cpp
@@ -27,6 +27,8 @@
#define I18N_COLLATION true
#define I18N_SEARCH true
#define I18N_MESSAGE true
+#define I18N_REGEX true
+#define I18N_LOCALE true
#define BUF_SIZE 65536 // 2^16, since we're using a 2-byte length header
#define STR_LEN 32768
@@ -47,6 +49,8 @@
#include "unicode/utypes.h"
+#include "unicode/uregex.h"
+
#include "erl_nif.h"
@@ -1432,11 +1436,7 @@ static ERL_NIF_TERM open_format(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg
(char *) locale,
NULL,
&status);
- if(U_FAILURE(status)) {
- return enif_make_badarg(env);
- }
-
-
+ CHECK(env, status);
res = (cloner*) enif_alloc_resource(message_type, sizeof(cloner));
@@ -1602,6 +1602,250 @@ static int i18n_message_load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load
+#ifdef I18N_REGEX
+
+static ErlNifResourceType* regex_type = 0;
+
+
+// Called from erl_nif.
+void regex_dtor(ErlNifEnv* env, void* obj)
+{
+ // Free memory
+ cloner_destroy((cloner*) obj);
+}
+
+
+
+// Called from cloner for each thread.
+void regex_destr(char* obj)
+{
+ if (obj != NULL)
+ uregex_close((URegularExpression*) obj);
+}
+char* regex_clone(char* obj)
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ obj = (char*) uregex_clone(
+ (URegularExpression*) obj,
+ &status
+ );
+ if(U_FAILURE(status)) {
+ return NULL;
+ }
+ return obj;
+}
+
+int regex_open(URegularExpression * obj, cloner* c)
+{
+ return cloner_open((char *) obj, c, &regex_clone, &regex_destr);
+}
+
+
+
+
+
+
+
+
+/**
+ * NIFs
+ */
+
+// Get a message format
+static ERL_NIF_TERM open_regex(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ERL_NIF_TERM out;
+ ErlNifBinary in;
+ UErrorCode status = U_ZERO_ERROR;
+ UParseError* pe = NULL;
+ URegularExpression* re;
+ cloner* res;
+
+ re = uregex_open((UChar *) in.data,
+ div(in.size, sizeof(UChar)).quot,
+ 0,
+ pe,
+ &status);
+ CHECK(env, status);
+
+ res = (cloner*) enif_alloc_resource(regex_type, sizeof(cloner));
+ if (regex_open(re, res)) {
+ enif_release_resource(res);
+ return enif_make_badarg(env);
+ }
+ out = enif_make_resource(env, res);
+ enif_release_resource(res);
+ /* resource now only owned by "Erlang" */
+ return out;
+}
+
+
+static ERL_NIF_TERM regex_replace(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ ErlNifBinary in, in2;
+ ERL_NIF_TERM out;
+ int32_t ulen, len;
+ UChar buf[STR_LEN];
+ unsigned char* bin;
+ URegularExpression* re;
+ cloner* ptr;
+ UErrorCode status = U_ZERO_ERROR;
+
+ // Second argument must be a binary
+ if(!(enif_inspect_binary(env, argv[1], &in)
+ && enif_inspect_binary(env, argv[2], &in2)
+ && enif_get_resource(env, argv[0], regex_type, (void**) &ptr))) {
+ return enif_make_badarg(env);
+ }
+
+ re = (URegularExpression*) cloner_get(ptr);
+ uregex_setText(re,
+ (const UChar *) in2.data,
+ (int32_t) div(in2.size, sizeof(UChar)).quot, // in UChars
+ &status);
+ CHECK(env, status);
+
+ ulen = uregex_replaceAll(re,
+ (UChar*) in.data,
+ (int32_t) div(in.size, sizeof(UChar)).quot, // in UChars
+ (UChar *) buf,
+ (int32_t) STR_LEN,
+ &status);
+ CHECK(env, status);
+
+ // length in bytes
+ len = ulen * sizeof(UChar);
+
+
+ bin = enif_make_new_binary(env, (size_t) len, &out);
+ memcpy(bin, &buf, len);
+ return out;
+}
+
+
+
+
+
+
+
+
+static int i18n_regex_load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info)
+{
+ regex_type = enif_open_resource_type(env, NULL, "regex_type",
+ regex_dtor, ERL_NIF_RT_CREATE, NULL);
+ if (regex_type == NULL) return 6;
+ return 0;
+}
+#endif
+
+
+
+
+
+
+
+
+
+
+
+#ifdef I18N_LOCALE
+static ERL_NIF_TERM locale_name(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ int32_t value_len, key_len;
+ char value[LOCALE_LEN], key[LOCALE_LEN];
+
+
+ key_len = enif_get_atom(env, argv[0], (char*) key, LOCALE_LEN, ERL_NIF_LATIN1);
+
+ if (!key_len) {
+ return enif_make_badarg(env);
+ }
+
+
+ value_len = uloc_getName((const char*) key, // Locale Id
+ (char *) value, // name
+ (int32_t) LOCALE_LEN,
+ &status);
+ CHECK(env, status);
+
+ return enif_make_atom(env, value);
+}
+static ERL_NIF_TERM locale_parent(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ int32_t value_len, key_len;
+ char value[LOCALE_LEN], key[LOCALE_LEN];
+
+
+ key_len = enif_get_atom(env, argv[0], (char*) key, LOCALE_LEN, ERL_NIF_LATIN1);
+
+ if (!key_len) {
+ return enif_make_badarg(env);
+ }
+
+
+ value_len = uloc_getParent((const char*) key, // Locale Id
+ (char *) value, // name
+ (int32_t) LOCALE_LEN,
+ &status);
+ CHECK(env, status);
+
+ return enif_make_atom(env, value);
+}
+static ERL_NIF_TERM locale_language_tag(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ int32_t value_len, key_len;
+ char value[LOCALE_LEN], key[LOCALE_LEN];
+
+
+ key_len = enif_get_atom(env, argv[0], (char*) key, LOCALE_LEN, ERL_NIF_LATIN1);
+
+ if (!key_len) {
+ return enif_make_badarg(env);
+ }
+
+
+ value_len = uloc_toLanguageTag((const char*) key, // Locale Id
+ (char *) value, // name
+ (int32_t) LOCALE_LEN,
+ FALSE,
+ &status);
+ CHECK(env, status);
+
+ return enif_make_atom(env, value);
+}
+static ERL_NIF_TERM locale_base_name(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[])
+{
+ UErrorCode status = U_ZERO_ERROR;
+
+ int32_t value_len, key_len;
+ char value[LOCALE_LEN], key[LOCALE_LEN];
+
+
+ key_len = enif_get_atom(env, argv[0], (char*) key, LOCALE_LEN, ERL_NIF_LATIN1);
+
+ if (!key_len) {
+ return enif_make_badarg(env);
+ }
+
+
+ value_len = uloc_getBaseName((const char*) key, // Locale Id
+ (char *) value, // name
+ (int32_t) LOCALE_LEN,
+ &status);
+ CHECK(env, status);
+
+ return enif_make_atom(env, value);
+}
+#endif
+
+
@@ -1637,6 +1881,12 @@ static int load(ErlNifEnv *env, void **priv_data, ERL_NIF_TERM load_info)
if (code) return code;
#endif
+
+#ifdef I18N_REGEX
+ code = i18n_regex_load(env, priv_data, load_info);
+ if (code) return code;
+#endif
+
return 0;
}
@@ -1699,13 +1949,30 @@ static ErlNifFunc nif_funcs[] =
-
#ifdef I18N_MESSAGE
{"open_format", 2, open_format},
{"format", 2, format},
{"format", 3, format},
#endif
+
+
+
+#ifdef I18N_REGEX
+ {"open_regex", 1, open_regex},
+ {"regex_replace", 3, regex_replace},
+#endif
+
+
+
+
+#ifdef I18N_LOCALE
+ {"locale_name", 1, locale_name},
+ {"locale_parent", 1, locale_parent},
+ {"locale_language_tag", 1, locale_language_tag},
+ {"locale_base_name", 1, locale_base_name},
+#endif
+
};
ERL_NIF_INIT(i18n_nif,nif_funcs,load,reload,upgrade,unload)
View
BIN  fprof.trace
Binary file not shown
View
12 include/i18n.hrl
@@ -1 +1,11 @@
--define(ISTR(X), i18n_string:from(X)).
+
+-compile({parse_transform, i18n_expand}).
+
+% X is a variable.
+-define(_ISTR(X), i18n_string:from(X)).
+
+% X is a conctant.
+-define(ISTR(X),
+ ct_expand:term(
+ i18n_string:native_from(X)
+ )).
View
1  priv/README.md
@@ -0,0 +1 @@
+
View
BIN  priv/i18n_nif.so
Binary file not shown
View
272 src/ct_expand.erl
@@ -0,0 +1,272 @@
+%%% The contents of this file are subject to the Erlang Public License,
+%%% Version 1.0, (the "License"); you may not use this file except in
+%%% compliance with the License. You may obtain a copy of the License at
+%%% http://www.erlang.org/license/EPL1_0.txt
+%%%
+%%% Software distributed under the License is distributed on an "AS IS"
+%%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%%% the License for the specific language governing rights and limitations
+%%% under the License.
+%%%
+%%% The Original Code is ___
+%%%
+%%% The Initial Developer of the Original Code is Ericsson Telecom
+%%% AB. Portions created by Ericsson are Copyright (C), 1998, Ericsson
+%%% Telecom AB. All Rights Reserved.
+%%%
+%%% Contributor(s): ______________________________________.
+%%%
+%%% ------------------------------------------------------------------
+-module(ct_expand).
+-id('').
+-vsn('').
+-date('2006-08-22').
+-author('ulf.wiger@ericsson.com').
+
+%%% This module is a quick hack to allow for compile-time expansion of
+%%% complex expressions. To trigger the expansion, wrap the expression
+%%% inside a call to ct_expand:term/1.
+%%%
+%%% A simple example:
+%%%
+%%% f() ->
+%%% ct_expand:term(
+%%% ordsets:from_list([this, is, a, list, 'of', atoms])
+%%% ).
+%%%
+%%% The parse_transform evaluates this into:
+%%%
+%%% f() -> [a, atoms, is, list, 'of', this].
+%%%
+
+
+-export([function/4,
+ format_error/1]).
+-export([parse_transform/2]).
+-export([term/1]).
+
+
+-define(ERROR(R, T, F, I),
+ begin
+ rpt_error(R, T, F, I),
+ throw({error,erl_syntax:get_pos(
+ proplists:get_value(form,I)),{unknown,R}})
+ end).
+
+-import(erl_syntax, [clause/3,
+ clause_patterns/1,
+ clause_body/1,
+ clause_guard/1,
+ match_expr/2,
+ function_clauses/1,
+ get_pos/1,
+ add_ann/2,
+ get_ann/1]).
+
+term(X) -> X.
+
+
+%%% ==================================================================
+%%% This is the custom code that's been added. The rest of the module
+%%% is 'library code' (or should be...)
+%%% Any expression wrapped inside a call to ct_expand:term(Expr) is
+%%% replaced at compile-time with the results from erl_eval([Expr], [])
+%%% (note: no bindings)
+%%%
+parse_transform(Forms, Options) ->
+ function({?MODULE, term, 1},
+ fun(Form, _Context) ->
+ io:format("expanding...~n", []),
+ case erl_syntax:application_arguments(Form) of
+ [Expr] ->
+ case erl_eval:exprs(revert_tree([Expr]), []) of
+ {value, Value, _} ->
+ erl_syntax:abstract(Value);
+ Other ->
+ erlang:error({cannot_evaluate,
+ [Expr, Other]})
+ end;
+ _Args ->
+ erlang:error(illegal_form)
+ end
+ end, Forms, Options).
+
+%%% End custom code.
+%%% ==================================================================
+
+
+%%% API: function({Module, Function, Arity}, Fun, Forms, Options) ->
+%%% NewForms
+%%%
+%%% Forms and Options are the arguments passed to the parse_transform/2
+%%% function.
+%%% {Module, Function, Arity} is the function call to transform
+%%% Fun(Form, Context) -> NewForm is the fun provided by the caller.
+%%%
+%%% Context is a property list, containing the following properties:
+%%% - {file, Filename}
+%%% - {module, ModuleName}
+%%% - {function, FunctionName} % name of the enclosing function
+%%% - {arity, Arity :: integer()} % arity of same
+%%% - {var_names, Vars :: [atom()]} % generated variables binding the
+%%% % function arguments.
+%%% % length(Vars) == Arity
+%%%
+function({_Module, _Function, _Arity} = MFA, F,
+ Forms, Options) when is_function(F) ->
+ parse_transform(MFA, F, Forms, Options).
+
+parse_transform(MFA, Fun, Forms, _Options) ->
+ [File|_] = [F || {attribute,_,file,{F,_}} <- Forms],
+ try begin
+ NewTree = xform(MFA, Fun, Forms, [{file, File}]),
+ revert_tree(NewTree)
+ end
+ catch
+ throw:{error,Ln,What} ->
+ {error, [{File, [{Ln,?MODULE,What}]}], []}
+ end.
+
+revert_tree(Tree) ->
+ [erl_syntax:revert(T) || T <- lists:flatten(Tree)].
+
+
+format_error(Other) ->
+ lists:flatten(
+ io_lib:format("unknown error in parse_transform: ~p", [Other])).
+
+
+
+
+xform({M,F,A}, Fun, Forms, Context0) ->
+ Bef = fun(function, Form, Ctxt) ->
+ {Fname, Arity} = erl_syntax_lib:analyze_function(Form),
+ VarNames = erl_syntax_lib:new_variable_names(
+ Arity,
+ erl_syntax_lib:variables(Form)),
+ {Form, [{function, Fname},
+ {arity, Arity},
+ {var_names, VarNames}|Ctxt]};
+ (_, Form, Context) ->
+ {Form, Context}
+ end,
+ Aft = fun(application, Form, Context) ->
+ case erl_syntax_lib:analyze_application(Form) of
+ {M, {F, A}} ->
+ add_ann(
+ bind_state,
+ Fun(Form, Context));
+ _ ->
+ Form
+ end;
+ (function, Form, Context) ->
+ Form1 =
+ erl_syntax_lib:map_subtrees(
+ fun(Clause) ->
+ case should_i_bind(Clause) of
+ true ->
+ Pats = clause_patterns(Clause),
+ CBod = clause_body(Clause),
+ CGd = clause_guard(Clause),
+ Pats1 =
+ lists:zipwith(
+ fun(V, P) ->
+ match_expr(v(V), P)
+ end,
+ proplists:get_value(
+ var_names, Context),
+ Pats),
+ clause(Pats1, CGd, CBod);
+ false ->
+ Clause
+ end
+ end, Form),
+ Form1;
+ (_, Form, _Context) ->
+ Form
+ end,
+ [Module] = [Mx || {attribute, _, module, Mx} <- Forms],
+ transform(Forms, Bef, Aft, [{module, Module}|Context0]).
+
+
+transform(Forms, Before, After, Context) ->
+ F1 =
+ fun(Form) ->
+ Type = erl_syntax:type(Form),
+ {Form1, Context1} =
+ try Before(Type, Form, Context)
+ catch
+ error:Reason ->
+ ?ERROR(Reason, 'before', Before,
+ [{type, Type},
+ {context, Context},
+ {form, Form}])
+ end,
+ Form2 =
+ case erl_syntax:subtrees(Form1) of
+ [] ->
+ Form1;
+ List ->
+ NewList =
+ transform(
+ List, Before, After, Context1),
+ erl_syntax:update_tree(Form, NewList)
+ end,
+ Type2 = erl_syntax:type(Form2),
+ try After(Type2, Form2, Context1)
+ catch
+ error:Reason2 ->
+ ?ERROR(Reason2, 'after', After,
+ [{type, Type2},
+ {context, Context1},
+ {form, Form2}])
+ end
+ end,
+ F2 = fun(List) when is_list(List) ->
+ map(F1, List);
+ (Form) ->
+ F1(Form)
+ end,
+ map(F2, Forms).
+
+%%% Slightly modified version of lists:mapfoldl/3
+%%% Here, F/2 is able to insert forms before and after the form
+%%% in question. The inserted forms are not transformed afterwards.
+map(F, [Hd|Tail]) ->
+ {Before, Res, After} =
+ case F(Hd) of
+ {Be, _, Af} = Result when is_list(Be), is_list(Af) ->
+ Result;
+ R1 ->
+ {[], R1, []}
+ end,
+ Rs = map(F, Tail),
+ Before ++ [Res| After ++ Rs];
+map(F, []) when is_function(F, 1) -> [].
+
+
+
+rpt_error(Reason, BeforeOrAfter, Fun, Info) ->
+ Fmt = lists:flatten(
+ ["*** ERROR in parse_transform function:~n"
+ "*** Reason = ~p~n"
+ "*** applying ~w fun (~p)~n",
+ ["*** ~10w = ~p~n" || _ <- Info]]),
+ Args = [Reason, BeforeOrAfter, Fun |
+ lists:foldr(
+ fun({K,V}, Acc) ->
+ [K, V | Acc]
+ end, [], Info)],
+ io:format(Fmt, Args).
+
+
+should_i_bind(Tree) ->
+ erl_syntax_lib:fold(
+ fun(T, Flag) ->
+ lists:member(bind_state, get_ann(T)) or Flag
+ end, false, Tree).
+
+
+
+v(V) ->
+ erl_syntax:variable(V).
View
4 src/i18n.erl
@@ -1,11 +1,7 @@
-%% @author Mochi Media <dev@mochimedia.com>
-%% @copyright 2010 Mochi Media <dev@mochimedia.com>
-
%% @doc i18n.
%% @private
-module(i18n).
--author("Mochi Media <dev@mochimedia.com>").
-export([start/0, stop/0]).
-export([repeat/2]).
View
3  src/i18n_collation_tests.erl
@@ -29,6 +29,7 @@
-ifdef(TEST).
-include_lib("eunit/include/eunit.hrl").
-include_lib("triq/include/triq.hrl").
+
simple_open_test() ->
i18n_collation:open().
@@ -127,7 +128,7 @@ natural_sort_test_() ->
"Xiph Xlater 10000"],
C = i18n_collation:open('root', [numeric]),
- KeyFn = fun(X) -> i18n_collation:sort_key(C, ?ISTR(X)) end,
+ KeyFn = fun(X) -> i18n_collation:sort_key(C, ?_ISTR(X)) end,
F = fun(X) -> i18n_collation:map_sort(KeyFn, X) end,
View
6 src/i18n_expand.erl
@@ -0,0 +1,6 @@
+-module(i18n_expand).
+
+-export([parse_transform/2]).
+
+parse_transform(AST, Options) ->
+ ct_expand:parse_transform(AST, Options).
View
14 src/i18n_locale.erl
@@ -22,12 +22,15 @@
%%% =====================================================================
-module(i18n_locale).
+-include("i18n.hrl").
-export([set_locale/1, set_default_locale/1, get_locale/0]).
+-export([base_name/1]).
-define(SERVER, 'i18n_locale_server').
set_locale(Value) ->
- set_value('i18n_locale', Value).
+ LName = ?TRY_ATOM(?IM:locale_name(Value)),
+ set_value('i18n_locale', LName).
set_default_locale(Value) ->
set_default_value('i18n_locale', Value).
@@ -42,10 +45,12 @@ get_locale() ->
set_value(Key, Value) ->
- erlang:put(Key, Value).
+ erlang:put(Key, Value),
+ Value.
set_default_value(Key, Value) ->
- ?SERVER:set_default(Key, Value).
+ ?SERVER:set_default(Key, Value),
+ Value.
@@ -59,3 +64,6 @@ get_value(Key) ->
Value ->
Value
end.
+
+base_name(LocaleId) ->
+ ?TRY_ATOM(?IM:locale_base_name(LocaleId)).
View
8 src/i18n_locale_server.erl
@@ -90,10 +90,10 @@ lookup(Key) ->
[{Key, Value}] = ets:lookup(?CFG_TABLE_NAME, Key),
Value
catch
-%% error:badarg ->
-%% % Server is not running.
-%% i18n:start(),
-%% lookup(Key);
+ error:badarg ->
+ % Server is not running.
+ i18n:start(),
+ lookup(Key);
error:{badmatch, _V} ->
% Key is not found.
View
39 src/i18n_nif.erl
@@ -45,6 +45,10 @@
-export([collator_set_attr/3]).
-export([open_format/2, format/2, format/3]).
+-export([open_regex/1, regex_replace/3]).
+
+-export([locale_name/1, locale_parent/1, locale_language_tag/1,
+ locale_base_name/1]).
init() ->