Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 13 additions & 12 deletions src/app/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
#include "platformutilities.h"
#include "qfield.h"
#include "qgismobileapp.h"
#include "translatormanager.h"
#if WITH_SENTRY
#include "sentry_wrapper.h"
#endif
Expand Down Expand Up @@ -188,31 +189,31 @@ int main( int argc, char **argv )
auto sentryClose = qScopeGuard( [] { sentry_wrapper::close(); } );
#endif

QTranslator qfieldTranslator;
QTranslator qtTranslator;
QTranslator *qfieldTranslator = TranslatorManager::instance()->qfieldTranslator();
QTranslator *qtTranslator = TranslatorManager::instance()->qtTranslator();
bool qfieldTranslatorLoaded = false;
bool qtTranslatorLoaded = false;
if ( !customLanguage.isEmpty() )
{
qfieldTranslatorLoaded = qfieldTranslator.load( QStringLiteral( "qfield_%1" ).arg( customLanguage ), QStringLiteral( ":/i18n/" ), "_" );
qtTranslatorLoaded = qtTranslator.load( QStringLiteral( "qt_%1" ).arg( customLanguage ), QStringLiteral( ":/i18n/" ), "_" );
qfieldTranslatorLoaded = qfieldTranslator->load( QStringLiteral( "qfield_%1" ).arg( customLanguage ), QStringLiteral( ":/i18n/" ), "_" );
qtTranslatorLoaded = qtTranslator->load( QStringLiteral( "qt_%1" ).arg( customLanguage ), QStringLiteral( ":/i18n/" ), "_" );
}
dummyApp->installTranslator( &qtTranslator );
dummyApp->installTranslator( &qfieldTranslator );
dummyApp->installTranslator( qtTranslator );
dummyApp->installTranslator( qfieldTranslator );

delete dummyApp;

QtWebView::initialize();

QgsApplication app( argc, argv, true, profilePath, QStringLiteral( "mobile" ) );

if ( !qfieldTranslatorLoaded || qfieldTranslator.isEmpty() )
if ( !qfieldTranslatorLoaded || qfieldTranslator->isEmpty() )
{
( void ) qfieldTranslator.load( QStringLiteral( "qfield_%1" ).arg( QLocale().name() ), QStringLiteral( ":/i18n/" ), "_" );
( void ) qfieldTranslator->load( QStringLiteral( "qfield_%1" ).arg( QLocale().name() ), QStringLiteral( ":/i18n/" ), "_" );
}
if ( !qtTranslatorLoaded || qtTranslator.isEmpty() )
if ( !qtTranslatorLoaded || qtTranslator->isEmpty() )
{
( void ) qtTranslator.load( QStringLiteral( "qt_%1" ).arg( QLocale().name() ), QStringLiteral( ":/i18n/" ), "_" );
( void ) qtTranslator->load( QStringLiteral( "qt_%1" ).arg( QLocale().name() ), QStringLiteral( ":/i18n/" ), "_" );
}

if ( !customLanguage.isEmpty() )
Expand Down Expand Up @@ -354,8 +355,8 @@ int main( int argc, char **argv )
QCoreApplication::setOrganizationDomain( "opengis.ch" );
QCoreApplication::setApplicationName( qfield::appName );

app.installTranslator( &qtTranslator );
app.installTranslator( &qfieldTranslator );
app.installTranslator( qtTranslator );
app.installTranslator( qfieldTranslator );

