/indra/llxuixml/lltrans.cpp

https://bitbucket.org/lindenlab/viewer-beta/ · C++ · 295 lines · 208 code · 37 blank · 50 comment · 43 complexity · 93f91de4d2e970108c82cd81a026e28f MD5 · raw file

  1. /**
  2. * @file lltrans.cpp
  3. * @brief LLTrans implementation
  4. *
  5. * $LicenseInfo:firstyear=2000&license=viewerlgpl$
  6. * Second Life Viewer Source Code
  7. * Copyright (C) 2010, Linden Research, Inc.
  8. *
  9. * This library is free software; you can redistribute it and/or
  10. * modify it under the terms of the GNU Lesser General Public
  11. * License as published by the Free Software Foundation;
  12. * version 2.1 of the License only.
  13. *
  14. * This library is distributed in the hope that it will be useful,
  15. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  17. * Lesser General Public License for more details.
  18. *
  19. * You should have received a copy of the GNU Lesser General Public
  20. * License along with this library; if not, write to the Free Software
  21. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  22. *
  23. * Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA
  24. * $/LicenseInfo$
  25. */
  26. #include "linden_common.h"
  27. #include "lltrans.h"
  28. #include "llfasttimer.h" // for call count statistics
  29. #include "llxuiparser.h"
  30. #include "llsd.h"
  31. #include "llxmlnode.h"
  32. #include <map>
  33. LLTrans::template_map_t LLTrans::sStringTemplates;
  34. LLStringUtil::format_map_t LLTrans::sDefaultArgs;
  35. struct StringDef : public LLInitParam::Block<StringDef>
  36. {
  37. Mandatory<std::string> name;
  38. Mandatory<std::string> value;
  39. StringDef()
  40. : name("name"),
  41. value("value")
  42. {}
  43. };
  44. struct StringTable : public LLInitParam::Block<StringTable>
  45. {
  46. Multiple<StringDef> strings;
  47. StringTable()
  48. : strings("string")
  49. {}
  50. };
  51. //static
  52. bool LLTrans::parseStrings(LLXMLNodePtr &root, const std::set<std::string>& default_args)
  53. {
  54. std::string xml_filename = "(strings file)";
  55. if (!root->hasName("strings"))
  56. {
  57. llerrs << "Invalid root node name in " << xml_filename
  58. << ": was " << root->getName() << ", expected \"strings\"" << llendl;
  59. }
  60. StringTable string_table;
  61. LLXUIParser parser;
  62. parser.readXUI(root, string_table, xml_filename);
  63. if (!string_table.validateBlock())
  64. {
  65. llerrs << "Problem reading strings: " << xml_filename << llendl;
  66. return false;
  67. }
  68. sStringTemplates.clear();
  69. sDefaultArgs.clear();
  70. for(LLInitParam::ParamIterator<StringDef>::const_iterator it = string_table.strings.begin();
  71. it != string_table.strings.end();
  72. ++it)
  73. {
  74. LLTransTemplate xml_template(it->name, it->value);
  75. sStringTemplates[xml_template.mName] = xml_template;
  76. std::set<std::string>::const_iterator iter = default_args.find(xml_template.mName);
  77. if (iter != default_args.end())
  78. {
  79. std::string name = *iter;
  80. if (name[0] != '[')
  81. name = llformat("[%s]",name.c_str());
  82. sDefaultArgs[name] = xml_template.mText;
  83. }
  84. }
  85. return true;
  86. }
  87. //static
  88. bool LLTrans::parseLanguageStrings(LLXMLNodePtr &root)
  89. {
  90. std::string xml_filename = "(language strings file)";
  91. if (!root->hasName("strings"))
  92. {
  93. llerrs << "Invalid root node name in " << xml_filename
  94. << ": was " << root->getName() << ", expected \"strings\"" << llendl;
  95. }
  96. StringTable string_table;
  97. LLXUIParser parser;
  98. parser.readXUI(root, string_table, xml_filename);
  99. if (!string_table.validateBlock())
  100. {
  101. llerrs << "Problem reading strings: " << xml_filename << llendl;
  102. return false;
  103. }
  104. for(LLInitParam::ParamIterator<StringDef>::const_iterator it = string_table.strings.begin();
  105. it != string_table.strings.end();
  106. ++it)
  107. {
  108. // share the same map with parseStrings() so we can search the strings using the same getString() function.- angela
  109. LLTransTemplate xml_template(it->name, it->value);
  110. sStringTemplates[xml_template.mName] = xml_template;
  111. }
  112. return true;
  113. }
  114. static LLFastTimer::DeclareTimer FTM_GET_TRANS("Translate string");
  115. //static
  116. std::string LLTrans::getString(const std::string &xml_desc, const LLStringUtil::format_map_t& msg_args)
  117. {
  118. // Don't care about time as much as call count. Make sure we're not
  119. // calling LLTrans::getString() in an inner loop. JC
  120. LLFastTimer timer(FTM_GET_TRANS);
  121. template_map_t::iterator iter = sStringTemplates.find(xml_desc);
  122. if (iter != sStringTemplates.end())
  123. {
  124. std::string text = iter->second.mText;
  125. LLStringUtil::format_map_t args = sDefaultArgs;
  126. args.insert(msg_args.begin(), msg_args.end());
  127. LLStringUtil::format(text, args);
  128. return text;
  129. }
  130. else
  131. {
  132. LL_WARNS_ONCE("configuration") << "Missing String in strings.xml: [" << xml_desc << "]" << LL_ENDL;
  133. return "MissingString("+xml_desc+")";
  134. }
  135. }
  136. //static
  137. std::string LLTrans::getString(const std::string &xml_desc, const LLSD& msg_args)
  138. {
  139. // Don't care about time as much as call count. Make sure we're not
  140. // calling LLTrans::getString() in an inner loop. JC
  141. LLFastTimer timer(FTM_GET_TRANS);
  142. template_map_t::iterator iter = sStringTemplates.find(xml_desc);
  143. if (iter != sStringTemplates.end())
  144. {
  145. std::string text = iter->second.mText;
  146. LLStringUtil::format(text, msg_args);
  147. return text;
  148. }
  149. else
  150. {
  151. LL_WARNS_ONCE("configuration") << "Missing String in strings.xml: [" << xml_desc << "]" << LL_ENDL;
  152. return "MissingString("+xml_desc+")";
  153. }
  154. }
  155. //static
  156. bool LLTrans::findString(std::string &result, const std::string &xml_desc, const LLStringUtil::format_map_t& msg_args)
  157. {
  158. LLFastTimer timer(FTM_GET_TRANS);
  159. template_map_t::iterator iter = sStringTemplates.find(xml_desc);
  160. if (iter != sStringTemplates.end())
  161. {
  162. std::string text = iter->second.mText;
  163. LLStringUtil::format_map_t args = sDefaultArgs;
  164. args.insert(msg_args.begin(), msg_args.end());
  165. LLStringUtil::format(text, args);
  166. result = text;
  167. return true;
  168. }
  169. else
  170. {
  171. LL_WARNS_ONCE("configuration") << "Missing String in strings.xml: [" << xml_desc << "]" << LL_ENDL;
  172. return false;
  173. }
  174. }
  175. //static
  176. bool LLTrans::findString(std::string &result, const std::string &xml_desc, const LLSD& msg_args)
  177. {
  178. LLFastTimer timer(FTM_GET_TRANS);
  179. template_map_t::iterator iter = sStringTemplates.find(xml_desc);
  180. if (iter != sStringTemplates.end())
  181. {
  182. std::string text = iter->second.mText;
  183. LLStringUtil::format(text, msg_args);
  184. result = text;
  185. return true;
  186. }
  187. else
  188. {
  189. LL_WARNS_ONCE("configuration") << "Missing String in strings.xml: [" << xml_desc << "]" << LL_ENDL;
  190. return false;
  191. }
  192. }
  193. //static
  194. std::string LLTrans::getCountString(const std::string& language, const std::string& xml_desc, S32 count)
  195. {
  196. // Compute which string identifier to use
  197. const char* form = "";
  198. if (language == "ru") // Russian
  199. {
  200. // From GNU ngettext()
  201. // Plural-Forms: nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;
  202. if (count % 10 == 1
  203. && count % 100 != 11)
  204. {
  205. // singular, "1 item"
  206. form = "A";
  207. }
  208. else if (count % 10 >= 2
  209. && count % 10 <= 4
  210. && (count % 100 < 10 || count % 100 >= 20) )
  211. {
  212. // special case "2 items", "23 items", but not "13 items"
  213. form = "B";
  214. }
  215. else
  216. {
  217. // English-style plural, "5 items"
  218. form = "C";
  219. }
  220. }
  221. else if (language == "fr" || language == "pt") // French, Brazilian Portuguese
  222. {
  223. // French and Portuguese treat zero as a singular "0 item" not "0 items"
  224. if (count == 0 || count == 1)
  225. {
  226. form = "A";
  227. }
  228. else
  229. {
  230. // English-style plural
  231. form = "B";
  232. }
  233. }
  234. else // default
  235. {
  236. // languages like English with 2 forms, singular and plural
  237. if (count == 1)
  238. {
  239. // "1 item"
  240. form = "A";
  241. }
  242. else
  243. {
  244. // "2 items", also use plural for "0 items"
  245. form = "B";
  246. }
  247. }
  248. // Translate that string
  249. LLStringUtil::format_map_t args;
  250. args["[COUNT]"] = llformat("%d", count);
  251. // Look up "AgeYearsB" or "AgeWeeksC" including the "form"
  252. std::string key = llformat("%s%s", xml_desc.c_str(), form);
  253. return getString(key, args);
  254. }
  255. void LLTrans::setDefaultArg(const std::string& name, const std::string& value)
  256. {
  257. sDefaultArgs[name] = value;
  258. }