/src/i18n/z_trans.erl

https://code.google.com/p/zotonic/ · Erlang · 222 lines · 148 code · 32 blank · 42 comment · 0 complexity · 2a00f4852e5f01ea216a512745271dd5 MD5 · raw file

  1. %% @author Marc Worrell <marc@worrell.nl>
  2. %% @copyright 2009 Marc Worrell
  3. %% @doc Translate english sentences into other languages, following
  4. %% the GNU gettext principle.
  5. %% Copyright 2009 Marc Worrell
  6. %%
  7. %% Licensed under the Apache License, Version 2.0 (the "License");
  8. %% you may not use this file except in compliance with the License.
  9. %% You may obtain a copy of the License at
  10. %%
  11. %% http://www.apache.org/licenses/LICENSE-2.0
  12. %%
  13. %% Unless required by applicable law or agreed to in writing, software
  14. %% distributed under the License is distributed on an "AS IS" BASIS,
  15. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  16. %% See the License for the specific language governing permissions and
  17. %% limitations under the License.
  18. -module(z_trans).
  19. -author("Marc Worrell <marc@worrell.nl>").
  20. -export([
  21. translations/2,
  22. parse_translations/1,
  23. trans/2,
  24. lookup/2,
  25. lookup/3,
  26. lookup_fallback/2,
  27. lookup_fallback/3,
  28. default_language/1,
  29. is_language/1,
  30. language_list/1,
  31. lc2/1,
  32. lc2descr/1
  33. ]).
  34. -include_lib("zotonic.hrl").
  35. %% @doc Fetch all translations for the given string.
  36. %% @spec translations(From, Context) -> #trans{} | binary()
  37. translations({trans, Tr0} = Trans0, Context) ->
  38. {en, From} = proplists:lookup(en, Tr0),
  39. case translations(From, Context) of
  40. {trans, Tr1} -> merge_trs(Tr0, Tr1);
  41. _ -> Trans0
  42. end;
  43. translations(From, Context) when is_binary(From) ->
  44. case ets:lookup(z_trans_server:table(Context), From) of
  45. [] ->
  46. From;
  47. [{_, Trans}] ->
  48. {trans, Trans}
  49. end;
  50. translations(From, Context) ->
  51. translations(z_convert:to_binary(From), Context).
  52. merge_trs([], Acc) ->
  53. Acc;
  54. merge_trs([{Lang,_} = LT|Rest], Acc) ->
  55. case proplists:is_defined(Lang, Acc) of
  56. true -> merge_trs(Rest, Acc);
  57. false -> merge_trs(Rest, [LT|Acc])
  58. end.
  59. %% @doc Prepare a translations table based on all .po files in the active modules.
  60. parse_translations(Context) ->
  61. Mods = z_module_indexer:translations(Context),
  62. build_index(parse_mod_trans(Mods, []), dict:new()).
  63. %% @doc Parse all .po files. Results in a dict {label, [iso_code,trans]}
  64. parse_mod_trans([], Acc) ->
  65. lists:reverse(Acc);
  66. parse_mod_trans([{_Module, {_Dir, Trans}}|Rest], Acc) ->
  67. Acc1 = parse_trans(Trans, Acc),
  68. parse_mod_trans(Rest, Acc1).
  69. parse_trans([], Acc) ->
  70. Acc;
  71. parse_trans([{Lang,File}|Rest], Acc) ->
  72. parse_trans(Rest, [{Lang, z_gettext:parse_po(File)}|Acc]).
  73. build_index([], Dict) ->
  74. Dict;
  75. build_index([{Lang, Labels}|Rest], Dict) ->
  76. build_index(Rest, add_labels(Lang, Labels, Dict)).
  77. add_labels(_Lang, [], Dict) ->
  78. Dict;
  79. add_labels(Lang, [{header,_}|Rest],Dict) ->
  80. add_labels(Lang, Rest,Dict);
  81. add_labels(Lang, [{Label,Trans}|Rest], Dict) ->
  82. LabelB = list_to_binary(Label),
  83. case dict:find(LabelB, Dict) of
  84. {ok, Ts} ->
  85. case proplists:get_value(Lang, Ts) of
  86. undefined -> add_labels(Lang, Rest, dict:store(LabelB, [{Lang,list_to_binary(Trans)}|Ts], Dict));
  87. _PrevTrans -> add_labels(Lang, Rest, Dict)
  88. end;
  89. error ->
  90. add_labels(Lang, Rest, dict:store(LabelB,[{Lang,to_binary(Trans)}],Dict))
  91. end.
  92. to_binary(header) -> "";
  93. to_binary(L) -> list_to_binary(L).
  94. %% @doc Strict translation lookup of a language version
  95. lookup(Trans, Context) ->
  96. lookup(Trans, z_context:language(Context), Context).
  97. lookup({trans, Tr}, Lang, _Context) ->
  98. proplists:get_value(Lang, Tr);
  99. lookup(Text, Lang, Context) ->
  100. case z_context:language(Context) of
  101. Lang -> Text;
  102. _ -> undefined
  103. end.
  104. %% @doc Non strict translation lookup of a language version.
  105. %% In order check: requested language, default configured language, english, any
  106. lookup_fallback(Trans, Context) ->
  107. lookup_fallback(Trans, z_context:language(Context), Context).
  108. lookup_fallback({trans, Tr}, Lang, Context) ->
  109. case proplists:get_value(Lang, Tr) of
  110. undefined ->
  111. case default_language(Context) of
  112. undefined -> take_english_or_first(Tr);
  113. CfgLang ->
  114. case proplists:get_value(z_convert:to_atom(CfgLang), Tr) of
  115. undefined -> take_english_or_first(Tr);
  116. Text -> Text
  117. end
  118. end;
  119. Text ->
  120. Text
  121. end;
  122. lookup_fallback(Text, _Lang, _Context) ->
  123. Text.
  124. take_english_or_first(Tr) ->
  125. case proplists:get_value(en, Tr) of
  126. undefined ->
  127. case Tr of
  128. [{_,Text}|_] -> Text;
  129. _ -> undefined
  130. end;
  131. EnglishText ->
  132. EnglishText
  133. end.
  134. %% @doc translate a string or trans record into another language
  135. %% @spec trans(From, Language) -> String
  136. %% From = #trans{} | String
  137. %% Language = atom()
  138. trans({trans, Tr}, Lang) when is_atom(Lang) ->
  139. proplists:get_value(Lang, Tr);
  140. trans(Text, Lang) when is_atom(Lang) ->
  141. Text;
  142. trans(Text, Context) ->
  143. trans(Text, Context#context.language, Context).
  144. trans({trans, Tr0}, Language, Context) ->
  145. case proplists:lookup(en, Tr0) of
  146. {en, Text} ->
  147. case translations(Text, Context) of
  148. {trans, Tr} ->
  149. case proplists:get_value(Language, Tr) of
  150. undefined -> proplists:get_value(Language, Tr0, Text);
  151. Translated -> Translated
  152. end;
  153. _ ->
  154. proplists:get_value(Language, Tr0, Text)
  155. end;
  156. none ->
  157. proplists:get_value(Language, Tr0)
  158. end;
  159. trans(Text, Language, Context) ->
  160. case translations(Text, Context) of
  161. {trans, Tr} ->
  162. proplists:get_value(Language, Tr, Text);
  163. _ -> Text
  164. end.
  165. %% @doc Return the configured default language for this server
  166. default_language(Context) ->
  167. z_convert:to_atom(m_config:get_value(i18n, language, en, Context)).
  168. %% @doc Return the list of languages selected for this site
  169. %% @todo Make this configurable
  170. language_list(_Context) ->
  171. [ en, nl ].
  172. %% @doc check if the two letter code is a valid language
  173. %% @spec is_language(LanguageString) -> bool()
  174. %% LanguageString = string()
  175. is_language(LanguageString) ->
  176. Language = iso639:lc2lang(LanguageString),
  177. Language /= "".
  178. %% @doc Translate a language to an atom, fail when unknown language
  179. %% @spec lc2(LanguageString) -> Language
  180. %% LanguageString = string()
  181. %% Language = atom()
  182. lc2(LanguageString) ->
  183. true = is_language(iso639:lc2lang(LanguageString)),
  184. list_to_atom(LanguageString).
  185. %% @doc Return a descriptive (english) string for the language
  186. %% @spec lc2descr(Language) -> Descr
  187. %% Language = atom()
  188. %% Descr = list()
  189. lc2descr(Language) ->
  190. iso639:lc2lang(atom_to_list(Language)).