mirror of
https://github.com/processone/ejabberd.git
synced 2024-11-22 16:20:52 +01:00
Merge branch 'master' of github.com:processone/ejabberd
This commit is contained in:
commit
c1d3d1318e
@ -102,7 +102,7 @@ xref: all
|
||||
|
||||
|
||||
translations:
|
||||
contrib/extract_translations/prepare-translation.sh -updateall
|
||||
tools/prepare-tr.sh
|
||||
|
||||
edoc:
|
||||
$(ERL) -noinput +B -eval \
|
||||
|
@ -1,21 +0,0 @@
|
||||
extract_translations - auxiliary tool that extracts lines to be translated
|
||||
from ejabberd source tree.
|
||||
|
||||
Building:
|
||||
erlc extract_translations.erl
|
||||
|
||||
Invoking 1:
|
||||
erl -noinput -s extract_translations -extra dirname message_file
|
||||
|
||||
where dirname is the directory "src" in ejabberd's source tree root,
|
||||
message_file is a file with translated messages (src/msgs/*.msg).
|
||||
|
||||
Result is a list of messages from source files which aren't contained in
|
||||
message file.
|
||||
|
||||
Invoking 2:
|
||||
erl -noinput -s extract_translations -extra -unused dirname message_file
|
||||
|
||||
Result is a list of messages from message file which aren't in source
|
||||
files anymore.
|
||||
|
@ -1,307 +0,0 @@
|
||||
%%%----------------------------------------------------------------------
|
||||
%%% File : extract_translations.erl
|
||||
%%% Author : Sergei Golovan <sgolovan@nes.ru>
|
||||
%%% Purpose : Auxiliary tool for interface/messages translators
|
||||
%%% Created : 23 Apr 2005 by Sergei Golovan <sgolovan@nes.ru>
|
||||
%%% Id : $Id$
|
||||
%%%----------------------------------------------------------------------
|
||||
|
||||
-module(extract_translations).
|
||||
-author('sgolovan@nes.ru').
|
||||
|
||||
-export([start/0]).
|
||||
|
||||
-define(STATUS_SUCCESS, 0).
|
||||
-define(STATUS_ERROR, 1).
|
||||
-define(STATUS_USAGE, 2).
|
||||
|
||||
-include_lib("kernel/include/file.hrl").
|
||||
|
||||
|
||||
start() ->
|
||||
ets:new(translations, [named_table, public]),
|
||||
ets:new(translations_obsolete, [named_table, public]),
|
||||
ets:new(files, [named_table, public]),
|
||||
ets:new(vars, [named_table, public]),
|
||||
case init:get_plain_arguments() of
|
||||
["-srcmsg2po", Dir, File] ->
|
||||
print_po_header(File),
|
||||
Status = process(Dir, File, srcmsg2po),
|
||||
halt(Status);
|
||||
["-unused", Dir, File] ->
|
||||
Status = process(Dir, File, unused),
|
||||
halt(Status);
|
||||
[Dir, File] ->
|
||||
Status = process(Dir, File, used),
|
||||
halt(Status);
|
||||
_ ->
|
||||
print_usage(),
|
||||
halt(?STATUS_USAGE)
|
||||
end.
|
||||
|
||||
|
||||
process(Dir, File, Used) ->
|
||||
case load_file(File) of
|
||||
{error, Reason} ->
|
||||
io:format("~s: ~s~n", [File, file:format_error(Reason)]),
|
||||
?STATUS_ERROR;
|
||||
_ ->
|
||||
FileList = find_src_files(Dir),
|
||||
lists:foreach(
|
||||
fun(F) ->
|
||||
parse_file(Dir, F, Used)
|
||||
end, FileList),
|
||||
case Used of
|
||||
unused ->
|
||||
ets:foldl(fun({Key, _}, _) ->
|
||||
io:format("~p~n", [Key])
|
||||
end, ok, translations);
|
||||
srcmsg2po ->
|
||||
ets:foldl(fun({Key, Trans}, _) ->
|
||||
print_translation_obsolete(Key, Trans)
|
||||
end, ok, translations_obsolete);
|
||||
_ ->
|
||||
ok
|
||||
end,
|
||||
?STATUS_SUCCESS
|
||||
end.
|
||||
|
||||
parse_file(Dir, File, Used) ->
|
||||
ets:delete_all_objects(vars),
|
||||
case epp:parse_file(File, [Dir, filename:dirname(File) | code:get_path()], []) of
|
||||
{ok, Forms} ->
|
||||
lists:foreach(
|
||||
fun(F) ->
|
||||
parse_form(Dir, File, F, Used)
|
||||
end, Forms);
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
parse_form(Dir, File, Form, Used) ->
|
||||
case Form of
|
||||
%%{undefined, Something} ->
|
||||
%% io:format("Undefined: ~p~n", [Something]);
|
||||
{call,
|
||||
_,
|
||||
{remote, _, {atom, _, translate}, {atom, _, translate}},
|
||||
[_, {string, Line, Str}]
|
||||
} ->
|
||||
process_string(Dir, File, Line, Str, Used);
|
||||
{call,
|
||||
_,
|
||||
{remote, _, {atom, _, translate}, {atom, _, translate}},
|
||||
[_,
|
||||
{bin,_,
|
||||
[{bin_element,_,
|
||||
{string,Line,Str},
|
||||
default,default}]}]
|
||||
} ->
|
||||
process_string(Dir, File, Line, Str, Used);
|
||||
{call,
|
||||
_,
|
||||
{remote, _, {atom, _, translate}, {atom, _, translate}},
|
||||
[_, {var, _, Name}]
|
||||
} ->
|
||||
case ets:lookup(vars, Name) of
|
||||
[{_Name, Value, Line}] ->
|
||||
process_string(Dir, File, Line, Value, Used);
|
||||
_ ->
|
||||
ok
|
||||
end;
|
||||
{match,
|
||||
_,
|
||||
{var, _, Name},
|
||||
{string, Line, Value}
|
||||
} ->
|
||||
ets:insert(vars, {Name, Value, Line});
|
||||
{match,
|
||||
_,
|
||||
{var, _, Name},
|
||||
{bin,Line,[{bin_element,_,{string,_,Value},_,_}]}
|
||||
} ->
|
||||
ets:insert(vars, {Name, Value, Line});
|
||||
L when is_list(L) ->
|
||||
lists:foreach(
|
||||
fun(F) ->
|
||||
parse_form(Dir, File, F, Used)
|
||||
end, L);
|
||||
T when is_tuple(T) ->
|
||||
lists:foreach(
|
||||
fun(F) ->
|
||||
parse_form(Dir, File, F, Used)
|
||||
end, tuple_to_list(T));
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
process_string(_Dir, _File, _Line, "", _Used) ->
|
||||
ok;
|
||||
|
||||
process_string(_Dir, File, Line, Str, Used) ->
|
||||
case {ets:lookup(translations, Str), Used} of
|
||||
{[{_Key, _Trans}], unused} ->
|
||||
ets:delete(translations, Str);
|
||||
{[{_Key, _Trans}], used} ->
|
||||
ok;
|
||||
{[{_Key, Trans}], srcmsg2po} ->
|
||||
ets:delete(translations_obsolete, Str),
|
||||
print_translation(File, Line, Str, Trans);
|
||||
{_, used} ->
|
||||
case ets:lookup(files, File) of
|
||||
[{_}] ->
|
||||
ok;
|
||||
_ ->
|
||||
io:format("~n% ~s~n", [File]),
|
||||
ets:insert(files, {File})
|
||||
end,
|
||||
case Str of
|
||||
[] -> ok;
|
||||
_ -> io:format("{~p, \"\"}.~n", [Str])
|
||||
end,
|
||||
ets:insert(translations, {Str, ""});
|
||||
{_, srcmsg2po} ->
|
||||
case ets:lookup(files, File) of
|
||||
[{_}] ->
|
||||
ok;
|
||||
_ ->
|
||||
ets:insert(files, {File})
|
||||
end,
|
||||
ets:insert(translations, {Str, ""}),
|
||||
print_translation(File, Line, Str, "");
|
||||
_ ->
|
||||
ok
|
||||
end.
|
||||
|
||||
load_file(File) ->
|
||||
case file:consult(File) of
|
||||
{ok, Terms} ->
|
||||
lists:foreach(
|
||||
fun({Orig, Trans}) ->
|
||||
case Trans of
|
||||
"" ->
|
||||
ok;
|
||||
_ ->
|
||||
ets:insert(translations, {Orig, Trans}),
|
||||
ets:insert(translations_obsolete, {Orig, Trans})
|
||||
end
|
||||
end, Terms);
|
||||
Err ->
|
||||
Err
|
||||
end.
|
||||
|
||||
find_src_files(Dir) ->
|
||||
case file:list_dir(Dir) of
|
||||
{ok, FileList} ->
|
||||
recurse_filelist(
|
||||
lists:map(
|
||||
fun(F) ->
|
||||
filename:join(Dir, F)
|
||||
end, FileList));
|
||||
_ ->
|
||||
[]
|
||||
end.
|
||||
|
||||
recurse_filelist(FileList) ->
|
||||
recurse_filelist(FileList, []).
|
||||
|
||||
recurse_filelist([], Acc) ->
|
||||
lists:reverse(Acc);
|
||||
|
||||
recurse_filelist([H | T], Acc) ->
|
||||
case file:read_file_info(H) of
|
||||
{ok, #file_info{type = directory}} ->
|
||||
recurse_filelist(T, lists:reverse(find_src_files(H)) ++ Acc);
|
||||
{ok, #file_info{type = regular}} ->
|
||||
case string:substr(H, string:len(H) - 3) of
|
||||
".erl" ->
|
||||
recurse_filelist(T, [H | Acc]);
|
||||
".hrl" ->
|
||||
recurse_filelist(T, [H | Acc]);
|
||||
_ ->
|
||||
recurse_filelist(T, Acc)
|
||||
end;
|
||||
_ ->
|
||||
recurse_filelist(T, Acc)
|
||||
end.
|
||||
|
||||
|
||||
print_usage() ->
|
||||
io:format(
|
||||
"Usage: extract_translations [-unused] dir file~n"
|
||||
"~n"
|
||||
"Example:~n"
|
||||
" extract_translations . ./msgs/ru.msg~n"
|
||||
).
|
||||
|
||||
|
||||
%%%
|
||||
%%% Gettext
|
||||
%%%
|
||||
|
||||
print_po_header(File) ->
|
||||
MsgProps = get_msg_header_props(File),
|
||||
{Language, [LastT | AddT]} = prepare_props(MsgProps),
|
||||
print_po_header(Language, LastT, AddT).
|
||||
|
||||
get_msg_header_props(File) ->
|
||||
{ok, F} = file:open(File, [read]),
|
||||
Lines = get_msg_header_props(F, []),
|
||||
file:close(F),
|
||||
Lines.
|
||||
|
||||
get_msg_header_props(F, Lines) ->
|
||||
String = io:get_line(F, ""),
|
||||
case io_lib:fread("% ", String) of
|
||||
{ok, [], RemString} ->
|
||||
case io_lib:fread("~s", RemString) of
|
||||
{ok, [Key], Value} when Value /= "\n" ->
|
||||
%% The first character in Value is a blankspace:
|
||||
%% And the last characters are 'slash n'
|
||||
ValueClean = string:substr(Value, 2, string:len(Value)-2),
|
||||
get_msg_header_props(F, Lines ++ [{Key, ValueClean}]);
|
||||
_ ->
|
||||
get_msg_header_props(F, Lines)
|
||||
end;
|
||||
_ ->
|
||||
Lines
|
||||
end.
|
||||
|
||||
prepare_props(MsgProps) ->
|
||||
Language = proplists:get_value("Language:", MsgProps),
|
||||
Authors = proplists:get_all_values("Author:", MsgProps),
|
||||
{Language, Authors}.
|
||||
|
||||
print_po_header(Language, LastTranslator, AdditionalTranslatorsList) ->
|
||||
AdditionalTranslatorsString = build_additional_translators(AdditionalTranslatorsList),
|
||||
HeaderString =
|
||||
"msgid \"\"\n"
|
||||
"msgstr \"\"\n"
|
||||
++ "\"X-Language: " ++ Language ++ "\\n\"\n"
|
||||
"\"Last-Translator: " ++ LastTranslator ++ "\\n\"\n"
|
||||
++ AdditionalTranslatorsString ++
|
||||
"\"MIME-Version: 1.0\\n\"\n"
|
||||
"\"Content-Type: text/plain; charset=UTF-8\\n\"\n"
|
||||
"\"Content-Transfer-Encoding: 8bit\\n\"\n",
|
||||
io:format("~s~n", [HeaderString]).
|
||||
|
||||
build_additional_translators(List) ->
|
||||
lists:foldl(
|
||||
fun(T, Str) ->
|
||||
Str ++ "\"X-Additional-Translator: " ++ T ++ "\\n\"\n"
|
||||
end,
|
||||
"",
|
||||
List).
|
||||
|
||||
print_translation(File, Line, Str, StrT) ->
|
||||
StrQ = ejabberd_regexp:greplace(list_to_binary(Str), <<"\\\"">>, <<"\\\\\"">>),
|
||||
StrTQ = ejabberd_regexp:greplace(list_to_binary(StrT), <<"\\\"">>, <<"\\\\\"">>),
|
||||
io:format("#: ~s:~p~nmsgid \"~s\"~nmsgstr \"~s\"~n~n", [File, Line, StrQ, StrTQ]).
|
||||
|
||||
print_translation_obsolete(Str, StrT) ->
|
||||
File = "unknown.erl",
|
||||
Line = 1,
|
||||
StrQ = ejabberd_regexp:greplace(Str, "\\\"", "\\\\\""),
|
||||
StrTQ = ejabberd_regexp:greplace(StrT, "\\\"", "\\\\\""),
|
||||
io:format("#: ~s:~p~n#~~ msgid \"~s\"~n#~~ msgstr \"~s\"~n~n", [File, Line, StrQ, StrTQ]).
|
||||
|
@ -1,366 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Frontend for ejabberd's extract_translations.erl
|
||||
# by Badlop
|
||||
|
||||
# How to create template files for a new language:
|
||||
# NEWLANG=zh
|
||||
# cp msgs/ejabberd.pot msgs/$NEWLANG.po
|
||||
# echo \{\"\",\"\"\}. > msgs/$NEWLANG.msg
|
||||
# ../../extract_translations/prepare-translation.sh -updateall
|
||||
|
||||
prepare_dirs ()
|
||||
{
|
||||
# Where is Erlang binary
|
||||
ERL=`which erl`
|
||||
|
||||
EJA_SRC_DIR=$EJA_DIR/src/
|
||||
EJA_MSGS_DIR=$EJA_DIR/priv/msgs/
|
||||
EXTRACT_DIR=$EJA_DIR/contrib/extract_translations/
|
||||
EXTRACT_ERL=$EXTRACT_DIR/extract_translations.erl
|
||||
EXTRACT_BEAM=$EXTRACT_DIR/extract_translations.beam
|
||||
|
||||
SRC_DIR=$RUN_DIR/src
|
||||
EBIN_DIR=$RUN_DIR/ebin
|
||||
MSGS_DIR=$EJA_DIR/priv/msgs
|
||||
|
||||
if !([[ -n $EJA_DIR ]])
|
||||
then
|
||||
echo "ejabberd dir does not exist: $EJA_DIR"
|
||||
fi
|
||||
|
||||
if !([[ -x $EXTRACT_BEAM ]])
|
||||
then
|
||||
sh -c "cd $EXTRACT_DIR; $ERL -compile $EXTRACT_ERL"
|
||||
fi
|
||||
}
|
||||
|
||||
extract_lang ()
|
||||
{
|
||||
MSGS_FILE=$1
|
||||
MSGS_FILE2=$MSGS_FILE.translate
|
||||
MSGS_PATH=$MSGS_DIR/$MSGS_FILE
|
||||
MSGS_PATH2=$MSGS_DIR/$MSGS_FILE2
|
||||
|
||||
echo -n "Extracting language strings for '$MSGS_FILE':"
|
||||
|
||||
echo -n " new..."
|
||||
cd $SRC_DIR
|
||||
$ERL -pa $EXTRACT_DIR -noinput -noshell -s extract_translations -s init stop -extra . $MSGS_PATH >$MSGS_PATH.new
|
||||
sed -e 's/^% \.\//% /g;' $MSGS_PATH.new > $MSGS_PATH.new2
|
||||
mv $MSGS_PATH.new2 $MSGS_PATH.new
|
||||
|
||||
echo -n " old..."
|
||||
$ERL -pa $EXTRACT_DIR -noinput -noshell -s extract_translations -s init stop -extra -unused . $MSGS_PATH >$MSGS_PATH.unused
|
||||
find_unused_full $MSGS_FILE $MSGS_FILE.unused
|
||||
|
||||
echo "" >$MSGS_PATH2
|
||||
echo " ***** Translation file for ejabberd ***** " >>$MSGS_PATH2
|
||||
echo "" >>$MSGS_PATH2
|
||||
|
||||
echo "" >>$MSGS_PATH2
|
||||
echo " *** New strings: Can you please translate them? *** " >>$MSGS_PATH2
|
||||
cat $MSGS_PATH.new >>$MSGS_PATH2
|
||||
|
||||
echo "" >>$MSGS_PATH2
|
||||
echo "" >>$MSGS_PATH2
|
||||
echo " *** Unused strings: They will be removed automatically *** " >>$MSGS_PATH2
|
||||
cat $MSGS_PATH.unused.full >>$MSGS_PATH2
|
||||
|
||||
echo "" >>$MSGS_PATH2
|
||||
echo "" >>$MSGS_PATH2
|
||||
echo " *** Already translated strings: you can also modify any of them if you want *** " >>$MSGS_PATH2
|
||||
echo "" >>$MSGS_PATH2
|
||||
cat $MSGS_PATH.old_cleaned >>$MSGS_PATH2
|
||||
|
||||
echo " ok"
|
||||
|
||||
rm $MSGS_PATH.new
|
||||
rm $MSGS_PATH.old_cleaned
|
||||
rm $MSGS_PATH.unused.full
|
||||
}
|
||||
|
||||
extract_lang_all ()
|
||||
{
|
||||
cd $MSGS_DIR
|
||||
for i in $( ls *.msg ) ; do
|
||||
extract_lang $i;
|
||||
done
|
||||
|
||||
echo -e "File\tMissing\tLanguage\t\tLast translator"
|
||||
echo -e "----\t-------\t--------\t\t---------------"
|
||||
cd $MSGS_DIR
|
||||
for i in $( ls *.msg ) ; do
|
||||
MISSING=`cat $i.translate | grep "\", \"\"}." | wc -l`
|
||||
LANGUAGE=`grep "X-Language:" $i.translate | sed 's/% Language: //g'`
|
||||
LASTAUTH=`grep "Author:" $i.translate | head -n 1 | sed 's/% Author: //g'`
|
||||
echo -e "$i\t$MISSING\t$LANGUAGE\t$LASTAUTH"
|
||||
done
|
||||
|
||||
cd $MSGS_DIR
|
||||
REVISION=`git describe --always`
|
||||
zip $HOME/ejabberd-langs-$REVISION.zip *.translate;
|
||||
|
||||
rm *.translate
|
||||
}
|
||||
|
||||
find_unused_full ()
|
||||
{
|
||||
DATFILE=$1
|
||||
DATFILEI=$1.old_cleaned
|
||||
DELFILE=$2
|
||||
cd msgs
|
||||
|
||||
DATFILE1=$DATFILE.t1
|
||||
DATFILE2=$DATFILE.t2
|
||||
|
||||
DELFILE1=$DELFILE.t1
|
||||
DELFILE2=$DELFILE.t2
|
||||
DELFILEF=$DATFILE.unused.full
|
||||
echo "" >$DELFILEF
|
||||
|
||||
grep -v "\\\\" $DELFILE >$DELFILE2
|
||||
echo ENDFILEMARK >>$DELFILE2
|
||||
cp $DATFILE $DATFILEI
|
||||
cp $DATFILE $DATFILE2
|
||||
|
||||
cp $DELFILE2 $DELFILE1
|
||||
STRING=`head -1 $DELFILE1`
|
||||
until [[ $STRING == ENDFILEMARK ]]; do
|
||||
cp $DELFILE2 $DELFILE1
|
||||
cp $DATFILE2 $DATFILE1
|
||||
|
||||
STRING=`head -1 $DELFILE1`
|
||||
|
||||
cat $DATFILE1 | grep "$STRING" >>$DELFILEF
|
||||
cat $DATFILE1 | grep -v "$STRING" >$DATFILE2
|
||||
cat $DELFILE1 | grep -v "$STRING" >$DELFILE2
|
||||
done
|
||||
|
||||
mv $DATFILE2 $DATFILEI
|
||||
|
||||
rm -f $MSGS_PATH.t1
|
||||
rm $MSGS_PATH.unused
|
||||
rm -f $MSGS_PATH.unused.t1
|
||||
rm $MSGS_PATH.unused.t2
|
||||
|
||||
cd ..
|
||||
}
|
||||
|
||||
extract_lang_srcmsg2po ()
|
||||
{
|
||||
LANG=$1
|
||||
LANG_CODE=$LANG.$PROJECT
|
||||
MSGS_PATH=$MSGS_DIR/$LANG_CODE.msg
|
||||
PO_PATH=$MSGS_DIR/$LANG_CODE.po
|
||||
|
||||
echo $MSGS_PATH
|
||||
|
||||
cd $SRC_DIR
|
||||
$ERL -pa $EXTRACT_DIR -pa $EBIN_DIR -pa $EJA_SRC_DIR -pa ../include -noinput -noshell -s extract_translations -s init stop -extra -srcmsg2po . $MSGS_PATH >$PO_PATH.1
|
||||
sed -e 's/ \[\]$/ \"\"/g;' $PO_PATH.1 > $PO_PATH.2
|
||||
msguniq --sort-by-file $PO_PATH.2 --output-file=$PO_PATH
|
||||
|
||||
rm $PO_PATH.*
|
||||
}
|
||||
|
||||
extract_lang_src2pot ()
|
||||
{
|
||||
LANG_CODE=$PROJECT
|
||||
MSGS_PATH=$MSGS_DIR/$LANG_CODE.msg
|
||||
POT_PATH=$MSGS_DIR/$LANG_CODE.pot
|
||||
|
||||
echo -n "" >$MSGS_PATH
|
||||
echo "% Language: Language Name" >>$MSGS_PATH
|
||||
echo "% Author: Translator name and contact method" >>$MSGS_PATH
|
||||
echo "" >>$MSGS_PATH
|
||||
|
||||
cd $SRC_DIR
|
||||
$ERL -pa $EXTRACT_DIR -pa $EBIN_DIR -pa $EJA_SRC_DIR -pa ../include -noinput -noshell -s extract_translations -s init stop -extra -srcmsg2po . $MSGS_PATH >$POT_PATH.1
|
||||
sed -e 's/ \[\]$/ \"\"/g;' $POT_PATH.1 > $POT_PATH.2
|
||||
|
||||
#msguniq --sort-by-file $POT_PATH.2 $EJA_MSGS_DIR --output-file=$POT_PATH
|
||||
msguniq --sort-by-file $POT_PATH.2 --output-file=$POT_PATH
|
||||
|
||||
rm $POT_PATH.*
|
||||
rm $MSGS_PATH
|
||||
|
||||
# If the project is a specific module, not the main ejabberd
|
||||
if [[ $PROJECT != ejabberd ]] ; then
|
||||
# Remove from project.pot the strings that are already present in the general ejabberd
|
||||
EJABBERD_MSG_FILE=$EJA_MSGS_DIR/es.po # This is just some file with translated strings
|
||||
POT_PATH_TEMP=$POT_PATH.temp
|
||||
msgattrib --set-obsolete --only-file=$EJABBERD_MSG_FILE -o $POT_PATH_TEMP $POT_PATH
|
||||
mv $POT_PATH_TEMP $POT_PATH
|
||||
fi
|
||||
}
|
||||
|
||||
extract_lang_popot2po ()
|
||||
{
|
||||
LANG_CODE=$1
|
||||
PO_PATH=$MSGS_DIR/$LANG_CODE.po
|
||||
POT_PATH=$MSGS_DIR/$PROJECT.pot
|
||||
|
||||
msgmerge $PO_PATH $POT_PATH >$PO_PATH.translate 2>/dev/null
|
||||
mv $PO_PATH.translate $PO_PATH
|
||||
}
|
||||
|
||||
extract_lang_po2msg ()
|
||||
{
|
||||
LANG_CODE=$1
|
||||
PO_PATH=$LANG_CODE.po
|
||||
MS_PATH=$PO_PATH.ms
|
||||
MSGID_PATH=$PO_PATH.msgid
|
||||
MSGSTR_PATH=$PO_PATH.msgstr
|
||||
MSGS_PATH=$LANG_CODE.msg
|
||||
|
||||
cd $MSGS_DIR
|
||||
|
||||
# Check PO has correct ~
|
||||
# Let's convert to C format so we can use msgfmt
|
||||
PO_TEMP=$LANG_CODE.po.temp
|
||||
cat $PO_PATH | sed 's/%/perc/g' | sed 's/~/%/g' | sed 's/#:.*/#, c-format/g' >$PO_TEMP
|
||||
msgfmt $PO_TEMP --check-format
|
||||
result=$?
|
||||
rm $PO_TEMP
|
||||
if [ $result -ne 0 ] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
msgattrib $PO_PATH --translated --no-fuzzy --no-obsolete --no-location --no-wrap | grep "^msg" | tail --lines=+3 >$MS_PATH
|
||||
grep "^msgid" $PO_PATH.ms | sed 's/^msgid //g' >$MSGID_PATH
|
||||
grep "^msgstr" $PO_PATH.ms | sed 's/^msgstr //g' >$MSGSTR_PATH
|
||||
echo "%% -*- coding: latin-1 -*-" >$MSGS_PATH
|
||||
paste $MSGID_PATH $MSGSTR_PATH --delimiter=, | awk '{print "{" $0 "}."}' | sort -g >>$MSGS_PATH
|
||||
|
||||
rm $MS_PATH
|
||||
rm $MSGID_PATH
|
||||
rm $MSGSTR_PATH
|
||||
}
|
||||
|
||||
extract_lang_updateall ()
|
||||
{
|
||||
echo "Generating POT"
|
||||
extract_lang_src2pot
|
||||
|
||||
cd $MSGS_DIR
|
||||
echo ""
|
||||
echo -e "File Missing Language Last translator"
|
||||
echo -e "---- ------- -------- ---------------"
|
||||
for i in $( ls *.msg ) ; do
|
||||
LANG_CODE=${i%.msg}
|
||||
echo -n $LANG_CODE | awk '{printf "%-6s", $1 }'
|
||||
|
||||
# Convert old MSG file to PO
|
||||
PO=$LANG_CODE.po
|
||||
[ -f $PO ] || extract_lang_srcmsg2po $LANG_CODE
|
||||
|
||||
extract_lang_popot2po $LANG_CODE
|
||||
extract_lang_po2msg $LANG_CODE
|
||||
|
||||
MISSING=`msgfmt --statistics $PO 2>&1 | awk '{printf "%5s", $4 }'`
|
||||
echo -n " $MISSING"
|
||||
|
||||
LANGUAGE=`grep "X-Language:" $PO | sed 's/\"X-Language: //g' | sed 's/\\\\n\"//g' | awk '{printf "%-12s", $1}'`
|
||||
echo -n " $LANGUAGE"
|
||||
|
||||
LASTAUTH=`grep "Last-Translator" $PO | sed 's/\"Last-Translator: //g' | sed 's/\\\\n\"//g'`
|
||||
echo " $LASTAUTH"
|
||||
done
|
||||
echo ""
|
||||
rm messages.mo
|
||||
|
||||
cd ..
|
||||
}
|
||||
|
||||
translation_instructions ()
|
||||
{
|
||||
echo ""
|
||||
echo " A new file has been created for you, with the current, the new and the deprecated strings:"
|
||||
echo " $MSGS_PATH2"
|
||||
echo ""
|
||||
echo " At the end of that file you will find the strings you must update:"
|
||||
echo " - Untranslated strings are like this: {"March", ""}."
|
||||
echo " To translate the string, add the text inside the commas. Example:"
|
||||
echo " {"March", "Marzo"}."
|
||||
echo " - Old strings that are not used: "Woowoa""
|
||||
echo " Search the entire file for those strings and remove them"
|
||||
echo ""
|
||||
echo " Once you have translated all the strings and removed all the old ones,"
|
||||
echo " rename the file to overwrite the previous one:"
|
||||
echo " $MSGS_PATH"
|
||||
}
|
||||
|
||||
EJA_DIR=`pwd`
|
||||
RUN_DIR=`pwd`
|
||||
PROJECT=ejabberd
|
||||
|
||||
while [ $# -ne 0 ] ; do
|
||||
PARAM=$1
|
||||
shift
|
||||
case $PARAM in
|
||||
--) break ;;
|
||||
-project)
|
||||
PROJECT=$1
|
||||
shift
|
||||
;;
|
||||
-ejadir)
|
||||
EJA_DIR=$1
|
||||
shift
|
||||
;;
|
||||
-rundir)
|
||||
RUN_DIR=$1
|
||||
shift
|
||||
;;
|
||||
-lang)
|
||||
LANGU=$1
|
||||
prepare_dirs
|
||||
extract_lang $LANGU
|
||||
shift
|
||||
;;
|
||||
-langall)
|
||||
prepare_dirs
|
||||
extract_lang_all
|
||||
;;
|
||||
-srcmsg2po)
|
||||
LANG_CODE=$1
|
||||
prepare_dirs
|
||||
extract_lang_srcmsg2po $LANG_CODE
|
||||
shift
|
||||
;;
|
||||
-popot2po)
|
||||
LANG_CODE=$1
|
||||
prepare_dirs
|
||||
extract_lang_popot2po $LANG_CODE
|
||||
shift
|
||||
;;
|
||||
-src2pot)
|
||||
prepare_dirs
|
||||
extract_lang_src2pot
|
||||
;;
|
||||
-po2msg)
|
||||
LANG_CODE=$1
|
||||
prepare_dirs
|
||||
extract_lang_po2msg $LANG_CODE
|
||||
shift
|
||||
;;
|
||||
-updateall)
|
||||
prepare_dirs
|
||||
extract_lang_updateall
|
||||
;;
|
||||
*)
|
||||
echo "Options:"
|
||||
echo " -langall"
|
||||
echo " -lang LANGUAGE_FILE"
|
||||
echo " -srcmsg2po LANGUAGE Construct .msg file using source code to PO file"
|
||||
echo " -src2pot Generate template POT file from source code"
|
||||
echo " -popot2po LANGUAGE Update PO file with template POT file"
|
||||
echo " -po2msg LANGUAGE Export PO file to MSG file"
|
||||
echo " -updateall Generate POT and update all PO"
|
||||
echo ""
|
||||
echo "Example:"
|
||||
echo " ./prepare-translation.sh -lang es.msg"
|
||||
exit 0
|
||||
;;
|
||||
esac
|
||||
done
|
@ -22,7 +22,7 @@
|
||||
{tag, {if_version_above, "17", "3.4.2", "3.2.1"}}}},
|
||||
{p1_utils, ".*", {git, "https://github.com/processone/p1_utils", {tag, "1.0.9"}}},
|
||||
{cache_tab, ".*", {git, "https://github.com/processone/cache_tab", {tag, "1.0.10"}}},
|
||||
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", {tag, "1.0.15"}}},
|
||||
{fast_tls, ".*", {git, "https://github.com/processone/fast_tls", "fc3ef32"}},
|
||||
{stringprep, ".*", {git, "https://github.com/processone/stringprep", {tag, "1.0.9"}}},
|
||||
{fast_xml, ".*", {git, "https://github.com/processone/fast_xml", {tag, "1.1.23"}}},
|
||||
{xmpp, ".*", {git, "https://github.com/processone/xmpp", "d98be4a3159"}},
|
||||
|
@ -25,7 +25,7 @@
|
||||
|
||||
-module(mod_http_upload).
|
||||
-author('holger@zedat.fu-berlin.de').
|
||||
-compile(export_all).
|
||||
|
||||
-protocol({xep, 363, '0.1'}).
|
||||
|
||||
-define(SERVICE_REQUEST_TIMEOUT, 5000). % 5 seconds.
|
||||
|
@ -51,13 +51,15 @@
|
||||
-type host_port() :: {inet:hostname(), inet:port_number()}.
|
||||
-type ip_port() :: {inet:ip_address(), inet:port_number()}.
|
||||
-type network_error() :: {error, inet:posix() | inet_res:res_error()}.
|
||||
-type tls_error_reason() :: inet:posix() | atom() | binary().
|
||||
-type socket_error_reason() :: inet:posix() | atom().
|
||||
-type stop_reason() :: {idna, bad_string} |
|
||||
{dns, inet:posix() | inet_res:res_error()} |
|
||||
{stream, reset | {in | out, stream_error()}} |
|
||||
{tls, inet:posix() | atom() | binary()} |
|
||||
{tls, tls_error_reason()} |
|
||||
{pkix, binary()} |
|
||||
{auth, atom() | binary() | string()} |
|
||||
{socket, inet:posix() | atom()} |
|
||||
{socket, socket_error_reason()} |
|
||||
internal_failure.
|
||||
-export_type([state/0, stop_reason/0]).
|
||||
-callback init(list()) -> {ok, state()} | {error, term()} | ignore.
|
||||
@ -276,19 +278,20 @@ handle_cast(connect, #{remote_server := RemoteServer,
|
||||
process_stream_end({idna, bad_string}, State);
|
||||
ASCIIName ->
|
||||
case resolve(binary_to_list(ASCIIName), State) of
|
||||
{ok, AddrPorts} ->
|
||||
case connect(AddrPorts, State) of
|
||||
{{ok, AddrPorts}, Encrypted} ->
|
||||
case connect(AddrPorts, State, Encrypted) of
|
||||
{ok, Socket, AddrPort} ->
|
||||
SocketMonitor = SockMod:monitor(Socket),
|
||||
State1 = State#{ip => AddrPort,
|
||||
socket => Socket,
|
||||
stream_encrypted => Encrypted,
|
||||
socket_monitor => SocketMonitor},
|
||||
State2 = State1#{stream_state => wait_for_stream},
|
||||
send_header(State2);
|
||||
{error, Why} ->
|
||||
process_stream_end({socket, Why}, State)
|
||||
{error, {Class, Why}} ->
|
||||
process_stream_end({Class, Why}, State)
|
||||
end;
|
||||
{error, Why} ->
|
||||
{{error, Why}, _} ->
|
||||
process_stream_end({dns, Why}, State)
|
||||
end
|
||||
end);
|
||||
@ -578,11 +581,8 @@ process_sasl_mechanisms(Mechs, #{user := User, server := Server} = State) ->
|
||||
end.
|
||||
|
||||
-spec process_starttls(state()) -> state().
|
||||
process_starttls(#{sockmod := SockMod, socket := Socket, mod := Mod} = State) ->
|
||||
TLSOpts = try Mod:tls_options(State)
|
||||
catch _:undef -> []
|
||||
end,
|
||||
case SockMod:starttls(Socket, [connect|TLSOpts]) of
|
||||
process_starttls(#{socket := Socket} = State) ->
|
||||
case starttls(Socket, State) of
|
||||
{ok, TLSSocket} ->
|
||||
State1 = State#{socket => TLSSocket,
|
||||
stream_id => new_id(),
|
||||
@ -770,6 +770,19 @@ close_socket(State) ->
|
||||
State#{stream_timeout => infinity,
|
||||
stream_state => disconnected}.
|
||||
|
||||
-spec starttls(term(), state()) -> {ok, term()} | {error, tls_error_reason()}.
|
||||
starttls(Socket, #{sockmod := SockMod, mod := Mod,
|
||||
xmlns := NS, remote_server := RemoteServer} = State) ->
|
||||
TLSOpts = try Mod:tls_options(State)
|
||||
catch _:undef -> []
|
||||
end,
|
||||
SNI = idna_to_ascii(RemoteServer),
|
||||
ALPN = case NS of
|
||||
?NS_SERVER -> <<"xmpp-server">>;
|
||||
?NS_CLIENT -> <<"xmpp-client">>
|
||||
end,
|
||||
SockMod:starttls(Socket, [connect, {sni, SNI}, {alpn, [ALPN]}|TLSOpts]).
|
||||
|
||||
-spec select_lang(binary(), binary()) -> binary().
|
||||
select_lang(Lang, <<"">>) -> Lang;
|
||||
select_lang(_, Lang) -> Lang.
|
||||
@ -841,17 +854,17 @@ idna_to_ascii(Host) ->
|
||||
{error, _} -> ejabberd_idna:domain_utf8_to_ascii(Host)
|
||||
end.
|
||||
|
||||
-spec resolve(string(), state()) -> {ok, [ip_port()]} | network_error().
|
||||
-spec resolve(string(), state()) -> {{ok, [ip_port()]} | network_error(), boolean()}.
|
||||
resolve(Host, State) ->
|
||||
case srv_lookup(Host, State) of
|
||||
{error, _Reason} ->
|
||||
{{error, _Reason}, _} ->
|
||||
DefaultPort = get_default_port(State),
|
||||
a_lookup([{Host, DefaultPort}], State);
|
||||
{ok, HostPorts} ->
|
||||
a_lookup(HostPorts, State)
|
||||
{a_lookup([{Host, DefaultPort}], State), false};
|
||||
{{ok, HostPorts}, TLS} ->
|
||||
{a_lookup(HostPorts, State), TLS}
|
||||
end.
|
||||
|
||||
-spec srv_lookup(string(), state()) -> {ok, [host_port()]} | network_error().
|
||||
-spec srv_lookup(string(), state()) -> {{ok, [host_port()]} | network_error(), boolean()}.
|
||||
srv_lookup(_Host, #{xmlns := ?NS_COMPONENT}) ->
|
||||
%% Do not attempt to lookup SRV for component connections
|
||||
{error, nxdomain};
|
||||
@ -867,21 +880,35 @@ srv_lookup(Host, State) ->
|
||||
{error, _} ->
|
||||
Timeout = get_dns_timeout(State),
|
||||
Retries = get_dns_retries(State),
|
||||
srv_lookup(Host, Timeout, Retries)
|
||||
case is_starttls_available(State) of
|
||||
true ->
|
||||
case srv_lookup("_xmpps-server._tcp." ++ Host,
|
||||
Timeout, Retries) of
|
||||
{error, _} ->
|
||||
{srv_lookup("_xmpp-server._tcp." ++ Host,
|
||||
Timeout, Retries),
|
||||
false};
|
||||
{ok, Res} ->
|
||||
{{ok, Res}, true}
|
||||
end;
|
||||
false ->
|
||||
{srv_lookup("_xmpp-server._tcp." ++ Host,
|
||||
Timeout, Retries),
|
||||
false}
|
||||
end
|
||||
end
|
||||
end.
|
||||
|
||||
-spec srv_lookup(string(), timeout(), integer()) ->
|
||||
{ok, [host_port()]} | network_error().
|
||||
srv_lookup(_Host, _Timeout, Retries) when Retries < 1 ->
|
||||
srv_lookup(_SRVName, _Timeout, Retries) when Retries < 1 ->
|
||||
{error, timeout};
|
||||
srv_lookup(Host, Timeout, Retries) ->
|
||||
SRVName = "_xmpp-server._tcp." ++ Host,
|
||||
srv_lookup(SRVName, Timeout, Retries) ->
|
||||
case inet_res:getbyname(SRVName, srv, Timeout) of
|
||||
{ok, HostEntry} ->
|
||||
host_entry_to_host_ports(HostEntry);
|
||||
{error, timeout} ->
|
||||
srv_lookup(Host, Timeout, Retries - 1);
|
||||
srv_lookup(SRVName, Timeout, Retries - 1);
|
||||
{error, _} = Err ->
|
||||
Err
|
||||
end.
|
||||
@ -971,10 +998,22 @@ host_entry_to_addr_ports(#hostent{h_addr_list = AddrList}, Port) ->
|
||||
_ -> {ok, AddrPorts}
|
||||
end.
|
||||
|
||||
-spec connect([ip_port()], state()) -> {ok, term(), ip_port()} | network_error().
|
||||
connect(AddrPorts, #{sockmod := SockMod} = State) ->
|
||||
-spec connect([ip_port()], state(), boolean()) -> {ok, term(), ip_port()} |
|
||||
{error, {socket, socket_error_reason()}} |
|
||||
{error, {tls, tls_error_reason()}}.
|
||||
connect(AddrPorts, #{sockmod := SockMod} = State, TLS) ->
|
||||
Timeout = get_connect_timeout(State),
|
||||
connect(AddrPorts, SockMod, Timeout, {error, nxdomain}).
|
||||
case connect(AddrPorts, SockMod, Timeout, {error, nxdomain}) of
|
||||
{ok, Socket, AddrPort} when TLS ->
|
||||
case starttls(Socket, State) of
|
||||
{ok, TLSSocket} -> {ok, TLSSocket, AddrPort};
|
||||
{error, Why} -> {error, {tls, Why}}
|
||||
end;
|
||||
{ok, _Socket, _AddrPort} = OK ->
|
||||
OK;
|
||||
{error, Why} ->
|
||||
{error, {socket, Why}}
|
||||
end.
|
||||
|
||||
-spec connect([ip_port()], module(), timeout(), network_error()) ->
|
||||
{ok, term(), ip_port()} | network_error().
|
||||
|
100
tools/prepare-tr.sh
Executable file
100
tools/prepare-tr.sh
Executable file
@ -0,0 +1,100 @@
|
||||
#!/bin/bash
|
||||
|
||||
# Frontend for ejabberd's extract-tr.sh
|
||||
|
||||
# How to create template files for a new language:
|
||||
# NEWLANG=zh
|
||||
# cp priv/msgs/ejabberd.pot priv/msgs/$NEWLANG.po
|
||||
# echo \{\"\",\"\"\}. > priv/msgs/$NEWLANG.msg
|
||||
# make translations
|
||||
|
||||
extract_lang_src2pot ()
|
||||
{
|
||||
./tools/extract-tr.sh ebin/ > priv/msgs/ejabberd.pot
|
||||
}
|
||||
|
||||
extract_lang_popot2po ()
|
||||
{
|
||||
LANG_CODE=$1
|
||||
PO_PATH=$MSGS_DIR/$LANG_CODE.po
|
||||
POT_PATH=$MSGS_DIR/$PROJECT.pot
|
||||
|
||||
msgmerge $PO_PATH $POT_PATH >$PO_PATH.translate 2>/dev/null
|
||||
mv $PO_PATH.translate $PO_PATH
|
||||
}
|
||||
|
||||
extract_lang_po2msg ()
|
||||
{
|
||||
LANG_CODE=$1
|
||||
PO_PATH=$LANG_CODE.po
|
||||
MS_PATH=$PO_PATH.ms
|
||||
MSGID_PATH=$PO_PATH.msgid
|
||||
MSGSTR_PATH=$PO_PATH.msgstr
|
||||
MSGS_PATH=$LANG_CODE.msg
|
||||
|
||||
cd $MSGS_DIR
|
||||
|
||||
# Check PO has correct ~
|
||||
# Let's convert to C format so we can use msgfmt
|
||||
PO_TEMP=$LANG_CODE.po.temp
|
||||
cat $PO_PATH | sed 's/%/perc/g' | sed 's/~/%/g' | sed 's/#:.*/#, c-format/g' >$PO_TEMP
|
||||
msgfmt $PO_TEMP --check-format
|
||||
result=$?
|
||||
rm $PO_TEMP
|
||||
if [ $result -ne 0 ] ; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
msgattrib $PO_PATH --translated --no-fuzzy --no-obsolete --no-location --no-wrap | grep "^msg" | tail --lines=+3 >$MS_PATH
|
||||
grep "^msgid" $PO_PATH.ms | sed 's/^msgid //g' >$MSGID_PATH
|
||||
grep "^msgstr" $PO_PATH.ms | sed 's/^msgstr //g' >$MSGSTR_PATH
|
||||
echo "%% -*- coding: latin-1 -*-" >$MSGS_PATH
|
||||
paste $MSGID_PATH $MSGSTR_PATH --delimiter=, | awk '{print "{" $0 "}."}' | sort -g >>$MSGS_PATH
|
||||
|
||||
rm $MS_PATH
|
||||
rm $MSGID_PATH
|
||||
rm $MSGSTR_PATH
|
||||
}
|
||||
|
||||
extract_lang_updateall ()
|
||||
{
|
||||
echo ""
|
||||
echo "Generating POT..."
|
||||
extract_lang_src2pot
|
||||
|
||||
cd $MSGS_DIR
|
||||
echo ""
|
||||
echo -e "File Missing (fuzzy) Language Last translator"
|
||||
echo -e "---- ------- ------- -------- ---------------"
|
||||
for i in $( ls *.msg ) ; do
|
||||
LANG_CODE=${i%.msg}
|
||||
echo -n $LANG_CODE | awk '{printf "%-6s", $1 }'
|
||||
|
||||
PO=$LANG_CODE.po
|
||||
|
||||
extract_lang_popot2po $LANG_CODE
|
||||
extract_lang_po2msg $LANG_CODE
|
||||
|
||||
MISSING=`msgfmt --statistics $PO 2>&1 | awk '{printf "%5s", $4+$7 }'`
|
||||
echo -n " $MISSING"
|
||||
|
||||
FUZZY=`msgfmt --statistics $PO 2>&1 | awk '{printf "%7s", $4 }'`
|
||||
echo -n " $FUZZY"
|
||||
|
||||
LANGUAGE=`grep "X-Language:" $PO | sed 's/\"X-Language: //g' | sed 's/\\\\n\"//g' | awk '{printf "%-12s", $1}'`
|
||||
echo -n " $LANGUAGE"
|
||||
|
||||
LASTAUTH=`grep "Last-Translator" $PO | sed 's/\"Last-Translator: //g' | sed 's/\\\\n\"//g'`
|
||||
echo " $LASTAUTH"
|
||||
done
|
||||
echo ""
|
||||
rm messages.mo
|
||||
|
||||
cd ..
|
||||
}
|
||||
|
||||
EJA_DIR=`pwd`
|
||||
PROJECT=ejabberd
|
||||
MSGS_DIR=$EJA_DIR/priv/msgs
|
||||
|
||||
extract_lang_updateall
|
Loading…
Reference in New Issue
Block a user