Browse Source

Merge pull request #9026 from glassez/www-translate

Use independent translation for WebUI
adaptive-webui-19844
Vladimir Golovnev 6 years ago committed by GitHub
parent
commit
5d2e35d14d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 1
      .gitignore
  2. 15
      src/lang/lang.pri
  3. 1
      src/webui/CMakeLists.txt
  4. 97
      src/webui/extra_translations.h
  5. 88
      src/webui/webapplication.cpp
  6. 4
      src/webui/webapplication.h
  7. 23
      src/webui/webui.pri
  8. 44
      src/webui/www/private/index.html
  9. 16
      src/webui/www/private/preferences_content.html
  10. 1846
      src/webui/www/translations/webui_ar.ts
  11. 1846
      src/webui/www/translations/webui_be.ts
  12. 1846
      src/webui/www/translations/webui_bg.ts
  13. 1846
      src/webui/www/translations/webui_ca.ts
  14. 1846
      src/webui/www/translations/webui_cs.ts
  15. 1846
      src/webui/www/translations/webui_da.ts
  16. 1846
      src/webui/www/translations/webui_de.ts
  17. 1846
      src/webui/www/translations/webui_el.ts
  18. 1846
      src/webui/www/translations/webui_en.ts
  19. 1846
      src/webui/www/translations/webui_en_AU.ts
  20. 1846
      src/webui/www/translations/webui_en_GB.ts
  21. 1846
      src/webui/www/translations/webui_eo.ts
  22. 1846
      src/webui/www/translations/webui_es.ts
  23. 1846
      src/webui/www/translations/webui_eu.ts
  24. 1846
      src/webui/www/translations/webui_fi.ts
  25. 1846
      src/webui/www/translations/webui_fr.ts
  26. 1846
      src/webui/www/translations/webui_gl.ts
  27. 1846
      src/webui/www/translations/webui_he.ts
  28. 1846
      src/webui/www/translations/webui_hi_IN.ts
  29. 1846
      src/webui/www/translations/webui_hr.ts
  30. 1846
      src/webui/www/translations/webui_hu.ts
  31. 1846
      src/webui/www/translations/webui_hy.ts
  32. 1846
      src/webui/www/translations/webui_id.ts
  33. 1846
      src/webui/www/translations/webui_is.ts
  34. 1846
      src/webui/www/translations/webui_it.ts
  35. 1846
      src/webui/www/translations/webui_ja.ts
  36. 1846
      src/webui/www/translations/webui_ka.ts
  37. 1846
      src/webui/www/translations/webui_ko.ts
  38. 1846
      src/webui/www/translations/webui_lt.ts
  39. 1846
      src/webui/www/translations/webui_lv_LV.ts
  40. 1846
      src/webui/www/translations/webui_ms_MY.ts
  41. 1846
      src/webui/www/translations/webui_nb.ts
  42. 1846
      src/webui/www/translations/webui_nl.ts
  43. 1846
      src/webui/www/translations/webui_oc.ts
  44. 1846
      src/webui/www/translations/webui_pl.ts
  45. 1846
      src/webui/www/translations/webui_pt_BR.ts
  46. 1846
      src/webui/www/translations/webui_pt_PT.ts
  47. 1846
      src/webui/www/translations/webui_ro.ts
  48. 1846
      src/webui/www/translations/webui_ru.ts
  49. 1846
      src/webui/www/translations/webui_sk.ts
  50. 1846
      src/webui/www/translations/webui_sl.ts
  51. 1846
      src/webui/www/translations/webui_sr.ts
  52. 1846
      src/webui/www/translations/webui_sv.ts
  53. 1846
      src/webui/www/translations/webui_tr.ts
  54. 1846
      src/webui/www/translations/webui_uk.ts
  55. 1846
      src/webui/www/translations/webui_uz@Latn.ts
  56. 1846
      src/webui/www/translations/webui_vi.ts
  57. 1846
      src/webui/www/translations/webui_zh.ts
  58. 1846
      src/webui/www/translations/webui_zh_HK.ts
  59. 1846
      src/webui/www/translations/webui_zh_TW.ts
  60. 181
      src/webui/www/tstool.py
  61. 50
      src/webui/www/webui.qrc

1
.gitignore vendored

@ -22,6 +22,7 @@ qrc_*.cpp @@ -22,6 +22,7 @@ qrc_*.cpp
ui_*.h
*.moc
src/lang/qbittorrent_*.qm
src/webui/www/translations/webui_*.qm
.DS_Store
.qmake.stash
src/qbittorrent.app

15
src/lang/lang.pri

@ -7,14 +7,15 @@ for(file, TS_FILES) { @@ -7,14 +7,15 @@ for(file, TS_FILES) {
}
isEmpty(QMAKE_LRELEASE) {
win32:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]\\lrelease.exe
else:QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease
win32: QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease.exe
else: QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease
unix {
equals(QT_MAJOR_VERSION, 5) {
!exists($$QMAKE_LRELEASE) { QMAKE_LRELEASE = lrelease-qt5 }
}
} else {
!exists($$QMAKE_LRELEASE) { QMAKE_LRELEASE = lrelease }
equals(QT_MAJOR_VERSION, 5) {
!exists($$QMAKE_LRELEASE): QMAKE_LRELEASE = lrelease-qt5
}
}
else {
!exists($$QMAKE_LRELEASE): QMAKE_LRELEASE = lrelease
}
}

1
src/webui/CMakeLists.txt

@ -11,7 +11,6 @@ api/synccontroller.h @@ -11,7 +11,6 @@ api/synccontroller.h
api/torrentscontroller.h
api/transfercontroller.h
api/serialize/serialize_torrent.h
extra_translations.h
webapplication.h
webui.h

97
src/webui/extra_translations.h

