diff --git a/include/translate.hrl b/include/translate.hrl new file mode 100644 index 000000000..b0e50e7d6 --- /dev/null +++ b/include/translate.hrl @@ -0,0 +1 @@ +-define(T(S), <>). diff --git a/src/mod_vcard_ldap.erl b/src/mod_vcard_ldap.erl index 869f9348b..88621fc0e 100644 --- a/src/mod_vcard_ldap.erl +++ b/src/mod_vcard_ldap.erl @@ -42,6 +42,7 @@ -include("logger.hrl"). -include("eldap.hrl"). -include("xmpp.hrl"). +-include("translate.hrl"). -define(PROCNAME, ejabberd_mod_vcard_ldap). @@ -324,31 +325,31 @@ default_vcard_map() -> {<<"PHOTO">>, <<"%s">>, [<<"jpegPhoto">>]}]. default_search_fields() -> - [{translate:mark(<<"User">>), <<"%u">>}, - {translate:mark(<<"Full Name">>), <<"displayName">>}, - {translate:mark(<<"Given Name">>), <<"givenName">>}, - {translate:mark(<<"Middle Name">>), <<"initials">>}, - {translate:mark(<<"Family Name">>), <<"sn">>}, - {translate:mark(<<"Nickname">>), <<"%u">>}, - {translate:mark(<<"Birthday">>), <<"birthDay">>}, - {translate:mark(<<"Country">>), <<"c">>}, - {translate:mark(<<"City">>), <<"l">>}, - {translate:mark(<<"Email">>), <<"mail">>}, - {translate:mark(<<"Organization Name">>), <<"o">>}, - {translate:mark(<<"Organization Unit">>), <<"ou">>}]. + [{?T("User"), <<"%u">>}, + {?T("Full Name"), <<"displayName">>}, + {?T("Given Name"), <<"givenName">>}, + {?T("Middle Name"), <<"initials">>}, + {?T("Family Name"), <<"sn">>}, + {?T("Nickname"), <<"%u">>}, + {?T("Birthday"), <<"birthDay">>}, + {?T("Country"), <<"c">>}, + {?T("City"), <<"l">>}, + {?T("Email"), <<"mail">>}, + {?T("Organization Name"), <<"o">>}, + {?T("Organization Unit"), <<"ou">>}]. default_search_reported() -> - [{translate:mark(<<"Full Name">>), <<"FN">>}, - {translate:mark(<<"Given Name">>), <<"FIRST">>}, - {translate:mark(<<"Middle Name">>), <<"MIDDLE">>}, - {translate:mark(<<"Family Name">>), <<"LAST">>}, - {translate:mark(<<"Nickname">>), <<"NICK">>}, - {translate:mark(<<"Birthday">>), <<"BDAY">>}, - {translate:mark(<<"Country">>), <<"CTRY">>}, - {translate:mark(<<"City">>), <<"LOCALITY">>}, - {translate:mark(<<"Email">>), <<"EMAIL">>}, - {translate:mark(<<"Organization Name">>), <<"ORGNAME">>}, - {translate:mark(<<"Organization Unit">>), <<"ORGUNIT">>}]. + [{?T("Full Name"), <<"FN">>}, + {?T("Given Name"), <<"FIRST">>}, + {?T("Middle Name"), <<"MIDDLE">>}, + {?T("Family Name"), <<"LAST">>}, + {?T("Nickname"), <<"NICK">>}, + {?T("Birthday"), <<"BDAY">>}, + {?T("Country"), <<"CTRY">>}, + {?T("City"), <<"LOCALITY">>}, + {?T("Email"), <<"EMAIL">>}, + {?T("Organization Name"), <<"ORGNAME">>}, + {?T("Organization Unit"), <<"ORGUNIT">>}]. parse_options(Host, Opts) -> MyHosts = gen_mod:get_opt_hosts(Host, Opts, <<"vjud.@HOST@">>), diff --git a/src/mod_vcard_mnesia.erl b/src/mod_vcard_mnesia.erl index 08dd4f940..3e742ec15 100644 --- a/src/mod_vcard_mnesia.erl +++ b/src/mod_vcard_mnesia.erl @@ -36,6 +36,7 @@ -include("xmpp.hrl"). -include("mod_vcard.hrl"). -include("logger.hrl"). +-include("translate.hrl"). %%%=================================================================== %%% API @@ -95,32 +96,32 @@ search(LServer, Data, AllowReturnAll, MaxMatch) -> end. search_fields(_LServer) -> - [{translate:mark(<<"User">>), <<"user">>}, - {translate:mark(<<"Full Name">>), <<"fn">>}, - {translate:mark(<<"Name">>), <<"first">>}, - {translate:mark(<<"Middle Name">>), <<"middle">>}, - {translate:mark(<<"Family Name">>), <<"last">>}, - {translate:mark(<<"Nickname">>), <<"nick">>}, - {translate:mark(<<"Birthday">>), <<"bday">>}, - {translate:mark(<<"Country">>), <<"ctry">>}, - {translate:mark(<<"City">>), <<"locality">>}, - {translate:mark(<<"Email">>), <<"email">>}, - {translate:mark(<<"Organization Name">>), <<"orgname">>}, - {translate:mark(<<"Organization Unit">>), <<"orgunit">>}]. + [{?T("User"), <<"user">>}, + {?T("Full Name"), <<"fn">>}, + {?T("Name"), <<"first">>}, + {?T("Middle Name"), <<"middle">>}, + {?T("Family Name"), <<"last">>}, + {?T("Nickname"), <<"nick">>}, + {?T("Birthday"), <<"bday">>}, + {?T("Country"), <<"ctry">>}, + {?T("City"), <<"locality">>}, + {?T("Email"), <<"email">>}, + {?T("Organization Name"), <<"orgname">>}, + {?T("Organization Unit"), <<"orgunit">>}]. search_reported(_LServer) -> - [{translate:mark(<<"Jabber ID">>), <<"jid">>}, - {translate:mark(<<"Full Name">>), <<"fn">>}, - {translate:mark(<<"Name">>), <<"first">>}, - {translate:mark(<<"Middle Name">>), <<"middle">>}, - {translate:mark(<<"Family Name">>), <<"last">>}, - {translate:mark(<<"Nickname">>), <<"nick">>}, - {translate:mark(<<"Birthday">>), <<"bday">>}, - {translate:mark(<<"Country">>), <<"ctry">>}, - {translate:mark(<<"City">>), <<"locality">>}, - {translate:mark(<<"Email">>), <<"email">>}, - {translate:mark(<<"Organization Name">>), <<"orgname">>}, - {translate:mark(<<"Organization Unit">>), <<"orgunit">>}]. + [{?T("Jabber ID"), <<"jid">>}, + {?T("Full Name"), <<"fn">>}, + {?T("Name"), <<"first">>}, + {?T("Middle Name"), <<"middle">>}, + {?T("Family Name"), <<"last">>}, + {?T("Nickname"), <<"nick">>}, + {?T("Birthday"), <<"bday">>}, + {?T("Country"), <<"ctry">>}, + {?T("City"), <<"locality">>}, + {?T("Email"), <<"email">>}, + {?T("Organization Name"), <<"orgname">>}, + {?T("Organization Unit"), <<"orgunit">>}]. remove_user(LUser, LServer) -> US = {LUser, LServer}, diff --git a/src/mod_vcard_sql.erl b/src/mod_vcard_sql.erl index 28a6f2ce5..6092c3500 100644 --- a/src/mod_vcard_sql.erl +++ b/src/mod_vcard_sql.erl @@ -37,6 +37,7 @@ -include("mod_vcard.hrl"). -include("logger.hrl"). -include("ejabberd_sql_pt.hrl"). +-include("translate.hrl"). %%%=================================================================== %%% API @@ -150,32 +151,32 @@ search(LServer, Data, AllowReturnAll, MaxMatch) -> end. search_fields(_LServer) -> - [{translate:mark(<<"User">>), <<"user">>}, - {translate:mark(<<"Full Name">>), <<"fn">>}, - {translate:mark(<<"Name">>), <<"first">>}, - {translate:mark(<<"Middle Name">>), <<"middle">>}, - {translate:mark(<<"Family Name">>), <<"last">>}, - {translate:mark(<<"Nickname">>), <<"nick">>}, - {translate:mark(<<"Birthday">>), <<"bday">>}, - {translate:mark(<<"Country">>), <<"ctry">>}, - {translate:mark(<<"City">>), <<"locality">>}, - {translate:mark(<<"Email">>), <<"email">>}, - {translate:mark(<<"Organization Name">>), <<"orgname">>}, - {translate:mark(<<"Organization Unit">>), <<"orgunit">>}]. + [{?T("User"), <<"user">>}, + {?T("Full Name"), <<"fn">>}, + {?T("Name"), <<"first">>}, + {?T("Middle Name"), <<"middle">>}, + {?T("Family Name"), <<"last">>}, + {?T("Nickname"), <<"nick">>}, + {?T("Birthday"), <<"bday">>}, + {?T("Country"), <<"ctry">>}, + {?T("City"), <<"locality">>}, + {?T("Email"), <<"email">>}, + {?T("Organization Name"), <<"orgname">>}, + {?T("Organization Unit"), <<"orgunit">>}]. search_reported(_LServer) -> - [{translate:mark(<<"Jabber ID">>), <<"jid">>}, - {translate:mark(<<"Full Name">>), <<"fn">>}, - {translate:mark(<<"Name">>), <<"first">>}, - {translate:mark(<<"Middle Name">>), <<"middle">>}, - {translate:mark(<<"Family Name">>), <<"last">>}, - {translate:mark(<<"Nickname">>), <<"nick">>}, - {translate:mark(<<"Birthday">>), <<"bday">>}, - {translate:mark(<<"Country">>), <<"ctry">>}, - {translate:mark(<<"City">>), <<"locality">>}, - {translate:mark(<<"Email">>), <<"email">>}, - {translate:mark(<<"Organization Name">>), <<"orgname">>}, - {translate:mark(<<"Organization Unit">>), <<"orgunit">>}]. + [{?T("Jabber ID"), <<"jid">>}, + {?T("Full Name"), <<"fn">>}, + {?T("Name"), <<"first">>}, + {?T("Middle Name"), <<"middle">>}, + {?T("Family Name"), <<"last">>}, + {?T("Nickname"), <<"nick">>}, + {?T("Birthday"), <<"bday">>}, + {?T("Country"), <<"ctry">>}, + {?T("City"), <<"locality">>}, + {?T("Email"), <<"email">>}, + {?T("Organization Name"), <<"orgname">>}, + {?T("Organization Unit"), <<"orgunit">>}]. remove_user(LUser, LServer) -> ejabberd_sql:sql_transaction( diff --git a/src/translate.erl b/src/translate.erl index aaadcd967..240a423d6 100644 --- a/src/translate.erl +++ b/src/translate.erl @@ -29,7 +29,7 @@ -behaviour(gen_server). --export([start_link/0, reload/0, translate/2, mark/1]). +-export([start_link/0, reload/0, translate/2]). %% gen_server callbacks -export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). @@ -219,10 +219,6 @@ translate(Msg) -> end end. --spec mark(binary()) -> binary(). -mark(Msg) -> - Msg. - ascii_tolower(B) -> iolist_to_binary(ascii_tolower_s(binary_to_list(B))). diff --git a/tools/extract-tr.sh b/tools/extract-tr.sh index 1a864f6a8..8f88b16ff 100755 --- a/tools/extract-tr.sh +++ b/tools/extract-tr.sh @@ -6,25 +6,13 @@ main([Dir]) -> Txts = filelib:fold_files( Dir, ".+\.beam\$", false, - fun(FileIn, Res) -> - case get_forms(FileIn) of - {ok, Forms} -> - Tree = erl_syntax:form_list(Forms), - Mod = mod(FileIn), - erl_syntax_lib:fold_subtrees( - fun(Form, Acc) -> - case erl_syntax:type(Form) of - function -> - case map(Form, Mod) of - [] -> - Acc; - Vars -> - Vars ++ Acc - end; - _ -> - Acc - end - end, [], Tree) ++ Res; + fun(BeamFile, Res) -> + Mod = mod(BeamFile), + ErlFile = filename:join("src", Mod ++ ".erl"), + case get_forms(BeamFile, ErlFile) of + {ok, BeamForms, ErlForms} -> + process_forms(BeamForms, Mod, application) ++ + process_forms(ErlForms, Mod, macro) ++ Res; _Err -> Res end @@ -40,12 +28,31 @@ main([Dir]) -> end, dict:new(), Txts), generate_pot(Dict). -map(Tree, Mod) -> +process_forms(Forms, Mod, Type) -> + Tree = erl_syntax:form_list(Forms), + erl_syntax_lib:fold_subtrees( + fun(Form, Acc) -> + case erl_syntax:type(Form) of + function -> + case map(Form, Mod, Type) of + [] -> + Acc; + Vars -> + Vars ++ Acc + end; + _ -> + Acc + end + end, [], Tree). + +map(Tree, Mod, Type) -> Vars = erl_syntax_lib:fold( fun(Form, Acc) -> case erl_syntax:type(Form) of - application -> + Type when Type == application -> analyze_app(Form, Mod) ++ Acc; + Type when Type == macro -> + analyze_macro(Form, Mod) ++ Acc; _ -> Acc end @@ -130,8 +137,7 @@ analyze_app(Form, Mod) -> {xmpp, "err_" ++ _, 2, [T|_]} -> T; {xmpp, "serr_" ++ _, 2, [T|_]} -> T; {xmpp, "mk_text", 2, [T|_]} -> T; - {translate, "translate", 2, [_,T|_]} -> T; - {translate, "mark", 1, [T]} -> T + {translate, "translate", 2, [_,T|_]} -> T end, Pos = erl_syntax:get_pos(Txt), case erl_syntax:type(Txt) of @@ -165,6 +171,24 @@ analyze_app(Form, Mod) -> [] end. +analyze_macro(Form, Mod) -> + try + Name = erl_syntax:macro_name(Form), + variable = erl_syntax:type(Name), + 'T' = erl_syntax:variable_name(Name), + [Txt] = erl_syntax:macro_arguments(Form), + string = erl_syntax:type(Txt), + Pos = erl_syntax:get_pos(Txt), + try [{list_to_binary(erl_syntax:string_value(Txt)), Pos}] + catch _:_ -> + log("~s:~p: not a binary: ~s~n", + [Mod, Pos, erl_prettypr:format(Txt)]), + [] + end + catch _:{badmatch, _} -> + [] + end. + generate_pot(Dict) -> io:format("~s~n~n", [pot_header()]), lists:foreach( @@ -213,20 +237,64 @@ pot_header() -> io_lib:nl()). mod(Path) -> - filename:rootname(filename:basename(Path)) ++ ".erl". + filename:rootname(filename:basename(Path)). log(Format, Args) -> io:format(standard_error, Format, Args). -get_forms(File) -> +get_forms(BeamFile, ErlFile) -> + try + {ok, BeamForms} = get_beam_forms(BeamFile), + {ok, ErlForms} = get_erl_forms(ErlFile), + {ok, BeamForms, ErlForms} + catch _:{badmatch, error} -> + error + end. + +get_beam_forms(File) -> case beam_lib:chunks(File, [abstract_code]) of {ok, {_, List}} -> case lists:keyfind(abstract_code, 1, List) of {abstract_code, {raw_abstract_v1, Abstr}} -> {ok, Abstr}; - _ -> + _Err -> + log("failed to get abstract code from ~s~n", [File]), error end; - _ -> + Err -> + log("failed to read chunks from ~s: ~p~n", [File, Err]), error end. + +get_erl_forms(Path) -> + case file:open(Path, [read]) of + {ok, Fd} -> + parse(Path, Fd, 1, []); + {error, Why} -> + log("failed to read ~s: ~s~n", [Path, file:format_error(Why)]), + error + end. + +parse(Path, Fd, Line, Acc) -> + {ok, Pos} = file:position(Fd, cur), + case epp_dodger:parse_form(Fd, Line) of + {ok, Form, NewLine} -> + {ok, NewPos} = file:position(Fd, cur), + {ok, RawForm} = file:pread(Fd, Pos, NewPos - Pos), + file:position(Fd, {bof, NewPos}), + AnnForm = erl_syntax:set_ann(Form, RawForm), + parse(Path, Fd, NewLine, [AnnForm|Acc]); + {eof, _} -> + {ok, NewPos} = file:position(Fd, cur), + if NewPos > Pos -> + {ok, RawForm} = file:pread(Fd, Pos, NewPos - Pos), + Form = erl_syntax:text(""), + AnnForm = erl_syntax:set_ann(Form, RawForm), + {ok, lists:reverse([AnnForm|Acc])}; + true -> + {ok, lists:reverse(Acc)} + end; + Err -> + log("failed to parse ~s: ~p~n", [Path, Err]), + error + end.