qputenv( "QT_QUICK_CONTROLS_STYLE", QByteArray( "Material" ) );
qputenv( "QT_QUICK_CONTROLS_MATERIAL_VARIANT", QByteArray( "Dense" ) );
Expand Down
2 changes: 2 additions & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ set(QFIELD_CORE_SRCS
pluginmanager.cpp
pluginmodel.cpp
resourcesource.cpp
translatormanager.cpp
printlayoutlistmodel.cpp
projectinfo.cpp
projectsource.cpp
Expand Down Expand Up @@ -234,6 +235,7 @@ set(QFIELD_CORE_HDRS
pluginmanager.h
pluginmodel.h
resourcesource.h
translatormanager.h
printlayoutlistmodel.h
projectinfo.h
projectsource.h
Expand Down
54 changes: 54 additions & 0 deletions src/core/appinterface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,20 @@
#include "platformutilities.h"
#include "qfield.h"
#include "qgismobileapp.h"
#include "translatormanager.h"
#if WITH_SENTRY
#include "sentry_wrapper.h"
#endif

#include <QCoreApplication>
#include <QDirIterator>
#include <QFileInfo>
#include <QImageReader>
#include <QLocale>
#include <QQuickItem>
#include <QSettings>
#include <QTemporaryFile>
#include <QTranslator>
#include <qgsapplication.h>
#include <qgsauthmanager.h>
#include <qgsmessagelog.h>
Expand Down Expand Up @@ -227,6 +232,55 @@ QVariantMap AppInterface::availableLanguages() const
return languages;
}

void AppInterface::changeLanguage( const QString &languageCode )
{
if ( !languageCode.isEmpty() && !availableLanguages().contains( languageCode ) )
{
qWarning() << "Language code" << languageCode << "is not available, ignoring language change request";
return;
}

QTranslator *qfieldTranslator = TranslatorManager::instance()->qfieldTranslator();
QTranslator *qtTranslator = TranslatorManager::instance()->qtTranslator();

QCoreApplication::removeTranslator( qtTranslator );
QCoreApplication::removeTranslator( qfieldTranslator );

if ( !qfieldTranslator->load( QStringLiteral( "qfield_%1" ).arg( languageCode ), QStringLiteral( ":/i18n/" ), "_" ) )
{
qWarning() << "Failed to load QField translation for" << languageCode;
}
if ( !qtTranslator->load( QStringLiteral( "qt_%1" ).arg( languageCode ), QStringLiteral( ":/i18n/" ), "_" ) )
{
qWarning() << "Failed to load Qt translation for" << languageCode;
}

QCoreApplication::installTranslator( qtTranslator );
QCoreApplication::installTranslator( qfieldTranslator );

QSettings settings;
settings.setValue( QStringLiteral( "/customLanguage" ), languageCode );

if ( !languageCode.isEmpty() )
{
QLocale customLocale( languageCode );
QLocale::setDefault( customLocale );
QgsApplication::setTranslation( languageCode );
QgsApplication::setLocale( QLocale() );
}
else
{
QLocale systemLocale = QLocale::system();
QLocale::setDefault( systemLocale );
QgsApplication::setTranslation( systemLocale.name() );
QgsApplication::setLocale( systemLocale );
}
if ( mApp )
{
mApp->retranslate();
}
}

bool AppInterface::isFileExtensionSupported( const QString &filename ) const
{
const QFileInfo fi( filename );
Expand Down
8 changes: 8 additions & 0 deletions src/core/appinterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ class AppInterface : public QObject
//! Returns a list of available UI translation languages
Q_INVOKABLE QVariantMap availableLanguages() const;

/**
* Changes the application language to the specified \a languageCode.
* This will reload translators and refresh all QML translations without restarting the app.
* \param languageCode The language code (e.g., "en", "de")
* \see availableLanguages
*/
Q_INVOKABLE void changeLanguage( const QString &languageCode );

//! Returns TRUE if a given \a filename can be opened as a project or standalone dataset.
Q_INVOKABLE bool isFileExtensionSupported( const QString &filename ) const;

Expand Down
40 changes: 40 additions & 0 deletions src/core/translatormanager.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
/***************************************************************************
translatormanager.cpp - TranslatorManager

---------------------
begin : June 2025
copyright : (C) 2025 by Mohsen Dehghanzadeh
email : [email protected]
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "translatormanager.h"

#include <QTranslator>

TranslatorManager::TranslatorManager()
: mQFieldTranslator( std::make_unique<QTranslator>() )
, mQtTranslator( std::make_unique<QTranslator>() )
{
}

QTranslator *TranslatorManager::qfieldTranslator()
{
return mQFieldTranslator.get();
}

QTranslator *TranslatorManager::qtTranslator()
{
return mQtTranslator.get();
}

TranslatorManager *TranslatorManager::instance()
{
static TranslatorManager *sInstance = new TranslatorManager();
return sInstance;
}
56 changes: 56 additions & 0 deletions src/core/translatormanager.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
/***************************************************************************
translatormanager.h - TranslatorManager

---------------------
begin : June 2025
copyright : (C) 2025 by Mohsen Dehghanzadeh
email : [email protected]
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/

#ifndef TRANSLATORMANAGER_H
#define TRANSLATORMANAGER_H

#include <memory>

class QTranslator;

/**
* \brief Provides access to shared translator instances for runtime language switching.
*
* Translators are shared between startup (main.cpp) and runtime switching (AppInterface::changeLanguage).
* \ingroup core
*/
class TranslatorManager
{
public:
//! Constructor
TranslatorManager();

//! Copy constructor is deleted (singleton pattern)
TranslatorManager( const TranslatorManager & ) = delete;

//! Assignment operator is deleted (singleton pattern)
TranslatorManager &operator=( const TranslatorManager & ) = delete;

//! Returns the global QField translator instance.
QTranslator *qfieldTranslator();

//! Returns the global Qt framework translator instance.
QTranslator *qtTranslator();

//! Returns the singleton instance of TranslatorManager.
static TranslatorManager *instance();

private:
std::unique_ptr<QTranslator> mQFieldTranslator;
std::unique_ptr<QTranslator> mQtTranslator;
};

#endif // TRANSLATORMANAGER_H
22 changes: 6 additions & 16 deletions src/qml/QFieldSettings.qml
Original file line number Diff line number Diff line change
Expand Up @@ -715,18 +715,6 @@ Page {
wrapMode: Text.WordWrap
}

Label {
id: languageTip
visible: false

Layout.fillWidth: true
text: qsTr("To apply the selected user interface language, QField needs to completely shutdown and restart.")
font: Theme.tipFont
color: Theme.warningColor

wrapMode: Text.WordWrap
}

QfComboBox {
id: languageComboBox
enabled: true
Expand All @@ -743,8 +731,11 @@ Page {

onCurrentIndexChanged: {
if (currentLanguageCode != undefined) {
settings.setValue("customLanguage", languageCodes[currentIndex]);
languageTip.visible = languageCodes[currentIndex] !== currentLanguageCode;
var newLanguageCode = languageCodes[currentIndex];
if (newLanguageCode !== currentLanguageCode) {
iface.changeLanguage(newLanguageCode);
currentLanguageCode = newLanguageCode;
}
}
}

Expand All @@ -758,12 +749,11 @@ Page {
languageComboBox.model = items.concat(Object.values(languages));
languageComboBox.currentIndex = languageCodes.indexOf(customLanguageCode);
currentLanguageCode = customLanguageCode || '';
languageTip.visible = false;
}
}

Label {
text: qsTr("Found a missing or incomplete language? %1Join the translator community.%2").arg('<a href="https://www.transifex.com/opengisch/qfield-for-qgis/">').arg('</a>')
text: qsTr("Found a missing or incomplete language? %1Join the translator community.%2").arg('<a href="https://explore.transifex.com/opengisch/qfield-for-qgis/">').arg('</a>')
font: Theme.tipFont
color: Theme.secondaryTextColor
textFormat: Qt.RichText
Expand Down
Loading