@ -1,97 +0,0 @@ @@ -1,97 +0,0 @@
/*
* Bittorrent Client using Qt and libtorrent.
* Copyright (C) 2014 Vladimir Golovnev <glassez@yandex.ru>
*
* 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) 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* In addition, as a special exception, the copyright holders give permission to
* link this program with the OpenSSL project's "OpenSSL" library (or with
* modified versions of it that use the same license as the "OpenSSL" library),
* and distribute the linked executables. You must obey the GNU General Public
* License in all respects for all of the code used other than "OpenSSL". If you
* modify file(s), you may extend this exception to your version of the file(s),
* but you are not obligated to do so. If you do not wish to do so, delete this
* exception statement from your version.
*/
#ifndef EXTRA_TRANSLATIONS_H
#define EXTRA_TRANSLATIONS_H
#include <QObject>
// Additional translations for Web UI
const char *QBT_WEBUI_TRANSLATIONS[] = {
QT_TRANSLATE_NOOP("HttpServer", "Logout"),
QT_TRANSLATE_NOOP("HttpServer", "Exit qBittorrent"),
QT_TRANSLATE_NOOP("HttpServer", "Download Torrents from their URLs or Magnet links"),
QT_TRANSLATE_NOOP("HttpServer", "Only one link per line"),
QT_TRANSLATE_NOOP("HttpServer", "Upload local torrent"),
QT_TRANSLATE_NOOP("HttpServer", "Are you sure you want to delete the selected torrents from the transfer list?"),
QT_TRANSLATE_NOOP("HttpServer", "Global upload rate limit must be greater than 0 or disabled."),
QT_TRANSLATE_NOOP("HttpServer", "Global download rate limit must be greater than 0 or disabled."),
QT_TRANSLATE_NOOP("HttpServer", "Alternative upload rate limit must be greater than 0 or disabled."),
QT_TRANSLATE_NOOP("HttpServer", "Alternative download rate limit must be greater than 0 or disabled."),
QT_TRANSLATE_NOOP("HttpServer", "Maximum active downloads must be greater than -1."),
QT_TRANSLATE_NOOP("HttpServer", "Maximum active uploads must be greater than -1."),
QT_TRANSLATE_NOOP("HttpServer", "Maximum active torrents must be greater than -1."),
QT_TRANSLATE_NOOP("HttpServer", "Maximum number of connections limit must be greater than 0 or disabled."),
QT_TRANSLATE_NOOP("HttpServer", "Maximum number of connections per torrent limit must be greater than 0 or disabled."),
QT_TRANSLATE_NOOP("HttpServer", "Maximum number of upload slots per torrent limit must be greater than 0 or disabled."),
QT_TRANSLATE_NOOP("HttpServer", "Unable to save program preferences, qBittorrent is probably unreachable."),
QT_TRANSLATE_NOOP("HttpServer", "The port used for incoming connections must be between 1 and 65535."),
QT_TRANSLATE_NOOP("HttpServer", "The port used for the Web UI must be between 1 and 65535."),
QT_TRANSLATE_NOOP("HttpServer", "Save"),
QT_TRANSLATE_NOOP("HttpServer", "qBittorrent client is not reachable"),
QT_TRANSLATE_NOOP("HttpServer", "qBittorrent has been shutdown."),
QT_TRANSLATE_NOOP("HttpServer", "Unable to log in, qBittorrent is probably unreachable."),
QT_TRANSLATE_NOOP("HttpServer", "Invalid Username or Password."),
QT_TRANSLATE_NOOP("HttpServer", "Username"),
QT_TRANSLATE_NOOP("HttpServer", "Password"),
QT_TRANSLATE_NOOP("HttpServer", "Login"),
QT_TRANSLATE_NOOP("HttpServer", "Original authors"),
QT_TRANSLATE_NOOP("HttpServer", "Apply"),
QT_TRANSLATE_NOOP("HttpServer", "Add"),
QT_TRANSLATE_NOOP("HttpServer", "Save files to location:"),
QT_TRANSLATE_NOOP("HttpServer", "Cookie:"),
QT_TRANSLATE_NOOP("HttpServer", "Type folder here"),
QT_TRANSLATE_NOOP("HttpServer", "More information"),
QT_TRANSLATE_NOOP("HttpServer", "Information about certificates"),
QT_TRANSLATE_NOOP("HttpServer", "Save Files to"),
QT_TRANSLATE_NOOP("HttpServer", "IRC: #qbittorrent on Freenode"),
QT_TRANSLATE_NOOP("HttpServer", "Invalid category name:\nPlease do not use any special characters in the category name."),
QT_TRANSLATE_NOOP("HttpServer", "Unknown"),
QT_TRANSLATE_NOOP("HttpServer", "Hard Disk"),
QT_TRANSLATE_NOOP("HttpServer", "Share ratio limit must be between 0 and 9998."),
QT_TRANSLATE_NOOP("HttpServer", "Seeding time limit must be between 0 and 525600 minutes."),
QT_TRANSLATE_NOOP("HttpServer", "Set location"),
QT_TRANSLATE_NOOP("HttpServer", "Limit upload rate"),
QT_TRANSLATE_NOOP("HttpServer", "Limit download rate"),
QT_TRANSLATE_NOOP("HttpServer", "Rename torrent"),
QT_TRANSLATE_NOOP("HttpServer", "Unable to create category")
};
const struct { const char *source; const char *comment; } QBT_WEBUI_COMMENTED_TRANSLATIONS[] = {
QT_TRANSLATE_NOOP3("HttpServer", "Other...", "Save Files to: Watch Folder / Default Folder / Other..."),
QT_TRANSLATE_NOOP3("HttpServer", "Monday", "Schedule the use of alternative rate limits on ..."),
QT_TRANSLATE_NOOP3("HttpServer", "Tuesday", "Schedule the use of alternative rate limits on ..."),
QT_TRANSLATE_NOOP3("HttpServer", "Wednesday", "Schedule the use of alternative rate limits on ..."),
QT_TRANSLATE_NOOP3("HttpServer", "Thursday", "Schedule the use of alternative rate limits on ..."),
QT_TRANSLATE_NOOP3("HttpServer", "Friday", "Schedule the use of alternative rate limits on ..."),
QT_TRANSLATE_NOOP3("HttpServer", "Saturday", "Schedule the use of alternative rate limits on ..."),
QT_TRANSLATE_NOOP3("HttpServer", "Sunday", "Schedule the use of alternative rate limits on ..."),
QT_TRANSLATE_NOOP3("HttpServer", "Upload Torrents", "Upload torrent files to qBittorent using WebUI")
};
#endif // EXTRA_TRANSLATIONS_H

