Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F36623438
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
28 KB
Subscribers
None
View Options
diff --git a/client/firsttimedialog.cpp b/client/firsttimedialog.cpp
index 750dc54..7b80a3a 100644
--- a/client/firsttimedialog.cpp
+++ b/client/firsttimedialog.cpp
@@ -1,446 +1,446 @@
// SPDX-FileCopyrightText: 2024 g10 code Gmbh
// SPDX-Contributor: Carl Schwan <carl.schwan@gnupg.com>
// SPDX-License-Identifier: GPL-2.0-or-later
#include "firsttimedialog.h"
#include "config.h"
#include "gpgolweb_version.h"
#include "rootcagenerator/controller.h"
#include "ui_confpageinstalladdin.h"
#include "ui_confpageproxyoptions.h"
#include "ui_confpagetlscertificate.h"
#include "ui_confpagewelcome.h"
#include "ui_firsttimedialog.h"
#include "websocketclient.h"
#include <QCheckBox>
#include <QClipboard>
#include <QCloseEvent>
#include <QDesktopServices>
#include <QFile>
#include <QMargins>
#include <QDialog>
#include <QDialogButtonBox>
#include <QSaveFile>
#include <QSettings>
#include <QStandardPaths>
#include <QStatusBar>
#include <QStyle>
#include <QTemporaryDir>
#include <QToolBar>
#include <Libkleo/Compliance>
#include <KColorScheme>
#include <KIO/OpenFileManagerWindowJob>
#include <KTitleWidget>
using namespace Qt::StringLiterals;
class PairingDialog : public QDialog {
public:
PairingDialog(QWidget *parent) : QDialog(parent) {
setWindowTitle(i18n("Pairing mode active"));
auto l = new QVBoxLayout(this);
auto lab = new QLabel(i18n("<p>Copy and paste the code shown below into the input field shown at the top of the Add-In:</p>"));
lab->setWordWrap(true);
l->addWidget(lab);
m_pairingTokenLabel = new QLabel(i18nc("status message", "<p align='center'><b>Obtaining token...</b></p>"));
l->addWidget(m_pairingTokenLabel);
auto bb = new QDialogButtonBox;
auto endButton = bb->addButton(i18nc("@button", "End pairing mode"), QDialogButtonBox::RejectRole);
connect(endButton, &QAbstractButton::clicked, this, &QDialog::reject);
m_copyButton = bb->addButton(i18nc("@button", "Copy code to clipboard"), QDialogButtonBox::ActionRole);
m_copyButton->setEnabled(false);
l->addWidget(bb);
setFixedSize(sizeHint());
connect(&WebsocketClient::self(), &WebsocketClient::pairingStatusChanged, this, &PairingDialog::pairingStatusChanged);
WebsocketClient::self().enterPairingMode();
}
void pairingStatusChanged(const QString& token, bool pairingActive) {
if (!pairingActive) {
reject();
return;
}
m_pairingTokenLabel->setText(QString(u"<p align='center'><b>%1</b></p>").arg(token));
m_copyButton->setEnabled(true);
connect(m_copyButton, &QAbstractButton::clicked, [this, token]() {
qApp->clipboard()->setText(token);
});
}
private:
QLabel* m_pairingTokenLabel;
QAbstractButton* m_copyButton;
};
FirstTimeDialog::FirstTimeDialog(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::FirstTimeDialog)
, confPageWelcome(new Ui::ConfPageWelcome)
, confPageInstallAddin(new Ui::ConfPageInstallAddin)
, confPageProxyOptions(new Ui::ConfPageProxyOptions)
, confPageTLSCertificate(new Ui::ConfPageTLSCertificate)
, m_systemTrayIcon(QIcon::fromTheme(u"com.gnupg.gpgolweb"_s))
{
ui->setupUi(this);
confPageWelcome->setupUi(ui->confPageWelcome);
ui->confPageWelcome->setProperty("title", i18nc("@title", "Welcome to GpgOL/Web"));
confPageProxyOptions->setupUi(ui->confPageProxyOptions);
ui->confPageProxyOptions->setProperty("title", i18nc("@title", "Configure Proxy and Optional Features"));
confPageInstallAddin->setupUi(ui->confPageInstallAddin);
ui->confPageInstallAddin->setProperty("title", i18nc("@title", "Install Outlook Add-In"));
confPageTLSCertificate->setupUi(ui->confPageTLSCertificate);
ui->confPageTLSCertificate->setProperty("title", i18nc("@title", "Setting Up TLS Certificate for Local Proxy"));
if (ui->stack->indexOf(ui->confPageWelcome) != ConfPageWelcome) {
qFatal("Welcome page misplaced");
}
if (ui->stack->indexOf(ui->confPageTLSCertificate) != ConfPageTLSCertificate) {
qFatal("Tls certification page misplaced");
}
if (ui->stack->indexOf(ui->confPageProxyOptions) != ConfPageProxyOptions) {
qFatal("Proxy options page misplaced");
}
if (ui->stack->indexOf(ui->confPageInstallAddin) != ManifestPage) {
qFatal("Manifest install page misplaced");
}
confPageProxyOptions->reencryptOption->setChecked(Config::self()->reencrypt());
connect(confPageProxyOptions->reencryptOption, &QCheckBox::stateChanged, this, [](int state) {
Config::self()->setReencrypt(state == Qt::Checked);
Config::self()->save();
});
auto margins = confPageProxyOptions->remoteServerLayout->contentsMargins();
margins.setLeft(margins.left() + style()->pixelMetric(QStyle::PM_RadioButtonLabelSpacing) + style()->pixelMetric(QStyle::PM_ExclusiveIndicatorWidth));
confPageProxyOptions->remoteServerLayout->setContentsMargins(margins);
m_systemTrayIcon.setMainWindow(this);
m_systemTrayIcon.show();
m_backAction = new QAction(this);
connect(m_backAction, &QAction::triggered, this, [this]() {
if (Controller::certificateAlreadyGenerated() || !Config::self()->isLocalServer()) {
ui->stack->setCurrentIndex(ui->stack->currentIndex() > 1 ? ConfPageProxyOptions : ConfPageWelcome);
} else {
ui->stack->setCurrentIndex(ui->stack->currentIndex() == ConfPageProxyOptions ? ConfPageWelcome : ConfPageProxyOptions);
}
});
connect(confPageTLSCertificate->backButton, &QAbstractButton::clicked, m_backAction, &QAction::trigger);
connect(confPageInstallAddin->backButton, &QAbstractButton::clicked, m_backAction, &QAction::trigger);
connect(confPageProxyOptions->backButton, &QAbstractButton::clicked, m_backAction, &QAction::trigger);
auto toolbar = new QToolBar(this);
toolbar->setMovable(false);
auto titleWidget = new KTitleWidget(this);
toolbar->addWidget(titleWidget);
addToolBar(Qt::TopToolBarArea, toolbar);
titleWidget->setText(ui->stack->currentWidget()->property("title").toString());
connect(ui->stack, &QStackedWidget::currentChanged, this, [titleWidget, this]() {
titleWidget->setText(ui->stack->currentWidget()->property("title").toString());
});
QPixmap logo = QIcon::fromTheme(u"com.gnupg.gpgolweb"_s).pixmap(64, 64);
confPageWelcome->logo->setPixmap(logo);
confPageWelcome->titleWelcome->setText(i18nc("@info", "GpgOL/Web %1", QString::fromLocal8Bit(GPGOLWEB_VERSION_STRING)));
auto statusBar = new QStatusBar(this);
confPageInstallAddin->showOnStartup->setChecked(Config::self()->showLauncher());
connect(confPageInstallAddin->showOnStartup, &QCheckBox::toggled, this, [](bool checked) {
Config::self()->setShowLauncher(checked);
Config::self()->save();
});
m_status = new QLabel;
statusBar->addPermanentWidget(m_status);
auto version = new QLabel(i18nc("@info", "Version: %1", QString::fromLocal8Bit(GPGOLWEB_VERSION_STRING)));
statusBar->addPermanentWidget(version);
if (Kleo::DeVSCompliance::isActive()) {
auto statusLbl = std::make_unique<QLabel>(Kleo::DeVSCompliance::name());
{
auto statusPalette = qApp->palette();
KColorScheme::adjustForeground(statusPalette,
Kleo::DeVSCompliance::isCompliant() ? KColorScheme::NormalText : KColorScheme::NegativeText,
statusLbl->foregroundRole(),
KColorScheme::View);
statusLbl->setAutoFillBackground(true);
KColorScheme::adjustBackground(statusPalette,
Kleo::DeVSCompliance::isCompliant() ? KColorScheme::PositiveBackground : KColorScheme::NegativeBackground,
QPalette::Window,
KColorScheme::View);
statusLbl->setPalette(statusPalette);
}
statusBar->addPermanentWidget(statusLbl.release());
}
setStatusBar(statusBar);
connect(confPageProxyOptions->continueButton, &QPushButton::clicked, this, &FirstTimeDialog::slotSetup);
connect(confPageWelcome->configureButton, &QPushButton::clicked, this, [this]() {
ui->stack->setCurrentIndex(ConfPageProxyOptions);
});
connect(confPageTLSCertificate->continueButton, &QPushButton::clicked, this, &FirstTimeDialog::slotSetup);
confPageTLSCertificate->continueButton->setEnabled(false);
confPageTLSCertificate->installButton->setVisible(false);
confPageTLSCertificate->label->setVisible(false);
connect(confPageTLSCertificate->installButton, &QPushButton::clicked, this, [this]() {
if (m_controller) {
m_controller->install();
}
});
confPageInstallAddin->manifestPath->setText(QLatin1StringView(DATAROUTDIR) + u"/gpgol/manifest.xml"_s);
connect(confPageInstallAddin->testPageButton, &QPushButton::clicked, this, [this]() {
QDesktopServices::openUrl(QUrl(u"https://"_s + serverDomain() + u"/test"_s));
});
connect(confPageInstallAddin->pairWebClientButton, &QPushButton::clicked, this, [this]() {
PairingDialog d(this);
d.exec();
WebsocketClient::self().quitPairingMode();
});
connect(confPageInstallAddin->minimizeButton, &QPushButton::clicked, this, &QWidget::hide);
connect(confPageInstallAddin->manifestPathCopy, &QPushButton::clicked, this, [this]() {
QGuiApplication::clipboard()->setText(confPageInstallAddin->manifestPath->text());
});
connect(confPageInstallAddin->manifestPathOpenFolder, &QPushButton::clicked, this, [this]() {
auto job = new KIO::OpenFileManagerWindowJob();
job->setHighlightUrls({QUrl::fromUserInput(confPageInstallAddin->manifestPath->text())});
if (!qEnvironmentVariableIsEmpty("XDG_ACTIVATION_TOKEN")) {
job->setStartupId(qgetenv("XDG_ACTIVATION_TOKEN"));
}
job->start();
});
confPageProxyOptions->remoteLabel->setEnabled(!Config::self()->isLocalServer());
confPageProxyOptions->remoteServer->setEnabled(!Config::self()->isLocalServer());
confPageProxyOptions->remoteServer->setText(Config::self()->remoteAddress().toString());
confPageProxyOptions->remoteOption->setChecked(!Config::self()->isLocalServer());
connect(confPageProxyOptions->remoteOption, &QRadioButton::toggled, this, [this](bool checked) {
Config::self()->setIsLocalServer(!checked);
Config::self()->save();
confPageProxyOptions->remoteLabel->setEnabled(!Config::self()->isLocalServer());
confPageProxyOptions->remoteServer->setEnabled(!Config::self()->isLocalServer());
});
connect(confPageProxyOptions->remoteServer, &QLineEdit::textChanged, this, [this]() {
Config::self()->setRemoteAddress(QUrl::fromUserInput(confPageProxyOptions->remoteServer->text()));
Config::self()->save();
});
if (Controller::certificateAlreadyGenerated() || !Config::self()->isLocalServer()) {
ui->stack->setCurrentIndex(ConfPageWelcome);
if (Controller::certificateAlreadyGenerated() && Config::self()->isLocalServer()) {
startLocalServer();
}
startWebsocketClient();
} else {
ui->stack->setCurrentIndex(ConfPageProxyOptions);
}
connect(&m_serverProcess, &QProcess::readyReadStandardError, this, [this]() {
qWarning().noquote() << m_serverProcess.readAllStandardError();
});
connect(&m_serverProcess, &QProcess::readyReadStandardOutput, this, [this]() {
qWarning().noquote() << m_serverProcess.readAllStandardOutput();
});
connect(&m_serverProcess, &QProcess::errorOccurred, this, [this](QProcess::ProcessError err) {
qWarning() << "Process error" << err;
qWarning().noquote() << m_serverProcess.readAllStandardError();
});
connect(&m_serverProcess, &QProcess::finished, this, [this](int exitCode, QProcess::ExitStatus status) {
qWarning() << "Process finished" << exitCode << status;
if (status == QProcess::NormalExit) {
qWarning() << "Status code" << m_serverProcess.exitCode();
}
qWarning().noquote() << m_serverProcess.readAllStandardError();
});
#ifdef Q_OS_WIN
// We intentionally don't use our own config for this: If users disable autostart via
// the Windows settings menu, we want to respect that, too.
{
QSettings winreg(u"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"_s, QSettings::NativeFormat);
confPageWelcome->autostartBox->setChecked(!winreg.value(QCoreApplication::applicationName()).toString().isEmpty());
}
connect(confPageWelcome->autostartBox, &QCheckBox::checkStateChanged, this, [this](Qt::CheckState state) {
QSettings winreg(u"HKEY_CURRENT_USER\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"_s, QSettings::NativeFormat);
if (state) {
winreg.setValue(QCoreApplication::applicationName(),
QDir::toNativeSeparators(QCoreApplication::applicationFilePath()));
} else {
winreg.remove(QCoreApplication::applicationName());
}
});
#else
confPageWelcome->autostartBox->setVisible(false);
#endif
}
FirstTimeDialog::~FirstTimeDialog() = default;
void FirstTimeDialog::slotStateChanged(const QString &stateDisplay)
{
m_status->setText(stateDisplay);
m_systemTrayIcon.stateChanged(stateDisplay, WebsocketClient::self().state());
}
void FirstTimeDialog::closeEvent(QCloseEvent *e)
{
e->ignore();
hide();
}
void FirstTimeDialog::slotSetup()
{
if (confPageProxyOptions->localOption->isChecked()) {
if (!Controller::certificateAlreadyGenerated()) {
delete m_controller;
m_controller = new Controller(this);
confPageTLSCertificate->plainTextEdit->clear();
connect(m_controller, &Controller::generationDone, this, [this]() {
confPageTLSCertificate->installButton->setVisible(true);
confPageTLSCertificate->installButton->setEnabled(true);
confPageTLSCertificate->label->setText(
i18nc("@info", "About to install certificate with fingerprint: %1 ", m_controller->rootFingerprint()));
confPageTLSCertificate->label->setVisible(true);
confPageTLSCertificate->continueButton->setVisible(false);
});
ui->stack->setCurrentIndex(ConfPageTLSCertificate);
connect(m_controller, &Controller::result, this, [this](KJob *) {
if (m_controller->error()) {
confPageTLSCertificate->plainTextEdit->appendPlainText(m_controller->errorText());
return;
}
confPageTLSCertificate->installButton->setVisible(false);
confPageTLSCertificate->continueButton->setVisible(true);
confPageTLSCertificate->label->setText(i18nc("@info", "Installed certificate with fingerprint: %1", m_controller->rootFingerprint()));
confPageTLSCertificate->continueButton->setEnabled(true);
});
connect(m_controller, &Controller::debutOutput, this, &FirstTimeDialog::slotTlsDebutOutput);
m_controller->start();
} else {
startLocalServer();
startWebsocketClient();
generateManifest();
}
} else {
generateManifest();
}
}
void FirstTimeDialog::startLocalServer()
{
if (m_serverProcess.state() != QProcess::NotRunning) {
return;
}
m_serverProcess.start(u"gpgol-server"_s);
}
void FirstTimeDialog::startWebsocketClient()
{
const auto clientId = QUuid::createUuid().toString(QUuid::WithoutBraces);
auto &websocketClient = WebsocketClient::self(QUrl(u"wss://"_s + serverDomain() + u"/websocket"_s), clientId);
connect(&websocketClient, &WebsocketClient::stateChanged, this, &FirstTimeDialog::slotStateChanged);
slotStateChanged(websocketClient.stateDisplay());
}
void FirstTimeDialog::slotTlsDebutOutput(const QString &output)
{
confPageTLSCertificate->plainTextEdit->appendPlainText(output);
}
static QByteArray ampersandEncode(const QString &input)
{
QByteArray encoded;
for(int i = 0; i < input.size(); ++i) {
QChar ch = input.at(i);
if(ch.unicode() > 127) {
encoded += QString(u"&#%1;"_s).arg(static_cast<int>(ch.unicode())).toLatin1();
} else {
encoded += ch.toLatin1();
}
}
return encoded;
}
void FirstTimeDialog::generateManifest()
{
QFile file(u":/gpgol-client/manifest.xml.in"_s);
if (!file.open(QIODeviceBase::ReadOnly)) {
Q_ASSERT(false);
return;
}
ui->stack->setCurrentIndex(ManifestPage);
QByteArray manifest = file.readAll();
manifest.replace("%HOST%", serverDomain().toUtf8());
manifest.replace("%VERSION%", GPGOLWEB_VERSION_STRING);
// HACK: For a manifest loaded from local file - as single users will do - MS does not apply translations.
// They claim it's a feature, not a bug. To work around this, we localize the default value, here, instead.
// At the same time, we also have to keep translations in the manifest, so as not to break translations
// for origanization-installed manifests.
int offset = 0;
const auto attrib = QByteArray("DefaultValue=\"");
while ((offset = manifest.indexOf("GPGOLI18N=\"true\"", offset)) > -1) {
int strBegin = manifest.indexOf(attrib, offset) + attrib.length();
Q_ASSERT(strBegin > attrib.length());
int strEnd = manifest.indexOf("\"", strBegin);
Q_ASSERT(strEnd > 0);
- const auto translation = ki18nd("manifest", manifest.mid(strBegin, strEnd-strBegin).constData()).toString();
+ const auto translation = ki18nd("gpgol-js-manifest", manifest.mid(strBegin, strEnd-strBegin).constData()).toString();
manifest.replace(offset, strEnd-offset, QByteArray(attrib + ampersandEncode(translation).constData()));
}
const auto saveFilePath = QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation) + u"/gpgol-web-manifest.xml"_s;
QSaveFile saveFile(saveFilePath);
if (!saveFile.open(QIODeviceBase::WriteOnly)) {
Q_ASSERT(false);
return;
}
saveFile.write(manifest);
saveFile.commit();
confPageInstallAddin->manifestPath->setText(QDir::toNativeSeparators(saveFilePath));
}
QString FirstTimeDialog::serverDomain() const
{
return confPageProxyOptions->localOption->isChecked() ? u"localhost:5656"_s : confPageProxyOptions->remoteServer->text();
}
#ifdef Q_OS_WIN
#include <windows.h>
#endif
void FirstTimeDialog::strongActivateWindow(QWidget* window)
{
#ifdef Q_OS_WIN
// HACK: Simulate Alt-keyPress while bringing the window to the front.
// This helps when our app does not currently have focus - and
// frequently it does not, because we have just clicked in browser/outlook.
// https://stackoverflow.com/questions/72620538/whats-the-correct-way-to-bring-a-window-to-the-front
keybd_event(VK_RMENU, 0, 0, 0);
#endif
window->show();
window->activateWindow();
window->raise();
#ifdef Q_OS_WIN
keybd_event(VK_RMENU, 0, KEYEVENTF_KEYUP, 0);
#endif
}
diff --git a/i18n.py b/i18n.py
index 8d17127..bc349c0 100644
--- a/i18n.py
+++ b/i18n.py
@@ -1,195 +1,195 @@
# SPDX-FileCopyrightText: 2024 Carl Schwan <carl@carlschwan.eu>
# SPDX-License-Identifier: LGPL-2.0-or-later
import argparse
import polib
from lxml import etree
import os
import os.path
import subprocess
import glob
import json
def extract_strings(output_dir):
print("Extracting")
ui_files = glob.glob("client/**/*.ui", recursive = True) + glob.glob("client/**/*.rc", recursive = True) + glob.glob("client/**/*.kcfg", recursive = True)
extracted = subprocess.run(["scripts/extractrc"] + ui_files, stdout=subprocess.PIPE).stdout
rc = open("rc.cpp", "w")
rc.write(extracted.decode())
rc.close()
subprocess.run(["xgettext", "-C", "--from-code=UTF-8", "-ci18n", "-ki18n:1", "-ki18nc:1c,2", "web/src/App.vue", "-o", output_dir + "/gpgol-js-web.pot"])
cpp_files = glob.glob("client/**/*.cpp", recursive = True) + glob.glob("client/**/*.h", recursive = True) + glob.glob("server/**/*.cpp", recursive = True) + glob.glob("server/**/*.h", recursive = True) + ["rc.cpp"]
subprocess.run(["xgettext", "-C", "--from-code=UTF-8", "-kde", "-ci18n", "-ki18n:1", "-ki18nc:1c,2",
"-ki18ncp:1c,2,3", "-ki18nd:2", "-ki18ndc:2c,3", "-ki18ndp:2,3", "-ki18ndcp:2c,3,4",
"-kki18n:1", "-kki18nc:1c,2", "-kki18np:1,2", "-kki18ncp:1c,2,3",
"-kki18nd:2", "-kki18ndc:2c,3", "-kki18ndp:2,3", "-kki18ndcp:2c,3,4",
"-kxi18n:1", "-kxi18nc:1c,2", "-kxi18np:1,2", "-kxi18ncp:1c,2,3",
"-kxi18nd:2", "-kxi18ndc:2c,3", "-kxi18ndp:2,3", "-kxi18ndcp:2c,3,4",
"-kkxi18n:1", "-kkxi18nc:1c,2", "-kkxi18np:1,2", "-kkxi18ncp:1c,2,3",
"-kkxi18nd:2", "-kkxi18ndc:2c,3", "-kkxi18ndp:2,3", "-kkxi18ndcp:2c,3,4",
"-kkli18n:1", "-kkli18nc:1c,2", "-kkli18np:1,2", "-kkli18ncp:1c,2,3",
"-kklxi18n:1", "-kklxi18nc:1c,2", "-kklxi18np:1,2", "-kklxi18ncp:1c,2,3",
"-kI18N_NOOP:1", "-kI18NC_NOOP:1c,2",
"-kI18N_NOOP2:1c,2", "-kI18N_NOOP2_NOSTRIP:1c,2",
"-ktr2i18n:1", "-ktr2xi18n:1"] + cpp_files + ["-o", output_dir + "/gpgol-js-native.pot"])
os.remove("rc.cpp")
tree = etree.parse('client/manifest.xml.in')
root = tree.getroot()
xml_version1 = "{http://schemas.microsoft.com/office/mailappversionoverrides}";
xml_version = "{http://schemas.microsoft.com/office/mailappversionoverrides/1.1}";
xml_bt = "{http://schemas.microsoft.com/office/officeappbasictypes/1.0}"
xml_office = "{http://schemas.microsoft.com/office/appforoffice/1.1}"
resources = root.find(xml_version1 + "VersionOverrides").find(xml_version + "VersionOverrides").find(xml_version + "Resources")
short_strings = resources.find(xml_bt + "ShortStrings")
long_strings = resources.find(xml_bt + "LongStrings")
description = root.find(xml_office + "Description")
po = polib.POFile()
po.metadata = {
'Project-Id-Version': '1.0',
'Report-Msgid-Bugs-To': 'you@example.com',
'POT-Creation-Date': '2007-10-18 14:00+0100',
'PO-Revision-Date': '2007-10-18 14:00+0100',
'Last-Translator': 'you <you@example.com>',
'Language-Team': 'English <yourteam@example.com>',
'MIME-Version': '1.0',
'Content-Type': 'text/plain; charset=utf-8',
'Content-Transfer-Encoding': '8bit',
}
po.append(polib.POEntry(
msgid=description.get("DefaultValue"),
msgstr=u'',
occurrences=[('client/manifest.xml.in', description.sourceline)]
))
for child in short_strings:
entry = polib.POEntry(
msgid=child.get("DefaultValue"),
msgstr=u'',
occurrences=[('client/manifest.xml.in', child.sourceline)]
)
po.append(entry)
for child in long_strings:
entry = polib.POEntry(
msgid=child.get("DefaultValue"),
msgstr=u'',
occurrences=[('client/manifest.xml.in', child.sourceline)]
)
po.append(entry)
- po.save(output_dir + '/manifest.pot')
+ po.save(output_dir + '/gpgol-js-manifest.pot')
def import_strings(input_dir):
print("Importing")
## Import string for manifest.py
parser = etree.XMLParser(remove_blank_text=True)
tree = etree.parse('client/manifest.xml.in', parser)
root = tree.getroot()
xml_version1 = "{http://schemas.microsoft.com/office/mailappversionoverrides}";
xml_version = "{http://schemas.microsoft.com/office/mailappversionoverrides/1.1}";
xml_bt = "{http://schemas.microsoft.com/office/officeappbasictypes/1.0}"
xml_office = "{http://schemas.microsoft.com/office/appforoffice/1.1}"
resources = root.find(xml_version1 + "VersionOverrides").find(xml_version + "VersionOverrides").find(xml_version + "Resources")
short_strings = resources.find(xml_bt + "ShortStrings")
long_strings = resources.find(xml_bt + "LongStrings")
description = root.find(xml_office + "Description")
for lang in next(os.walk(input_dir))[1]:
- manifest_po_path = input_dir + '/' + lang + '/manifest.po'
+ manifest_po_path = input_dir + '/' + lang + '/gpgol-js-manifest.po'
if not os.path.isfile(manifest_po_path):
continue
po = polib.pofile(manifest_po_path)
def fill_string(child, bt=True):
for entry in po.translated_entries():
if entry.msgid == child.get("DefaultValue"):
lang_xml = lang + '-' + lang.upper() # HACK to use same format as outlook wants
override_found = False
for override in child:
if override.get('Locale') == lang_xml:
override.set("Value", entry.msgstr)
override_found = True
if not override_found:
if bt:
override = etree.Element(xml_bt + "Override")
else:
override = etree.Element("Override")
override.set("Locale", lang_xml)
override.set("Value", entry.msgstr)
child.append(override)
fill_string(description, bt=False)
for string in short_strings:
fill_string(string)
for string in long_strings:
fill_string(string)
tree.write('client/manifest.xml.in', pretty_print=True)
## Import strings for web vue
lang_obj = {}
for lang in next(os.walk(input_dir))[1]:
web_po_path = input_dir + '/' + lang + '/gpgol-js-web.po'
if not os.path.isfile(web_po_path):
continue
po = polib.pofile(web_po_path)
obj = {}
for entry in po.translated_entries():
obj[entry.msgid] = entry.msgstr
lang_obj[lang + '-' + lang.upper()] = obj
f = open("web/src/translation.js", "w")
f.write("export default " + json.dumps(lang_obj))
f.close()
def main():
parser = argparse.ArgumentParser()
subparsers = parser.add_subparsers(title="subcommands",
description='valid subcommands',
dest='command')
extract_parser = subparsers.add_parser('extract', help="Extract strings from source code")
extract_parser.add_argument("output_dir", default="pot_dir")
import_parser = subparsers.add_parser('import', help="Import strings to source code")
import_parser.add_argument("input_dir", default="po")
args = parser.parse_args()
if args.command == 'extract':
try:
os.mkdir(args.output_dir)
except FileExistsError:
pass
extract_strings(args.output_dir)
elif args.command == 'import':
import_strings(args.input_dir)
if __name__ == "__main__":
main()
diff --git a/po/de/manifest.po b/po/de/gpgol-js-manifest.po
similarity index 100%
rename from po/de/manifest.po
rename to po/de/gpgol-js-manifest.po
diff --git a/po/fr/manifest.po b/po/fr/gpgol-js-manifest.po
similarity index 100%
rename from po/fr/manifest.po
rename to po/fr/gpgol-js-manifest.po
diff --git a/pot/manifest.pot b/pot/gpgol-js-manifest.pot
similarity index 100%
rename from pot/manifest.pot
rename to pot/gpgol-js-manifest.pot
diff --git a/scripts/update_i18n.sh b/scripts/update_i18n.sh
index d4db5fd..125ba4c 100755
--- a/scripts/update_i18n.sh
+++ b/scripts/update_i18n.sh
@@ -1,17 +1,17 @@
#!/usr/bin/env bash
if [ -f ./i18n.py ] ; then
python ./i18n.py extract pot
for i in de fr ; do
- for j in gpgol-js-native gpgol-js-web manifest ; do
+ for j in gpgol-js-native gpgol-js-web gpgol-js-manifest ; do
msgmerge -U po/${i}/${j}.po pot/${j}.pot
done
done
else
echo "please call this script from the top level directory of GpgOL/Web!"
exit 1
fi
exit 0
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Feb 26, 6:52 PM (1 d, 15 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
a5/b0/5991241d1325ef7b8d4633705c7b
Attached To
rOJ GpgOL.js
Event Timeline
Log In to Comment