1 |
|
%%%---------------------------------------------------------------------- |
2 |
|
%%% File : translate.erl |
3 |
|
%%% Author : Alexey Shchepin <alexey@process-one.net> |
4 |
|
%%% Purpose : Localization helper |
5 |
|
%%% Created : 6 Jan 2003 by Alexey Shchepin <alexey@process-one.net> |
6 |
|
%%% |
7 |
|
%%% |
8 |
|
%%% ejabberd, Copyright (C) 2002-2011 ProcessOne |
9 |
|
%%% |
10 |
|
%%% This program is free software; you can redistribute it and/or |
11 |
|
%%% modify it under the terms of the GNU General Public License as |
12 |
|
%%% published by the Free Software Foundation; either version 2 of the |
13 |
|
%%% License, or (at your option) any later version. |
14 |
|
%%% |
15 |
|
%%% This program is distributed in the hope that it will be useful, |
16 |
|
%%% but WITHOUT ANY WARRANTY; without even the implied warranty of |
17 |
|
%%% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
18 |
|
%%% General Public License for more details. |
19 |
|
%%% |
20 |
|
%%% You should have received a copy of the GNU General Public License |
21 |
|
%%% along with this program; if not, write to the Free Software |
22 |
|
%%% Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
23 |
|
%%% |
24 |
|
%%%---------------------------------------------------------------------- |
25 |
|
|
26 |
|
-module(translate). |
27 |
|
-author('alexey@process-one.net'). |
28 |
|
|
29 |
|
-export([start/0, |
30 |
|
translate/2]). |
31 |
|
|
32 |
|
-include("mongoose.hrl"). |
33 |
|
|
34 |
|
-define(MSGS_DIR, "msgs"). |
35 |
|
|
36 |
|
%% |
37 |
|
%% Public |
38 |
|
%% |
39 |
|
|
40 |
|
-spec start() -> 'ok'. |
41 |
|
start() -> |
42 |
33 |
ets:new(translations, [named_table, public]), |
43 |
33 |
ok = load_translations_from_dir(lang_files_directory()), |
44 |
33 |
ok. |
45 |
|
|
46 |
|
-spec translate(ejabberd:lang(), binary()) -> binary(). |
47 |
|
translate(Lang, Msg) -> |
48 |
1656 |
LLang = to_lower(Lang), |
49 |
1656 |
case get_translation(LLang, Msg) of |
50 |
808 |
{ok, Trans} -> Trans; |
51 |
848 |
{error, not_found} -> get_default_server_lang_translation(Msg) |
52 |
|
end. |
53 |
|
|
54 |
|
%% |
55 |
|
%% Private |
56 |
|
%% |
57 |
|
|
58 |
|
-spec lang_files_directory() -> file:filename(). |
59 |
|
lang_files_directory() -> |
60 |
33 |
case os:getenv("EJABBERD_MSGS_PATH") of |
61 |
|
false -> |
62 |
33 |
case code:priv_dir(mongooseim) of |
63 |
:-( |
{error, _} -> ?MSGS_DIR; |
64 |
33 |
Path -> Path |
65 |
|
end; |
66 |
:-( |
Path -> Path |
67 |
|
end. |
68 |
|
|
69 |
|
-spec load_translations_from_dir(file:filename()) -> ok. |
70 |
|
load_translations_from_dir(Dir) -> |
71 |
33 |
case file:list_dir(Dir) of |
72 |
|
{ok, Files} -> |
73 |
33 |
MsgFiles = lists:filter(fun has_msg_extension/1, Files), |
74 |
33 |
load_translation_files(Dir, MsgFiles); |
75 |
|
{error, Reason} -> |
76 |
:-( |
?LOG_ERROR(#{what => load_translations_from_dir_failed, |
77 |
:-( |
directory => Dir, reason => Reason}), |
78 |
:-( |
ok |
79 |
|
end. |
80 |
|
|
81 |
|
-spec load_translation_files(file:filename(), [file:filename()]) -> ok. |
82 |
|
load_translation_files(Dir, MsgFiles) -> |
83 |
33 |
lists:foreach(fun(Filename) -> |
84 |
825 |
Lang = lang_from_file_name(Filename), |
85 |
825 |
load_file(Lang, Dir ++ "/" ++ Filename) |
86 |
|
end, MsgFiles). |
87 |
|
|
88 |
|
-spec lang_from_file_name(file:filename()) -> string(). |
89 |
|
lang_from_file_name(Filename) -> |
90 |
825 |
string:to_lower(filename:rootname(Filename)). |
91 |
|
|
92 |
|
-spec has_msg_extension(file:filename()) -> boolean(). |
93 |
|
has_msg_extension(FileName) -> |
94 |
2112 |
filename:extension(FileName) == ".msg". |
95 |
|
|
96 |
|
-spec load_file(string(), file:name()) -> 'ok'. |
97 |
|
load_file(Lang, File) -> |
98 |
825 |
BLang = list_to_binary(Lang), |
99 |
825 |
case file:consult(File) of |
100 |
|
{ok, Terms} -> |
101 |
825 |
lists:foreach(fun({Orig, Trans}) -> |
102 |
303633 |
insert_translation(BLang, |
103 |
|
unicode:characters_to_binary(Orig), |
104 |
|
unicode:characters_to_binary(Trans)) |
105 |
|
end, Terms); |
106 |
|
{error, Reason} -> |
107 |
:-( |
ExitText = iolist_to_binary(File ++ ": " ++ file:format_error(Reason)), |
108 |
:-( |
?LOG_ERROR(#{what => load_translation_file_failed, |
109 |
|
text => <<"Problem loading translation file">>, |
110 |
:-( |
filename => File, reason => ExitText}), |
111 |
:-( |
exit(ExitText) |
112 |
|
end. |
113 |
|
|
114 |
|
-spec insert_translation(ejabberd:lang(), binary(), binary()) -> true. |
115 |
|
insert_translation(Lang, Msg, <<"">>) -> |
116 |
:-( |
insert_translation(Lang, Msg, Msg); %% use key if it is not defined |
117 |
|
insert_translation(Lang, Msg, Trans) -> |
118 |
303633 |
ets:insert(translations, {{Lang, Msg}, Trans}). |
119 |
|
|
120 |
|
-spec get_default_server_lang_translation(binary()) -> binary(). |
121 |
|
get_default_server_lang_translation(Msg) -> |
122 |
848 |
case get_translation(?MYLANG, Msg) of |
123 |
848 |
{ok, DefaultTrans} -> DefaultTrans; |
124 |
:-( |
{error, not_found} -> Msg |
125 |
|
end. |
126 |
|
|
127 |
|
-spec get_translation(ejabberd:lang(), binary()) -> {ok, binary()} | {error, not_found}. |
128 |
|
get_translation(LLang, Msg) -> |
129 |
2504 |
case read_trans(LLang, Msg) of |
130 |
|
{error, not_found} -> |
131 |
848 |
read_trans(short_lang(LLang), Msg); |
132 |
|
{ok, Trans} -> |
133 |
1656 |
{ok, Trans} |
134 |
|
end. |
135 |
|
|
136 |
|
-spec read_trans(ejabberd:lang(), binary()) -> {ok, binary()} | {error, not_found}. |
137 |
|
read_trans(<<"en">>, Msg) -> |
138 |
1656 |
{ok, Msg}; |
139 |
|
read_trans(LLang, Msg) -> |
140 |
1696 |
case ets:lookup(translations, {LLang, Msg}) of |
141 |
:-( |
[{_, Trans}] -> {ok, Trans}; |
142 |
1696 |
_ -> {error, not_found} |
143 |
|
end. |
144 |
|
|
145 |
|
-spec short_lang(ejabberd:lang()) -> ejabberd:lang(). |
146 |
|
short_lang(LLang) -> |
147 |
848 |
case string:tokens(binary_to_list(LLang), "-") of |
148 |
848 |
[] -> LLang; |
149 |
:-( |
[ShortLang | _] -> list_to_binary(ShortLang) |
150 |
|
end. |
151 |
|
|
152 |
|
-spec to_lower(binary()) -> binary(). |
153 |
|
to_lower(Bin) -> |
154 |
1656 |
list_to_binary(string:to_lower(binary_to_list(Bin))). |