88
src/webui/webapplication.cpp

@ -34,7 +34,6 @@ @@ -34,7 +34,6 @@
#include <stdexcept>
#include <vector>
#include <QCoreApplication>
#include <QDateTime>
#include <QDebug>
#include <QFile>
@ -91,46 +90,6 @@ namespace @@ -91,46 +90,6 @@ namespace
return ret;
}
void translateDocument(const QString &locale, QString &data)
{
const QRegularExpression regex("QBT_TR\\((([^\\)]|\\)(?!QBT_TR))+)\\)QBT_TR(\\[CONTEXT=([a-zA-Z_][a-zA-Z0-9_]*)\\])");
const QRegularExpression mnemonic("\\(?&([a-zA-Z]?\\))?");
const bool isTranslationNeeded = !locale.startsWith("en")
|| locale.startsWith("en_AU") || locale.startsWith("en_GB");
int i = 0;
bool found = true;
while (i < data.size() && found) {
QRegularExpressionMatch regexMatch;
i = data.indexOf(regex, i, &regexMatch);
if (i >= 0) {
const QString word = regexMatch.captured(1);
const QString context = regexMatch.captured(4);
QString translation = isTranslationNeeded
? qApp->translate(context.toUtf8().constData(), word.toUtf8().constData(), nullptr, 1)
: word;
// Remove keyboard shortcuts
translation.remove(mnemonic);
// Use HTML code for quotes to prevent issues with JS
translation.replace('\'', "&#39;");
translation.replace('\"', "&#34;");
data.replace(i, regexMatch.capturedLength(), translation);
i += translation.length();
}
else {
found = false; // no more translatable strings
}
data.replace(QLatin1String("${LANG}"), locale.left(2));
data.replace(QLatin1String("${VERSION}"), QBT_VERSION);
}
}
inline QUrl urlFromHostHeader(const QString &hostHeader)
{
if (!hostHeader.contains(QLatin1String("://")))
@ -234,6 +193,43 @@ void WebApplication::sendWebUIFile() @@ -234,6 +193,43 @@ void WebApplication::sendWebUIFile()
sendFile(localPath);
}
void WebApplication::translateDocument(QString &data)
{
const QRegularExpression regex("QBT_TR\\((([^\\)]|\\)(?!QBT_TR))+)\\)QBT_TR\\[CONTEXT=([a-zA-Z_][a-zA-Z0-9_]*)\\]");
const bool isTranslationNeeded = !m_currentLocale.startsWith("en")
|| m_currentLocale.startsWith("en_AU") || m_currentLocale.startsWith("en_GB")
|| !m_translator.isEmpty();
int i = 0;
bool found = true;
while (i < data.size() && found) {
QRegularExpressionMatch regexMatch;
i = data.indexOf(regex, i, &regexMatch);
if (i >= 0) {
const QString word = regexMatch.captured(1);
const QString context = regexMatch.captured(3);
QString translation = isTranslationNeeded
? m_translator.translate(context.toUtf8().constData(), word.toUtf8().constData(), nullptr, 1)
: word;
// Use HTML code for quotes to prevent issues with JS
translation.replace('\'', "&#39;");
translation.replace('\"', "&#34;");
data.replace(i, regexMatch.capturedLength(), translation);
i += translation.length();
}
else {
found = false; // no more translatable strings
}
data.replace(QLatin1String("${LANG}"), m_currentLocale.left(2));
data.replace(QLatin1String("${VERSION}"), QBT_VERSION);
}
}
WebSession *WebApplication::session()
{
return m_currentSession;
@ -429,6 +425,14 @@ void WebApplication::configure() @@ -429,6 +425,14 @@ void WebApplication::configure()
if (m_currentLocale != newLocale) {
m_currentLocale = newLocale;
m_translatedFiles.clear();
if (m_translator.load(m_rootFolder + QLatin1String("/translations/webui_") + m_currentLocale)) {
LogMsg(tr("WebUI translation for selected locale (%1) is successfully loaded.")
.arg(m_currentLocale));
}
else {
LogMsg(tr("Couldn't load WebUI translation for selected locale (%1). Falling back to default (en).")
.arg(m_currentLocale), Log::WARNING);
}
}
m_isLocalAuthEnabled = pref->isWebUiLocalAuthEnabled();
@ -490,7 +494,7 @@ void WebApplication::sendFile(const QString &path) @@ -490,7 +494,7 @@ void WebApplication::sendFile(const QString &path)
// Translate the file
if (isTranslatable) {
QString dataStr {data};
translateDocument(m_currentLocale, dataStr);
translateDocument(dataStr);
data = dataStr.toUtf8();
m_translatedFiles[path] = {data, lastModified}; // caching translated file

4
src/webui/webapplication.h

@ -34,6 +34,7 @@ @@ -34,6 +34,7 @@
#include <QObject>
#include <QRegularExpression>
#include <QSet>
#include <QTranslator>
#include "api/isessionmanager.h"
#include "base/http/irequesthandler.h"
@ -109,6 +110,8 @@ private: @@ -109,6 +110,8 @@ private:
void sendFile(const QString &path);
void sendWebUIFile();
void translateDocument(QString &data);
// Session management
QString generateSid() const;
void sessionInitialize();
@ -142,6 +145,7 @@ private: @@ -142,6 +145,7 @@ private:
};
QMap<QString, TranslatedFile> m_translatedFiles;
QString m_currentLocale;
QTranslator m_translator;
bool m_isLocalAuthEnabled;
bool m_isAuthSubnetWhitelistEnabled;

23
src/webui/webui.pri

@ -10,7 +10,6 @@ HEADERS += \ @@ -10,7 +10,6 @@ HEADERS += \
$$PWD/api/torrentscontroller.h \
$$PWD/api/transfercontroller.h \
$$PWD/api/serialize/serialize_torrent.h \
$$PWD/extra_translations.h \
$$PWD/webapplication.h \
$$PWD/webui.h
@ -29,3 +28,25 @@ SOURCES += \ @@ -29,3 +28,25 @@ SOURCES += \
$$PWD/webui.cpp
RESOURCES += $$PWD/www/webui.qrc
# WebUI Translation
isEmpty(QMAKE_LRELEASE) {
win32: QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease.exe
else: QMAKE_LRELEASE = $$[QT_INSTALL_BINS]/lrelease
unix {
equals(QT_MAJOR_VERSION, 5) {
!exists($$QMAKE_LRELEASE): QMAKE_LRELEASE = lrelease-qt5
}
}
else {
!exists($$QMAKE_LRELEASE): QMAKE_LRELEASE = lrelease
}
}
WEBUI_TRANSLATIONS = $$files(www/translations/webui_*.ts)
WEBUI_TRANSLATIONS_NOEXT = $$replace(WEBUI_TRANSLATIONS, ".ts", "")
message("Building WebUI translations...")
for(L, WEBUI_TRANSLATIONS_NOEXT) {
message("Processing $${L}")
system("$$QMAKE_LRELEASE -silent $${L}.ts -qm $${L}.qm")
!exists("$${L}.qm"): error("Building WebUI translations failed, cannot continue!")
}

44
src/webui/www/private/index.html

@ -37,22 +37,22 @@ @@ -37,22 +37,22 @@
<div id="desktopNavbar">
<ul>
<li>
<a class="returnFalse">QBT_TR(&File)QBT_TR[CONTEXT=MainWindow]</a>
<a class="returnFalse">QBT_TR(File)QBT_TR[CONTEXT=MainWindow]</a>
<ul>
<li><a id="uploadLink"><img class="MyMenuIcon" alt="QBT_TR(&Add Torrent File...)QBT_TR[CONTEXT=MainWindow]" src="images/qbt-theme/list-add.svg" width="16" height="16"/>QBT_TR(&Add Torrent File...)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="downloadLink"><img class="MyMenuIcon" alt="QBT_TR(Add Torrent &Link...)QBT_TR[CONTEXT=MainWindow]" src="images/qbt-theme/insert-link.svg" width="16" height="16"/>QBT_TR(Add Torrent &Link...)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="uploadLink"><img class="MyMenuIcon" alt="QBT_TR(Add Torrent File...)QBT_TR[CONTEXT=MainWindow]" src="images/qbt-theme/list-add.svg" width="16" height="16"/>QBT_TR(Add Torrent File...)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="downloadLink"><img class="MyMenuIcon" alt="QBT_TR(Add Torrent Link...)QBT_TR[CONTEXT=MainWindow]" src="images/qbt-theme/insert-link.svg" width="16" height="16"/>QBT_TR(Add Torrent Link...)QBT_TR[CONTEXT=MainWindow]</a></li>
<li class="divider"><a id="logoutLink"><img class="MyMenuIcon" alt="QBT_TR(Logout)QBT_TR[CONTEXT=HttpServer]" src="images/qbt-theme/system-log-out.svg" width="16" height="16"/>QBT_TR(Logout)QBT_TR[CONTEXT=HttpServer]</a></li>
<li><a id="shutdownLink"><img class="MyMenuIcon" alt="QBT_TR(Exit qBittorrent)QBT_TR[CONTEXT=HttpServer]" src="images/qbt-theme/application-exit.svg" width="16" height="16"/>QBT_TR(Exit qBittorrent)QBT_TR[CONTEXT=HttpServer]</a></li>
</ul>
</li>
<li>
<a class="returnFalse">QBT_TR(&Edit)QBT_TR[CONTEXT=MainWindow]</a>
<a class="returnFalse">QBT_TR(Edit)QBT_TR[CONTEXT=MainWindow]</a>
<ul>
<li><a id="resumeAllLink"><img class="MyMenuIcon" alt="QBT_TR(R&esume All)QBT_TR[CONTEXT=MainWindow]" src="images/qbt-theme/media-playback-start.svg" width="16" height="16"/>QBT_TR(R&esume All)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="pauseAllLink"><img class="MyMenuIcon" alt="QBT_TR(P&ause All)QBT_TR[CONTEXT=MainWindow]" src="images/qbt-theme/media-playback-pause.svg" width="16" height="16"/>QBT_TR(P&ause All)QBT_TR[CONTEXT=MainWindow]</a></li>
<li class="divider"><a id="resumeLink"><img class="MyMenuIcon" alt="QBT_TR(&Resume)QBT_TR[CONTEXT=MainWindow]" src="images/qbt-theme/media-playback-start.svg" width="16" height="16"/>QBT_TR(&Resume)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="pauseLink"><img class="MyMenuIcon" src="images/qbt-theme/media-playback-pause.svg" alt="QBT_TR(&Pause)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(&Pause)QBT_TR[CONTEXT=MainWindow]</a></li>
<li class="divider"><a id="deleteLink"><img class="MyMenuIcon" src="images/qbt-theme/list-remove.svg" alt="QBT_TR(&Delete)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(&Delete)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="resumeAllLink"><img class="MyMenuIcon" alt="QBT_TR(Resume All)QBT_TR[CONTEXT=MainWindow]" src="images/qbt-theme/media-playback-start.svg" width="16" height="16"/>QBT_TR(Resume All)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="pauseAllLink"><img class="MyMenuIcon" alt="QBT_TR(Pause All)QBT_TR[CONTEXT=MainWindow]" src="images/qbt-theme/media-playback-pause.svg" width="16" height="16"/>QBT_TR(Pause All)QBT_TR[CONTEXT=MainWindow]</a></li>
<li class="divider"><a id="resumeLink"><img class="MyMenuIcon" alt="QBT_TR(Resume)QBT_TR[CONTEXT=MainWindow]" src="images/qbt-theme/media-playback-start.svg" width="16" height="16"/>QBT_TR(Resume)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="pauseLink"><img class="MyMenuIcon" src="images/qbt-theme/media-playback-pause.svg" alt="QBT_TR(Pause)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Pause)QBT_TR[CONTEXT=MainWindow]</a></li>
<li class="divider"><a id="deleteLink"><img class="MyMenuIcon" src="images/qbt-theme/list-remove.svg" alt="QBT_TR(Delete)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Delete)QBT_TR[CONTEXT=MainWindow]</a></li>
<li id="topPrioItem" class="divider"><a id="topPrioLink"><img class="MyMenuIcon" src="images/qbt-theme/go-top.svg" alt="QBT_TR(Top Priority)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Top Priority)QBT_TR[CONTEXT=MainWindow]</a></li>
<li id="increasePrioItem"><a id="increasePrioLink"><img class="MyMenuIcon" src="images/qbt-theme/go-up.svg" alt="QBT_TR(Increase Priority)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Increase Priority)QBT_TR[CONTEXT=MainWindow]</a></li>
<li id="decreasePrioItem"><a id="decreasePrioLink"><img class="MyMenuIcon" src="images/qbt-theme/go-down.svg" alt="QBT_TR(Decrease Priority)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Decrease Priority)QBT_TR[CONTEXT=MainWindow]</a></li>
@ -61,34 +61,34 @@ @@ -61,34 +61,34 @@
</ul>
</li>
<li>
<a class="returnFalse">QBT_TR(&View)QBT_TR[CONTEXT=MainWindow]</a>
<a class="returnFalse">QBT_TR(View)QBT_TR[CONTEXT=MainWindow]</a>
<ul>
<li><a id="showTopToolbarLink"><img class="MyMenuIcon" src="images/qbt-theme/checked.svg" alt="QBT_TR(&Top Toolbar)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(&Top Toolbar)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="showStatusBarLink"><img class="MyMenuIcon" src="images/qbt-theme/checked.svg" alt="QBT_TR(Status &Bar)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Status &Bar)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="speedInBrowserTitleBarLink"><img class="MyMenuIcon" src="images/qbt-theme/checked.svg" alt="QBT_TR(S&peed in Title Bar)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(S&peed in Title Bar)QBT_TR[CONTEXT=MainWindow]</a></li>
<li class="divider"><a id=StatisticsLink><img class="MyMenuIcon" src="images/qbt-theme/view-statistics.svg" alt="QBT_TR(&Statistics)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(&Statistics)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="showTopToolbarLink"><img class="MyMenuIcon" src="images/qbt-theme/checked.svg" alt="QBT_TR(Top Toolbar)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Top Toolbar)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="showStatusBarLink"><img class="MyMenuIcon" src="images/qbt-theme/checked.svg" alt="QBT_TR(Status Bar)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Status Bar)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="speedInBrowserTitleBarLink"><img class="MyMenuIcon" src="images/qbt-theme/checked.svg" alt="QBT_TR(Speed in Title Bar)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Speed in Title Bar)QBT_TR[CONTEXT=MainWindow]</a></li>
<li class="divider"><a id=StatisticsLink><img class="MyMenuIcon" src="images/qbt-theme/view-statistics.svg" alt="QBT_TR(Statistics)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Statistics)QBT_TR[CONTEXT=MainWindow]</a></li>
</ul>
</li>
<li>
<a class="returnFalse">QBT_TR(&Tools)QBT_TR[CONTEXT=MainWindow]</a>
<a class="returnFalse">QBT_TR(Tools)QBT_TR[CONTEXT=MainWindow]</a>
<ul>
<li><a id="preferencesLink"><img class="MyMenuIcon" src="images/qbt-theme/configure.svg" alt="QBT_TR(&Options...)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(&Options...)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="preferencesLink"><img class="MyMenuIcon" src="images/qbt-theme/configure.svg" alt="QBT_TR(Options...)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Options...)QBT_TR[CONTEXT=MainWindow]</a></li>
</ul>
</li>
<li>
<a class="returnFalse">QBT_TR(&Help)QBT_TR[CONTEXT=MainWindow]</a>
<a class="returnFalse">QBT_TR(Help)QBT_TR[CONTEXT=MainWindow]</a>
<ul>
<li><a id="docsLink" target="_blank" href="http://wiki.qbittorrent.org/"><img class="MyMenuIcon" src="images/qbt-theme/help-contents.svg" alt="QBT_TR(&Documentation)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(&Documentation)QBT_TR[CONTEXT=MainWindow]</a></li>
<li class="divider"><a id="bugLink" target="_blank" href="https://www.qbittorrent.org/donate"><img class="MyMenuIcon" src="images/qbt-theme/wallet-open.svg" alt="QBT_TR(Do&nate!)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Do&nate!)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="aboutLink"><img class="MyMenuIcon" src="images/qbt-theme/help-about.svg" alt="QBT_TR(&About)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(&About)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="docsLink" target="_blank" href="http://wiki.qbittorrent.org/"><img class="MyMenuIcon" src="images/qbt-theme/help-contents.svg" alt="QBT_TR(Documentation)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Documentation)QBT_TR[CONTEXT=MainWindow]</a></li>
<li class="divider"><a id="bugLink" target="_blank" href="https://www.qbittorrent.org/donate"><img class="MyMenuIcon" src="images/qbt-theme/wallet-open.svg" alt="QBT_TR(Donate!)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(Donate!)QBT_TR[CONTEXT=MainWindow]</a></li>
<li><a id="aboutLink"><img class="MyMenuIcon" src="images/qbt-theme/help-about.svg" alt="QBT_TR(About)QBT_TR[CONTEXT=MainWindow]" width="16" height="16"/>QBT_TR(About)QBT_TR[CONTEXT=MainWindow]</a></li>
</ul>
</li>
</ul>
</div>
<div id="mochaToolbar">
&nbsp;&nbsp;
<a id="downloadButton"><img class="mochaToolButton" title="QBT_TR(Add Torrent &Link...)QBT_TR[CONTEXT=MainWindow]" src="images/qbt-theme/insert-link.svg" alt="QBT_TR(Add Torrent &Link...)QBT_TR[CONTEXT=MainWindow]" width="24" height="24"/></a>
<a id="uploadButton"><img class="mochaToolButton" title="QBT_TR(&Add Torrent File...)QBT_TR[CONTEXT=MainWindow]" src="images/qbt-theme/list-add.svg" alt="QBT_TR(&Add Torrent File...)QBT_TR[CONTEXT=MainWindow]" width="24" height="24"/></a>
<a id="downloadButton"><img class="mochaToolButton" title="QBT_TR(Add Torrent Link...)QBT_TR[CONTEXT=MainWindow]" src="images/qbt-theme/insert-link.svg" alt="QBT_TR(Add Torrent Link...)QBT_TR[CONTEXT=MainWindow]" width="24" height="24"/></a>
<a id="uploadButton"><img class="mochaToolButton" title="QBT_TR(Add Torrent File...)QBT_TR[CONTEXT=MainWindow]" src="images/qbt-theme/list-add.svg" alt="QBT_TR(Add Torrent File...)QBT_TR[CONTEXT=MainWindow]" width="24" height="24"/></a>
<a id="deleteButton" class="divider"><img class="mochaToolButton" title="QBT_TR(Delete)QBT_TR[CONTEXT=TransferListWidget]" src="images/qbt-theme/list-remove.svg" alt="QBT_TR(Delete)QBT_TR[CONTEXT=TransferListWidget]" width="24" height="24"/></a>
<a id="resumeButton" class="divider"><img class="mochaToolButton" title="QBT_TR(Resume)QBT_TR[CONTEXT=TransferListWidget]" src="images/qbt-theme/media-playback-start.svg" alt="QBT_TR(Resume)QBT_TR[CONTEXT=TransferListWidget]" width="24" height="24"/></a>
<a id="pauseButton"><img class="mochaToolButton" title="QBT_TR(Pause)QBT_TR[CONTEXT=TransferListWidget]" src="images/qbt-theme/media-playback-pause.svg" alt="QBT_TR(Pause)QBT_TR[CONTEXT=TransferListWidget]" width="24" height="24"/></a>

