improve pip_vtt - no-obsolete option, <location> support
This commit is contained in:
@@ -44,7 +44,7 @@ void header() {
|
|||||||
piCout << Bold << "PIP ValueTree translator";
|
piCout << Bold << "PIP ValueTree translator";
|
||||||
piCout << Cyan << "Version" << Bold << PIPVersion() << NewLine;
|
piCout << Cyan << "Version" << Bold << PIPVersion() << NewLine;
|
||||||
piCout << Green << Bold << "Usage:" << Default
|
piCout << Green << Bold << "Usage:" << Default
|
||||||
<< "\"pip_vtt [-hHq] -l <lang> -o <output_file> <file1/dir1> [<file2/dir2>] [<file3/dir3>] [...]\"" << NewLine;
|
<< "\"pip_vtt [-hHqn] -l <lang> -o <output_file> <file1/dir1> [<file2/dir2>] [<file3/dir3>] [...]\"" << NewLine;
|
||||||
}
|
}
|
||||||
|
|
||||||
void usage() {
|
void usage() {
|
||||||
@@ -57,6 +57,7 @@ void usage() {
|
|||||||
piCout << "";
|
piCout << "";
|
||||||
piCout << Bold << "Output control";
|
piCout << Bold << "Output control";
|
||||||
piCout << "-l <lang> " << Green << "- translation language (e.g. en_US, ru_RU)";
|
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 << "-o <output_file> " << Green << "- output file for translation (QtLinguist *.ts file)";
|
||||||
piCout << "";
|
piCout << "";
|
||||||
piCout << Bold << "Input control";
|
piCout << Bold << "Input control";
|
||||||
@@ -74,20 +75,25 @@ void printError(const PIString & msg) {
|
|||||||
|
|
||||||
|
|
||||||
PISet<PIString> strings;
|
PISet<PIString> strings;
|
||||||
|
PIMap<uint, PIString> locations;
|
||||||
|
|
||||||
void gatherStrings(const PIValueTree & vt) {
|
void addString(const PIString & s, const PIString & loc) {
|
||||||
|
if (s.isEmpty()) return;
|
||||||
|
strings << s;
|
||||||
|
locations[s.hash()] = loc;
|
||||||
|
}
|
||||||
|
|
||||||
|
void gatherStrings(const PIValueTree & vt, const PIString & loc) {
|
||||||
const static PIStringList attrs({Attribute::prefix, Attribute::suffix});
|
const static PIStringList attrs({Attribute::prefix, Attribute::suffix});
|
||||||
for (const auto & c: vt.children()) {
|
for (const auto & c: vt.children()) {
|
||||||
if (c.isArray()) return;
|
addString(c.name(), loc);
|
||||||
if (c.name().isNotEmpty()) strings << c.name();
|
addString(c.comment(), loc);
|
||||||
if (c.comment().isNotEmpty()) strings << c.comment();
|
|
||||||
for (const auto & a: attrs) {
|
for (const auto & a: attrs) {
|
||||||
if (c.attributes().contains(a)) {
|
if (c.attributes().contains(a)) {
|
||||||
PIString sa = c.attributes().value(a).toString();
|
addString(c.attributes().value(a).toString(), loc);
|
||||||
if (sa.isNotEmpty()) strings << sa;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
gatherStrings(c);
|
if (!c.isArray()) gatherStrings(c, loc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -97,6 +103,8 @@ struct TSMessage {
|
|||||||
PIString source;
|
PIString source;
|
||||||
PIString translation;
|
PIString translation;
|
||||||
PIString type;
|
PIString type;
|
||||||
|
PIString filename;
|
||||||
|
PIString line;
|
||||||
};
|
};
|
||||||
|
|
||||||
PIMap<PIString, TSMessage> readTS(const PIString & path) {
|
PIMap<PIString, TSMessage> readTS(const PIString & path) {
|
||||||
@@ -131,8 +139,18 @@ PIMap<PIString, TSMessage> readTS(const PIString & path) {
|
|||||||
} else if (line.startsWith("<source>")) {
|
} else if (line.startsWith("<source>")) {
|
||||||
line.cutLeft(8).cutRight(9);
|
line.cutLeft(8).cutRight(9);
|
||||||
msg.source = line;
|
msg.source = line;
|
||||||
|
} else if (line.startsWith("<location")) {
|
||||||
|
PIString trs = line.takeRange('<', '>').cutLeft(8);
|
||||||
|
while (trs.isNotEmpty()) {
|
||||||
|
PIString t = trs.takeCWord();
|
||||||
|
trs.cutLeft(1);
|
||||||
|
PIString v = trs.takeRange('\"', '\"');
|
||||||
|
if (t == "filename") msg.filename = v;
|
||||||
|
if (t == "line") msg.line = v;
|
||||||
|
if (trs.size_s() <= 2) break;
|
||||||
|
}
|
||||||
} else if (line.startsWith("<translation")) {
|
} else if (line.startsWith("<translation")) {
|
||||||
PIString trs = line.takeRange('<', '>');
|
PIString trs = line.takeRange('<', '>').cutLeft(11);
|
||||||
while (trs.isNotEmpty()) {
|
while (trs.isNotEmpty()) {
|
||||||
PIString t = trs.takeCWord();
|
PIString t = trs.takeCWord();
|
||||||
trs.cutLeft(1);
|
trs.cutLeft(1);
|
||||||
@@ -159,6 +177,7 @@ int main(int argc, char * argv[]) {
|
|||||||
cli.addArgument("help");
|
cli.addArgument("help");
|
||||||
cli.addArgument("Help");
|
cli.addArgument("Help");
|
||||||
cli.addArgument("quiet");
|
cli.addArgument("quiet");
|
||||||
|
cli.addArgument("no-obsolete");
|
||||||
if (cli.hasArgument("Help")) {
|
if (cli.hasArgument("Help")) {
|
||||||
help();
|
help();
|
||||||
return 0;
|
return 0;
|
||||||
@@ -187,12 +206,14 @@ int main(int argc, char * argv[]) {
|
|||||||
|
|
||||||
|
|
||||||
piCout << Cyan << Bold << "Read" << files.size_s() << "files ...";
|
piCout << Cyan << Bold << "Read" << files.size_s() << "files ...";
|
||||||
|
PIDir out_dir(PIFile::FileInfo(out_path).dir());
|
||||||
for (const auto & p: files) {
|
for (const auto & p: files) {
|
||||||
PIString ext = PIFile::FileInfo(p).extension().toLowerCase().trim();
|
PIString ext = PIFile::FileInfo(p).extension().toLowerCase().trim();
|
||||||
PIFile f(p, PIIODevice::ReadOnly);
|
PIFile f(p, PIIODevice::ReadOnly);
|
||||||
if (ext == "conf") gatherStrings(PIValueTreeConversions::fromText(PIString::fromUTF8(f.readAll())));
|
if (ext == "conf") gatherStrings(PIValueTreeConversions::fromText(PIString::fromUTF8(f.readAll())), out_dir.relative(p));
|
||||||
if (ext == "json") gatherStrings(PIValueTreeConversions::fromJSON(PIJSON::fromJSON(PIString::fromUTF8(f.readAll()))));
|
if (ext == "json")
|
||||||
if (ext == "bin") gatherStrings(piDeserialize<PIValueTree>(f.readAll()));
|
gatherStrings(PIValueTreeConversions::fromJSON(PIJSON::fromJSON(PIString::fromUTF8(f.readAll()))), out_dir.relative(p));
|
||||||
|
if (ext == "bin") gatherStrings(piDeserialize<PIValueTree>(f.readAll()), PIString());
|
||||||
}
|
}
|
||||||
piCout << Cyan << Bold << "Reading done";
|
piCout << Cyan << Bold << "Reading done";
|
||||||
|
|
||||||
@@ -213,23 +234,38 @@ int main(int argc, char * argv[]) {
|
|||||||
}
|
}
|
||||||
outf.clear();
|
outf.clear();
|
||||||
PIIOTextStream ts(&outf);
|
PIIOTextStream ts(&outf);
|
||||||
|
auto writeTSMessage = [&ts](const PIString & s, const TSMessage & m) {
|
||||||
|
ts << " <message>\n";
|
||||||
|
ts << " <source>" << s << "</source>\n";
|
||||||
|
if (m.filename.isNotEmpty()) {
|
||||||
|
ts << " <location filename=\"" << m.filename << "\"";
|
||||||
|
if (m.line.isNotEmpty()) ts << " line=\"" << m.line << "\"";
|
||||||
|
ts << "/>\n";
|
||||||
|
}
|
||||||
|
ts << " <translation";
|
||||||
|
if (m.source.isEmpty()) {
|
||||||
|
ts << " type=\"unfinished\"";
|
||||||
|
} else {
|
||||||
|
if (m.type.isNotEmpty()) ts << " type=\"" << m.type << "\"";
|
||||||
|
}
|
||||||
|
ts << ">" << m.translation << "</translation>\n";
|
||||||
|
ts << " </message>\n";
|
||||||
|
};
|
||||||
ts << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
ts << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
||||||
ts << "<!DOCTYPE TS>\n";
|
ts << "<!DOCTYPE TS>\n";
|
||||||
ts << "<TS version=\"2.1\" language=\"" << cli.argumentValue("language") << "\">\n";
|
ts << "<TS version=\"2.1\" language=\"" << cli.argumentValue("language") << "\">\n";
|
||||||
ts << "<context>\n";
|
ts << "<context>\n";
|
||||||
ts << " <name>" << context << "</name>\n";
|
ts << " <name>" << context << "</name>\n";
|
||||||
for (const auto & s: strings) {
|
for (const auto & s: strings) {
|
||||||
ts << " <message>\n";
|
|
||||||
ts << " <source>" << s.first << "</source>\n";
|
|
||||||
ts << " <translation";
|
|
||||||
TSMessage m = old.value(s.first);
|
TSMessage m = old.value(s.first);
|
||||||
if (m.source.isEmpty())
|
m.filename = locations.value(s.first.hash());
|
||||||
ts << " type=\"unfinished\"";
|
writeTSMessage(s.first, m);
|
||||||
else {
|
old.remove(s.first);
|
||||||
if (m.type.isNotEmpty()) ts << " type=\"" << m.type << "\"";
|
}
|
||||||
|
if (!cli.hasArgument("no-obsolete")) {
|
||||||
|
for (const auto & i: old) {
|
||||||
|
writeTSMessage(i.first, i.second);
|
||||||
}
|
}
|
||||||
ts << ">" << m.translation << "</translation>\n";
|
|
||||||
ts << " </message>\n";
|
|
||||||
}
|
}
|
||||||
ts << "</context>\n";
|
ts << "</context>\n";
|
||||||
ts << "</TS>\n";
|
ts << "</TS>\n";
|
||||||
|
|||||||
Reference in New Issue
Block a user