From 7f99f8803d0d28128e29c2ccebd9899c40b6ef29 Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 18 Oct 2023 15:26:07 +0200 Subject: [PATCH 1/3] Remove deprecated option `--confdir` This was meant to be used before roaming profiles were supported on Windows. Fixes: #11246 --- src/gui/main.cpp | 8 -------- src/libsync/configfile.h | 1 + 2 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/gui/main.cpp b/src/gui/main.cpp index fa8a51d739c..81a878a4f30 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -127,7 +127,6 @@ CommandLineOptions parseOptions(const QStringList &arguments) auto logDirOption = addOption({QStringLiteral("logdir"), QStringLiteral("Write each sync log output in a new file in folder."), QStringLiteral("name")}); auto logFlushOption = addOption({QStringLiteral("logflush"), QStringLiteral("Flush the log file after every write.")}); auto logDebugOption = addOption({QStringLiteral("logdebug"), QStringLiteral("Output debug-level messages in the log.")}); - auto confDirOption = addOption({QStringLiteral("confdir"), QStringLiteral("Use the given configuration folder."), QStringLiteral("dirname")}); auto debugOption = addOption({QStringLiteral("debug"), QStringLiteral("Enable debug mode.")}); addOption({QStringLiteral("cmd"), QStringLiteral("Forward all arguments to the cmd client. This argument must be the first.")}); @@ -160,13 +159,6 @@ CommandLineOptions parseOptions(const QStringList &arguments) if (parser.isSet(logDebugOption)) { out.logDebug = true; } - if (parser.isSet(confDirOption)) { - const auto confDir = parser.value(confDirOption); - if (!ConfigFile::setConfDir(confDir)) { - displayHelpText(QStringLiteral("Invalid path passed to --confdir")); - std::exit(1); - } - } if (parser.isSet(debugOption)) { out.logDebug = true; out.debugMode = true; diff --git a/src/libsync/configfile.h b/src/libsync/configfile.h index a17026c11df..9a20250c486 100644 --- a/src/libsync/configfile.h +++ b/src/libsync/configfile.h @@ -150,6 +150,7 @@ class OWNCLOUDSYNC_EXPORT ConfigFile bool moveToTrash() const; void setMoveToTrash(bool); + /// Used for testing, so we do not change the user's config file. static bool setConfDir(const QString &value); bool optionalDesktopNotifications() const; From 552c0e3a809d92be30ca534ee59316073e32083a Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 18 Oct 2023 15:28:33 +0200 Subject: [PATCH 2/3] Do translation setup before command-line option parsing Now the messages during early start-up can be localized. Fixes: #11142 --- src/gui/application.cpp | 115 ++------------------------------------ src/gui/application.h | 6 +- src/gui/main.cpp | 121 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 125 insertions(+), 117 deletions(-) diff --git a/src/gui/application.cpp b/src/gui/application.cpp index fd9eb1179f2..a16007f3781 100644 --- a/src/gui/application.cpp +++ b/src/gui/application.cpp @@ -45,10 +45,8 @@ #include #include #include -#include #include #include -#include #if QT_VERSION >= QT_VERSION_CHECK(6, 3, 0) #include @@ -70,13 +68,12 @@ ownCloudGui *Application::gui() const Application *Application::_instance = nullptr; -Application::Application(Platform *platform, bool debugMode) +Application::Application(Platform *platform, const QString &displayLanguage, bool debugMode) : _debugMode(debugMode) + , _displayLanguage(displayLanguage) { platform->migrate(); - setupTranslations(); - qCInfo(lcApplication) << "Plugin search paths:" << qApp->libraryPaths(); // Check vfs plugins @@ -246,110 +243,6 @@ bool Application::debugMode() return _debugMode; } -QString substLang(const QString &lang) -{ - // Map the more appropriate script codes - // to country codes as used by Qt and - // transifex translation conventions. - - // Simplified Chinese - if (lang == QLatin1String("zh_Hans")) - return QStringLiteral("zh_CN"); - // Traditional Chinese - if (lang == QLatin1String("zh_Hant")) - return QStringLiteral("zh_TW"); - return lang; -} - -void Application::setupTranslations() -{ - const auto trPath = Translations::translationsDirectoryPath(); - qCDebug(lcApplication) << "Translations directory path:" << trPath; - - QStringList uiLanguages = QLocale::system().uiLanguages(); - qCDebug(lcApplication) << "UI languages:" << uiLanguages; - - // the user can also set a locale in the settings, so we need to load the config file - const ConfigFile cfg; - - // we need to track the enforced language separately, since we need to distinguish between locale-provided - // and user-enforced one below - const QString enforcedLocale = cfg.uiLanguage(); - qCDebug(lcApplication) << "Enforced language:" << enforcedLocale; - - // note that user-enforced language are prioritized over the theme enforced one - // to make testing easier. - if (!enforcedLocale.isEmpty()) { - uiLanguages.prepend(enforcedLocale); - } - - QTranslator *translator = new QTranslator(this); - QTranslator *qtTranslator = new QTranslator(this); - QTranslator *qtkeychainTranslator = new QTranslator(this); - - for (QString lang : qAsConst(uiLanguages)) { - lang.replace(QLatin1Char('-'), QLatin1Char('_')); // work around QTBUG-25973 - lang = substLang(lang); - const QString trFile = Translations::translationsFilePrefix() + lang; - if (translator->load(trFile, trPath) || lang.startsWith(QLatin1String("en"))) { - // Permissive approach: Qt and keychain translations - // may be missing, but Qt translations must be there in order - // for us to accept the language. Otherwise, we try with the next. - // "en" is an exception as it is the default language and may not - // have a translation file provided. - qCInfo(lcApplication) << "Using" << lang << "translation"; - _displayLanguage = lang; - - const QString qtTrPath = QLibraryInfo::path(QLibraryInfo::TranslationsPath); - qCDebug(lcApplication) << "qtTrPath:" << qtTrPath; - const QString qtTrFile = QLatin1String("qt_") + lang; - qCDebug(lcApplication) << "qtTrFile:" << qtTrFile; - const QString qtBaseTrFile = QLatin1String("qtbase_") + lang; - qCDebug(lcApplication) << "qtBaseTrFile:" << qtBaseTrFile; - - if (!qtTranslator->load(qtTrFile, qtTrPath)) { - if (!qtTranslator->load(qtTrFile, trPath)) { - if (!qtTranslator->load(qtBaseTrFile, qtTrPath)) { - if (!qtTranslator->load(qtBaseTrFile, trPath)) { - qCCritical(lcApplication) << "Could not load Qt translations"; - } - } - } - } - - const QString qtkeychainTrFile = QLatin1String("qtkeychain_") + lang; - if (!qtkeychainTranslator->load(qtkeychainTrFile, qtTrPath)) { - if (!qtkeychainTranslator->load(qtkeychainTrFile, trPath)) { - qCCritical(lcApplication) << "Could not load qtkeychain translations"; - } - } - - if (!translator->isEmpty() && !qApp->installTranslator(translator)) { - qCCritical(lcApplication) << "Failed to install translator"; - } - if (!qtTranslator->isEmpty() && !qApp->installTranslator(qtTranslator)) { - qCCritical(lcApplication) << "Failed to install Qt translator"; - } - if (!qtkeychainTranslator->isEmpty() && !qApp->installTranslator(qtkeychainTranslator)) { - qCCritical(lcApplication) << "Failed to install qtkeychain translator"; - } - - // makes sure widgets with locale-dependent formatting, e.g., QDateEdit, display the correct formatting - // if the language is provided by the system locale anyway (i.e., coming from QLocale::system().uiLanguages()), we should - // not mess with the system locale, though - // if we did, we would enforce a locale for no apparent reason - // see https://github.com/owncloud/client/issues/8608 for more information - if (enforcedLocale == lang) { - QLocale newLocale(lang); - qCDebug(lcApplication) << "language" << lang << "was enforced, changing default locale to" << newLocale; - QLocale::setDefault(newLocale); - } - - break; - } - } -} - void Application::openVirtualFile(const QString &filename) { QString virtualFileExt = Theme::instance()->appDotVirtualFileSuffix(); @@ -396,10 +289,10 @@ bool Application::eventFilter(QObject *obj, QEvent *event) return QObject::eventFilter(obj, event); } -std::unique_ptr Application::createInstance(Platform *platform, bool debugMode) +std::unique_ptr Application::createInstance(Platform *platform, const QString &displayLanguage, bool debugMode) { Q_ASSERT(!_instance); - _instance = new Application(platform, debugMode); + _instance = new Application(platform, displayLanguage, debugMode); return std::unique_ptr(_instance); } diff --git a/src/gui/application.h b/src/gui/application.h index e2343d1abc8..8ae203d041a 100644 --- a/src/gui/application.h +++ b/src/gui/application.h @@ -47,7 +47,7 @@ class Application : public QObject { Q_OBJECT public: - static std::unique_ptr createInstance(Platform *platform, bool debugMode); + static std::unique_ptr createInstance(Platform *platform, const QString &displayLanguage, bool debugMode); ~Application(); bool debugMode(); @@ -72,8 +72,6 @@ public slots: void tryTrayAgain(); protected: - void setupTranslations(); - bool eventFilter(QObject *obj, QEvent *event) override; protected slots: @@ -83,7 +81,7 @@ protected slots: void slotAccountStateRemoved() const; private: - explicit Application(Platform *platform, bool debugMode); + explicit Application(Platform *platform, const QString &displayLanguage, bool debugMode); QPointer _gui = {}; diff --git a/src/gui/main.cpp b/src/gui/main.cpp index 81a878a4f30..29a58f5e2ed 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -36,9 +36,11 @@ #include #include +#include #include #include #include +#include #ifdef Q_OS_WIN #include #endif @@ -242,7 +244,119 @@ void setupLogging(const CommandLineOptions &options) << "version:" << Theme::instance()->aboutVersions(Theme::VersionFormat::OneLiner); qCInfo(lcMain) << "Arguments:" << qApp->arguments(); } + +QString setupTranslations(QApplication *app) +{ + const auto trPath = Translations::translationsDirectoryPath(); + qCDebug(lcMain) << "Translations directory path:" << trPath; + + QStringList uiLanguages = QLocale::system().uiLanguages(); + qCDebug(lcMain) << "UI languages:" << uiLanguages; + + // the user can also set a locale in the settings, so we need to load the config file + const ConfigFile cfg; + + // we need to track the enforced language separately, since we need to distinguish between locale-provided + // and user-enforced one below + const QString enforcedLocale = cfg.uiLanguage(); + qCDebug(lcMain) << "Enforced language:" << enforcedLocale; + + // note that user-enforced language are prioritized over the theme enforced one + // to make testing easier. + if (!enforcedLocale.isEmpty()) { + uiLanguages.prepend(enforcedLocale); + } + + QString displayLanguage; + + auto substLang = [](const QString &lang) { + // Map the more appropriate script codes + // to country codes as used by Qt and + // transifex translation conventions. + + if (lang == QLatin1String("zh_Hans")) { + // Simplified Chinese + return QStringLiteral("zh_CN"); + } else if (lang == QLatin1String("zh_Hant")) { + // Traditional Chinese + return QStringLiteral("zh_TW"); + } + + return lang; + }; + + for (QString lang : qAsConst(uiLanguages)) { + lang.replace(QLatin1Char('-'), QLatin1Char('_')); // work around QTBUG-25973 + lang = substLang(lang); + const QString trFile = Translations::translationsFilePrefix() + lang; + QTranslator *translator = new QTranslator(app); + + if (translator->load(trFile, trPath) || lang.startsWith(QLatin1String("en"))) { + // Permissive approach: Qt and keychain translations + // may be missing, but Qt translations must be there in order + // for us to accept the language. Otherwise, we try with the next. + // "en" is an exception as it is the default language and may not + // have a translation file provided. + qCInfo(lcMain) << "Using" << lang << "translation"; + displayLanguage = lang; + + const QString qtTrPath = QLibraryInfo::path(QLibraryInfo::TranslationsPath); + qCDebug(lcMain) << "qtTrPath:" << qtTrPath; + const QString qtTrFile = QLatin1String("qt_") + lang; + qCDebug(lcMain) << "qtTrFile:" << qtTrFile; + const QString qtBaseTrFile = QLatin1String("qtbase_") + lang; + qCDebug(lcMain) << "qtBaseTrFile:" << qtBaseTrFile; + + QTranslator *qtTranslator = new QTranslator(app); + QTranslator *qtkeychainTranslator = new QTranslator(app); + + if (!qtTranslator->load(qtTrFile, qtTrPath)) { + if (!qtTranslator->load(qtTrFile, trPath)) { + if (!qtTranslator->load(qtBaseTrFile, qtTrPath)) { + if (!qtTranslator->load(qtBaseTrFile, trPath)) { + qCCritical(lcMain) << "Could not load Qt translations"; + } + } + } + } + + const QString qtkeychainTrFile = QLatin1String("qtkeychain_") + lang; + if (!qtkeychainTranslator->load(qtkeychainTrFile, qtTrPath)) { + if (!qtkeychainTranslator->load(qtkeychainTrFile, trPath)) { + qCCritical(lcMain) << "Could not load qtkeychain translations"; + } + } + + if (!translator->isEmpty() && !qApp->installTranslator(translator)) { + qCCritical(lcMain) << "Failed to install translator"; + } + if (!qtTranslator->isEmpty() && !qApp->installTranslator(qtTranslator)) { + qCCritical(lcMain) << "Failed to install Qt translator"; + } + if (!qtkeychainTranslator->isEmpty() && !qApp->installTranslator(qtkeychainTranslator)) { + qCCritical(lcMain) << "Failed to install qtkeychain translator"; + } + + // makes sure widgets with locale-dependent formatting, e.g., QDateEdit, display the correct formatting + // if the language is provided by the system locale anyway (i.e., coming from QLocale::system().uiLanguages()), we should + // not mess with the system locale, though + // if we did, we would enforce a locale for no apparent reason + // see https://github.com/owncloud/client/issues/8608 for more information + if (enforcedLocale == lang) { + QLocale newLocale(lang); + qCDebug(lcMain) << "language" << lang << "was enforced, changing default locale to" << newLocale; + QLocale::setDefault(newLocale); + } + + break; + } + + delete translator; + } + + return displayLanguage; } +} // Anonymous namespace int main(int argc, char **argv) { @@ -298,6 +412,9 @@ int main(int argc, char **argv) app.setWindowIcon(Theme::instance()->applicationIcon()); app.setApplicationVersion(Theme::instance()->versionSwitchOutput()); + // Load the translations before option parsing, so we can localize help text and error messages. + QString displayLanguage = setupTranslations(&app); + // parse the arguments before we handle singleApplication // errors and help/version need to be handled in this instance const auto options = parseOptions(app.arguments()); @@ -336,7 +453,7 @@ int main(int argc, char **argv) auto folderManager = FolderMan::createInstance(); if (!AccountManager::instance()->restore()) { - qCCritical(lcApplication) << "Could not read the account settings, quitting"; + qCCritical(lcMain) << "Could not read the account settings, quitting"; QMessageBox::critical(nullptr, QCoreApplication::translate("account loading", "Error accessing the configuration file"), QCoreApplication::translate("account loading", "There was an error while accessing the configuration file at %1.").arg(ConfigFile::configFile()), QMessageBox::Close); @@ -354,7 +471,7 @@ int main(int argc, char **argv) folderManager->setSyncEnabled(true); - auto ocApp = Application::createInstance(platform.get(), options.debugMode); + auto ocApp = Application::createInstance(platform.get(), displayLanguage, options.debugMode); QObject::connect(platform.get(), &Platform::requestAttention, ocApp->gui(), &ownCloudGui::slotShowSettings); From 8b4766c17ac49e25f029428971650c776c7ec61d Mon Sep 17 00:00:00 2001 From: Erik Verbruggen Date: Wed, 18 Oct 2023 15:32:46 +0200 Subject: [PATCH 3/3] Make command-line options and messages translatable --- changelog/unreleased/11300 | 10 ++++++++++ src/gui/main.cpp | 30 ++++++++++++++++-------------- 2 files changed, 26 insertions(+), 14 deletions(-) create mode 100644 changelog/unreleased/11300 diff --git a/changelog/unreleased/11300 b/changelog/unreleased/11300 new file mode 100644 index 00000000000..94f935affe5 --- /dev/null +++ b/changelog/unreleased/11300 @@ -0,0 +1,10 @@ +Change: Make messages translatable that occur early in start-up + +These messages include the command-line messages (both errors and the +help text). To do this, the deprecated `--confdir` option has been +removed. This option was only used on windows before roaming profiles +were supported. + +https://github.com/owncloud/client/issues/11142 +https://github.com/owncloud/client/issues/11246 +https://github.com/owncloud/client/pull/11300 diff --git a/src/gui/main.cpp b/src/gui/main.cpp index 29a58f5e2ed..19f36c1f3ae 100644 --- a/src/gui/main.cpp +++ b/src/gui/main.cpp @@ -95,9 +95,9 @@ CommandLineOptions parseOptions(const QStringList &arguments) QString descriptionText; QTextStream descriptionTextStream(&descriptionText); - descriptionTextStream - << QStringLiteral("%1 version %2\r\nFile synchronization desktop utility.").arg(Theme::instance()->appName(), OCC::Version::displayString()) - << Qt::endl; + descriptionTextStream << QApplication::translate("CommandLine", "%1 version %2\r\nFile synchronization desktop utility.") + .arg(Theme::instance()->appName(), OCC::Version::displayString()) + << Qt::endl; if (Theme::instance()->appName() == QLatin1String("ownCloud")) { descriptionTextStream @@ -122,19 +122,21 @@ CommandLineOptions parseOptions(const QStringList &arguments) parser.addOption(showSettingsLegacyOption); auto showOption = addOption({{QStringLiteral("s"), QStringLiteral("show")}, - QStringLiteral( + QApplication::translate("CommandLine", "Start with the main window visible, or if it is already running, bring it to the front. By default, the client launches in the background.")}); - auto quitInstanceOption = addOption({{QStringLiteral("q"), QStringLiteral("quit")}, QStringLiteral("Quit the running instance.")}); - auto logFileOption = addOption({QStringLiteral("logfile"), QStringLiteral("Write log to file (use - to write to stdout)."), QStringLiteral("filename")}); - auto logDirOption = addOption({QStringLiteral("logdir"), QStringLiteral("Write each sync log output in a new file in folder."), QStringLiteral("name")}); - auto logFlushOption = addOption({QStringLiteral("logflush"), QStringLiteral("Flush the log file after every write.")}); - auto logDebugOption = addOption({QStringLiteral("logdebug"), QStringLiteral("Output debug-level messages in the log.")}); - auto debugOption = addOption({QStringLiteral("debug"), QStringLiteral("Enable debug mode.")}); - addOption({QStringLiteral("cmd"), QStringLiteral("Forward all arguments to the cmd client. This argument must be the first.")}); + auto quitInstanceOption = addOption({{QStringLiteral("q"), QStringLiteral("quit")}, QApplication::translate("CommandLine", "Quit the running instance.")}); + auto logFileOption = addOption( + {QStringLiteral("logfile"), QApplication::translate("CommandLine", "Write log to file (use - to write to stdout)."), QStringLiteral("filename")}); + auto logDirOption = addOption( + {QStringLiteral("logdir"), QApplication::translate("CommandLine", "Write each sync log output in a new file in folder."), QStringLiteral("name")}); + auto logFlushOption = addOption({QStringLiteral("logflush"), QApplication::translate("CommandLine", "Flush the log file after every write.")}); + auto logDebugOption = addOption({QStringLiteral("logdebug"), QApplication::translate("CommandLine", "Output debug-level messages in the log.")}); + auto debugOption = addOption({QStringLiteral("debug"), QApplication::translate("CommandLine", "Enable debug mode.")}); + addOption({QStringLiteral("cmd"), QApplication::translate("CommandLine", "Forward all arguments to the cmd client. This argument must be the first.")}); // virtual file system parameters (optional) - parser.addPositionalArgument( - QStringLiteral("vfs file"), QStringLiteral("Virtual file system file to be opened (optional)."), {QStringLiteral("[]")}); + parser.addPositionalArgument(QStringLiteral("vfs file"), QApplication::translate("CommandLine", "Virtual file system file to be opened (optional)."), + {QStringLiteral("[]")}); parser.process(arguments); @@ -150,7 +152,7 @@ CommandLineOptions parseOptions(const QStringList &arguments) } if (parser.isSet(logDirOption)) { if (parser.isSet(logFileOption)) { - displayHelpText(QStringLiteral("--logfile and --logdir are mutually exclusive")); + displayHelpText(QApplication::translate("CommandLine", "--logfile and --logdir are mutually exclusive")); std::exit(1); } out.logDir = parser.value(logDirOption);