16
src/webui/www/private/preferences_content.html

@ -51,7 +51,7 @@ @@ -51,7 +51,7 @@
<fieldset class="settings">
<legend><input type="checkbox" id="mail_notification_checkbox" onclick="updateMailNotification();" />
<label for="mail_notification_checkbox">QBT_TR(Email notification &upon download completion)QBT_TR[CONTEXT=OptionsDialog]</label></legend>
<label for="mail_notification_checkbox">QBT_TR(Email notification upon download completion)QBT_TR[CONTEXT=OptionsDialog]</label></legend>
<div class="formRow">
<label for="dest_email_txt" class="leftLabelMedium">QBT_TR(To:)QBT_TR[CONTEXT=OptionsDialog]</label><input type="text" id="dest_email_txt" />
</div>
@ -74,7 +74,7 @@ @@ -74,7 +74,7 @@
<fieldset class="settings">
<legend><input type="checkbox" id="autorun_checkbox" onclick="updateAutoRun();" />
<label for="autorun_checkbox">QBT_TR(Run e&xternal program on torrent completion)QBT_TR[CONTEXT=OptionsDialog]</label></legend>
<label for="autorun_checkbox">QBT_TR(Run external program on torrent completion)QBT_TR[CONTEXT=OptionsDialog]</label></legend>
<input type="text" id="autorunProg_txt" style="width: 400px;" /><br/>
<div style="font-style: italic;">QBT_TR(Supported parameters (case sensitive):)QBT_TR[CONTEXT=OptionsDialog]
<ul>
@ -192,7 +192,7 @@ @@ -192,7 +192,7 @@
<fieldset class="settings">
<legend><input type="checkbox" id="ipfilter_enabled_checkbox" onclick="updateFilterSettings();" />
<label for="ipfilter_enabled_checkbox">QBT_TR(IP Fi&ltering)QBT_TR[CONTEXT=OptionsDialog]</label></legend>
<label for="ipfilter_enabled_checkbox">QBT_TR(IP Filtering)QBT_TR[CONTEXT=OptionsDialog]</label></legend>
<label for="ipfilter_text">QBT_TR(Filter path (.dat, .p2p, .p2b):)QBT_TR[CONTEXT=OptionsDialog]</label>
<input type="text" id="ipfilter_text" /><br/>
<input type="checkbox" id="ipfilter_trackers_checkbox" />
@ -248,7 +248,7 @@ @@ -248,7 +248,7 @@
<fieldset class="settings">
<legend><input type="checkbox" id="limit_sheduling_checkbox" onclick="updateSchedulingEnabled();" />
<label for="limit_sheduling_checkbox">QBT_TR(Schedule &the use of alternative rate limits)QBT_TR[CONTEXT=OptionsDialog]</label></legend>
<label for="limit_sheduling_checkbox">QBT_TR(Schedule the use of alternative rate limits)QBT_TR[CONTEXT=OptionsDialog]</label></legend>
QBT_TR(From:)QBT_TR[CONTEXT=OptionsDialog]
<input type="text" id="schedule_from_hour" style="width: 1.5em;" />:<input type="text" id="schedule_from_min" style="width: 1.5em;" /> QBT_TR(To:)QBT_TR[CONTEXT=OptionsDialog]
<input type="text" id="schedule_to_hour" style="width: 1.5em;" />:<input type="text" id="schedule_to_min" style="width: 1.5em;" />
@ -299,7 +299,7 @@ @@ -299,7 +299,7 @@
<fieldset class="settings">
<legend><input type="checkbox" id="queueing_checkbox" onclick="updateQueueingSystem();" />
<label for="queueing_checkbox">QBT_TR(&Torrent Queueing)QBT_TR[CONTEXT=OptionsDialog]</label></legend>
<label for="queueing_checkbox">QBT_TR(Torrent Queueing)QBT_TR[CONTEXT=OptionsDialog]</label></legend>
<div class="formRow">
<label for="max_active_dl_value" style="margin-left: 20px;" class="leftLabelLarge">QBT_TR(Maximum active downloads:)QBT_TR[CONTEXT=OptionsDialog]</label>
<input type="text" id="max_active_dl_value" style="width: 4em;" />
@ -354,7 +354,7 @@ @@ -354,7 +354,7 @@
<fieldset class="settings">
<legend><input type="checkbox" id="add_trackers_checkbox" onclick="updateAddTrackersEnabled();" />
<label for="add_trackers_checkbox">QBT_TR(A&utomatically add these trackers to new downloads:)QBT_TR[CONTEXT=OptionsDialog]</label></legend>
<label for="add_trackers_checkbox">QBT_TR(Automatically add these trackers to new downloads:)QBT_TR[CONTEXT=OptionsDialog]</label></legend>
<textarea id="add_trackers_textarea" rows="5" cols="70"></textarea>
</fieldset>
</div>
@ -426,7 +426,7 @@ @@ -426,7 +426,7 @@
<label for="webui_upnp_checkbox">QBT_TR(Use UPnP / NAT-PMP to forward the port from my router)QBT_TR[CONTEXT=OptionsDialog]</label><br/>
<fieldset class="settings">
<legend><input type="checkbox" id="use_https_checkbox" onclick="updateHttpsSettings();" />
<label for="use_https_checkbox">QBT_TR(&Use HTTPS instead of HTTP)QBT_TR[CONTEXT=OptionsDialog]</label></legend>
<label for="use_https_checkbox">QBT_TR(Use HTTPS instead of HTTP)QBT_TR[CONTEXT=OptionsDialog]</label></legend>
<div class="formRow">
<label class="leftLabelSmall" for="ssl_key_textarea">QBT_TR(Key:)QBT_TR[CONTEXT=OptionsDialog]</label>
<textarea id="ssl_key_textarea" rows="5" cols="70"></textarea>
@ -471,7 +471,7 @@ @@ -471,7 +471,7 @@
<fieldset class="settings">
<legend><input type="checkbox" id="use_dyndns_checkbox" onclick="updateDynDnsSettings();" />
<label for="use_dyndns_checkbox">QBT_TR(Upda&te my dynamic domain name)QBT_TR[CONTEXT=OptionsDialog]</label></legend>
<label for="use_dyndns_checkbox">QBT_TR(Update my dynamic domain name)QBT_TR[CONTEXT=OptionsDialog]</label></legend>
<select id="dyndns_select">
<option value="0">DynDNS</option>
<option value="1">NO-IP</option>

