diff --git a/src/catalog/gettextheader.cpp b/src/catalog/gettextheader.cpp index 3a488fc..c116b6b 100644 --- a/src/catalog/gettextheader.cpp +++ b/src/catalog/gettextheader.cpp @@ -1,673 +1,683 @@ /* **************************************************************************** This file is part of Lokalize Copyright (C) 2008-2014 by Nick Shaforostoff 2018-2019 by Simon Depiets This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **************************************************************************** */ #include "gettextheader.h" #include "lokalize_debug.h" #include "project.h" #include "version.h" #include "prefs_lokalize.h" #include "prefs.h" #include #include #include #include #include #include #include #include #include #include /** * this data was obtained by running GNUPluralForms() * on all languages KDE knows of **/ struct langPInfo { const char *lang; const char *plural; }; static const langPInfo langsWithPInfo[] = { { "ar", "nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;" }, { "be@latin", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" }, { "be", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" }, { "br", "nplurals=1; plural=0;" }, { "bs", "nplurals=3; plural=n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;" }, { "csb", "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2)" }, { "cs", "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;" }, { "da", "nplurals=2; plural=(n != 1);" }, { "de", "nplurals=2; plural=(n != 1);" }, { "el", "nplurals=2; plural=(n != 1);" }, { "en", "nplurals=2; plural=(n != 1);" }, { "en_GB", "nplurals=2; plural=(n != 1);" }, { "en_US", "nplurals=2; plural=(n != 1);" }, { "eo", "nplurals=2; plural=(n != 1);" }, { "es", "nplurals=2; plural=(n != 1);" }, { "et", "nplurals=2; plural=(n != 1);" }, { "fa", "nplurals=1; plural=0;" }, { "fi", "nplurals=2; plural=(n != 1);" }, { "fo", "nplurals=2; plural=(n != 1);" }, { "fr", "nplurals=2; plural=(n > 1);" }, { "ga", "nplurals=5; plural=n==1 ? 0 : n==2 ? 1 : n<7 ? 2 : n < 11 ? 3 : 4" }, { "gd", "nplurals=4; plural=(n==1 || n==11) ? 0 : (n==2 || n==12) ? 1 : (n > 2 && n < 20) ? 2 : 3;" }, { "gu", "nplurals=2; plural=(n!=1);" }, { "he", "nplurals=2; plural=(n != 1);" }, { "hi", "nplurals=2; plural=(n!=1);" }, { "hne", "nplurals=2; plural=(n!=1);" }, { "hr", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" }, { "hsb", "nplurals=4; plural=n%100==1 ? 0 : n%100==2 ? 1 : n%100==3 || n%100==4 ? 2 : 3;" }, { "hu", "nplurals=2; plural=(n != 1);" }, { "hy", "nplurals=4; plural=n==1 ? 3 : n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2;" }, { "id", "nplurals=2; plural=(n != 1);" }, { "it", "nplurals=2; plural=(n != 1);" }, { "ja", "nplurals=1; plural=0;" }, { "ka", "nplurals=1; plural=0;" }, { "kk", "nplurals=1; plural=0;" }, { "km", "nplurals=1; plural=0;" }, { "ko", "nplurals=1; plural=0;" }, { "lt", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && (n%100<10 || n%100>=20) ? 1 : 2);" }, { "lv", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n != 0 ? 1 : 2);" }, { "mai", "nplurals=2; plural=(n!=1);" }, { "mk", "nplurals=3; plural=n%10==1 ? 0 : n%10==2 ? 1 : 2;" }, { "mr", "nplurals=2; plural=(n!=1);" }, { "ms", "nplurals=2; plural=1;" }, { "nb", "nplurals=2; plural=(n != 1);" }, { "nl", "nplurals=2; plural=(n != 1);" }, { "nn", "nplurals=2; plural=(n != 1);" }, { "oc", "nplurals=2; plural=(n > 1);" }, { "or", "nplurals=2; plural=(n!=1);" }, { "pl", "nplurals=3; plural=(n==1 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" }, { "pt", "nplurals=2; plural=(n != 1);" }, { "pt_BR", "nplurals=2; plural=(n > 1);" }, { "ro", "nplurals=3; plural=n==1 ? 0 : (n==0 || (n%100 > 0 && n%100 < 20)) ? 1 : 2;" }, { "ru", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" }, { "sk", "nplurals=3; plural=(n==1) ? 0 : (n>=2 && n<=4) ? 1 : 2;" }, { "sl", "nplurals=4; plural=(n%100==1 ? 1 : n%100==2 ? 2 : n%100==3 || n%100==4 ? 3 : 0);" }, { "sr", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" }, { "sr@latin", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" }, { "sv", "nplurals=2; plural=(n != 1);" }, { "te", "nplurals=2; plural=(n != 1);" }, { "th", "nplurals=1; plural=0;" }, { "tr", "nplurals=2; plural=(n > 1);" }, { "ug", "nplurals=1; plural=0;" }, { "uk", "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);" }, { "uz", "nplurals=1; plural=0;" }, { "uz@cyrillic", "nplurals=1; plural=0;" }, { "vi", "nplurals=1; plural=0;" }, { "zh_CN", "nplurals=1; plural=0;" }, { "zh_HK", "nplurals=1; plural=0;" }, { "zh_TW", "nplurals=1; plural=0;" } }; static const size_t langsWithPInfoCount = sizeof(langsWithPInfo) / sizeof(langsWithPInfo[0]); int numberOfPluralFormsFromHeader(const QString& header) { QRegExp rxplural(QStringLiteral("Plural-Forms:\\s*nplurals=(.);")); if (rxplural.indexIn(header) == -1) return 0; bool ok; int result = rxplural.cap(1).toShort(&ok); return ok ? result : 0; } int numberOfPluralFormsForLangCode(const QString& langCode) { QString expr = GNUPluralForms(langCode); QRegExp rxplural(QStringLiteral("nplurals=(.);")); if (rxplural.indexIn(expr) == -1) return 0; bool ok; int result = rxplural.cap(1).toShort(&ok); return ok ? result : 0; } QString GNUPluralForms(const QString& lang) { QByteArray l(lang.toUtf8()); int i = langsWithPInfoCount; while (--i >= 0 && l != langsWithPInfo[i].lang) ; if (Q_LIKELY(i >= 0)) return QString::fromLatin1(langsWithPInfo[i].plural); i = langsWithPInfoCount; while (--i >= 0 && !l.startsWith(langsWithPInfo[i].lang)) ; if (Q_LIKELY(i >= 0)) return QString::fromLatin1(langsWithPInfo[i].plural); //BEGIN alternative // NOTE does this work under M$ OS? qCDebug(LOKALIZE_LOG) << "gonna call msginit"; QString def = QStringLiteral("nplurals=2; plural=n != 1;"); QStringList arguments; arguments << QLatin1String("-l") << lang << QLatin1String("-i") << QLatin1String("-") << QLatin1String("-o") << QLatin1String("-") << QLatin1String("--no-translator") << QLatin1String("--no-wrap"); QProcess msginit; msginit.start(QLatin1String("msginit"), arguments); msginit.waitForStarted(5000); if (Q_UNLIKELY(msginit.state() != QProcess::Running)) { //qCWarning(LOKALIZE_LOG)<<"msginit error"; return def; } msginit.write( "# SOME DESCRIPTIVE TITLE.\n" "# Copyright (C) YEAR Free Software Foundation, Inc.\n" "# FIRST AUTHOR , YEAR.\n" "#\n" "#, fuzzy\n" "msgid \"\"\n" "msgstr \"\"\n" "\"Project-Id-Version: PACKAGE VERSION\\n\"\n" "\"POT-Creation-Date: 2002-06-25 03:23+0200\\n\"\n" "\"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n\"\n" "\"Last-Translator: FULL NAME \\n\"\n" "\"Language-Team: LANGUAGE \\n\"\n" "\"Language: LL\\n\"\n" "\"MIME-Version: 1.0\\n\"\n" "\"Content-Type: text/plain; charset=UTF-8\\n\"\n" "\"Content-Transfer-Encoding: ENCODING\\n\"\n" // "\"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\\n\"\n" ); msginit.closeWriteChannel(); if (Q_UNLIKELY(!msginit.waitForFinished(5000))) { qCWarning(LOKALIZE_LOG) << "msginit error"; return def; } QByteArray result = msginit.readAll(); int pos = result.indexOf("Plural-Forms: "); if (Q_UNLIKELY(pos == -1)) { //qCWarning(LOKALIZE_LOG)<<"msginit error"<'); temp = QStringLiteral("Last-Translator: ") % authorNameEmail % BACKSLASH_N; QRegExp lt(QStringLiteral("^ *Last-Translator:.*")); for (it = headerList.begin(), found = false; it != headerList.end() && !found; ++it) { if (it->contains(lt)) { if (forSaving) *it = temp; found = true; } } if (Q_UNLIKELY(!found)) headerList.append(temp); QLocale cLocale(QLocale::C); QString dateTimeString = cLocale.toString(QDateTime::currentDateTime(), QStringLiteral("yyyy-MM-dd hh:mm")); const int offset_seconds = QDateTime::currentDateTime().offsetFromUtc(); const int offset_hours = abs(offset_seconds) / 3600; const int offset_minutes = abs(offset_seconds % 3600) / 60; QString zoneOffsetString = (offset_seconds >= 0 ? '+' : '-') % (offset_hours < 10 ? QStringLiteral("0") : QStringLiteral("")) % QString::number(offset_hours) % (offset_minutes < 10 ? QStringLiteral("0") : QStringLiteral("")) % QString::number(offset_minutes); temp = QStringLiteral("PO-Revision-Date: ") % dateTimeString % zoneOffsetString % BACKSLASH_N; QRegExp poRevDate(QStringLiteral("^ *PO-Revision-Date:.*")); for (it = headerList.begin(), found = false; it != headerList.end() && !found; ++it) { found = it->contains(poRevDate); if (found && forSaving) *it = temp; } if (Q_UNLIKELY(!found)) headerList.append(temp); temp = QStringLiteral("Project-Id-Version: ") % CatalogProjectId % BACKSLASH_N; //temp.replace( "@PACKAGE@", packageName()); QRegExp projectIdVer(QStringLiteral("^ *Project-Id-Version:.*")); for (it = headerList.begin(), found = false; it != headerList.end() && !found; ++it) { found = it->contains(projectIdVer); if (found && it->contains(QLatin1String("PACKAGE VERSION"))) *it = temp; } if (Q_UNLIKELY(!found)) headerList.append(temp); langCode = Project::instance()->isLoaded() ? Project::instance()->langCode() : Settings::defaultLangCode(); QString language; //initialized with preexisting value or later QString mailingList; //initialized with preexisting value or later static QMap langEnums; if (!langEnums.size()) for (int l = QLocale::Abkhazian; l <= QLocale::Akoose; ++l) langEnums[QLocale::languageToString((QLocale::Language)l)] = (QLocale::Language)l; static QRegExp langTeamRegExp(QStringLiteral("^ *Language-Team:.*")); for (it = headerList.begin(), found = false; it != headerList.end() && !found; ++it) { found = it->contains(langTeamRegExp); if (found) { //really parse header QRegExp re(QStringLiteral("^ *Language-Team: *(.*) *<([^>]*)>")); if (re.indexIn(*it) != -1) { if (langEnums.contains(re.cap(1).trimmed())) { language = re.cap(1).trimmed(); mailingList = re.cap(2).trimmed(); QList locales = QLocale::matchingLocales(langEnums.value(language), QLocale::AnyScript, QLocale::AnyCountry); if (locales.size()) langCode = locales.first().name().left(2); } } ait = it; } } if (language.isEmpty()) { language = QLocale::languageToString(QLocale(langCode).language()); if (language.isEmpty()) language = langCode; } if (mailingList.isEmpty() || belongsToProject) { if (Project::instance()->isLoaded()) mailingList = Project::instance()->mailingList(); else //if (mailingList.isEmpty()) mailingList = Settings::defaultMailingList(); } - temp = QStringLiteral("Language-Team: ") % language % QStringLiteral(" <") % mailingList % QStringLiteral(">\\n"); + Project::LangSource projLangSource = Project::instance()->languageSource(); + QString projLT = Project::instance()->projLangTeam(); + if (projLangSource == Project::LangSource::Project) { + temp = QStringLiteral("Language-Team: ")%projLT%QStringLiteral("\\n"); + } + else if ((projLangSource == Project::LangSource::Application) && (Settings::overrideLangTeam())) { + temp = QStringLiteral("Language-Team: ")%Settings::userLangTeam()%QStringLiteral("\\n"); + } + else { + temp = QStringLiteral("Language-Team: ")%language%QStringLiteral(" <")%mailingList%QStringLiteral(">\\n"); + } if (Q_LIKELY(found)) (*ait) = temp; else headerList.append(temp); static QRegExp langCodeRegExp(QStringLiteral("^ *Language: *([^ \\\\]*)")); temp = QStringLiteral("Language: ") % langCode % BACKSLASH_N; for (it = headerList.begin(), found = false; it != headerList.end() && !found; ++it) { found = (langCodeRegExp.indexIn(*it) != -1); if (found && langCodeRegExp.cap(1).isEmpty()) *it = temp; //if (found) qCWarning(LOKALIZE_LOG)<<"got explicit lang code:"<contains(ctRe); if (found) *it = temp; } if (Q_UNLIKELY(!found)) headerList.append(temp); temp = QStringLiteral("Content-Transfer-Encoding: 8bit\\n"); QRegExp cteRe(QStringLiteral("^ *Content-Transfer-Encoding:.*")); for (it = headerList.begin(), found = false; it != headerList.end() && !found; ++it) found = it->contains(cteRe); if (!found) headerList.append(temp); // ensure MIME-Version header temp = QStringLiteral("MIME-Version: 1.0\\n"); QRegExp mvRe(QStringLiteral("^ *MIME-Version:")); for (it = headerList.begin(), found = false; it != headerList.end() && !found; ++it) { found = it->contains(mvRe); if (found) *it = temp; } if (Q_UNLIKELY(!found)) headerList.append(temp); //qCDebug(LOKALIZE_LOG)<<"testing for GNUPluralForms"; // update plural form header QRegExp pfRe(QStringLiteral("^ *Plural-Forms:")); for (it = headerList.begin(), found = false; it != headerList.end() && !found; ++it) found = it->contains(pfRe); if (found) { --it; //qCDebug(LOKALIZE_LOG)<<"GNUPluralForms found"; int num = numberOfPluralFormsFromHeader(header); if (!num) { if (generatedFromDocbook) num = 1; else { qCDebug(LOKALIZE_LOG) << "No plural form info in header, using project-defined one" << langCode; QString t = GNUPluralForms(langCode); //qCWarning(LOKALIZE_LOG)<<"generated: " << t; if (!t.isEmpty()) { static QRegExp pf(QStringLiteral("^ *Plural-Forms:\\s*nplurals.*\\\\n")); pf.setMinimal(true); temp = QStringLiteral("Plural-Forms: %1\\n").arg(t); it->replace(pf, temp); num = numberOfPluralFormsFromHeader(temp); } else { qCWarning(LOKALIZE_LOG) << "no... smth went wrong :(\ncheck your gettext install"; num = 2; } } } numberOfPluralForms = num; } else if (!generatedFromDocbook) { //qCDebug(LOKALIZE_LOG)<<"generating GNUPluralForms"<contains(xgRe); if (found) *it = temp; } if (Q_UNLIKELY(!found)) headerList.append(temp); //m_header.setMsgstr( headerList.join( "\n" ) ); header = headerList.join(QStringLiteral("\n")); //END header itself //BEGIN comment = description, copyrights // U+00A9 is the Copyright sign QRegExp fsfc(QStringLiteral("^# *Copyright (\\(C\\)|\\x00a9).*Free Software Foundation, Inc")); for (it = commentList.begin(), found = false; it != commentList.end() && !found; ++it) { found = it->contains(fsfc) ; if (found) it->replace(QStringLiteral("YEAR"), cLocale.toString(QDate::currentDate(), QStringLiteral("yyyy"))); } /* if( saveOptions.FSFCopyright == ProjectSettingsBase::Update ) { //update years QString cy = cLocale.toString(QDate::currentDate(), "yyyy"); if( !it->contains( QRegExp(cy)) ) // is the year already included? { int index = it->lastIndexOf( QRegExp("[\\d]+[\\d\\-, ]*") ); if( index == -1 ) { KMessageBox::information(nullptr,i18n("Free Software Foundation Copyright does not contain any year. " "It will not be updated.")); } else { it->insert(index+1, QString(", ")+cy); } } }*/ #if 0 if ((!usePrefs || saveOptions.updateDescription) && (!saveOptions.descriptionString.isEmpty())) { temp = "# " + saveOptions.descriptionString; temp.replace("@PACKAGE@", packageName()); temp.replace("@LANGUAGE@", identityOptions.languageName); temp = temp.trimmed(); // The description strings has often buggy variants already in the file, these must be removed QString regexpstr = "^#\\s+" + QRegExp::escape(saveOptions.descriptionString.trimmed()) + "\\s*$"; regexpstr.replace("@PACKAGE@", ".*"); regexpstr.replace("@LANGUAGE@", ".*"); //qCDebug(LOKALIZE_LOG) << "REGEXPSTR: " << regexpstr; QRegExp regexp(regexpstr); // The buggy variants exist in English too (of a time before KBabel got a translation for the corresponding language) QRegExp regexpUntranslated("^#\\s+translation of .* to .*\\s*$"); qCDebug(LOKALIZE_LOG) << "Temp is '" << temp << "'"; found = false; bool foundTemplate = false; it = commentList.begin(); while (it != commentList.end()) { qCDebug(LOKALIZE_LOG) << "testing '" << (*it) << "'"; bool deleteItem = false; if ((*it) == temp) { qCDebug(LOKALIZE_LOG) << "Match "; if (found) deleteItem = true; else found = true; } else if (regexp.indexIn(*it) >= 0) { // We have a similar (translated) string (from another project or another language (perhaps typos)). Remove it. deleteItem = true; } else if (regexpUntranslated.indexIn(*it) >= 0) { // We have a similar (untranslated) string (from another project or another language (perhaps typos)). Remove it. deleteItem = true; } else if ((*it) == "# SOME DESCRIPTIVE TITLE.") { // We have the standard title placeholder, remove it deleteItem = true; } if (deleteItem) it = commentList.erase(it); else ++it; } if (!found) commentList.prepend(temp); } #endif // qCDebug(LOKALIZE_LOG) << "HEADER COMMENT: " << commentList; /* if ( (!usePrefs || saveOptions.updateTranslatorCopyright) && ( ! identityOptions->readEntry("authorName","").isEmpty() ) && ( ! identityOptions->readEntry("Email","").isEmpty() ) ) // An email address can be used as ersatz of a name {*/ // return; QStringList foundAuthors; temp = QStringLiteral("# ") % authorNameEmail % QStringLiteral(", ") % cLocale.toString(QDate::currentDate(), QStringLiteral("yyyy")) % '.'; // ### TODO: it would be nice if the entry could start with "COPYRIGHT" and have the "(C)" symbol (both not mandatory) QRegExp regexpAuthorYear(QStringLiteral("^#.*(<.+@.+>)?,\\s*([\\d]+[\\d\\-, ]*|YEAR)")); QRegExp regexpYearAlone(QStringLiteral("^# , \\d{4}.?\\s*$")); if (commentList.isEmpty()) { commentList.append(temp); commentList.append(QString()); } else { it = commentList.begin(); while (it != commentList.end()) { bool deleteItem = false; if (it->indexOf(QLatin1String("copyright"), 0, Qt::CaseInsensitive) != -1) { // We have a line with a copyright. It should not be moved. } else if (it->contains(QRegExp(QStringLiteral("#, *fuzzy")))) deleteItem = true; else if (it->contains(regexpYearAlone)) { // We have found a year number that is preceded by a comma. // That is typical of KBabel 1.10 (and earlier?) when there is neither an author name nor an email // Remove the entry deleteItem = true; } else if (it->contains(QLatin1String("# FIRST AUTHOR , YEAR."))) deleteItem = true; else if (it->contains(QLatin1String("# SOME DESCRIPTIVE TITLE"))) deleteItem = true; else if (it->contains(regexpAuthorYear)) { // email address followed by year if (!foundAuthors.contains((*it))) { // The author line is new (and not a duplicate), so add it to the author line list foundAuthors.append((*it)); } // Delete also non-duplicated entry, as now all what is needed will be processed in foundAuthors deleteItem = true; } if (deleteItem) it = commentList.erase(it); else ++it; } if (!foundAuthors.isEmpty()) { found = false; bool foundAuthor = false; const QString cy = cLocale.toString(QDate::currentDate(), QStringLiteral("yyyy")); ait = foundAuthors.end(); for (it = foundAuthors.begin() ; it != foundAuthors.end(); ++it) { if (it->contains(Settings::authorName()) || it->contains(Settings::authorEmail())) { foundAuthor = true; if (it->contains(cy)) found = true; else ait = it; } } if (!found) { if (!foundAuthor) foundAuthors.append(temp); else if (ait != foundAuthors.end()) { //update years const int index = (*ait).lastIndexOf(QRegExp(QStringLiteral("[\\d]+[\\d\\-, ]*"))); if (index == -1) (*ait) += QStringLiteral(", ") % cy; else ait->insert(index + 1, QStringLiteral(", ") % cy); } else qCDebug(LOKALIZE_LOG) << "INTERNAL ERROR: author found but iterator dangling!"; } } else foundAuthors.append(temp); foreach (QString author, foundAuthors) { // ensure dot at the end of copyright if (!author.endsWith(QLatin1Char('.'))) author += QLatin1Char('.'); commentList.append(author); } } //m_header.setComment( commentList.join( "\n" ) ); comment = commentList.join(QStringLiteral("\n")); //END comment = description, copyrights } QString fullUserName();// defined in helpers.cpp bool askAuthorInfoIfEmpty() { if (QThread::currentThread() == qApp->thread()) { if (Settings::authorName().isEmpty()) { bool ok; QString contact = QInputDialog::getText( SettingsController::instance()->mainWindowPtr(), i18nc("@window:title", "Author name missing"), i18n("Your name:"), QLineEdit::Normal, fullUserName(), &ok); Settings::self()->authorNameItem()->setValue(ok ? contact : fullUserName()); Settings::self()->save(); } if (Settings::authorEmail().isEmpty()) { bool ok; QString email = QInputDialog::getText( SettingsController::instance()->mainWindowPtr(), i18nc("@window:title", "Author email missing"), i18n("Your email:"), QLineEdit::Normal, QString(), &ok); if (ok) { Settings::self()->authorEmailItem()->setValue(email); Settings::self()->save(); } } } return !Settings::authorName().isEmpty() && !Settings::authorEmail().isEmpty(); } diff --git a/src/prefs/lokalize.kcfg b/src/prefs/lokalize.kcfg index fc0dc50..7435920 100644 --- a/src/prefs/lokalize.kcfg +++ b/src/prefs/lokalize.kcfg @@ -1,152 +1,166 @@ QLocale QFontDatabase kemailsettings.h kde-i18n-lists.h KEMailSettings().getSetting(KEMailSettings::RealName) KEMailSettings().getSetting(KEMailSettings::RealName) + + + false + + + + + KEMailSettings().getSetting(KEMailSettings::EmailAddress) QLocale::system().name() getMailingList() #99CCFF #FF9999 true QFontDatabase::systemFont(QFontDatabase::GeneralFont) false 0 true false kate %1:%2 true true false false 4 false false 7 0 true false false false posieve -u %u check-rules %f posieve -s lokalize check-rules %f diff --git a/src/prefs/prefs.cpp b/src/prefs/prefs.cpp index 00d0c3e..3cabe46 100644 --- a/src/prefs/prefs.cpp +++ b/src/prefs/prefs.cpp @@ -1,398 +1,406 @@ /* **************************************************************************** This file is part of Lokalize Copyright (C) 2007-2011 by Nick Shaforostoff 2018-2019 by Simon Depiets This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License or (at your option) version 3 or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 14 of version 3 of the license. 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . **************************************************************************** */ #include "prefs.h" #include "lokalize_debug.h" #include "prefs_lokalize.h" #include "project.h" #include "projectlocal.h" #include "projectmodel.h" #include "languagelistmodel.h" #include "dbfilesmodel.h" #include "ui_prefs_identity.h" #include "ui_prefs_editor.h" #include "ui_prefs_general.h" #include "ui_prefs_appearance.h" #include "ui_prefs_pology.h" #include "ui_prefs_tm.h" #include "ui_prefs_projectmain.h" #include "ui_prefs_project_advanced.h" #include "ui_prefs_project_local.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include //#include SettingsController* SettingsController::_instance = 0; void SettingsController::cleanupSettingsController() { delete SettingsController::_instance; SettingsController::_instance = 0; } SettingsController* SettingsController::instance() { if (_instance == 0) { _instance = new SettingsController; qAddPostRoutine(SettingsController::cleanupSettingsController); } return _instance; } SettingsController::SettingsController() : QObject(Project::instance()) , dirty(false) , m_projectActionsView(0) , m_mainWindowPtr(0) {} SettingsController::~SettingsController() {} void SettingsController::showSettingsDialog() { if (KConfigDialog::showDialog("lokalize_settings")) return; KConfigDialog *dialog = new KConfigDialog(m_mainWindowPtr, "lokalize_settings", Settings::self()); dialog->setFaceType(KPageDialog::List); // Identity QWidget *w = new QWidget(dialog); Ui_prefs_identity ui_prefs_identity; ui_prefs_identity.setupUi(w); KConfigGroup grp = Settings::self()->config()->group("Identity"); ui_prefs_identity.DefaultLangCode->setModel(LanguageListModel::instance()->sortModel()); ui_prefs_identity.DefaultLangCode->setCurrentIndex(LanguageListModel::instance()->sortModelRowForLangCode(grp.readEntry("DefaultLangCode", QLocale::system().name()))); connect(ui_prefs_identity.DefaultLangCode, QOverload::of(&KComboBox::activated), ui_prefs_identity.kcfg_DefaultLangCode, &LangCodeSaver::setLangCode); ui_prefs_identity.kcfg_DefaultLangCode->hide(); + connect(ui_prefs_identity.kcfg_overrideLangTeam, &QCheckBox::toggled, ui_prefs_identity.kcfg_userLangTeam, &QLineEdit::setEnabled); + connect(ui_prefs_identity.kcfg_overrideLangTeam, &QCheckBox::toggled, ui_prefs_identity.kcfg_userLangTeam, &QLineEdit::focusWidget); + dialog->addPage(w, i18nc("@title:tab", "Identity"), "preferences-desktop-user"); //General w = new QWidget(dialog); Ui_prefs_general ui_prefs_general; ui_prefs_general.setupUi(w); connect(ui_prefs_general.kcfg_CustomEditorEnabled, &QCheckBox::toggled, ui_prefs_general.kcfg_CustomEditorCommand, &QLineEdit::setEnabled); ui_prefs_general.kcfg_CustomEditorCommand->setEnabled(Settings::self()->customEditorEnabled()); dialog->addPage(w, i18nc("@title:tab", "General"), "preferences-system-windows"); //Editor w = new QWidget(dialog); Ui_prefs_editor ui_prefs_editor; ui_prefs_editor.setupUi(w); dialog->addPage(w, i18nc("@title:tab", "Editing"), "accessories-text-editor"); //Font w = new QWidget(dialog); Ui_prefs_appearance ui_prefs_appearance; ui_prefs_appearance.setupUi(w); dialog->addPage(w, i18nc("@title:tab", "Appearance"), "preferences-desktop-font"); //TM w = new QWidget(dialog); Ui_prefs_tm ui_prefs_tm; ui_prefs_tm.setupUi(w); dialog->addPage(w, i18nc("@title:tab", "Translation Memory"), "configure"); //Pology w = new QWidget(dialog); Ui_prefs_pology ui_prefs_pology; ui_prefs_pology.setupUi(w); dialog->addPage(w, i18nc("@title:tab", "Pology"), "preferences-desktop-filetype-association"); connect(dialog, &KConfigDialog::settingsChanged, this, &SettingsController::generalSettingsChanged); //Spellcheck #if 0 w = new Sonnet::ConfigWidget(Settings::self()->config(), dialog); w->setParent(this); dialog->addPage(w, i18nc("@title:tab", "Spellcheck"), "spellcheck_setting"); connect(dialog, SIGNAL(okClicked()), w, SLOT(save())); connect(dialog, SIGNAL(applyClicked()), w, SLOT(save())); connect(dialog, SIGNAL(defaultClicked()), w, SLOT(slotDefault())); #endif //connect(dialog,SIGNAL(settingsChanged(const QString&)),m_view, SLOT(settingsChanged())); dialog->show(); // dialog->addPage(new General(0, "General"), i18n("General") ); // dialog->addPage(new Appearance(0, "Style"), i18n("Appearance") ); // connect(dialog, SIGNAL(settingsChanged(const QString&)), mainWidget, SLOT(loadSettings())); // connect(dialog, SIGNAL(settingsChanged(const QString&)), this, SLOT(loadSettings())); } ScriptsView::ScriptsView(QWidget* parent): Kross::ActionCollectionView(parent) { setAcceptDrops(true); } void ScriptsView::dragEnterEvent(QDragEnterEvent* event) { if (!event->mimeData()->urls().isEmpty() && event->mimeData()->urls().first().path().endsWith(QLatin1String(".rc"))) event->accept(); } void ScriptsView::dropEvent(QDropEvent* event) { Kross::ActionCollectionModel* scriptsModel = static_cast(model()); foreach (const QUrl& url, event->mimeData()->urls()) if (url.path().endsWith(QLatin1String(".rc"))) scriptsModel->rootCollection()->readXmlFile(url.path()); } bool SettingsController::ensureProjectIsLoaded() { if (Project::instance()->isLoaded()) return true; int answer = KMessageBox::questionYesNoCancel(m_mainWindowPtr, i18n("You have accessed a feature that requires a project to be loaded. Do you want to create a new project or open an existing project?"), QString(), KGuiItem(i18nc("@action", "New"), QIcon::fromTheme("document-new")), KGuiItem(i18nc("@action", "Open"), QIcon::fromTheme("project-open")) ); if (answer == KMessageBox::Yes) return projectCreate(); if (answer == KMessageBox::No) return !projectOpen().isEmpty(); return false; } QString SettingsController::projectOpen(QString path, bool doOpen) { if (path.isEmpty()) { //Project::instance()->model()->weaver()->suspend(); //KDE5PORT mutex if needed path = QFileDialog::getOpenFileName(m_mainWindowPtr, QString(), QDir::homePath()/*_catalog->url().directory()*/, i18n("Lokalize translation project (*.lokalize)")/*"text/x-lokalize-project"*/); //Project::instance()->model()->weaver()->resume(); } if (!path.isEmpty() && doOpen) Project::instance()->load(path); return path; } bool SettingsController::projectCreate() { //Project::instance()->model()->weaver()->suspend(); //KDE5PORT mutex if needed QString desirablePath = Project::instance()->desirablePath(); if (desirablePath.isEmpty()) desirablePath = QDir::homePath() + "/index.lokalize"; QString path = QFileDialog::getSaveFileName(m_mainWindowPtr, i18nc("@window:title", "Select folder with Gettext .po files to translate"), desirablePath, i18n("Lokalize translation project (*.lokalize)") /*"text/x-lokalize-project"*/); //Project::instance()->model()->weaver()->resume(); if (path.isEmpty()) return false; if (m_projectActionsView && m_projectActionsView->model()) { //ActionCollectionModel is known to be have bad for the usecase of reinitializing krossplugin m_projectActionsView->model()->deleteLater(); m_projectActionsView->setModel(nullptr); } //TODO ask-n-save QDir projectFolder = QFileInfo(path).absoluteDir(); QString projectId = projectFolder.dirName(); if (projectFolder.cdUp()) projectId = projectFolder.dirName() % '-' % projectId;; Project::instance()->load(path, QString(), projectId); //Project::instance()->setDefaults(); //NOTE will this be an obstacle? //Project::instance()->setProjectID(); QTimer::singleShot(500, this, &SettingsController::projectConfigure); return true; } void SettingsController::projectConfigure() { if (Project::instance()->path().isEmpty()) { KMessageBox::error(mainWindowPtr(), i18n("Create software or OpenDocument translation project first.")); return; } if (KConfigDialog::showDialog("project_settings")) { if (!m_projectActionsView->model()) m_projectActionsView->setModel(new Kross::ActionCollectionModel(m_projectActionsView, Kross::Manager::self().actionCollection()->collection(Project::instance()->kind()))); return; } KConfigDialog *dialog = new KConfigDialog(m_mainWindowPtr, "project_settings", Project::instance()); dialog->setFaceType(KPageDialog::List); // Main QWidget *w = new QWidget(dialog); Ui_prefs_projectmain ui_prefs_projectmain; ui_prefs_projectmain.setupUi(w); dialog->addPage(w, i18nc("@title:tab", "General"), "preferences-desktop-locale"); ui_prefs_projectmain.kcfg_LangCode->hide(); ui_prefs_projectmain.kcfg_PoBaseDir->hide(); ui_prefs_projectmain.kcfg_GlossaryTbx->hide(); Project& p = *(Project::instance()); ui_prefs_projectmain.LangCode->setModel(LanguageListModel::instance()->sortModel()); ui_prefs_projectmain.LangCode->setCurrentIndex(LanguageListModel::instance()->sortModelRowForLangCode(p.langCode())); connect(ui_prefs_projectmain.LangCode, QOverload::of(&KComboBox::activated), ui_prefs_projectmain.kcfg_LangCode, &LangCodeSaver::setLangCode); ui_prefs_projectmain.poBaseDir->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly); ui_prefs_projectmain.glossaryTbx->setMode(KFile::File | KFile::ExistingOnly | KFile::LocalOnly); ui_prefs_projectmain.glossaryTbx->setFilter("*.tbx\n*.xml"); connect(ui_prefs_projectmain.poBaseDir, &KUrlRequester::textChanged, ui_prefs_projectmain.kcfg_PoBaseDir, &RelPathSaver::setText); connect(ui_prefs_projectmain.glossaryTbx, &KUrlRequester::textChanged, ui_prefs_projectmain.kcfg_GlossaryTbx, &RelPathSaver::setText); ui_prefs_projectmain.poBaseDir->setUrl(QUrl::fromLocalFile(p.poDir())); ui_prefs_projectmain.glossaryTbx->setUrl(QUrl::fromLocalFile(p.glossaryPath())); + auto kcfg_ProjLangTeam = ui_prefs_projectmain.kcfg_ProjLangTeam; + connect(ui_prefs_projectmain.kcfg_LanguageSource, static_cast(&KComboBox::currentIndexChanged), + this, [kcfg_ProjLangTeam](int index) { kcfg_ProjLangTeam->setEnabled(static_cast(index) == Project::LangSource::Project); }); + connect(ui_prefs_projectmain.kcfg_LanguageSource, static_cast(&KComboBox::currentIndexChanged), + this, [kcfg_ProjLangTeam] { kcfg_ProjLangTeam->setFocus(); }); // RegExps w = new QWidget(dialog); Ui_project_advanced ui_project_advanced; ui_project_advanced.setupUi(w); ui_project_advanced.kcfg_PotBaseDir->hide(); ui_project_advanced.kcfg_BranchDir->hide(); ui_project_advanced.kcfg_AltDir->hide(); ui_project_advanced.potBaseDir->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly); ui_project_advanced.branchDir->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly); ui_project_advanced.altDir->setMode(KFile::Directory | KFile::ExistingOnly | KFile::LocalOnly); connect(ui_project_advanced.potBaseDir, &KUrlRequester::textChanged, ui_project_advanced.kcfg_PotBaseDir, &RelPathSaver::setText); connect(ui_project_advanced.branchDir, &KUrlRequester::textChanged, ui_project_advanced.kcfg_BranchDir, &RelPathSaver::setText); connect(ui_project_advanced.altDir, &KUrlRequester::textChanged, ui_project_advanced.kcfg_AltDir, &RelPathSaver::setText); ui_project_advanced.potBaseDir->setUrl(QUrl::fromLocalFile(p.potDir())); ui_project_advanced.branchDir->setUrl(QUrl::fromLocalFile(p.branchDir())); ui_project_advanced.altDir->setUrl(QUrl::fromLocalFile(p.altTransDir())); dialog->addPage(w, i18nc("@title:tab", "Advanced"), "applications-development-translation"); //Scripts w = new QWidget(dialog); QVBoxLayout* layout = new QVBoxLayout(w); layout->setSpacing(6); layout->setMargin(11); //m_projectActionsEditor=new Kross::ActionCollectionEditor(Kross::Manager::self().actionCollection()->collection(Project::instance()->projectID()),w); m_projectActionsView = new ScriptsView(w); layout->addWidget(m_projectActionsView); m_projectActionsView->setModel(new Kross::ActionCollectionModel(w, Kross::Manager::self().actionCollection()->collection(Project::instance()->kind()))); QHBoxLayout* btns = new QHBoxLayout(); layout->addLayout(btns); btns->addWidget(m_projectActionsView->createButton(w, "edit")); dialog->addPage(w, i18nc("@title:tab", "Scripts"), "preferences-system-windows-actions"); w = new QWidget(dialog); Ui_prefs_project_local ui_prefs_project_local; ui_prefs_project_local.setupUi(w); dialog->addPage(w, Project::local(), i18nc("@title:tab", "Personal"), "preferences-desktop-user"); connect(dialog, &KConfigDialog::settingsChanged, Project::instance(), &Project::reinit); connect(dialog, &KConfigDialog::settingsChanged, Project::instance(), &Project::save, Qt::QueuedConnection); connect(dialog, &KConfigDialog::settingsChanged, TM::DBFilesModel::instance(), &TM::DBFilesModel::updateProjectTmIndex); connect(dialog, &KConfigDialog::settingsChanged, this, &SettingsController::reflectProjectConfigChange); dialog->show(); } void SettingsController::reflectProjectConfigChange() { //TODO check target language change: reflect changes in TM and glossary TM::DBFilesModel::instance()->openDB(Project::instance()->projectID()); } void SettingsController::reflectRelativePathsHack() { //m_scriptsRelPrefWidget->clear(); QStringList actionz(m_scriptsPrefWidget->items()); QString projectDir(Project::instance()->projectDir()); int i = actionz.size(); while (--i >= 0) actionz[i] = QDir(projectDir).relativeFilePath(actionz.at(i)); m_scriptsRelPrefWidget->setItems(actionz); } void LangCodeSaver::setLangCode(int index) { setText(LanguageListModel::instance()->langCodeForSortModelRow(index)); } void RelPathSaver::setText(const QString& txt) { QLineEdit::setText(QDir(Project::instance()->projectDir()).relativeFilePath(txt)); } void writeUiState(const char* elementName, const QByteArray& state) { KConfig config; KConfigGroup cg(&config, "MainWindow"); cg.writeEntry(elementName, state.toBase64()); } QByteArray readUiState(const char* elementName) { KConfig config; KConfigGroup cg(&config, "MainWindow"); return QByteArray::fromBase64(cg.readEntry(elementName, QByteArray())); } diff --git a/src/prefs/prefs_identity.ui b/src/prefs/prefs_identity.ui index 3d74a08..c5b5d9b 100644 --- a/src/prefs/prefs_identity.ui +++ b/src/prefs/prefs_identity.ui @@ -1,170 +1,196 @@ prefs_identity 0 0 612 375 Fill in your identity and information about your translation team. This information is used when updating the header of a file. - + + 11 + + + 11 + + + 11 + + 11 6 Default language: false - Email: + &Email: false kcfg_authorEmail - Name: + &Name: false kcfg_authorName - + Your name, in English Please enter here your name and surname written in English - Default mailing list: + &Default mailing list: kcfg_DefaultMailingList - + The email of your team mailing list Write the email of your translating team mailing list - + Language you translate to Set the default language you are going to translate to - + Qt::Vertical 20 40 - + Write your email Write your email here so it will appear in the po file header with your name - + true - + Your name in your own language Write your name and surname in your language with your language alphabet. - + Localized name: + + + + Default Language-Team: + + + + + + + false + + + + + + KComboBox QComboBox
kcombobox.h
LangCodeSaver QLineEdit
prefs.h
kcfg_authorName kcfg_authorEmail DefaultLangCode kcfg_DefaultMailingList kcfg_authorLocalizedName kcfg_DefaultLangCode
diff --git a/src/project/prefs_projectmain.ui b/src/project/prefs_projectmain.ui index cf1245b..6507eff 100644 --- a/src/project/prefs_projectmain.ui +++ b/src/project/prefs_projectmain.ui @@ -1,187 +1,220 @@ prefs_projectmain 0 0 611 340 - - + + + + Glossary: + + - + Target language: + + + + + + + Project ID is used to differentiate translation memories of absolutely different projects and languages. + +For example, if you have several projects for translating KDE applications +(e.g. they are in different repositories), use same ID for all of them. + + + 400 0 Target language of the project. - - + + - + 0 0 400 16 - - This is main setting. Set this to path of a folder with translation files -for your project (or a subproject for your target language). + + + + + + Mailing list: - - + + + + ID: + + + + + + + + - + 0 0 400 16 + + This is main setting. Set this to path of a folder with translation files +for your project (or a subproject for your target language). + - + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + Root folder: false - - - Glossary: - - - - - - - Project ID is used to differentiate translation memories of absolutely different projects and languages. - -For example, if you have several projects for translating KDE applications -(e.g. they are in different repositories), use same ID for all of them. - + + + + Automatic + + + + + Application + + + + + Project + + - - - - ID: + + + + false - - - - - + + - Mailing list: + Language-Team: - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - KUrlRequester - QFrame + QWidget
kurlrequester.h
1
KComboBox QComboBox
kcombobox.h
- RelPathSaver + LangCodeSaver QLineEdit
prefs.h
- LangCodeSaver + RelPathSaver QLineEdit
prefs.h
kcfg_ProjectID LangCode kcfg_MailingList poBaseDir glossaryTbx LangCode currentIndexChanged(QString) kcfg_LangCode setText(QString) 361 48 359 190
diff --git a/src/project/projectbase.kcfg b/src/project/projectbase.kcfg index 2df707f..e3ac6fc 100644 --- a/src/project/projectbase.kcfg +++ b/src/project/projectbase.kcfg @@ -1,67 +1,94 @@ kde-i18n-lists.h default kde en_US getMailingList() + + + + + + + false + + + + + + + + + + + + + Application + ./ ../templates ./terms.tbx ./main.lqa & (<[^>]+>)+|(&[A-Za-z_:][A-Za-z0-9_\.:-]*;|%[0-9])+ 80 diff --git a/src/project/projectbase.kcfgc b/src/project/projectbase.kcfgc index 289fd81..2999109 100644 --- a/src/project/projectbase.kcfgc +++ b/src/project/projectbase.kcfgc @@ -1,5 +1,6 @@ File=projectbase.kcfg ClassName=ProjectBase Singleton=false Mutators=true GlobalEnums=true +UseEnumTypes=true