Compare commits

...

3 Commits

Author SHA1 Message Date
Adriaan de Groot
4beb5fe77a [welcome] Migrate to translation-binding 2025-02-08 23:58:04 +01:00
Adriaan de Groot
11000e11bb [libcalamares] Retire RETRANSLATE_FOR
- Writing out the lambda is not a difficult job.
2025-02-08 23:58:04 +01:00
Adriaan de Groot
24480b29d0 [libcalamaresui] Support for easier retranslation
- Separate out of Retranslator
- Move to separate libcalamares namespace Translation
- Add Binding class as a helper for calling setText() on widgets.
2025-02-08 23:58:04 +01:00
6 changed files with 277 additions and 19 deletions

View File

@@ -19,6 +19,7 @@
#include "Settings.h"
#include "ViewManager.h"
#include "progresstree/ProgressTreeView.h"
#include "translation/Binding.h"
#include "utils/Gui.h"
#include "utils/Logger.h"
#include "utils/Qml.h"
@@ -153,10 +154,14 @@ getWidgetSidebar( Calamares::DebugWindowManager* debug,
aboutDialog->setObjectName( "aboutButton" );
aboutDialog->setIcon( Calamares::defaultPixmap(
Calamares::Information, Calamares::Original, 2 * QSize( defaultFontHeight, defaultFontHeight ) ) );
CALAMARES_RETRANSLATE_FOR(
aboutDialog, aboutDialog->setText( QCoreApplication::translate( "calamares-sidebar", "About", "@button" ) );
aboutDialog->setToolTip(
QCoreApplication::translate( "calamares-sidebar", "Show information about Calamares", "@tooltip" ) ); );
Calamares::Retranslator::attach(
aboutDialog,
[ = ]()
{
aboutDialog->setText( QCoreApplication::translate( "calamares-sidebar", "About", "@button" ) );
aboutDialog->setToolTip( QCoreApplication::translate(
"calamares-sidebar", "Show information about Calamares", "@tooltip" ) );
} );
extraButtons->addWidget( aboutDialog );
aboutDialog->setFlat( true );
aboutDialog->setCheckable( true );
@@ -168,11 +173,14 @@ getWidgetSidebar( Calamares::DebugWindowManager* debug,
debugWindowBtn->setObjectName( "debugButton" );
debugWindowBtn->setIcon( Calamares::defaultPixmap(
Calamares::Bugs, Calamares::Original, 2 * QSize( defaultFontHeight, defaultFontHeight ) ) );
CALAMARES_RETRANSLATE_FOR(
Calamares::Retranslator::attach(
debugWindowBtn,
debugWindowBtn->setText( QCoreApplication::translate( "calamares-sidebar", "Debug", "@button" ) );
debugWindowBtn->setToolTip(
QCoreApplication::translate( "calamares-sidebar", "Show debug information", "@tooltip" ) ); );
[ = ]()
{
debugWindowBtn->setText( QCoreApplication::translate( "calamares-sidebar", "Debug", "@button" ) );
debugWindowBtn->setToolTip(
QCoreApplication::translate( "calamares-sidebar", "Show debug information", "@tooltip" ) );
} );
extraButtons->addWidget( debugWindowBtn );
debugWindowBtn->setFlat( true );
debugWindowBtn->setCheckable( true );

View File

@@ -14,10 +14,12 @@
#include "DllMacro.h"
#include "locale/Translation.h"
#include <QList>
#include <QObject>
#include <QString>
#include <functional>
#include <optional>
class QEvent;
class QLocale;
@@ -115,16 +117,7 @@ private:
* setup and translation code to be mixed together.
*/
#define CALAMARES_RETRANSLATE( body ) Calamares::Retranslator::attach( this, [ = ] { body } )
/** @brief Call code for the given object (widget) when language changes
*
* This is identical to CALAMARES_RETRANSLATE, except the @p body is called
* for the given object, not this object.
*
* NOTE: unlike plain QObject::connect(), the body is **also** called
* immediately after setting up the connection. This allows
* setup and translation code to be mixed together.
*/
#define CALAMARES_RETRANSLATE_FOR( object, body ) Calamares::Retranslator::attach( object, [ = ] { body } )
/** @brief Call a slot in this object when language changes
*
* Given a slot (in method-function-pointer notation), call that slot when the

View File

@@ -16,6 +16,7 @@ set(calamaresui_SOURCES
modulesystem/ModuleManager.cpp
modulesystem/ProcessJobModule.cpp
modulesystem/ViewModule.cpp
translation/Binding.cpp
utils/Gui.cpp
utils/ImageRegistry.cpp
utils/Paste.cpp

View File

@@ -0,0 +1,56 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2022 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#include "Binding.h"
#include "utils/Retranslator.h"
namespace Calamares
{
namespace Translation
{
Binding::Binding( QWidget* parent )
: m_parent( parent )
{
connect( Calamares::Retranslator::instance(), &Calamares::Retranslator::languageChanged, this, &Binding::update );
};
Binding::~Binding()
{
for ( auto* p : m_labels )
{
delete p;
}
}
void
Binding::update()
{
std::for_each( m_labels.begin(), m_labels.end(), [ parent = m_parent ]( BaseUpdater* p ) { p->update( parent ); } );
}
Binding::BaseUpdater::~BaseUpdater() = default;
QString
Binding::BaseUpdater::tr( QWidget* parent ) const
{
QString text = parent->tr( untranslated_string );
if ( args.has_value() )
{
for ( const QString& s : args.value() )
{
text = text.arg( s );
}
}
return text;
}
} // namespace Translation
} // namespace Calamares

View File

@@ -0,0 +1,179 @@
/* === This file is part of Calamares - <https://calamares.io> ===
*
* SPDX-FileCopyrightText: 2022 Adriaan de Groot <groot@kde.org>
* SPDX-License-Identifier: GPL-3.0-or-later
*
* Calamares is Free Software: see the License-Identifier above.
*
*/
#ifndef TRANSLATION_BINDING_H
#define TRANSLATION_BINDING_H
#include "DllMacro.h"
#include <QObject>
#include <QString>
#include <QStringList>
#include <QWidget>
#include <functional>
#include <optional>
namespace Calamares
{
namespace Translation
{
/** @brief An object that can re-translate a whole collection of widgets.
*
* A translation binding should be created for a top-level widget (e.g. a
* view page). Child widgets of that top-level can be added to the binding.
* This removes the need for CALAMARES_RETRANSLATE macro calls.
*
* For text widgets with fixed strings, use add().
* For text widgets with strings with parameters, use add() with a string-list
* parameter. The elements of the list will be substituted into the translated
* string (as usual with QString, using the %1 .. substitution syntax).
* For pages that use a .ui file and generate UI code via uic, use addUi().
*
*/
class UIDLLEXPORT Binding : public QObject
{
Q_OBJECT
public:
Binding( QWidget* parent );
virtual ~Binding() override;
void update();
/** @brief Adds (or updates) the string applied to a widget
*
* Sets the text on @p widget to be the (translated) @p string. If
* @p widget already has a translation set via the Labeler, the
* translation is updated with the new string.
*/
template < typename T >
void add( T* widget, const char* string )
{
auto* p = add_internal( widget, string );
p->update( m_parent );
}
/** @brief Adds (or updates) the string applied to a widget
*
* As add() without the list parameter @p s . The items of
* the list @p s are copied and used as substitutions in the
* (translation of) @p string .
*/
template < typename T >
void add( T* widget, const char* string, const QStringList& s )
{
auto* p = add_internal( widget, string );
p->setArgs( s );
p->update( m_parent );
}
/** @brief Update the substitutions for a widget
*
* When the string doesn't change, but the substitutions to put into
* it do, use updateArguments();
*/
template < typename T >
void updateArguments( T* widget, const QStringList& s )
{
auto* p = find_internal< T >( widget );
if ( p )
{
p->setArgs( s );
p->update( m_parent );
}
}
/** @brief Add a UI-based class to translations */
template < typename T >
void addUi( T* ui )
{
auto it = std::find_if( m_labels.begin(), m_labels.end(), [ = ]( BaseUpdater* p ) { return p->widget == ui; } );
if ( it == m_labels.end() )
{
auto* p = new UiUpdater< T > { ui };
m_labels.append( p );
p->update( m_parent );
}
}
private:
struct UIDLLEXPORT BaseUpdater
{
void* widget = nullptr;
const char* untranslated_string = nullptr;
std::optional< QStringList > args;
BaseUpdater( QWidget* w, const char* s )
: widget( w )
, untranslated_string( s )
{
}
virtual ~BaseUpdater();
virtual void update( QWidget* parent ) = 0;
QString tr( QWidget* parent ) const;
void setArgs( const QStringList& s ) { args = s; }
};
template < typename T >
struct Updater : public BaseUpdater
{
using BaseUpdater::BaseUpdater;
void update( QWidget* parent ) override
{
static_cast< T* >( widget )->setText( tr( parent ) ); // tr() is from BaseUpdater
}
};
template < typename T >
struct UiUpdater : public BaseUpdater
{
UiUpdater( T* ui )
: BaseUpdater( nullptr, nullptr )
{
widget = ui;
}
void update( QWidget* parent ) override { static_cast< T* >( widget )->retranslateUi( parent ); }
};
QWidget* m_parent = nullptr;
QList< BaseUpdater* > m_labels;
template < typename T >
BaseUpdater* add_internal( T* widget, const char* string )
{
auto* p = find_internal< T >( widget );
if ( p )
{
p->untranslated_string = string;
return p;
}
else
{
p = new Updater< T > { widget, string };
m_labels.append( p );
return p;
}
}
template < typename T >
BaseUpdater* find_internal( T* widget )
{
auto it
= std::find_if( m_labels.begin(), m_labels.end(), [ = ]( BaseUpdater* p ) { return p->widget == widget; } );
return ( it != m_labels.end() ) ? ( *it ) : nullptr;
}
};
} // namespace Translation
} // namespace Calamares
#endif

View File

@@ -23,6 +23,7 @@
#include "modulesystem/ModuleManager.h"
#include "modulesystem/RequirementsModel.h"
#include "translation/Binding.h"
#include "utils/Gui.h"
#include "utils/Logger.h"
#include "utils/NamedEnum.h"
@@ -73,7 +74,27 @@ WelcomePage::WelcomePage( Config* config, QWidget* parent )
initLanguages();
CALAMARES_RETRANSLATE_SLOT( &WelcomePage::retranslate );
auto* labeler = new Calamares::Translation::Binding( this );
labeler->add(
ui->supportButton, QT_TR_NOOP( "%1 support" ), { Calamares::Branding::instance()->shortProductName() } );
labeler->add( ui->mainText,
[]()
{
if ( Calamares::Settings::instance()->isSetupMode() )
{
return Calamares::Branding::instance()->welcomeStyleCalamares()
? QT_TR_NOOP( "<h1>Welcome to the Calamares setup program for %1.</h1>" )
: QT_TR_NOOP( "<h1>Welcome to %1 setup.</h1>" );
}
else
{
return Calamares::Branding::instance()->welcomeStyleCalamares()
? QT_TR_NOOP( "<h1>Welcome to the Calamares installer for %1.</h1>" )
: QT_TR_NOOP( "<h1>Welcome to the %1 installer.</h1>" );
}
}(),
{ Calamares::Branding::instance()->versionedName() } );
labeler->addUi( ui );
connect( Calamares::ModuleManager::instance(),
&Calamares::ModuleManager::requirementsComplete,