Cockatrice/libcockatrice_utility/libcockatrice/utility/translation_loader.h
BruebachL 675e1f7447
Some checks are pending
CodeQL / Analyze (cpp) (push) Waiting to run
CodeQL / Analyze (actions) (push) Waiting to run
Build Desktop / Configure (push) Waiting to run
Build Desktop / Debian 13 (push) Blocked by required conditions
Build Desktop / Debian 12 (push) Blocked by required conditions
Build Desktop / Fedora 44 (push) Blocked by required conditions
Build Desktop / Fedora 43 (push) Blocked by required conditions
Build Desktop / Servatrice_Debian 12 (push) Blocked by required conditions
Build Desktop / Ubuntu 24.04 (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 (push) Blocked by required conditions
Build Desktop / Arch (push) Blocked by required conditions
Build Desktop / Ubuntu 26.04 cover (push) Blocked by required conditions
Build Desktop / macOS 13 Intel (push) Blocked by required conditions
Build Desktop / macOS 14 (push) Blocked by required conditions
Build Desktop / macOS 15 (push) Blocked by required conditions
Build Desktop / macOS 26 Debug (push) Blocked by required conditions
Build Desktop / Windows 10 (push) Blocked by required conditions
Build Desktop / Prune caches (push) Waiting to run
Build Docker / Servatrice (arm) (push) Waiting to run
Build Docker / Servatrice (x86) (push) Waiting to run
Build Docker / Publish multi-platform Servatrice image (push) Blocked by required conditions
[Translation] Name languages without reading the translation files (#7662)
* [Translation] Name languages without reading the translation files

The language selector derived every label from the language's own .qm file:
it loaded the file and translated the literal "English" in the "i18n"
context. lupdate could never extract that literal from the callers, which
pass the DEFAULT_LANG_NAME macro, so a dummy QT_TRANSLATE_NOOP in
cache_settings.cpp existed to give it something to extract. #7050 deleted
that dummy, the next lupdate push dropped the string from Transifex, and
the following pull rewrote the .ts files without it. lrelease only emits
translated messages, so 22 of 25 .qm files returned an empty name.

The names are data, not translations: a selector has to be readable before
any translation is loaded, and a label must not depend on the translation
of the language it names. TranslationLoader::languageDisplayName now holds
them next to availableLanguages, falling back to Qt's native language name
and then to the code, so a language added later still gets a label.

Also select the configured language by item data instead of by matching the
translated "English" string, which pointed at the wrong row whenever the
lookup came back empty, and restore that English entry's own name: en_US is
"English (United States)" so it can no longer be confused with the
untranslated default.

Oracle shared the code path, so its wizard language page is fixed too.

* [Translation] Name the default language selection once

Review follow-up: log the unresolvable language through the page's own
category, and move the en_US entry both selectors fall back to for an unset
setting into TranslationLoader::defaultLanguageCode.

---------

Co-authored-by: Lukas Brübach <Bruebach.Lukas@bdosecurity.de>
2026-10-26 02:39:26 +01:00

112 lines
4.7 KiB
C++

#ifndef TRANSLATION_LOADER_H
#define TRANSLATION_LOADER_H
#include <QList>
#include <QLoggingCategory>
#include <QString>
class QTranslator;
Q_DECLARE_LOGGING_CATEGORY(TranslationLoaderLog)
/** @brief The outcome of a translation load attempt. */
enum class TranslationLoadResult
{
Loaded, /**< A translation file was found and loaded into the translator. */
Missing, /**< The search directories exist but hold no file for this language. */
NoDirectory /**< Not a single search directory exists, so no lookup could be attempted. */
};
namespace TranslationLoader
{
/**
* @brief Every directory that may hold Qt's own translations, in order of preference.
*
* More than one entry is needed because a relocated Qt (Flatpak, AppImage, a bundled SDK) can report
* a prefix-relative translations path that does not exist: the KDE runtime reports /usr/translations
* while its qt_*.qm files live in /usr/share/qt6/translations.
*/
QList<QString> qtTranslationCandidates();
/**
* @brief The candidates that actually exist, most likely first, so no caller searches a path that is
* not there.
*/
QList<QString> qtTranslationPaths();
/**
* @brief The directories to search for an application's own translation files, in order of preference.
*
* @param _installedPath The location the platform installs the translation files in, derived from the
* executable's path.
* @return The installed location, followed by the executable's own directory: a build tree keeps the
* .qm files next to the binary instead of in the installed layout, so a developer build would
* otherwise find no translations at all.
*/
QList<QString> applicationTranslationPaths(const QString &_installedPath);
/**
* @brief Loads the first _nameHint + language translation file found in _directories into _translator.
*
* QTranslator discards whatever it held before every load, so each translation file needs its own
* translator; loading a second file into the first one silently drops the first.
*
* @param _translator The translator to load into.
* @param _nameHint The translation file name without its language and suffix, e.g. "qt_de" or "cockatrice_de".
* @param _directories The directories to search, in order of preference.
* @return What the search found.
*/
TranslationLoadResult loadFrom(QTranslator &_translator, const QString &_nameHint, const QList<QString> &_directories);
/**
* @brief Loads Qt's own translation for _lang, which covers the strings of Qt's own dialogs.
*
* @param _translator The translator to load into; must not also be used for an application translation.
* @param _lang The language code, e.g. "de". An empty language loads nothing.
* @return What the search found.
*/
TranslationLoadResult loadQt(QTranslator &_translator, const QString &_lang);
/**
* @brief The language codes of the <_prefix>_<lang>.qm files in _directories, sorted by code.
*
* Lets a language selector offer exactly the languages that can be loaded.
*/
QList<QString> availableLanguages(const QString &_prefix, const QList<QString> &_directories);
/**
* @brief A human-readable name for a language, e.g. "Deutsch (German)".
*
* The names are data, not translations: a language selector has to be readable before any translation
* is loaded, and its labels must not depend on the translation of the language they name.
*
* @param _lang The language code, e.g. "de" or "pt_BR".
* @return The curated name; for a code it does not know, the native language name; the code itself when
* Qt has no name for it either; an empty string when _lang is empty.
*/
QString languageDisplayName(const QString &_lang);
/**
* @brief The language a client runs with when none is configured, for preselecting a selector entry.
*
* English is the untranslated default and ships no translation file of its own, so a selector has nothing
* of its own to point at and uses the entry that carries an English translation instead.
*/
QString defaultLanguageCode();
/**
* @brief Loads the <_prefix>_<_lang>.qm translation file from _directories into _translator.
*
* @param _translator The translator to load into; must not also be used for Qt's own translation.
* @param _prefix The translation file prefix, e.g. "cockatrice" or "oracle".
* @param _lang The language code, e.g. "de". An empty language loads nothing.
* @param _directories The directories to search, in order of preference.
* @return What the search found.
*/
TranslationLoadResult loadApplication(QTranslator &_translator,
const QString &_prefix,
const QString &_lang,
const QList<QString> &_directories);
} // namespace TranslationLoader
#endif // TRANSLATION_LOADER_H