1846
src/webui/www/translations/webui_ar.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_be.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_bg.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_ca.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_cs.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_da.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_de.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_el.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_en.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_en_AU.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_en_GB.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_eo.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_es.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_eu.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_fi.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_fr.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_gl.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_he.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_hi_IN.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_hr.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_hu.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_hy.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_id.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_is.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_it.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_ja.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_ka.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_ko.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_lt.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_lv_LV.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_ms_MY.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_nb.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_nl.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_oc.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_pl.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_pt_BR.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_pt_PT.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_ro.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_ru.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_sk.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_sl.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_sr.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_sv.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_tr.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_uk.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_uz@Latn.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_vi.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_zh.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_zh_HK.ts

File diff suppressed because it is too large Load Diff

1846
src/webui/www/translations/webui_zh_TW.ts

File diff suppressed because it is too large Load Diff

181
src/webui/www/tstool.py

@ -0,0 +1,181 @@ @@ -0,0 +1,181 @@
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# TSTool - script for update qBittorrent WebUI translation files
# Copyright (C) 2018 Vladimir Golovnev <glassez@yandex.ru>
#
# 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) 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# In addition, as a special exception, the copyright holders give permission to
# link this program with the OpenSSL project's "OpenSSL" library (or with
# modified versions of it that use the same license as the "OpenSSL" library),
# and distribute the linked executables. You must obey the GNU General Public
# License in all respects for all of the code used other than "OpenSSL". If you
# modify file(s), you may extend this exception to your version of the file(s),
# but you are not obligated to do so. If you do not wish to do so, delete this
# exception statement from your version.
import argparse
import copy
import os
import os.path
import re
import sys
import xml.etree.ElementTree as ET
accepted_exts = [".js", ".html", ".css"]
no_obsolete = False
www_folder = "."
ts_folder = os.path.join(www_folder, "translations")
def parseSource(filename, sources):
print("Parsing %s..." % (os.path.normpath(filename)))
with open(filename, encoding = 'utf-8', mode = 'r') as file:
regex = re.compile(
r"QBT_TR\((([^\)]|\)(?!QBT_TR))+)\)QBT_TR\[CONTEXT=([a-zA-Z_][a-zA-Z0-9_]*)\]")
for match in regex.finditer(file.read()):
string = match.group(1)
context = match.group(3)
if context not in sources:
sources[context] = set()
sources[context].add(string)
def processTranslation(filename, sources):
print('Processing %s...' % (os.path.normpath(filename)))
try:
tree = ET.ElementTree(file = filename)
except Exception:
print('\tFailed to parse %s!' % (os.path.normpath(filename)))
return
root = tree.getroot()
for context in root.findall('context'):
context_name = context.find('name').text
has_context = context_name in sources
if not has_context and no_obsolete:
root.remove(context)
continue
for message in context.findall('message'):
for location in message.findall('location'):
message.remove(location)
source = message.find('source').text
translation = message.find('translation')
if has_context and source in sources[context_name]:
sources[context_name].remove(source)
trtype = translation.attrib.get('type')
if (trtype == 'obsolete') or (trtype == 'vanished'):
del translation.attrib['type'] # i.e. finished
else:
if no_obsolete or (translation.attrib.get('type', '') == 'unfinished'):
context.remove(message)
else:
translation.attrib['type'] = 'vanished'
if not has_context:
continue
# add new messages for current context
for source in sources[context_name]:
message = ET.SubElement(context, 'message')
ET.SubElement(message, 'source').text = source
ET.SubElement(message, 'translation', {'type': 'unfinished'})
del sources[context_name]
# add messages for new contexts
for context_name in sources:
context = ET.SubElement(root, 'context')
ET.SubElement(context, 'name').text = context_name
for source in sources[context_name]:
message = ET.SubElement(context, 'message')
ET.SubElement(message, 'source').text = source
ET.SubElement(message, 'translation', {'type': 'unfinished'})
# prettify output xml
indent = ' ' * 4
root.text = '\n'
for context in root.findall('./context'):
context.text = '\n' + indent
context.tail = '\n'
context.find('./name').tail = '\n' + indent
messages = context.findall('./message')
if len(messages) == 0: continue
for message in messages:
message.text = '\n' + (indent * 2)
message.tail = '\n' + indent
elems = message.findall('./')
if len(elems) == 0: continue
for elem in elems:
elem.tail = '\n' + (indent * 2)
elems[-1:][0].tail = '\n' + indent
messages[-1:][0].tail = '\n'
try:
with open(filename, mode = 'wb') as file:
file.write(b'<?xml version="1.0" encoding="utf-8"?>\n'
b'<!DOCTYPE TS>\n')
tree.write(file, encoding = 'utf-8')
except Exception:
print('\tFailed to write %s!' % (os.path.normpath(filename)))
argp = argparse.ArgumentParser(
prog = 'tstool.py', description = 'Update qBittorrent WebUI translation files.')
argp.add_argument('--no-obsolete', dest = 'no_obsolete', action = 'store_true',
default = no_obsolete,
help = 'remove obsolete messages (default: mark them as obsolete)')
argp.add_argument('--www-folder', dest = 'www_folder', action = 'store',
default = www_folder,
help = 'folder with WebUI source files (default: "%s")' % (www_folder))
argp.add_argument('--ts-folder', dest = 'ts_folder', action = 'store',
default = ts_folder,
help = 'folder with WebUI translation files (default: "%s")' % (ts_folder))
args = argp.parse_args()
no_obsolete = args.no_obsolete
www_folder = args.www_folder
ts_folder = args.ts_folder
print("Processing source files...")
nfiles = 0
source_ts = {}
for root, dirs, files in os.walk(www_folder):
for file in files:
if os.path.splitext(file)[-1] in accepted_exts:
parseSource(os.path.join(root, file), source_ts)
nfiles += 1
if nfiles == 0:
print("No source files found!")
sys.exit()
nstrings = sum(len(sublist) for sublist in source_ts)
print("Found %d strings within %d contexts." % (nstrings, len(source_ts)))
print("")
print("Processing translation files...")
for entry in os.scandir(ts_folder):
if (entry.is_file() and entry.name.startswith('webui_')
and entry.name.endswith(".ts")):
processTranslation(entry.path, copy.deepcopy(source_ts))
print("Done!")

