Translate the filter string error at display time (#5283)

Keep the parser message untranslated so FilterStringLog stays in the source
language, and format the user-facing text in error() instead. That gives both
consumers what they need without localizing the log.

Q_DECLARE_TR_FUNCTIONS provides the tr() context without deriving from QObject,
which would have deleted copy/move assignment and broken the by-value uses in
PlayerActions and ViewZone. Drop the unreachable line-number branch; peglib
reports 1-based lines, so every message keeps master's "position" wording.

Co-authored-by: Lukas Brübach <44814898+BruebachL@users.noreply.github.com>
This commit is contained in:
ebbit1q 2026-11-07 16:26:31 +01:00 • committed by GitHub
parent 7487a383a3
commit 4b224f7fe5
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 34 additions and 6 deletions

View file

@ -506,6 +506,14 @@ FilterString::FilterString()
_error = "Not initialized";
}
QString FilterString::error() const
{
if (_errorColumn == 0) {
return _error;
}
return tr("Error at position %1: %2").arg(_errorColumn).arg(_error);
}
FilterString::FilterString(const QString &expr, const CardSearchLanguage &searchLanguage)
{
QByteArray ba = expr.simplified().toUtf8();
@ -522,7 +530,8 @@ FilterString::FilterString(const QString &expr, const CardSearchLanguage &search
searchLanguageContext = searchLanguage;
search.set_logger([&](size_t /*ln*/, size_t col, const std::string &msg) {
_error = QString("Error at position %1: %2").arg(col).arg(QString::fromStdString(msg));
_error = QString::fromStdString(msg);
_errorColumn = col;
});
if (!search.parse(ba.data(), result)) {

View file

@ -7,6 +7,7 @@
#ifndef FILTER_STRING_H
#define FILTER_STRING_H
#include <QCoreApplication>
#include <QLoggingCategory>
#include <QString>
#include <functional>
@ -33,6 +34,8 @@ typedef AstBase<EmptyType> Ast;
class FilterString
{
Q_DECLARE_TR_FUNCTIONS(FilterString)
public:
FilterString();
explicit FilterString(const QString &exp, const CardSearchLanguage &searchLanguage = {});
@ -45,18 +48,18 @@ public:
return result(card);
}
bool valid()
[[nodiscard]] bool valid() const
{
return _error.isEmpty();
}
QString error()
{
return _error;
}
[[nodiscard]] QString error() const;
private:
// Untranslated parser message; error() adds the translated prefix, logging uses it as-is.
QString _error;
// Column reported by the parser, or 0 when the message carries no position.
size_t _errorColumn = 0;
Filter result;
};

View file

@ -279,6 +279,22 @@ TEST_F(SetQuery, ComparisonMatchesByReleaseDate)
EXPECT_FALSE(FilterString("set<EOE").check(inBothSets));
}
TEST(FilterStringError, InvalidExpressionIsInvalidAndReportsAPosition)
{
const FilterString filter("t:(");
EXPECT_FALSE(filter.valid());
EXPECT_TRUE(filter.error().contains("position")) << "unexpected error text: " << filter.error().toStdString();
}
TEST(FilterStringError, ValidExpressionHasNoError)
{
const FilterString filter("t:creature");
EXPECT_TRUE(filter.valid());
EXPECT_TRUE(filter.error().isEmpty());
}
int main(int argc, char **argv)
{
::testing::InitGoogleTest(&argc, argv);