/* PIP - Platform Independent Primitives 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 "ts_file.h" #include "pifile.h" #include "piiostream.h" #include "pitranslator.h" bool TSFile::Context::confirm(const PIString & msg, const PIString & file, int line) { if (msg.isEmpty()) return false; bool is_new = !messages.contains(msg); auto & m(messages[msg]); m.source = msg; m.filename = file; m.line = line >= 0 ? PIString::fromNumber(line) : PIString(); m.type.setFlag(Message::Missing, false); if (is_new) m.type.setFlag(Message::Unfinished); return is_new; } PIString TSFile::mask(const PIString & in) { static const PIVector> map = { {"&", "&" }, {"<", "<" }, {">", ">" }, {"'", "'"}, {"\"", """}, }; PIString ret = in; for (const auto & i: map) ret.replaceAll(i.first, i.second); return ret; } PIString TSFile::unmask(const PIString & in) { static const PIVector> map = { {"<", "<" }, {">", ">" }, {"'", "'"}, {"\"", """}, {"&", "&" }, }; PIString ret = in; for (const auto & i: map) ret.replaceAll(i.second, i.first); return ret; } TSFile::Content TSFile::read(const PIString & path) { enum Phase { pHeader, pContext, pContextName, pMessage, pMessageBody, }; Content ret; PIFile f(path, PIIODevice::ReadOnly); if (!f.isOpened()) return ret; PIIOTextStream ts(&f); Context * cc = nullptr; Message msg; int phase = pHeader; bool multi_source = false, multi_translation = false, multi_comment = false; while (!ts.isEnd()) { auto line = ts.readLine().trim(); if (multi_source) { if (line.endsWith("")) { line.cutRight(9); multi_source = false; } msg.source += "\n" + unmask(line); continue; } if (multi_translation) { if (line.endsWith("")) { line.cutRight(14); multi_translation = false; } msg.translation += "\n" + unmask(line); continue; } if (multi_comment) { if (line.endsWith("")) { line.cutRight(20); multi_comment = false; } msg.comment += "\n" + unmask(line); continue; } switch (phase) { case pHeader: if (line.startsWith("") phase = pContextName; break; case pContextName: if (line.startsWith("")) { line.cutLeft(6).cutRight(7); cc = &(ret.contexts[line]); phase = pMessage; } break; case pMessage: if (line == "") { msg = {}; phase = pMessageBody; } break; case pMessageBody: if (line == "") { if (cc) cc->messages[msg.source] = msg; phase = pMessage; } else if (line.startsWith("")) { line.cutLeft(8); if (line.endsWith("")) line.cutRight(9); else multi_source = true; msg.source = unmask(line); } else if (line.startsWith("').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("').cutLeft(11); while (trs.isNotEmpty()) { PIString t = trs.takeCWord(); trs.cutLeft(1); PIString v = trs.takeRange('\"', '\"'); if (t == "type") { if (v == "unfinished") msg.type.setFlag(Message::Unfinished); else if (v == "vanished") msg.type.setFlag(Message::Missing); } } if (line.endsWith("")) line.cutRight(14); else multi_translation = true; msg.translation = unmask(line); } else if (line.startsWith("")) { line.cutLeft(19); if (line.endsWith("")) line.cutRight(20); else multi_comment = true; msg.comment = unmask(line); } break; } if (line == "") { cc = nullptr; phase = pContext; } } return ret; } bool TSFile::write(const PIString & path, const TSFile::Content & content, bool no_obsolete) { PIFile outf(path, PIIODevice::ReadWrite); if (!outf.isOpened()) { piCerr << "Can`t open file \"%1\"!"_tr("TSFile").arg(outf.path()); return false; } outf.clear(); PIIOTextStream ts(&outf); auto writeMessage = [&ts](const Message & m) { if (m.source.isEmpty()) return; ts << " \n"; if (m.filename.isNotEmpty()) { ts << " \n"; } ts << " " << mask(m.source) << "\n"; if (m.comment.isNotEmpty()) { ts << " " << mask(m.comment) << "\n"; } ts << " " << mask(m.translation) << "\n"; ts << " \n"; }; ts << "\n"; ts << "\n"; ts << "\n"; auto cit = content.contexts.makeIterator(); while (cit.next()) { if (cit.value().messages.isEmpty()) continue; ts << "\n"; ts << " " << cit.key() << "\n"; auto mit = cit.value().messages.makeIterator(); while (mit.next()) { if (mit.value().type[Message::Missing] && no_obsolete) continue; writeMessage(mit.value()); } ts << "\n"; }; ts << "\n"; return true; } void TSFile::Content::markAllMissing() { auto cit = contexts.makeIterator(); while (cit.next()) { auto mit = cit.value().messages.makeIterator(); while (mit.next()) { mit.value().type.setFlag(Message::Missing); } } }