diff --git a/src/acbf/AcbfBookinfo.h b/src/acbf/AcbfBookinfo.h
--- a/src/acbf/AcbfBookinfo.h
+++ b/src/acbf/AcbfBookinfo.h
@@ -346,6 +346,12 @@
* @param index
*/
Q_INVOKABLE void removeLanguage(int index);
+ /**
+ * @brief language
+ * @param index of language.
+ * @return language at index;
+ */
+ Q_INVOKABLE Language* language(int index);
/**
* @return a list of sequence objects that describe the series and
diff --git a/src/acbf/AcbfBookinfo.cpp b/src/acbf/AcbfBookinfo.cpp
--- a/src/acbf/AcbfBookinfo.cpp
+++ b/src/acbf/AcbfBookinfo.cpp
@@ -586,6 +586,11 @@
removeLanguage(d->languages.at(index));
}
+Language *BookInfo::language(int index)
+{
+ return d->languages.at(index);
+}
+
QList BookInfo::sequence()
{
return d->sequence;
diff --git a/src/acbf/AcbfPage.cpp b/src/acbf/AcbfPage.cpp
--- a/src/acbf/AcbfPage.cpp
+++ b/src/acbf/AcbfPage.cpp
@@ -301,7 +301,7 @@
}
}
}
- setTextLayer(to);
+ setTextLayer(to, languageTo);
}
QStringList Page::textLayerLanguages() const
diff --git a/src/creator/qml/BookMetainfoPage.qml b/src/creator/qml/BookMetainfoPage.qml
--- a/src/creator/qml/BookMetainfoPage.qml
+++ b/src/creator/qml/BookMetainfoPage.qml
@@ -21,6 +21,7 @@
import QtQuick 2.2
import QtQuick.Controls 2.2 as QtControls
+import QtQuick.Dialogs 1.2
import org.kde.kirigami 2.1 as Kirigami
@@ -109,6 +110,61 @@
text:root.model.acbfData ? root.model.acbfData.metaData.bookInfo.keywords("").join(", ") : "";
}
+ Kirigami.Heading {
+ width: parent.width;
+ height: paintedHeight + Kirigami.Units.smallSpacing * 2;
+ text: i18nc("label text for the edit field for the language list", "Languages");
+ }
+
+ Repeater {
+ model: root.model.acbfData ? root.model.acbfData.metaData.bookInfo.languageEntryList: 0;
+ delegate: QtControls.Label {
+ width:parent.width - removeLanguageButton.width - Kirigami.Units.smallSpacing;
+ text: modelData !== ""? Qt.locale(modelData).nativeLanguageName + " (%1)".arg(modelData): i18nc("default textlayer", "Default");
+ QtControls.Button {
+ id: removeLanguageButton;
+ anchors {
+ left: parent.right;
+ leftMargin: Kirigami.Units.smallSpacing;
+ }
+ contentItem: Kirigami.Icon {
+ source: "list-remove";
+ }
+ height: parent.height;
+ width: height;
+ onClicked: {
+ // When removing, set the model dirty first, and then remove the entry to avoid reference errors.
+ root.model.setDirty();
+ for (var i = 0; i< root.model.acbfData.body.pageCount; i++) {
+ root.model.acbfData.body.page(i).removeTextLayer(modelData);
+ }
+ root.model.acbfData.metaData.bookInfo.removeLanguage(index);
+ }
+ }
+ }
+ }
+ Item {
+ width: parent.width;
+ height: childrenRect.height;
+ QtControls.Button {
+ text: i18nc("Label for POT export button.", "Export default language POT");
+ width: (parent.width-Kirigami.Units.smallSpacing)/2;
+ onClicked: exportPOT.open();
+ id: exportPOTButton;
+ }
+ QtControls.Button {
+ anchors {
+ left: exportPOTButton.right;
+ leftMargin: Kirigami.Units.smallSpacing;
+ top: exportPOTButton.top;
+ }
+
+ text: i18nc("Label for PO impot button.", "Import translation PO");
+ width: (parent.width-Kirigami.Units.smallSpacing)/2;
+ onClicked: importPO.open();
+ }
+ }
+
Kirigami.Heading {
width: parent.width;
height: paintedHeight + Kirigami.Units.smallSpacing * 2;
@@ -1051,5 +1107,40 @@
root.model.setDirty();
}
}
+
+ FileDialog {
+ id: exportPOT;
+ title: i18nc("Title of the folder selection fialog for exporting pot","Please choose a location to save the POT file.")
+ folder: mainWindow.homeDir();
+ selectFolder: true;
+ property int splitPos: osIsWindows ? 8 : 7;
+ onAccepted: {
+ if(folder.toString().substring(0, 7) === "file://") {
+ var file = model.filename.split("/").pop();
+ file = file.split(".")[0];
+ root.model.generatePot( folder.toString().substring(splitPos)+"/"+file+".pot", "");
+ }
+ }
+ onRejected: {
+ // Just do nothing, we don't really care...
+ }
+ }
+
+ FileDialog {
+ id: importPO;
+ title: i18nc("Title of the folder selection fialog for exporting pot","Please choose a location to save the POT file.")
+ folder: mainWindow.homeDir();
+ nameFilters: ["PO translation files (*.po)"];
+ property int splitPos: osIsWindows ? 8 : 7;
+ onAccepted: {
+ if(fileUrl.toString().substring(0, 7) === "file://") {
+ console.log(fileUrl);
+ root.model.readPoFile(fileUrl.toString().substring(splitPos), true);
+ }
+ }
+ onRejected: {
+ // Just do nothing, we don't really care...
+ }
+ }
}
}
diff --git a/src/qtquick/ArchiveBookModel.h b/src/qtquick/ArchiveBookModel.h
--- a/src/qtquick/ArchiveBookModel.h
+++ b/src/qtquick/ArchiveBookModel.h
@@ -172,6 +172,16 @@
*/
Q_INVOKABLE QString createBook(QString folder, QString title, QString coverUrl);
+ /**
+ * @brief Generate a POT file from a given text layer. These can be
+ * used with translation programs to make translation files.
+ * @param fileName the filename to write the pot to.
+ * @param language the language of which to use the textlayer.
+ * @return whether creating the filename was succesful.
+ */
+ Q_INVOKABLE bool generatePot(const QString fileName, const QString language);
+ Q_INVOKABLE bool readPoFile(const QString fileName, bool addTranslatorEmail);
+
friend class ArchiveImageProvider;
protected:
const KArchiveFile* archiveFile(const QString& filePath);
diff --git a/src/qtquick/ArchiveBookModel.cpp b/src/qtquick/ArchiveBookModel.cpp
--- a/src/qtquick/ArchiveBookModel.cpp
+++ b/src/qtquick/ArchiveBookModel.cpp
@@ -30,6 +30,8 @@
#include
#include
#include
+#include
+#include
#include
#include
@@ -1251,3 +1253,249 @@
}
return false;
}
+
+bool ArchiveBookModel::generatePot(const QString fileName, const QString language)
+{
+ QFile file(fileName);
+ qDebug() << fileName;
+ bool success = false;
+
+ if (file.open(QFile::WriteOnly| QFile::Truncate) && acbfData()) {
+ AdvancedComicBookFormat::Document* acbf = qobject_cast(acbfData());
+ QTextStream pot(&file);
+ QString quote = "\"";
+ QString newLine = "\n";
+
+ pot << "msid " << quote+quote+newLine;
+ pot << "msgstr " << quote+quote+newLine;
+
+ pot << quote << "POT-Creation-Date: " << QDateTime::currentDateTimeUtc().toString() << "\\n" << quote << newLine;
+ pot << quote << "Content-Type: text/plain; charset=UTF-8\\n" << quote << newLine;
+ pot << quote << "Content-Transfer-Encoding: 8bit\\n" << quote << newLine;
+ pot << quote << "X-Generator: Peruse Creator\\n" << quote << newLine;
+
+ pot << newLine;
+ pot << "#. Title of the work" << newLine;
+ pot << "msgctxt \"@meta-title\"" << newLine;
+ pot << "msgid " << quote << acbf->metaData()->bookInfo()->title(language) << quote << newLine;
+ pot << "msgstr " << quote << quote << newLine;
+ pot << newLine;
+
+ pot << "#. The summary" << newLine;
+ pot << "msgctxt \"@meta-summary\"" << newLine;
+ pot << "msgid " << quote << quote << newLine;
+ for (int i =0; i < acbf->metaData()->bookInfo()->annotation(language).size(); i++) {
+ QString paragraph = acbf->metaData()->bookInfo()->annotation(language).at(i);
+ paragraph.replace(quote, "\\\"");
+ paragraph.replace("\'", "\\\'");
+ paragraph.replace("#", "\\#");
+ pot << quote << "" << paragraph << "
" << quote << newLine;
+ }
+ pot << "msgstr " << quote << quote << newLine;
+ pot << newLine;
+
+ pot << "#. The keywords, these need to be comma separated." + newLine;
+ pot << "msgctxt \"@meta-keywords\"" << newLine;
+ pot << "msgid " << quote << acbf->metaData()->bookInfo()->keywords(language).join(", ") << quote << newLine;
+ pot << "msgstr " << quote << quote << newLine;
+ pot << newLine;
+
+ for (int p = 0; p < acbf->body()->pageCount(); p++) {
+ AdvancedComicBookFormat::Textlayer* textlayer = acbf->body()->page(p)->textLayer(language);
+
+ if (!acbf->body()->page(p)->title("").isEmpty()) {
+ pot << "#. Page title";
+ pot << "msgctxt " << quote << "@page-title" << quote << newLine;
+ pot << "msgid " << quote << acbf->body()->page(p)->title("") << quote << newLine;
+ pot << "msgstr " << quote << quote << newLine;
+ pot << newLine;
+ }
+
+ for (int t = 0; t < textlayer->textareas().size(); t++) {
+ AdvancedComicBookFormat::Textarea* textarea = textlayer->textarea(t);
+
+ pot << newLine;
+ pot << "msgid " << quote << quote << newLine;
+ for (int i =0; i < textarea->paragraphs().size(); i++) {
+ QString paragraph = textarea->paragraphs().at(i);
+ paragraph.replace(quote, "\\\"");
+ paragraph.replace("\'", "\\\'");
+ paragraph.replace("#", "\\#");
+ pot << quote << "" << paragraph << "
" << quote << newLine;
+ }
+ pot << "msgstr " << quote << quote << newLine;
+ pot << newLine;
+ }
+ }
+ success = true;
+ }
+ file.close();
+ return success;
+}
+
+bool ArchiveBookModel::readPoFile(const QString fileName, bool addTranslatorEmail)
+{
+ QFile file(fileName);
+ bool success = false;
+ QString language;
+ QString translator;
+ QString multiline;
+ QString context;
+ QString id;
+ QString translation;
+ QHash table;
+
+ if (file.open(QFile::ReadOnly|QFile::Text)) {
+ QTextStream po(&file);
+ QString line;
+ while (po.readLineInto(&line)) {
+ if (line.startsWith("\"Last-Translator: "))
+ {
+ line.remove("\"Last-Translator: ");
+ translator = line.replace("\\n\"", "");
+ }
+
+ else if (line.startsWith("\"Language: "))
+ {
+ line.remove("\"Language: ");
+ language = line.replace("\\n\"", "");
+ }
+
+ else if (line.startsWith("msgctxt"))
+ {
+ if (!line.endsWith("\"\"")) {
+ line.remove("msgctxt \"");
+ context = line.replace("\"", "");
+ }
+ }
+
+ else if (line.startsWith("msgid"))
+ {
+ if (!multiline.isEmpty()) {
+ context.append(multiline);
+ multiline.clear();
+ }
+ if (!line.endsWith("\"\"")) {
+ line.remove("msgid \"");
+ id = line.replace("\"", "");
+ }
+ }
+
+ else if (line.startsWith("msgstr"))
+ {
+ if (!multiline.isEmpty()) {
+ id.append(multiline);
+ multiline.clear();
+ }
+ if (!line.endsWith("\"\"")) {
+ line.remove("msgstr \"");
+ translation = line.replace("\"", "");
+ }
+ }
+
+ else if (line.startsWith("\""))
+ {
+ if (!line.endsWith("\"\"")) {
+ line.remove("\"");
+ multiline.append(line.replace("\"", ""));
+ }
+ }
+
+ else if (line.isEmpty()) {
+ translation.append(multiline);
+ multiline.clear();
+ if (!id.isEmpty()) {
+ if (context.isEmpty()) {
+ table.insert(id, translation);
+ } else {
+ table.insert(context, translation);
+ }
+ }
+ context.clear();
+ id.clear();
+ translation.clear();
+ } else {
+ multiline.clear();
+ context.clear();
+ id.clear();
+ translation.clear();
+ }
+
+ }
+
+ AdvancedComicBookFormat::Document* acbf = qobject_cast(acbfData());
+ acbf->metaData()->bookInfo()->addLanguage(language);
+ if (!table.value("@meta-title").isEmpty()) {
+ acbf->metaData()->bookInfo()->setTitle(table.value("@meta-title"), language);
+ qDebug() << Q_FUNC_INFO << "adding translation" << language << acbf->metaData()->bookInfo()->title(language);
+ }
+ if (!table.value("@meta-summary").isEmpty()) {
+ QStringList paragraphs = table.value("@meta-summary").split("
", QString::SkipEmptyParts);
+ for (int i=0; i < paragraphs.size(); i++) {
+ QString p = paragraphs.at(i);
+ p.replace("\\\"", "\"");
+ p.replace("\\\'", "\'");
+ p.replace("\\#", "#");
+ paragraphs.replace(i, p.replace("", "").trimmed());
+ }
+ acbf->metaData()->bookInfo()->setAnnotation(paragraphs, language);
+ qDebug() << Q_FUNC_INFO << "adding translation" << language << paragraphs;
+ }
+ if (!table.value("@meta-keywords").isEmpty()) {
+ QStringList keys = table.value("@meta-keywords").split(",", QString::SkipEmptyParts);
+ for (int i=0; i < keys.size(); i++) {
+ QString p = keys.at(i);
+ keys.replace(i, p.trimmed());
+ }
+ qDebug() << Q_FUNC_INFO << "adding translation" << language << keys;
+ acbf->metaData()->bookInfo()->setKeywords(keys, language);
+ }
+
+ for (int p = 0; p < acbf->body()->pageCount(); p++) {
+ acbf->body()->page(p)->duplicateTextLayer("", language);
+ if (!table.value(acbf->body()->page(p)->title("")).isEmpty()) {
+ acbf->body()->page(p)->setTitle(table.value(acbf->body()->page(p)->title("")), language);
+ qDebug() << Q_FUNC_INFO << "adding translation" << language << acbf->body()->page(p)->title(language);
+ }
+ AdvancedComicBookFormat::Textlayer* translation = acbf->body()->page(p)->textLayer(language);
+ for (int t=0; t < translation->textareaPointStrings().size(); t++) {
+ QString key;
+ for (int i =0; i < translation->textarea(t)->paragraphs().size(); i++) {
+ QString paragraph = translation->textarea(t)->paragraphs().at(i);
+ paragraph.replace("\"", "\\\"");
+ paragraph.replace("\'", "\\\'");
+ paragraph.replace("#", "\\#");
+ key.append(paragraph);
+ }
+ QStringList paragraphs = table.value("
"+key+"
").split("", QString::SkipEmptyParts);
+ for (int i=0; i < paragraphs.size(); i++) {
+ QString p = paragraphs.at(i);
+ p.replace("\\\"", "\"");
+ p.replace("\\\'", "\'");
+ p.replace("\\#", "#");
+ paragraphs.replace(i, p.replace("", "").trimmed());
+ }
+ translation->textarea(t)->setParagraphs(paragraphs);
+ if (!paragraphs.isEmpty()) {
+ qDebug() << Q_FUNC_INFO << "adding translation" << language << translation->textarea(t)->paragraphs();
+ }
+ }
+ }
+ // Finally, add translator.
+ QStringList emails;
+ if (addTranslatorEmail) {
+ QString email = translator.split("<").at(1);
+ email.replace(">", "");
+ emails.append(email);
+ }
+ translator = translator.split("<").at(0);
+ qDebug() << Q_FUNC_INFO << "adding translator" << translator << emails;
+
+ if (acbf->metaData()->bookInfo()->authorNames().indexOf(translator) < 0) {
+ acbf->metaData()->bookInfo()->addAuthor("Translator", language, "", "", "", translator, QStringList(), emails);
+ }
+
+ }
+ file.close();
+ return success;
+}