50
src/webui/www/webui.qrc

@ -45,5 +45,55 @@ @@ -45,5 +45,55 @@
<file>public/css/login.css</file>
<file>public/login.html</file>
<file>public/scripts/lib/mootools-1.2-core-yc.js</file>
<file>translations/webui_ar.qm</file>
<file>translations/webui_be.qm</file>
<file>translations/webui_bg.qm</file>
<file>translations/webui_ca.qm</file>
<file>translations/webui_cs.qm</file>
<file>translations/webui_da.qm</file>
<file>translations/webui_de.qm</file>
<file>translations/webui_el.qm</file>
<file>translations/webui_en.qm</file>
<file>translations/webui_en_AU.qm</file>
<file>translations/webui_en_GB.qm</file>
<file>translations/webui_eo.qm</file>
<file>translations/webui_es.qm</file>
<file>translations/webui_eu.qm</file>
<file>translations/webui_fi.qm</file>
<file>translations/webui_fr.qm</file>
<file>translations/webui_gl.qm</file>
<file>translations/webui_he.qm</file>
<file>translations/webui_hi_IN.qm</file>
<file>translations/webui_hr.qm</file>
<file>translations/webui_hu.qm</file>
<file>translations/webui_hy.qm</file>
<file>translations/webui_id.qm</file>
<file>translations/webui_is.qm</file>
<file>translations/webui_it.qm</file>
<file>translations/webui_ja.qm</file>
<file>translations/webui_ka.qm</file>
<file>translations/webui_ko.qm</file>
<file>translations/webui_lt.qm</file>
<file>translations/webui_lv_LV.qm</file>
<file>translations/webui_ms_MY.qm</file>
<file>translations/webui_nb.qm</file>
<file>translations/webui_nl.qm</file>
<file>translations/webui_oc.qm</file>
<file>translations/webui_pl.qm</file>
<file>translations/webui_pt_BR.qm</file>
<file>translations/webui_pt_PT.qm</file>
<file>translations/webui_ro.qm</file>
<file>translations/webui_ru.qm</file>
<file>translations/webui_sk.qm</file>
<file>translations/webui_sl.qm</file>
<file>translations/webui_sr.qm</file>
<file>translations/webui_sv.qm</file>
<file>translations/webui_tr.qm</file>
<file>translations/webui_uk.qm</file>
<file>translations/webui_uz@Latn.qm</file>
<file>translations/webui_vi.qm</file>
<file>translations/webui_zh.qm</file>
<file>translations/webui_zh_HK.qm</file>
<file>translations/webui_zh_TW.qm</file>
</qresource>
</RCC>

Loading…
Cancel
Save