add pip_vtt util
This commit is contained in:
@@ -569,6 +569,7 @@ if(NOT PIP_FREERTOS)
|
|||||||
add_subdirectory("utils/code_model_generator")
|
add_subdirectory("utils/code_model_generator")
|
||||||
add_subdirectory("utils/resources_compiler")
|
add_subdirectory("utils/resources_compiler")
|
||||||
add_subdirectory("utils/deploy_tool")
|
add_subdirectory("utils/deploy_tool")
|
||||||
|
add_subdirectory("utils/value_tree_translator")
|
||||||
if(PIP_UTILS AND (NOT CROSSTOOLS))
|
if(PIP_UTILS AND (NOT CROSSTOOLS))
|
||||||
add_subdirectory("utils/system_test")
|
add_subdirectory("utils/system_test")
|
||||||
add_subdirectory("utils/udp_file_transfer")
|
add_subdirectory("utils/udp_file_transfer")
|
||||||
|
|||||||
16
utils/value_tree_translator/CMakeLists.txt
Normal file
16
utils/value_tree_translator/CMakeLists.txt
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
list(APPEND PIP_UTILS_LIST "pip_vtt")
|
||||||
|
set(PIP_UTILS_LIST ${PIP_UTILS_LIST} PARENT_SCOPE)
|
||||||
|
import_version(pip_vtt PIP)
|
||||||
|
set_deploy_property(pip_vtt
|
||||||
|
LABEL "PIP ValueTree translator"
|
||||||
|
FULLNAME "${PIP_DOMAIN}.pip_vtt"
|
||||||
|
COMPANY "${PIP_COMPANY}"
|
||||||
|
INFO "Platform-Independent Primitives")
|
||||||
|
make_rc(pip_vtt _RC)
|
||||||
|
add_executable(pip_vtt "main.cpp" ${_RC})
|
||||||
|
target_link_libraries(pip_vtt pip)
|
||||||
|
if (DEFINED LIB)
|
||||||
|
install(TARGETS pip_vtt DESTINATION ${CMAKE_INSTALL_PREFIX}/bin)
|
||||||
|
else()
|
||||||
|
install(TARGETS pip_vtt DESTINATION bin)
|
||||||
|
endif()
|
||||||
241
utils/value_tree_translator/main.cpp
Normal file
241
utils/value_tree_translator/main.cpp
Normal file
@@ -0,0 +1,241 @@
|
|||||||
|
/*
|
||||||
|
PIP - Platform Independent Primitives
|
||||||
|
ValueTree 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 <iostream>
|
||||||
|
|
||||||
|
using namespace PICoutManipulators;
|
||||||
|
using Attribute = PIValueTree::Attribute;
|
||||||
|
|
||||||
|
const char help_string[] = "Read input files or entire directories as PIValueTree\n"
|
||||||
|
"according to extension, and create QtLinguist translation\n"
|
||||||
|
"file (*.ts). Extensions:\n"
|
||||||
|
" * conf - PIValueTreeConversions::fromText\n"
|
||||||
|
" * json - PIValueTreeConversions::fromJSON\n"
|
||||||
|
" * bin - binary deserialization\n"
|
||||||
|
"Output file can be translated by QtLinguist and used\n"
|
||||||
|
"by QAD PIValueTreeEdit widget to translate strings.\n"
|
||||||
|
"";
|
||||||
|
|
||||||
|
void header() {
|
||||||
|
piCout << Bold << "PIP ValueTree translator";
|
||||||
|
piCout << Cyan << "Version" << Bold << PIPVersion() << NewLine;
|
||||||
|
piCout << Green << Bold << "Usage:" << Default
|
||||||
|
<< "\"pip_vtt [-hHq] -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 << "-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 printError(const PIString & msg) {
|
||||||
|
std::cerr << msg.data() << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PISet<PIString> strings;
|
||||||
|
|
||||||
|
void gatherStrings(const PIValueTree & vt) {
|
||||||
|
const static PIStringList attrs({Attribute::prefix, Attribute::suffix});
|
||||||
|
for (const auto & c: vt.children()) {
|
||||||
|
if (c.isArray()) return;
|
||||||
|
if (c.name().isNotEmpty()) strings << c.name();
|
||||||
|
if (c.comment().isNotEmpty()) strings << c.comment();
|
||||||
|
for (const auto & a: attrs) {
|
||||||
|
if (c.attributes().contains(a)) {
|
||||||
|
PIString sa = c.attributes().value(a).toString();
|
||||||
|
if (sa.isNotEmpty()) strings << sa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
gatherStrings(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const PIString context = "QAD::PIValueTreeEdit";
|
||||||
|
|
||||||
|
struct TSMessage {
|
||||||
|
PIString source;
|
||||||
|
PIString translation;
|
||||||
|
PIString type;
|
||||||
|
};
|
||||||
|
|
||||||
|
PIMap<PIString, TSMessage> readTS(const PIString & path) {
|
||||||
|
PIMap<PIString, TSMessage> ret;
|
||||||
|
PIFile f(path, PIIODevice::ReadOnly);
|
||||||
|
if (!f.isOpened()) return ret;
|
||||||
|
PIIOTextStream ts(&f);
|
||||||
|
TSMessage msg;
|
||||||
|
int phase = 0;
|
||||||
|
while (!ts.isEnd()) {
|
||||||
|
auto line = ts.readLine().trim();
|
||||||
|
switch (phase) {
|
||||||
|
case 0:
|
||||||
|
if (line == "<context>") phase = 1;
|
||||||
|
break;
|
||||||
|
case 1:
|
||||||
|
if (line.startsWith("<name>")) {
|
||||||
|
line.cutLeft(6).cutRight(7);
|
||||||
|
if (line == context) phase = 2;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 2:
|
||||||
|
if (line == "<message>") {
|
||||||
|
msg = {};
|
||||||
|
phase = 3;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case 3:
|
||||||
|
if (line == "</message>") {
|
||||||
|
ret[msg.source] = msg;
|
||||||
|
phase = 2;
|
||||||
|
} else if (line.startsWith("<source>")) {
|
||||||
|
line.cutLeft(8).cutRight(9);
|
||||||
|
msg.source = line;
|
||||||
|
} else if (line.startsWith("<translation")) {
|
||||||
|
PIString trs = line.takeRange('<', '>');
|
||||||
|
while (trs.isNotEmpty()) {
|
||||||
|
PIString t = trs.takeCWord();
|
||||||
|
trs.cutLeft(1);
|
||||||
|
PIString v = trs.takeRange('\"', '\"');
|
||||||
|
if (t == "type") msg.type = v;
|
||||||
|
}
|
||||||
|
line.cutRight(14);
|
||||||
|
msg.translation = line;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (line == "</context>") phase = 0;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
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");
|
||||||
|
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({"conf", "json", "bin"});
|
||||||
|
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 << "Read" << files.size_s() << "files ...";
|
||||||
|
for (const auto & p: files) {
|
||||||
|
PIString ext = PIFile::FileInfo(p).extension().toLowerCase().trim();
|
||||||
|
PIFile f(p, PIIODevice::ReadOnly);
|
||||||
|
if (ext == "conf") gatherStrings(PIValueTreeConversions::fromText(PIString::fromUTF8(f.readAll())));
|
||||||
|
if (ext == "json") gatherStrings(PIValueTreeConversions::fromJSON(PIJSON::fromJSON(PIString::fromUTF8(f.readAll()))));
|
||||||
|
if (ext == "bin") gatherStrings(piDeserialize<PIValueTree>(f.readAll()));
|
||||||
|
}
|
||||||
|
piCout << Cyan << Bold << "Reading done";
|
||||||
|
|
||||||
|
|
||||||
|
piCout << Cyan << Bold << "Reading ts file ...";
|
||||||
|
|
||||||
|
auto old = readTS(out_path);
|
||||||
|
|
||||||
|
piCout << Cyan << Bold << "Reading done";
|
||||||
|
|
||||||
|
|
||||||
|
piCout << Cyan << Bold << "Writing ts file ...";
|
||||||
|
|
||||||
|
PIFile outf(out_path, PIIODevice::ReadWrite);
|
||||||
|
if (!outf.isOpened()) {
|
||||||
|
printError("Can`t open \"" + outf.path() + "\"!");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
outf.clear();
|
||||||
|
PIIOTextStream ts(&outf);
|
||||||
|
ts << "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
|
||||||
|
ts << "<!DOCTYPE TS>\n";
|
||||||
|
ts << "<TS version=\"2.1\" language=\"" << cli.argumentValue("language") << "\">\n";
|
||||||
|
ts << "<context>\n";
|
||||||
|
ts << " <name>" << context << "</name>\n";
|
||||||
|
for (const auto & s: strings) {
|
||||||
|
ts << " <message>\n";
|
||||||
|
ts << " <source>" << s.first << "</source>\n";
|
||||||
|
ts << " <translation";
|
||||||
|
TSMessage m = old.value(s.first);
|
||||||
|
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 << "</context>\n";
|
||||||
|
ts << "</TS>\n";
|
||||||
|
|
||||||
|
piCout << Cyan << Bold << "Writing done";
|
||||||
|
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user