Page MenuHome GnuPG

No OneTemporary

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

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

Event Timeline