/* PIP - Platform Independent Primitives Translator Ivan Pelipenko peri4ko@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "parser.h" #include "picli.h" #include "pidir.h" #include "pifile.h" #include "piliterals_string.h" #include "pitranslator.h" #include "pitranslator_p.h" using namespace PICoutManipulators; const char help_string[] = "Read input files or entire directories as C++ sources\n" "and create QtLinguist translation file (*.ts).\n" "Search for next strings:\n" " * PITranslator::tr(\"msg\"[, \"ctx\"])\n" " * piTr(\"msg\"[, \"ctx\"])\n" " * \"msg\"_tr\n" "Output file can be translated by QtLinguist and used\n" "by PIP ?????? to translate strings.\n" ""; void header() { piCout << Bold << "PIP Translator"; piCout << Cyan << "Version" << Bold << PIPVersion() << NewLine; piCout << Green << Bold << "Usage:" << Default << "\"pip_tr -P [-hHqnr] -l -o [] [] [...]\"" << NewLine << "\"pip_tr -C [-hHq] -o \"" << NewLine; } void usage() { header(); piCout << Green << Bold << "Details:"; piCout << Bold << "Mode"; piCout << "-P --Parse " << Green << "- parse sources and create/update *.ts file"; piCout << "-C --Compile " << Green << "- compile *.ts file"; piCout << ""; piCout << Bold << "Debug control"; piCout << "-h " << Green << "- display this message and exit"; piCout << "-H " << Green << "- display details help"; piCout << "-q " << Green << "- quiet, no debug output to console"; piCout << ""; piCout << Bold << "Output control"; piCout << "-l " << Green << "- translation language (e.g. en_US, ru_RU)"; piCout << "-n, --no-obsolete " << Green << "- drop unused translations in output file"; piCout << "-o " << Green << "- output file for translation (QtLinguist *.ts file for parse mode or binary file for compile mode)"; piCout << ""; piCout << Bold << "Input control"; piCout << "-r, --recursive " << Green << "- scan directories recursively"; piCout << " " << Green << "- add source file/dir for parse mode or *.ts file for compile mode"; } void help() { header(); piCout << help_string; } enum Mode { mParse = 1, mCompile = 2, }; int main(int argc, char * argv[]) { PICLI cli(argc, argv); // piCout << cli.rawArguments(); cli.setOptionalArgumentsCount(-1); cli.addArgument("output", true); cli.addArgument("language", true); cli.addArgument("help"); cli.addArgument("Help"); cli.addArgument("quiet"); cli.addArgument("no-obsolete"); cli.addArgument("recursive"); cli.addArgument("Parse"); cli.addArgument("Compile"); if (cli.hasArgument("Help")) { help(); return 0; } int mode = 0; if (cli.hasArgument("Parse")) mode |= mParse; if (cli.hasArgument("Compile")) mode |= mCompile; if (cli.hasArgument("help") || cli.argumentValue("output").isEmpty() || cli.optionalArguments().isEmpty() || (mode != mParse && mode != mCompile)) { usage(); return 0; } if (mode == mParse && cli.argumentValue("language").isEmpty()) { usage(); return 0; } piDebug = !cli.hasArgument("quiet"); PIString out_path = cli.argumentValue("output"); if (mode == mParse) { piCout << Cyan << Bold << "Reading ts file ..."; auto content = TSFile::read(out_path); if (content.lang.isEmpty()) content.lang = cli.argumentValue("language"); content.markAllMissing(); piCout << Cyan << Bold << "Reading done"; bool recursive = cli.hasArgument("recursive"); PIStringList files; const static PIStringList ext({"h", "hpp", "cpp", "cxx"}); for (const PIString & a: cli.optionalArguments()) { if (PIDir::isExists(a)) { auto dl = recursive ? PIDir(a).allEntries() : PIDir(a).entries(); for (const auto & i: dl) { if (!i.isFile()) continue; if (ext.contains(i.extension().toLowerCase().trim())) files << i.path; } } else { if (PIFile::isExists(a)) files << a; } } piCout << Cyan << Bold << "Read" << files.size_s() << "files ..."; PITimeMeasurer tm; PIDir out_dir(PIFile::FileInfo(out_path).dir()); int count_all = 0, count_new = 0; for (const auto & p: files) { PIString file_loc = out_dir.relative(p); PIFile f(p, PIIODevice::ReadOnly); gatherStrings(content, PIString::fromUTF8(f.readAll()), file_loc, count_all, count_new); } auto elapsed = tm.elapsed(); piCout << Cyan << Bold << "Reading done, found %1 strings (%2 new) in %3 ms"_a.arg(count_all).arg(count_new).arg((int)elapsed.toMilliseconds()); piCout << Cyan << Bold << "Writing ts file ..."; if (!TSFile::write(out_path, content, cli.hasArgument("no-obsolete"))) return 1; piCout << Cyan << Bold << "Writing done"; } else if (mode == mCompile) { piCout << Cyan << Bold << "Reading ts file ..."; auto content = TSFile::read(cli.optionalArguments().front()); if (content.lang.isEmpty()) content.lang = cli.argumentValue("language"); piCout << Cyan << Bold << "Reading done"; int wcount = 0; PITranslatorPrivate::Translation t; t.lang = content.lang; auto cit = content.contexts.makeIterator(); while (cit.next()) { auto * c = t.createContext(cit.key()); auto mit = cit.value().messages.makeIterator(); while (mit.next()) { const auto & m(mit.value()); if (m.type[TSFile::Message::Unfinished] || m.type[TSFile::Message::Missing]) continue; c->add(m.source, m.translation); ++wcount; // piCout << "tr" << m.source << "->" << m.translation; } } PIFile outf(out_path, PIIODevice::WriteOnly); if (!outf.isOpened()) { piCerr << "Can`t open file \"%1\"!"_tr("TSFile").arg(outf.path()); return 1; } outf.clear(); piCout << Cyan << Bold << "Writing bin file ..."; PITranslatorPrivate::writeHeader(&outf); outf.write(t.save()); piCout << Cyan << Bold << "Writing done (%1 strings)"_a.arg(wcount); } return 0; }