add PIString::lineNumber() method
add ""_tr literal to translate string by PITranslator add pip_tr util, now useless, only can generate *.ts add qt_support internal lib, now only works with *.ts file pip_vtt migrate to qt_support
This commit is contained in:
220
utils/translator/main.cpp
Normal file
220
utils/translator/main.cpp
Normal file
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
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 <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
#include "picli.h"
|
||||
#include "pidir.h"
|
||||
#include "pifile.h"
|
||||
#include "piiostream.h"
|
||||
#include "pijson.h"
|
||||
#include "pivaluetree.h"
|
||||
#include "pivaluetree_conversions.h"
|
||||
#include "ts_file.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 [-hHqn] -l <lang> -o <output_file> <file1/dir1> [<file2/dir2>] [<file3/dir3>] [...]\"" << NewLine;
|
||||
}
|
||||
|
||||
void usage() {
|
||||
header();
|
||||
piCout << Green << Bold << "Details:";
|
||||
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 <lang> " << Green << "- translation language (e.g. en_US, ru_RU)";
|
||||
piCout << "-n, --no-obsolete " << Green << "- drop unused translations in output file";
|
||||
piCout << "-o <output_file> " << Green << "- output file for translation (QtLinguist *.ts file)";
|
||||
piCout << "";
|
||||
piCout << Bold << "Input control";
|
||||
piCout << "<file/dir> " << Green << "- add file or dir translation";
|
||||
}
|
||||
|
||||
void help() {
|
||||
header();
|
||||
piCout << help_string;
|
||||
}
|
||||
|
||||
|
||||
void gatherStrings(TSFile::Content & content, const PIString & file, const PIString & file_loc) {
|
||||
PIString source, context;
|
||||
int pos = -1, ppos = 0, line = -1;
|
||||
int len = 0;
|
||||
auto isCLetter = [](const PIChar c) { return c.isAlpha() || c.isDigit() || c == '_'; };
|
||||
static const PIStringList keyword({"PITranslator::tr", "piTr"});
|
||||
for (const auto & kw: keyword) {
|
||||
for (;;) {
|
||||
source.clear();
|
||||
context.clear();
|
||||
|
||||
pos = file.find(kw, pos + 1);
|
||||
if (pos < 0) break;
|
||||
if (pos > 0) {
|
||||
auto pc = file[pos - 1];
|
||||
if (isCLetter(pc) || pc == ':') {
|
||||
pos += kw.size_s();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
pos += kw.size_s();
|
||||
if (pos < file.size_s() - 1) {
|
||||
auto pc = file[pos];
|
||||
if (isCLetter(pc) || pc == ':') {
|
||||
pos += kw.size_s();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
// piCout << "PITranslator" << pos;
|
||||
|
||||
pos = file.find('(', pos);
|
||||
if (pos < 0) break;
|
||||
// piCout << "(" << pos;
|
||||
pos += 1;
|
||||
|
||||
pos = file.findRange('"', '"', '\\', pos, &len);
|
||||
if (pos < 0) break;
|
||||
// piCout << "\"" << pos;
|
||||
source = file.mid(pos, len);
|
||||
pos += len + 1;
|
||||
line = file.lineNumber(pos);
|
||||
ppos = pos;
|
||||
|
||||
while (pos < file.size_s()) {
|
||||
if (!file[pos].isSpace() && file[pos] != '\n' && file[pos] != '\r') break;
|
||||
++pos;
|
||||
}
|
||||
if (pos < file.size_s()) {
|
||||
// piCout << "check comma" << file[pos];
|
||||
if (file[pos] == ',') {
|
||||
pos += 1;
|
||||
pos = file.findRange('"', '"', '\\', pos, &len);
|
||||
// piCout << "check range" << file.mid(pos, len);
|
||||
if (pos >= 0) {
|
||||
context = file.mid(pos, len);
|
||||
pos += len + 1;
|
||||
ppos = pos;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
content[context].confirm(source, file_loc, line);
|
||||
pos = ppos;
|
||||
piCout << "Context = \"" << context << "\", message = \"" << source << "\"";
|
||||
}
|
||||
}
|
||||
|
||||
auto & ec(content[""]);
|
||||
content[context].confirm(source, file_loc, line);
|
||||
pos = -1;
|
||||
for (;;) {
|
||||
source.clear();
|
||||
|
||||
pos = file.find('"', pos + 1);
|
||||
if (pos < 0) break;
|
||||
pos = file.findRange('"', '"', '\\', pos, &len);
|
||||
if (pos < 0) break;
|
||||
// piCout << "\"" << pos;
|
||||
source = file.mid(pos, len);
|
||||
pos += len + 1;
|
||||
if (file.mid(pos, 3) == "_tr") {
|
||||
pos += 3;
|
||||
if (pos < file.size_s() - 1) {
|
||||
auto pc = file[pos];
|
||||
if (isCLetter(pc) || pc == ':') {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
ec.confirm(source, file_loc, file.lineNumber(pos));
|
||||
piCout << "_tr = \"" << source << "\"";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
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");
|
||||
if (cli.hasArgument("Help")) {
|
||||
help();
|
||||
return 0;
|
||||
}
|
||||
if (cli.hasArgument("help") || cli.argumentValue("output").isEmpty() || cli.argumentValue("language").isEmpty() ||
|
||||
cli.optionalArguments().isEmpty()) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
piDebug = !cli.hasArgument("quiet");
|
||||
PIString out_path = cli.argumentValue("output");
|
||||
|
||||
PIStringList files;
|
||||
const static PIStringList ext({"*.h", "*.hpp", "*.cpp", "*.cxx"});
|
||||
for (const PIString & a: cli.optionalArguments()) {
|
||||
if (PIDir::isExists(a)) {
|
||||
auto dl = PIDir(a).allEntries();
|
||||
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 << "Reading ts file ...";
|
||||
auto content = TSFile::read(out_path);
|
||||
piCout << Cyan << Bold << "Reading done";
|
||||
|
||||
piCout << Cyan << Bold << "Read" << files.size_s() << "files ...";
|
||||
PIDir out_dir(PIFile::FileInfo(out_path).dir());
|
||||
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);
|
||||
}
|
||||
piCout << Cyan << Bold << "Reading done";
|
||||
|
||||
piCout << Cyan << Bold << "Writing ts file ...";
|
||||
if (!TSFile::write(out_path, content, cli.argumentValue("language"), cli.hasArgument("no-obsolete"))) return 1;
|
||||
piCout << Cyan << Bold << "Writing done";
|
||||
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user