Helper for creating translation files for Typica.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

main.cpp 12KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302
  1. // ttsg
  2. // Typica .ts Generator
  3. // This is a program that scrapes configuration files for Typica
  4. // and generates a .ts file translators can use so that user
  5. // visible text in a configuration can be translated into another
  6. // language.
  7. //
  8. // This is very slapdash with a minimal feature set focusing on
  9. // having something usable for initial translations starting with
  10. // the Typica 1.7 release. There are certain valid Typica
  11. // configurations that this will fail on. This is very much
  12. // tailored to use with the currently provided example
  13. // configuration. If you're using Typica for something strange
  14. // this tool might need to be altered.
  15. //
  16. // Copyright 2016, Neal Evan Wilson
  17. // See LICENSE for more details.
  18. #include <QCoreApplication>
  19. #include <QString>
  20. #include <QStringList>
  21. #include <QFile>
  22. #include <QFileInfo>
  23. #include <QDir>
  24. #include <QDomDocument>
  25. #include <QDomElement>
  26. #include <QXmlStreamWriter>
  27. #include <QtDebug>
  28. struct Context
  29. {
  30. QString name;
  31. QString filename;
  32. QStringList messages;
  33. QStringList locations;
  34. QString reportDirectory;
  35. };
  36. Context* buildContext(QString filename, QString basedir);
  37. int generate(QString configfile, QString transfile);
  38. int main(int argc, char **argv);
  39. // Create a Context* with messages from a file.
  40. Context* buildContext(QString filename, QString basedir)
  41. {
  42. Context *context = new Context;
  43. context->filename = filename;
  44. QFile source(QString("%1/%2").arg(basedir).arg(filename));
  45. if(source.open(QIODevice::ReadOnly))
  46. {
  47. QDomDocument document;
  48. document.setContent(&source, true);
  49. QDomElement root = document.documentElement();
  50. context->name = root.attribute("id");
  51. QDomNodeList currentSet = document.elementsByTagName("button");
  52. QDomNode currentNode;
  53. QDomElement currentElement;
  54. for(int i = 0; i < currentSet.length(); i++)
  55. {
  56. currentNode = currentSet.at(i);
  57. context->locations.append(QString("%1").arg(currentNode.lineNumber()));
  58. currentElement = currentNode.toElement();
  59. context->messages.append(currentElement.attribute("name"));
  60. }
  61. currentSet = document.elementsByTagName("reporttitle");
  62. for(int i = 0; i < currentSet.length(); i++)
  63. {
  64. currentNode = currentSet.at(i);
  65. context->locations.append(QString("%1").arg(currentNode.lineNumber()));
  66. currentElement = currentNode.toElement();
  67. context->messages.append(currentElement.text());
  68. }
  69. currentSet = document.elementsByTagName("menu");
  70. for(int i = 0; i < currentSet.length(); i++)
  71. {
  72. currentNode = currentSet.at(i);
  73. context->locations.append(QString("%1").arg(currentNode.lineNumber()));
  74. currentElement = currentNode.toElement();
  75. context->messages.append(currentElement.attribute("name"));
  76. if(currentElement.attribute("type") == "reports")
  77. {
  78. context->reportDirectory = currentElement.attribute("src");
  79. }
  80. }
  81. currentSet = document.elementsByTagName("item");
  82. for(int i = 0; i < currentSet.length(); i++)
  83. {
  84. currentNode = currentSet.at(i);
  85. context->locations.append(QString("%1").arg(currentNode.lineNumber()));
  86. currentElement = currentNode.toElement();
  87. context->messages.append(currentElement.text());
  88. }
  89. currentSet = document.elementsByTagName("label");
  90. for(int i = 0; i < currentSet.length(); i++)
  91. {
  92. currentNode = currentSet.at(i);
  93. context->locations.append(QString("%1").arg(currentNode.lineNumber()));
  94. currentElement = currentNode.toElement();
  95. context->messages.append(currentElement.text());
  96. }
  97. currentSet = document.elementsByTagName("decoration");
  98. for(int i = 0; i < currentSet.length(); i++)
  99. {
  100. currentNode = currentSet.at(i);
  101. context->locations.append(QString("%1").arg(currentNode.lineNumber()));
  102. currentElement = currentNode.toElement();
  103. context->messages.append(currentElement.attribute("name"));
  104. }
  105. currentSet = document.elementsByTagName("spinbox");
  106. for(int i = 0; i < currentSet.length(); i++)
  107. {
  108. currentNode = currentSet.at(i);
  109. currentElement = currentNode.toElement();
  110. if(currentElement.hasAttribute("pretext"))
  111. {
  112. context->locations.append(QString("%1").arg(currentNode.lineNumber()));
  113. context->messages.append(currentElement.attribute("pretext"));
  114. }
  115. if(currentElement.hasAttribute("posttext"))
  116. {
  117. context->locations.append(QString("%1").arg(currentNode.lineNumber()));
  118. context->messages.append(currentElement.attribute("posttext"));
  119. }
  120. }
  121. currentSet = document.elementsByTagName("measurementtable");
  122. for(int i = 0; i < currentSet.length(); i++)
  123. {
  124. currentNode = currentSet.at(i);
  125. currentElement = currentNode.toElement();
  126. if(currentElement.hasChildNodes())
  127. {
  128. for(int j = 0; j < currentElement.childNodes().count(); j++)
  129. {
  130. QDomNode childNode = currentElement.childNodes().at(j);
  131. if(childNode.isElement())
  132. {
  133. QDomElement childElement = childNode.toElement();
  134. if(childElement.tagName() == "column")
  135. {
  136. context->locations.append(QString("%1").arg(childNode.lineNumber()));
  137. context->messages.append(childElement.text());
  138. }
  139. }
  140. }
  141. }
  142. }
  143. currentSet = document.elementsByTagName("sqltablearray");
  144. for(int i = 0; i < currentSet.length(); i++)
  145. {
  146. currentNode = currentSet.at(i);
  147. currentElement = currentNode.toElement();
  148. if(currentElement.hasChildNodes())
  149. {
  150. for(int j = 0; j < currentElement.childNodes().count(); j++)
  151. {
  152. QDomNode childNode = currentElement.childNodes().at(j);
  153. if(childNode.isElement())
  154. {
  155. QDomElement childElement = childNode.toElement();
  156. if(childElement.tagName() == "column")
  157. {
  158. context->locations.append(QString("%1").arg(childNode.lineNumber()));
  159. context->messages.append(childElement.attribute("name"));
  160. }
  161. }
  162. }
  163. }
  164. }
  165. currentSet = document.elementsByTagName("program");
  166. for(int i = 0; i < currentSet.length(); i++)
  167. {
  168. currentNode = currentSet.at(i);
  169. currentElement = currentNode.toElement();
  170. QString script = currentElement.text();
  171. QStringList scriptLines = script.split("\n");
  172. for(int j = 0; j < scriptLines.length(); j++)
  173. {
  174. if(scriptLines.at(j).contains("TTR"))
  175. {
  176. QString currentLine = scriptLines.at(j);
  177. context->locations.append(QString("%1").arg(currentNode.lineNumber() + j + 1));
  178. currentLine = currentLine.remove(0, currentLine.indexOf("TTR"));
  179. currentLine = currentLine.remove(0, currentLine.indexOf("\"")+1);
  180. currentLine = currentLine.remove(0, currentLine.indexOf("\"")+1);
  181. currentLine = currentLine.remove(0, currentLine.indexOf("\"")+1);
  182. currentLine = currentLine.remove(currentLine.indexOf("\""), currentLine.length());
  183. context->messages.append(currentLine);
  184. }
  185. }
  186. }
  187. }
  188. source.close();
  189. return context;
  190. }
  191. // Produce a .ts file from a Typica configuration file.
  192. int generate(QString configfile, QString transfile)
  193. {
  194. QFile source(configfile);
  195. QFileInfo info(configfile);
  196. QDir directory = info.dir();
  197. if(!source.open(QIODevice::ReadOnly))
  198. {
  199. qDebug() << "Failed to open: " << configfile;
  200. return 1;
  201. }
  202. QDomDocument configuration;
  203. configuration.setContent(&source, true);
  204. QDomElement root = configuration.documentElement();
  205. QDomNodeList children = root.childNodes();
  206. QList<Context *> contexts;
  207. for(int i = 0; i < children.size(); i++)
  208. {
  209. QDomNode currentNode = children.at(i);
  210. QDomElement currentElement;
  211. if(currentNode.nodeName() == "include")
  212. {
  213. currentElement = currentNode.toElement();
  214. if(currentElement.hasAttribute("src"))
  215. {
  216. Context *context = buildContext(currentElement.attribute("src"), directory.path());
  217. if(context)
  218. {
  219. contexts.append(context);
  220. if(context->reportDirectory.length() > 0)
  221. {
  222. QDir reportsDirectory(QString("%1/%2").arg(directory.path()).arg(context->reportDirectory));
  223. reportsDirectory.setFilter(QDir::Files);
  224. reportsDirectory.setSorting(QDir::Name);
  225. QStringList nameFilter;
  226. nameFilter << "*.xml";
  227. reportsDirectory.setNameFilters(nameFilter);
  228. QStringList reportFiles = reportsDirectory.entryList();
  229. for(int j = 0; j < reportFiles.size(); j++)
  230. {
  231. Context *reportContext = buildContext(QString("%1/%2").arg(context->reportDirectory).arg(reportFiles.at(j)), directory.path());
  232. if(reportContext)
  233. {
  234. contexts.append(reportContext);
  235. }
  236. }
  237. }
  238. }
  239. }
  240. }
  241. }
  242. source.close();
  243. QFile ts(transfile);
  244. if(!ts.open(QIODevice::WriteOnly | QIODevice::Text))
  245. {
  246. qDebug() << "Failed to open output file: " << transfile;
  247. return 1;
  248. }
  249. QXmlStreamWriter xmlout(&ts);
  250. xmlout.setAutoFormatting(true);
  251. xmlout.writeStartDocument("1.0");
  252. xmlout.writeDTD("<!DOCTYPE TS>");
  253. xmlout.writeStartElement("TS");
  254. xmlout.writeAttribute("version", "2.0");
  255. foreach(Context *c, contexts)
  256. {
  257. xmlout.writeStartElement("context");
  258. xmlout.writeStartElement("name");
  259. xmlout.writeCharacters(c->name);
  260. xmlout.writeEndElement();
  261. for(int i = 0; i < c->messages.length(); i++)
  262. {
  263. xmlout.writeStartElement("message");
  264. xmlout.writeStartElement("location");
  265. xmlout.writeAttribute("filename", c->filename);
  266. xmlout.writeAttribute("line", c->locations.at(i));
  267. xmlout.writeEndElement();
  268. xmlout.writeStartElement("source");
  269. xmlout.writeCharacters(c->messages.at(i));
  270. xmlout.writeEndElement();
  271. xmlout.writeStartElement("translation");
  272. xmlout.writeCharacters("Translated");
  273. xmlout.writeEndElement();
  274. xmlout.writeEndElement();
  275. }
  276. xmlout.writeEndElement();
  277. }
  278. xmlout.writeEndElement();
  279. xmlout.writeEndDocument();
  280. ts.close();
  281. return 0;
  282. }
  283. // Take the source and destination files from the command line arguments and
  284. // pass them to generate.
  285. int main(int argc, char **argv)
  286. {
  287. QCoreApplication app(argc, argv);
  288. QStringList arguments = app.arguments();
  289. if(arguments.length() != 3) {
  290. qDebug() << "Usage: ttsg configuration.xml translation.ts";
  291. return 1;
  292. }
  293. QString configfile = arguments.at(1);
  294. QString transfile = arguments.at(2);
  295. return generate(configfile, transfile);
  296. }