From 196e8fad6849a0407888a3d7791bbec59de3c16a Mon Sep 17 00:00:00 2001 From: erinn Date: Mon, 18 Mar 2019 16:52:46 -0700 Subject: [PATCH] language switcher --- go/gothings/gcd.go | 45 ++++++++++++++++++ i18n/translation_de.ts | 10 ++-- i18n/translation_en.qm | Bin 4085 -> 4085 bytes i18n/translation_en.ts | 10 ++-- main.go | 68 +++++++++++++++++----------- qml.qrc | 1 + qml/panes/SettingsPane.qml | 34 ++++++++++++-- qml/widgets/controls/FlagButton.qml | 43 ++++++++++++++++++ ui.pro | 3 +- 9 files changed, 172 insertions(+), 42 deletions(-) create mode 100644 qml/widgets/controls/FlagButton.qml diff --git a/go/gothings/gcd.go b/go/gothings/gcd.go index 48e2005b..d8bef4e6 100644 --- a/go/gothings/gcd.go +++ b/go/gothings/gcd.go @@ -4,6 +4,7 @@ import ( "cwtch.im/cwtch/event" "cwtch.im/ui/go/constants" "cwtch.im/ui/go/cwutil" + "github.com/therecipe/qt/qml" "cwtch.im/ui/go/gobjects" "cwtch.im/ui/go/the" @@ -19,6 +20,8 @@ type GrandCentralDispatcher struct { OutgoingMessages chan gobjects.Letter UIState InterfaceState + QMLEngine *qml.QQmlApplicationEngine + Translator *core.QTranslator _ string `property:"currentOpenConversation"` _ float32 `property:"themeScale"` @@ -42,6 +45,7 @@ type GrandCentralDispatcher struct { // settings helpers _ func(str string) `signal:"InvokePopup"` + _ func(zoom, locale string) `signal:"SupplySettings"` _ func(groupID, name, server, invitation string, accepted bool, addrbooknames, addrbookaddrs []string) `signal:"SupplyGroupSettings"` _ func(onion, nick string) `signal:"SupplyPeerSettings"` @@ -55,12 +59,15 @@ type GrandCentralDispatcher struct { _ func(server, groupName string) `signal:"createGroup,auto"` _ func(groupID string) `signal:"leaveGroup,auto"` _ func(groupID string) `signal:"acceptGroup,auto"` + _ func() `signal:"requestSettings,auto"` + _ func(zoom, locale string) `signal:"saveSettings,auto"` _ func(groupID string) `signal:"requestGroupSettings,auto"` _ func(groupID, nick string) `signal:"saveGroupSettings,auto"` _ func() `signal:"requestPeerSettings,auto"` _ func(onion, nick string) `signal:"savePeerSettings,auto"` _ func(onion, groupID string) `signal:"inviteToGroup,auto"` _ func(onion, key, nick string) `signal:"setAttribute,auto"` + _ func(locale string) `signal:"setLocale,auto"` } func (this *GrandCentralDispatcher) sendMessage(message string, mID string) { @@ -246,6 +253,25 @@ func (this *GrandCentralDispatcher) loadMessagesPaneHelper(handle string) { } } +func (this *GrandCentralDispatcher) requestSettings() { + zoom, _ := the.Peer.GetProfile().GetAttribute("settings.zoom") + locale, _ := the.Peer.GetProfile().GetAttribute("settings.locale") + this.SupplySettings(zoom, locale) +} + +func (this *GrandCentralDispatcher) saveSettings(zoom, locale string) { + // saveSettings accidentally gets called once when the app first starts but before the app has been prepared + // so let's just ignore that one + if the.CwtchApp == nil { + return + } + + the.CwtchApp.EventBus().Publish(event.NewEvent(event.SetAttribute, map[event.Field]string{ + event.Key: "settings.zoom", + event.Data: zoom, + })) +} + func (this *GrandCentralDispatcher) requestPeerSettings() { contact := the.Peer.GetContact(this.CurrentOpenConversation()) if contact == nil { @@ -479,4 +505,23 @@ func (this *GrandCentralDispatcher) setAttribute(onion, key, value string) { })) this.UIState.UpdateContactAttribute(onion, key, value) } +} + +func (this *GrandCentralDispatcher) setLocale(locale string) { + this.SetLocale_helper(locale) + + the.CwtchApp.EventBus().Publish(event.NewEvent(event.SetAttribute, map[event.Field]string{ + event.Key: "settings.locale", + event.Data: locale, + })) + + this.SupplySettings("", locale) +} + +func (this *GrandCentralDispatcher) SetLocale_helper(locale string) { + core.QCoreApplication_RemoveTranslator(this.Translator) + this.Translator = core.NewQTranslator(nil) + this.Translator.Load("translation_"+locale, ":/i18n/", "", "") + core.QCoreApplication_InstallTranslator(this.Translator) + this.QMLEngine.Retranslate() } \ No newline at end of file diff --git a/i18n/translation_de.ts b/i18n/translation_de.ts index 77d49604..929fd3b1 100644 --- a/i18n/translation_de.ts +++ b/i18n/translation_de.ts @@ -258,30 +258,30 @@ SettingsPane - + cwtch-settings-title Cwtch Settings title - + zoom-label Interface zoom (mostly affects text and button sizes) - + large-text-label - + default-scaling-text "Default size text (scale factor: " - + small-text-label diff --git a/i18n/translation_en.qm b/i18n/translation_en.qm index 86fa0225802b69c6f24db498307a3c5f48abad8f..0f7873d0249433a5c4f2110680ade10448544d22 100644 GIT binary patch delta 13 Ucmew=|5biNB|l^C SettingsPane - + cwtch-settings-title Cwtch Settings title Cwtch Settings - + zoom-label Interface zoom (mostly affects text and button sizes) Interface zoom (mostly affects text and button sizes) - + large-text-label Large - + default-scaling-text "Default size text (scale factor: " Default size text (scale factor: - + small-text-label Small diff --git a/main.go b/main.go index 30ae3c0b..16352f67 100644 --- a/main.go +++ b/main.go @@ -7,19 +7,19 @@ import ( "cwtch.im/ui/go/gobjects" "cwtch.im/ui/go/gothings" "cwtch.im/ui/go/the" + "fmt" "git.openprivacy.ca/openprivacy/libricochet-go/connectivity" "git.openprivacy.ca/openprivacy/libricochet-go/log" - "github.com/therecipe/qt/gui" "github.com/therecipe/qt/core" + "github.com/therecipe/qt/gui" "github.com/therecipe/qt/network" "github.com/therecipe/qt/qml" "github.com/therecipe/qt/quickcontrols2" "os" - "path" - "runtime" - "path/filepath" "os/user" - "fmt" + "path" + "path/filepath" + "runtime" ) const androidBaseDir = "/data/data/org.qtproject.example.go/" @@ -57,13 +57,35 @@ func main() { app := gui.NewQGuiApplication(len(os.Args), os.Args) app.SetWindowIcon(gui.NewQIcon5(":/qml/images/cwtch-icon.png")) - var translator = core.NewQTranslator(nil) - translator.Load("translation_"+core.QLocale_System().Name(), ":/i18n/", "", "") + // load english first so it becomes the default in case we don't have a .ts for the user's locale, or if it contains unfinished strings + translator := core.NewQTranslator(nil) + translator.Load("translation_en", ":/i18n/", "", "") core.QCoreApplication_InstallTranslator(translator) + gcd.Translator = core.NewQTranslator(nil) + gcd.Translator.Load("translation_"+core.QLocale_System().Name(), ":/i18n/", "", "") + core.QCoreApplication_InstallTranslator(gcd.Translator) + core.QCoreApplication_SetAttribute(core.Qt__AA_EnableHighDpiScaling, true) quickcontrols2.QQuickStyle_SetStyle("Universe") engine := qml.NewQQmlApplicationEngine(nil) + gcd.QMLEngine = engine + + // prevent qt from initiating network connections (possible deanon attempts!) + factory := qml.NewQQmlNetworkAccessManagerFactory() + factory.ConnectCreate(func(parent *core.QObject) *network.QNetworkAccessManager { + nam := network.NewQNetworkAccessManager(parent) + nam.SetNetworkAccessible(network.QNetworkAccessManager__NotAccessible) + proxy := network.NewQNetworkProxy() + proxy.SetHostName("0.0.0.0") + nam.SetProxy(proxy) + //nam.ConnectCreateRequest(func(op network.QNetworkAccessManager__Operation, originalReq *network.QNetworkRequest, outgoingData *core.QIODevice) *network.QNetworkReply { + // log.Errorf("network access request detected - possible remote content insertion bug!!!") + // return nil + //}) + return nam + }) + engine.SetNetworkAccessManagerFactory(factory) // variables we want to access from inside qml if runtime.GOOS == "android" { @@ -120,21 +142,13 @@ func main() { go characters.PresencePoller(gcd.UIState.GetContact, gcd.UIState.AddContact, gcd.UIState.UpdateContact) go characters.GroupPoller(gcd.UIState.GetContact, gcd.UIState.UpdateContact) - // prevent qt from initiating network connections (possible deanon attempts!) - factory := qml.NewQQmlNetworkAccessManagerFactory() - factory.ConnectCreate(func(parent *core.QObject) *network.QNetworkAccessManager { - nam := network.NewQNetworkAccessManager(parent) - nam.SetNetworkAccessible(network.QNetworkAccessManager__NotAccessible) - proxy := network.NewQNetworkProxy() - proxy.SetHostName("0.0.0.0") - nam.SetProxy(proxy) - //nam.ConnectCreateRequest(func(op network.QNetworkAccessManager__Operation, originalReq *network.QNetworkRequest, outgoingData *core.QIODevice) *network.QNetworkReply { - // log.Errorf("network access request detected - possible remote content insertion bug!!!") - // return nil - //}) - return nam - }) - engine.SetNetworkAccessManagerFactory(factory) + // load ui preferences + gcd.RequestSettings() + locale, exists := the.Peer.GetProfile().GetAttribute("settings.locale") + if exists { + gcd.SetLocale_helper(locale) + } + app.Exec() acn.Close() } @@ -209,12 +223,12 @@ func loadCwtchData(gcd *gothings.GrandCentralDispatcher, acn connectivity.ACN) { Server: group.GroupServer, Trusted: group.Accepted, }) - } - // Only send a join server packet if we haven't joined this server yet... - _,connecting := the.Peer.GetServers()[group.GroupServer] - if group.Accepted && !connecting { - the.Peer.JoinServer(group.GroupServer) + // Only send a join server packet if we haven't joined this server yet... + _,connecting := the.Peer.GetServers()[group.GroupServer] + if group.Accepted && !connecting { + the.Peer.JoinServer(group.GroupServer) + } } } } diff --git a/qml.qrc b/qml.qrc index 062de418..580f16f0 100644 --- a/qml.qrc +++ b/qml.qrc @@ -32,5 +32,6 @@ qml/widgets/controls/Variables.qml i18n/translation_en.qm i18n/translation_de.qm + i18n/translation_pt.qm diff --git a/qml/panes/SettingsPane.qml b/qml/panes/SettingsPane.qml index ffc18b2f..f3a3b988 100644 --- a/qml/panes/SettingsPane.qml +++ b/qml/panes/SettingsPane.qml @@ -7,6 +7,7 @@ import QtQuick.Window 2.11 import QtQuick.Controls 1.4 import "../widgets" +import "../widgets/controls" ColumnLayout { // settingsPane anchors.fill: parent @@ -36,6 +37,7 @@ ColumnLayout { // settingsPane updateValueWhileDragging: false onValueChanged: { gcd.themeScale = zoomSlider.value + saveSettings() } width: 400 } @@ -55,11 +57,35 @@ ColumnLayout { // settingsPane size: 8 } + FlagButton { + emoji: "1f1e8-1f1e6" + locale: "en" + } + + FlagButton { + emoji: "1f1e9-1f1ea" + locale: "de" + } + + FlagButton { + locale: "pt" + emoji: "1f1e7-1f1f7" + selected: true + } + }//end of column with padding - Component.onCompleted: { - zoomSlider.value = gcd.themeScale //Screen.pixelDensity / 3.2 // artistic license. set by erinn. fight me before changing - if (zoomSlider.value < zoomSlider.from) zoomSlider.value = zoomSlider.from - if (zoomSlider.value > zoomSlider.to) zoomSlider.value = zoomSlider.to + function saveSettings() { + // language switcher saves itself because erinn is a bad (read: amazing) programmer + gcd.saveSettings(zoomSlider.value, "") + } + + Connections { + target: gcd + + onSupplySettings: function(zoom, locale) { + if (zoom != "") zoomSlider.value = zoom + // (locale is handled automatically by FlagButton) + } } } \ No newline at end of file diff --git a/qml/widgets/controls/FlagButton.qml b/qml/widgets/controls/FlagButton.qml new file mode 100644 index 00000000..77b5f902 --- /dev/null +++ b/qml/widgets/controls/FlagButton.qml @@ -0,0 +1,43 @@ +import QtGraphicalEffects 1.0 +import QtQuick 2.7 +import QtQuick.Controls 2.4 +import QtQuick.Controls.Material 2.0 +import QtQuick.Layouts 1.3 +import QtQuick.Window 2.11 +import QtQuick.Controls 1.4 + +Rectangle { + width: img.width + 10 + height: img.height + 5 + property string emoji + color: selected ? windowItem.cwtch_color : windowItem.cwtch_background_color + property bool selected + property string locale + + + Image { + id: img + anchors.centerIn:parent + opacity: 1.0 + + + source: "qrc:/qml/fonts/twemoji/72x72/" + emoji + ".png" + + + MouseArea { + anchors.fill: parent + + onClicked: { + gcd.setLocale(locale) + } + } + } + + Connections { + target: gcd + + onSupplySettings: function(zoom, newLocale) { + selected = newLocale == locale + } + } +} \ No newline at end of file diff --git a/ui.pro b/ui.pro index a38e9ad9..201da0af 100644 --- a/ui.pro +++ b/ui.pro @@ -17,7 +17,8 @@ SOURCES += \ RESOURCES += qml.qrc TRANSLATIONS = i18n/translation_en.ts \ - i18n/translation_de.ts + i18n/translation_de.ts \ + i18n/translation_pt.ts # Additional import path used to resolve QML modules in Qt Creator's code model QML_IMPORT_PATH =