diff --git a/.github/workflows/go.yml b/.github/workflows/go.yml index b5779de00..5fa75d6ca 100644 --- a/.github/workflows/go.yml +++ b/.github/workflows/go.yml @@ -43,4 +43,4 @@ jobs: --enable staticcheck --enable gosimple \ --enable unconvert --enable ineffassign \ --enable structcheck --enable goimports \ - --enable misspell --enable unparam --enable golint + --enable unparam --enable golint diff --git a/ui/overview_page.go b/ui/overview_page.go index 16d2b1e30..c8eab2dbd 100644 --- a/ui/overview_page.go +++ b/ui/overview_page.go @@ -19,34 +19,6 @@ import ( const PageOverview = "Overview" -type overviewPageText struct { - balanceTitle, - statusTitle, - stepsTitle, - transactionsTitle, - connectedPeersTitle, - headersFetchedTitle, - syncingProgressTitle, - latestBlockTitle, - lastSyncedTitle, - noTransaction, - offlineStatus, - onlineStatus, - syncingStatus, - notSyncedStatus, - syncedStatus, - fetchingBlockHeaders, - reconnect, - disconnect, - noWallet, - cancel, - viewAllTx, - connectedPeersInfo, - noConnectedPeers, - showSyncDetails, - hideSyncDetails string -} - // walletSyncDetails contains sync data for each wallet when a sync // is in progress. type walletSyncDetails struct { @@ -76,7 +48,6 @@ type overviewPage struct { autoSyncWallet bool - text overviewPageText syncButtonHeight int moreButtonWidth int moreButtonHeight int @@ -107,44 +78,17 @@ func (win *Window) OverviewPage(c pageCommon) layout.Widget { isCheckingLockWL: false, autoSyncWallet: true, } - pg.text = overviewPageText{ - balanceTitle: "Current Total Balance", - statusTitle: "Wallet Status", - stepsTitle: "Step", - transactionsTitle: "Recent Transactions", - connectedPeersTitle: "Connected peers count", - connectedPeersInfo: "Connected to", - noConnectedPeers: "No connected peers", - headersFetchedTitle: "Block header fetched", - syncingProgressTitle: "Syncing progress", - latestBlockTitle: "Last Block Height", - lastSyncedTitle: "Last Block Mined", - noTransaction: "No transactions yet", - noWallet: "No wallet loaded", - offlineStatus: "Offline", - onlineStatus: "Online", - syncingStatus: "Syncing...", - notSyncedStatus: "Not Synced", - syncedStatus: "Synced", - fetchingBlockHeaders: "Fetching block headers", - reconnect: "Reconnect", - disconnect: "Disconnect", - cancel: "Cancel", - viewAllTx: "See all", - showSyncDetails: "Show details", - hideSyncDetails: "Hide details", - } - pg.toTransactions = c.theme.TextAndIconButton(new(widget.Clickable), pg.text.viewAllTx, c.icons.navigationArrowForward) + pg.toTransactions = c.theme.TextAndIconButton(new(widget.Clickable), values.String(values.StrSeeAll), c.icons.navigationArrowForward) pg.toTransactions.Color = c.theme.Color.Primary pg.toTransactions.BackgroundColor = c.theme.Color.Surface - pg.sync = c.theme.Button(new(widget.Clickable), pg.text.reconnect) + pg.sync = c.theme.Button(new(widget.Clickable), values.String(values.StrReconnect)) pg.sync.TextSize = values.TextSize10 pg.sync.Background = color.NRGBA{} pg.sync.Color = c.theme.Color.Text - pg.toggleSyncDetails = c.theme.Button(new(widget.Clickable), pg.text.showSyncDetails) + pg.toggleSyncDetails = c.theme.Button(new(widget.Clickable), values.String(values.StrShowDetails)) pg.toggleSyncDetails.TextSize = values.TextSize16 pg.toggleSyncDetails.Background = color.NRGBA{} pg.toggleSyncDetails.Color = c.theme.Color.Primary @@ -174,7 +118,7 @@ func (pg *overviewPage) Layout(gtx layout.Context, c pageCommon) layout.Dimensio return c.Layout(gtx, func(gtx C) D { return c.UniformPadding(gtx, func(gtx C) D { return layout.Center.Layout(gtx, func(gtx C) D { - return c.theme.H3(pg.text.noWallet).Layout(gtx) + return c.theme.H3(values.String(values.StrNoWalletLoaded)).Layout(gtx) }) }) }) @@ -225,7 +169,7 @@ func (pg *overviewPage) recentTransactionsSection(gtx layout.Context, common pag return Container{layout.Inset{Top: padding, Bottom: padding}}.Layout(gtx, func(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx C) D { - title := pg.theme.Body2(pg.text.transactionsTitle) + title := pg.theme.Body2(values.String(values.StrRecentTransactions)) title.Color = pg.theme.Color.Gray3 return pg.titleRow(gtx, title.Layout, pg.toTransactions.Layout) }), @@ -236,7 +180,7 @@ func (pg *overviewPage) recentTransactionsSection(gtx layout.Context, common pag }), layout.Rigid(func(gtx C) D { if len((*pg.walletTransactions).Txs) == 0 { - message := pg.theme.Body1(pg.text.noTransaction) + message := pg.theme.Body1(values.String(values.StrNoTransactionsYet)) message.Color = pg.theme.Color.Gray2 return Container{layout.Inset{ Left: values.MarginPadding16, @@ -381,7 +325,7 @@ func (pg *overviewPage) syncDormantContent(gtx layout.Context, uniform layout.In if pg.walletInfo.Synced { return pg.connectionPeer(gtx) } - latestBlockTitleLabel := pg.theme.Body1(pg.text.noConnectedPeers) + latestBlockTitleLabel := pg.theme.Body1(values.String(values.StrNoConnectedPeer)) latestBlockTitleLabel.Color = pg.theme.Color.Gray return latestBlockTitleLabel.Layout(gtx) }), @@ -392,7 +336,7 @@ func (pg *overviewPage) syncDormantContent(gtx layout.Context, uniform layout.In func (pg *overviewPage) blockInfoRow(gtx layout.Context) layout.Dimensions { return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, layout.Rigid(func(gtx C) D { - latestBlockTitleLabel := pg.theme.Body1(pg.text.latestBlockTitle) + latestBlockTitleLabel := pg.theme.Body1(values.String(values.StrLastBlockHeight)) latestBlockTitleLabel.Color = pg.theme.Color.Gray return latestBlockTitleLabel.Layout(gtx) }), @@ -413,7 +357,7 @@ func (pg *overviewPage) blockInfoRow(gtx layout.Context) layout.Dimensions { }) }), layout.Rigid(func(gtx C) D { - lastSyncedLabel := pg.theme.Body1("ago") + lastSyncedLabel := pg.theme.Body1(values.String(values.StrAgo)) lastSyncedLabel.Color = pg.theme.Color.Gray return lastSyncedLabel.Layout(gtx) }), @@ -423,7 +367,7 @@ func (pg *overviewPage) blockInfoRow(gtx layout.Context) layout.Dimensions { func (pg *overviewPage) connectionPeer(gtx layout.Context) layout.Dimensions { return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, layout.Rigid(func(gtx C) D { - connectedPeersInfoLabel := pg.theme.Body1(pg.text.connectedPeersInfo) + connectedPeersInfoLabel := pg.theme.Body1(values.String(values.StrConnectedTo)) connectedPeersInfoLabel.Color = pg.theme.Color.Gray return connectedPeersInfoLabel.Layout(gtx) }), @@ -442,12 +386,12 @@ func (pg *overviewPage) connectionPeer(gtx layout.Context) layout.Dimensions { // syncBoxTitleRow lays out widgets in the title row inside the sync status box. func (pg *overviewPage) syncBoxTitleRow(gtx layout.Context) layout.Dimensions { - title := pg.theme.Body2(pg.text.statusTitle) + title := pg.theme.Body2(values.String(values.StrWalletStatus)) title.Color = pg.theme.Color.Gray3 - statusLabel := pg.theme.Body1(pg.text.offlineStatus) + statusLabel := pg.theme.Body1(values.String(values.StrOffline)) pg.walletStatusIcon.Color = pg.theme.Color.Danger if pg.walletInfo.Synced || pg.walletInfo.Syncing { - statusLabel.Text = pg.text.onlineStatus + statusLabel.Text = values.String(values.StrOnline) pg.walletStatusIcon.Color = pg.theme.Color.Success } @@ -468,11 +412,11 @@ func (pg *overviewPage) syncBoxTitleRow(gtx layout.Context) layout.Dimensions { // syncStatusTextRow lays out sync status text and sync button. func (pg *overviewPage) syncStatusTextRow(gtx layout.Context, inset layout.Inset) layout.Dimensions { - syncStatusLabel := pg.theme.H6(pg.text.notSyncedStatus) + syncStatusLabel := pg.theme.H6(values.String(values.StrWalletNotSynced)) if pg.walletInfo.Syncing { - syncStatusLabel.Text = pg.text.syncingStatus + syncStatusLabel.Text = values.String(values.StrSyncingState) } else if pg.walletInfo.Synced { - syncStatusLabel.Text = pg.text.syncedStatus + syncStatusLabel.Text = values.String(values.StrSynced) } return inset.Layout(gtx, func(gtx C) D { @@ -492,7 +436,7 @@ func (pg *overviewPage) syncStatusTextRow(gtx layout.Context, inset layout.Inset } pg.sync.CornerRadius = values.MarginPadding10 - if pg.sync.Text == pg.text.reconnect { + if pg.sync.Text == values.String(values.StrReconnect) { pg.sync.Inset.Left = values.MarginPadding25 layout.Inset{Top: values.MarginPadding4, Left: values.MarginPadding7}.Layout(gtx, func(gtx C) D { pg.cachedIcon.Color = pg.theme.Color.Gray @@ -556,18 +500,15 @@ func (pg *overviewPage) walletSyncRow(gtx layout.Context, inset layout.Inset) la return layout.Inset{Top: values.MarginPadding10}.Layout(gtx, func(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx C) D { - totalSteps := pg.walletSyncStatus.TotalSteps - completedSteps := pg.theme.Body2(fmt.Sprintf("%s %d/%d", pg.text.stepsTitle, - pg.walletSyncStatus.Steps, totalSteps)) + completedSteps := pg.theme.Body2(values.StringF(values.StrSyncSteps, pg.walletSyncStatus.Steps)) completedSteps.Color = pg.theme.Color.Gray - headersFetched := pg.theme.Body1(fmt.Sprintf("%s · %v%%", pg.text.fetchingBlockHeaders, - pg.walletSyncStatus.HeadersFetchProgress)) + headersFetched := pg.theme.Body1(values.StringF(values.StrFetchingBlockHeaders, pg.walletSyncStatus.HeadersFetchProgress)) return inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { return endToEndRow(gtx, completedSteps.Layout, headersFetched.Layout) }) }), layout.Rigid(func(gtx C) D { - connectedPeersTitleLabel := pg.theme.Body2(pg.text.connectedPeersTitle) + connectedPeersTitleLabel := pg.theme.Body2(values.String(values.StrConnectedPeersCount)) connectedPeersTitleLabel.Color = pg.theme.Color.Gray connectedPeersLabel := pg.theme.Body1(fmt.Sprintf("%d", pg.walletSyncStatus.ConnectedPeers)) return inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { @@ -587,7 +528,7 @@ func (pg *overviewPage) walletSyncRow(gtx layout.Context, inset layout.Inset) la if w.BestBlockHeight > overallBlockHeight { overallBlockHeight = w.BestBlockHeight } - blockHeightProgress := fmt.Sprintf("%v of %v", w.BestBlockHeight, overallBlockHeight) + blockHeightProgress := values.StringF(values.StrBlockHeaderFetchedCount, w.BestBlockHeight, overallBlockHeight) details := pg.syncDetail(w.Name, w.Status, blockHeightProgress, w.DaysBehind) uniform := layout.UniformInset(values.MarginPadding5) walletSyncBoxes = append(walletSyncBoxes, @@ -621,14 +562,14 @@ func (pg *overviewPage) walletSyncBox(gtx layout.Context, inset layout.Inset, de }) }), layout.Rigid(func(gtx C) D { - headersFetchedTitleLabel := pg.theme.Body2(pg.text.headersFetchedTitle) + headersFetchedTitleLabel := pg.theme.Body2(values.String(values.StrBlockHeaderFetched)) headersFetchedTitleLabel.Color = pg.theme.Color.Gray return inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { return endToEndRow(gtx, headersFetchedTitleLabel.Layout, details.blockHeaderFetched.Layout) }) }), layout.Rigid(func(gtx C) D { - progressTitleLabel := pg.theme.Body2(pg.text.syncingProgressTitle) + progressTitleLabel := pg.theme.Body2(values.String(values.StrSyncingProgress)) progressTitleLabel.Color = pg.theme.Color.Gray return inset.Layout(gtx, func(gtx layout.Context) layout.Dimensions { return endToEndRow(gtx, progressTitleLabel.Layout, details.syncingProgress.Layout) @@ -651,14 +592,14 @@ func (pg *overviewPage) Handler(eq event.Queue, c pageCommon, win *Window) { } if pg.walletInfo.Synced { - pg.sync.Text = pg.text.disconnect + pg.sync.Text = values.String(values.StrDisconnect) } if pg.autoSyncWallet && !pg.walletInfo.Synced { walletsLocked := getLockedWallets(c.wallet.AllWallets()) if len(walletsLocked) == 0 { c.wallet.StartSync() - pg.sync.Text = pg.text.cancel + pg.sync.Text = values.String(values.StrCancel) pg.autoSyncWallet = false } } @@ -673,10 +614,10 @@ func (pg *overviewPage) Handler(eq event.Queue, c pageCommon, win *Window) { if pg.sync.Button.Clicked() { if pg.walletInfo.Synced || pg.walletInfo.Syncing { c.wallet.CancelSync() - pg.sync.Text = pg.text.reconnect + pg.sync.Text = values.String(values.StrReconnect) } else { c.wallet.StartSync() - pg.sync.Text = pg.text.cancel + pg.sync.Text = values.String(values.StrCancel) } } @@ -700,9 +641,9 @@ func (pg *overviewPage) Handler(eq event.Queue, c pageCommon, win *Window) { if pg.toggleSyncDetails.Button.Clicked() { pg.syncDetailsVisibility = !pg.syncDetailsVisibility if pg.syncDetailsVisibility { - pg.toggleSyncDetails.Text = pg.text.hideSyncDetails + pg.toggleSyncDetails.Text = values.String(values.StrHideDetails) } else { - pg.toggleSyncDetails.Text = pg.text.showSyncDetails + pg.toggleSyncDetails.Text = values.String(values.StrShowDetails) } } } @@ -711,7 +652,7 @@ func showWalletUnlockModal(c pageCommon, lockedWallets []*dcrlibwallet.Wallet) { go func() { c.modalReceiver <- &modalLoad{ template: UnlockWalletRestoreTemplate, - title: "Unlock to resume restoration", + title: values.String(values.StrResumeAccountDiscoveryTitle), confirm: func(pass string) { err := c.wallet.UnlockWallet(lockedWallets[0].ID, []byte(pass)) if err != nil { @@ -724,7 +665,7 @@ func showWalletUnlockModal(c pageCommon, lockedWallets []*dcrlibwallet.Wallet) { c.closeModal() } }, - confirmText: "Unlock", + confirmText: values.String(values.StrUnlock), } }() } diff --git a/ui/page.go b/ui/page.go index 51768d64a..6d750d230 100644 --- a/ui/page.go +++ b/ui/page.go @@ -228,12 +228,12 @@ func (win *Window) loadPage(ic pageIcons) { { clickable: new(widget.Clickable), image: ic.sendIcon, - page: PageSend, + page: values.String(values.StrSend), }, { clickable: new(widget.Clickable), image: ic.receiveIcon, - page: PageReceive, + page: values.String(values.StrReceive), }, } @@ -242,19 +242,19 @@ func (win *Window) loadPage(ic pageIcons) { clickable: new(widget.Clickable), image: ic.overviewIcon, imageInactive: ic.overviewIconInactive, - page: PageOverview, + page: values.String(values.StrOverview), }, { clickable: new(widget.Clickable), image: ic.transactionIcon, imageInactive: ic.transactionIconInactive, - page: PageTransactions, + page: values.String(values.StrTransactions), }, { clickable: new(widget.Clickable), image: ic.walletIcon, imageInactive: ic.walletIconInactive, - page: PageWallet, + page: values.String(values.StrWallets), }, { clickable: new(widget.Clickable), @@ -266,13 +266,13 @@ func (win *Window) loadPage(ic pageIcons) { clickable: new(widget.Clickable), image: ic.ticketIcon, imageInactive: ic.ticketIconInactive, - page: PageTickets, + page: values.String(values.StrTickets), }, { clickable: new(widget.Clickable), image: ic.moreIcon, imageInactive: ic.moreIconInactive, - page: PageMore, + page: values.String(values.StrMore), }, } diff --git a/ui/preference/list_preference.go b/ui/preference/list_preference.go new file mode 100644 index 000000000..f96a6c04b --- /dev/null +++ b/ui/preference/list_preference.go @@ -0,0 +1,204 @@ +package preference + +import ( + "image/color" + "sort" + + "gioui.org/layout" + "gioui.org/widget" + "github.com/planetdecred/godcr/ui/decredmaterial" + "github.com/planetdecred/godcr/ui/values" + "github.com/planetdecred/godcr/wallet" +) + +type ListPreference struct { + wallet *wallet.Wallet + preferenceKey string + defaultValue string // str-key + initialValue string + currentValue string + + theme *decredmaterial.Theme + + IsShowing bool + titleStrKey string + items map[string]string //[key]str-key + itemKeys []string + + clickable *widget.Clickable + optionsRadioGroup *widget.Enum + + positiveButtonClicked func() + positiveButtonStrKey string + positiveButton decredmaterial.Button + + negativeButtonClicked func() + negativeButtonStrKey string + negativeButton decredmaterial.Button +} + +func NewListPreference(wallet *wallet.Wallet, theme *decredmaterial.Theme, preferenceKey, defaultValue string, items map[string]string) *ListPreference { + + // sort keys to keep order when refreshed + sortedKeys := make([]string, 0) + for k := range items { + sortedKeys = append(sortedKeys, k) + } + + sort.Slice(sortedKeys, func(i int, j int) bool { return sortedKeys[i] < sortedKeys[j] }) + + return &ListPreference{ + wallet: wallet, + preferenceKey: preferenceKey, + defaultValue: defaultValue, + theme: theme, + + items: items, + itemKeys: sortedKeys, + + IsShowing: false, + + clickable: new(widget.Clickable), + optionsRadioGroup: new(widget.Enum), + + positiveButton: theme.Button(new(widget.Clickable), ""), + negativeButton: theme.Button(new(widget.Clickable), ""), + } +} + +func (lp *ListPreference) Title(titleStrKey string) *ListPreference { + lp.titleStrKey = titleStrKey + return lp +} + +func (lp *ListPreference) PostiveButton(strkey string, clicked func()) *ListPreference { + lp.positiveButtonStrKey = strkey + lp.positiveButtonClicked = clicked + + return lp +} + +func (lp *ListPreference) NegativeButton(strkey string, clicked func()) *ListPreference { + lp.negativeButtonStrKey = strkey + lp.negativeButtonClicked = clicked + + return lp +} + +func (lp *ListPreference) Clickable() *widget.Clickable { + return lp.clickable +} + +func (lp *ListPreference) Handle() { + + for lp.clickable.Clicked() { + initialValue := lp.wallet.ReadStringConfigValueForKey(lp.preferenceKey) + if initialValue == "" { + initialValue = lp.defaultValue + } + + lp.initialValue = initialValue + lp.currentValue = initialValue + lp.IsShowing = true + } + + for lp.negativeButton.Button.Clicked() { + lp.setValue(lp.initialValue) // reset value + lp.IsShowing = false + + lp.negativeButtonClicked() + } + + for lp.positiveButton.Button.Clicked() { + lp.setValue(lp.optionsRadioGroup.Value) // set value + lp.IsShowing = false + + lp.positiveButtonClicked() + } + + for lp.optionsRadioGroup.Changed() { + lp.currentValue = lp.optionsRadioGroup.Value + } +} + +func (lp *ListPreference) setValue(value string) { + lp.wallet.SaveConfigValueForKey(lp.preferenceKey, value) +} + +func (lp *ListPreference) Layout(gtx layout.Context, body layout.Dimensions) layout.Dimensions { + return layout.Stack{}.Layout(gtx, + layout.Expanded(func(gtx layout.Context) layout.Dimensions { + return body + }), + layout.Stacked(func(gtx layout.Context) layout.Dimensions { + return lp.modal(gtx) + }), + ) +} + +func (lp *ListPreference) modal(gtx layout.Context) layout.Dimensions { + w := []layout.Widget{ + func(gtx layout.Context) layout.Dimensions { + txt := lp.theme.H6(values.String(lp.titleStrKey)) + txt.Color = lp.theme.Color.Text + return txt.Layout(gtx) + }, + func(gtx layout.Context) layout.Dimensions { + return layout.Flex{Axis: layout.Vertical}.Layout(gtx, lp.layoutItems()...) + }, + func(gtx layout.Context) layout.Dimensions { + return layout.E.Layout(gtx, func(gtx layout.Context) layout.Dimensions { + return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, lp.layoutButtons()...) + }) + }, + } + + lp.optionsRadioGroup.Value = lp.currentValue + + return lp.theme.Modal().Layout(gtx, w, 1050) +} + +func (lp *ListPreference) layoutItems() []layout.FlexChild { + + items := make([]layout.FlexChild, 0) + for _, k := range lp.itemKeys { + radioItem := layout.Rigid(lp.theme.RadioButton(lp.optionsRadioGroup, k, values.String(lp.items[k])).Layout) + + items = append(items, radioItem) + } + + return items +} + +func (lp *ListPreference) layoutButtons() []layout.FlexChild { + + buttonLayout := func(button decredmaterial.Button) layout.FlexChild { + + l := layout.Rigid(func(gtx layout.Context) layout.Dimensions { + return layout.UniformInset(values.MarginPadding5).Layout(gtx, func(gtx layout.Context) layout.Dimensions { + button.Background, button.Color = color.NRGBA{}, lp.theme.Color.Primary + return button.Layout(gtx) + }) + }) + + return l + } + + buttons := make([]layout.FlexChild, 0) + + if lp.positiveButtonStrKey != "" { + positiveButtonLayout := buttonLayout(lp.positiveButton) + lp.positiveButton.Text = values.String(lp.positiveButtonStrKey) + + buttons = append(buttons, positiveButtonLayout) + } + + if lp.negativeButtonStrKey != "" { + negativeButtonLayout := buttonLayout(lp.negativeButton) + lp.negativeButton.Text = values.String(lp.negativeButtonStrKey) + + buttons = append(buttons, negativeButtonLayout) + } + + return buttons +} diff --git a/ui/settings_page.go b/ui/settings_page.go index f12956c73..f95b80c37 100644 --- a/ui/settings_page.go +++ b/ui/settings_page.go @@ -1,19 +1,24 @@ package ui import ( - "image/color" - "gioui.org/layout" "gioui.org/widget" "github.com/planetdecred/dcrlibwallet" "github.com/planetdecred/godcr/ui/decredmaterial" + "github.com/planetdecred/godcr/ui/preference" "github.com/planetdecred/godcr/ui/values" "github.com/planetdecred/godcr/wallet" ) const PageSettings = "Settings" -const USDExchangeValue = "USD (Bittrex)" + +const ( + USDExchangeValue = "usd_bittrex" + DefaultExchangeValue = "none" + + languagePreferenceKey = "app_language" +) type row struct { title string @@ -28,7 +33,6 @@ type settingsPage struct { walletInfo *wallet.MultiWalletInfo wal *wallet.Wallet - currencyConversion *widget.Clickable updateConnectToPeer *widget.Clickable updateUserAgent *widget.Clickable changeStartupPass *widget.Clickable @@ -50,12 +54,8 @@ type settingsPage struct { agentValue string errorReceiver chan error - currencyValue string - initialValue string - isCurrencyModalOpen bool - - currencyModal *decredmaterial.Modal - radioButtonsGroup *widget.Enum + currencyPreference *preference.ListPreference + languagePreference *preference.ListPreference } func (win *Window) SettingsPage(common pageCommon) layout.Widget { @@ -79,18 +79,34 @@ func (win *Window) SettingsPage(common pageCommon) layout.Widget { errorReceiver: make(chan error), - currencyConversion: new(widget.Clickable), updateConnectToPeer: new(widget.Clickable), updateUserAgent: new(widget.Clickable), changeStartupPass: new(widget.Clickable), confirm: win.theme.Button(new(widget.Clickable), "Ok"), - cancel: win.theme.Button(new(widget.Clickable), "Cancel"), - - currencyModal: common.theme.Modal(), - radioButtonsGroup: new(widget.Enum), + cancel: win.theme.Button(new(widget.Clickable), values.String(values.StrCancel)), } + languagePreference := preference.NewListPreference(common.wallet, common.theme, languagePreferenceKey, + values.DefaultLangauge, values.ArrLanguages). + Title(values.StrLanguage). + PostiveButton(values.StrConfirm, func() { + values.SetUserLanguage(pg.wal.ReadStringConfigValueForKey(languagePreferenceKey)) + }). + NegativeButton(values.StrCancel, func() {}) + pg.languagePreference = languagePreference + + currencyMap := make(map[string]string) + currencyMap[DefaultExchangeValue] = values.StrNone + currencyMap[USDExchangeValue] = values.StrUsdBittrex + + currencyPreference := preference.NewListPreference(common.wallet, common.theme, + dcrlibwallet.CurrencyConversionConfigKey, DefaultExchangeValue, currencyMap). + Title(values.StrCurrencyConversion). + PostiveButton(values.StrConfirm, func() {}). + NegativeButton(values.StrCancel, func() {}) + pg.currencyPreference = currencyPreference + color := common.theme.Color.LightGray pg.peerLabel = common.theme.Body1("") @@ -103,6 +119,8 @@ func (win *Window) SettingsPage(common pageCommon) layout.Widget { return func(gtx C) D { pg.handle(common, win) + pg.languagePreference.Handle() + pg.currencyPreference.Handle() return pg.Layout(gtx, common) } } @@ -112,7 +130,7 @@ func (pg *settingsPage) Layout(gtx layout.Context, common pageCommon) layout.Dim body := func(gtx C) D { page := SubPage{ - title: "Settings", + title: values.String(values.StrSettings), back: func() { common.changePage(PageMore) }, @@ -132,8 +150,16 @@ func (pg *settingsPage) Layout(gtx layout.Context, common pageCommon) layout.Dim return common.SubPageLayout(gtx, page) } - if pg.isCurrencyModalOpen { - return common.Modal(gtx, common.Layout(gtx, body), pg.currencyConversionSection(gtx)) + if pg.currencyPreference.IsShowing { + return pg.currencyPreference.Layout(gtx, common.Layout(gtx, func(gtx C) D { + return common.UniformPadding(gtx, body) + })) + } + + if pg.languagePreference.IsShowing { + return pg.languagePreference.Layout(gtx, common.Layout(gtx, func(gtx C) D { + return common.UniformPadding(gtx, body) + })) } return common.Layout(gtx, func(gtx C) D { @@ -143,24 +169,34 @@ func (pg *settingsPage) Layout(gtx layout.Context, common pageCommon) layout.Dim func (pg *settingsPage) general() layout.Widget { return func(gtx C) D { - return pg.mainSection(gtx, "General", func(gtx C) D { + return pg.mainSection(gtx, values.String(values.StrGeneral), func(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx C) D { return pg.subSectionSwitch(gtx, "Dark mode", pg.isDarkModeOn) }), layout.Rigid(func(gtx C) D { - return pg.subSectionSwitch(gtx, "Spending unconfirmed funds", pg.spendUnconfirmed) + return pg.subSectionSwitch(gtx, values.String(values.StrUnconfirmedFunds), pg.spendUnconfirmed) }), layout.Rigid(pg.lineSeparator()), layout.Rigid(func(gtx C) D { currencyConversionRow := row{ - title: "Currency conversion", - clickable: pg.currencyConversion, + title: values.String(values.StrCurrencyConversion), + clickable: pg.currencyPreference.Clickable(), icon: pg.chevronRightIcon, - label: pg.theme.Body2(pg.currencyValue), + label: pg.theme.Body2(pg.wal.ReadStringConfigValueForKey(dcrlibwallet.CurrencyConversionConfigKey)), } return pg.clickableRow(gtx, currencyConversionRow) }), + layout.Rigid(pg.lineSeparator()), + layout.Rigid(func(gtx C) D { + languageRow := row{ + title: values.String(values.StrLanguage), + clickable: pg.languagePreference.Clickable(), + icon: pg.chevronRightIcon, + label: pg.theme.Body2(pg.wal.ReadStringConfigValueForKey(languagePreferenceKey)), + } + return pg.clickableRow(gtx, languageRow) + }), ) }) } @@ -168,23 +204,23 @@ func (pg *settingsPage) general() layout.Widget { func (pg *settingsPage) notification() layout.Widget { return func(gtx C) D { - return pg.mainSection(gtx, "Notification", func(gtx C) D { - return pg.subSectionSwitch(gtx, "Beep for new blocks", pg.beepNewBlocks) + return pg.mainSection(gtx, values.String(values.StrNotifications), func(gtx C) D { + return pg.subSectionSwitch(gtx, values.String(values.StrBeepForNewBlocks), pg.beepNewBlocks) }) } } func (pg *settingsPage) security() layout.Widget { return func(gtx C) D { - return pg.mainSection(gtx, "Security", func(gtx C) D { + return pg.mainSection(gtx, values.String(values.StrSecurity), func(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx C) D { - return pg.subSectionSwitch(gtx, "Startup password", pg.startupPassword) + return pg.subSectionSwitch(gtx, values.String(values.StrStartupPassword), pg.startupPassword) }), layout.Rigid(func(gtx C) D { return pg.conditionalDisplay(gtx, pg.isStartupPassword, func(gtx C) D { changeStartupPassRow := row{ - title: "Change startup password", + title: values.String(values.StrChangeStartupPassword), clickable: pg.changeStartupPass, icon: pg.chevronRightIcon, label: pg.theme.Body1(""), @@ -199,14 +235,14 @@ func (pg *settingsPage) security() layout.Widget { func (pg *settingsPage) connection() layout.Widget { return func(gtx C) D { - return pg.mainSection(gtx, "Connection", func(gtx C) D { + return pg.mainSection(gtx, values.String(values.StrConnection), func(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx C) D { - return pg.subSectionSwitch(gtx, "Connect to specific peer", pg.connectToPeer) + return pg.subSectionSwitch(gtx, values.String(values.StrConnectToSpecificPeer), pg.connectToPeer) }), layout.Rigid(func(gtx C) D { peerAddrRow := row{ - title: "Change specific peer", + title: values.String(values.StrChangeSpecificPeer), clickable: pg.updateConnectToPeer, icon: pg.chevronRightIcon, label: pg.peerLabel, @@ -231,9 +267,9 @@ func (pg *settingsPage) agent() layout.Widget { m10 := values.MarginPadding10 return layout.Inset{Top: m10, Bottom: m10}.Layout(gtx, func(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, - layout.Rigid(pg.subSectionLabel("Custom user agent")), + layout.Rigid(pg.subSectionLabel(values.String(values.StrCustomUserAgent))), layout.Rigid(func(gtx C) D { - txt := pg.theme.Body2("For exchange rate fetching") + txt := pg.theme.Body2(values.String(values.StrUserAgentSummary)) txt.Color = pg.theme.Color.Gray return layout.Inset{Top: values.MarginPadding5}.Layout(gtx, func(gtx C) D { return txt.Layout(gtx) @@ -254,7 +290,7 @@ func (pg *settingsPage) agent() layout.Widget { layout.Rigid(func(gtx C) D { return pg.conditionalDisplay(gtx, pg.agentValue != "", func(gtx C) D { userAgentRow := row{ - title: "Change user agent", + title: values.String(values.StrUserAgentDialogTitle), clickable: pg.updateUserAgent, icon: pg.chevronRightIcon, label: pg.agentLabel, @@ -266,46 +302,6 @@ func (pg *settingsPage) agent() layout.Widget { } } -func (pg *settingsPage) currencyConversionSection(gtx layout.Context) layout.Dimensions { - w := []layout.Widget{ - func(gtx C) D { - txt := pg.theme.H6("Currency conversion") - txt.Color = pg.theme.Color.Text - return txt.Layout(gtx) - }, - func(gtx C) D { - return layout.Flex{Axis: layout.Vertical}.Layout(gtx, - layout.Rigid(pg.theme.RadioButton(pg.radioButtonsGroup, "None", "None").Layout), - layout.Rigid(func(gtx C) D { - return layout.Inset{Top: values.MarginPadding10}.Layout(gtx, func(gtx C) D { - return pg.theme.RadioButton(pg.radioButtonsGroup, USDExchangeValue, USDExchangeValue).Layout(gtx) - }) - }), - ) - }, - func(gtx C) D { - return layout.E.Layout(gtx, func(gtx C) D { - return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, - layout.Rigid(func(gtx C) D { - return layout.UniformInset(values.MarginPadding5).Layout(gtx, func(gtx C) D { - pg.cancel.Background, pg.cancel.Color = color.NRGBA{}, pg.theme.Color.Primary - return pg.cancel.Layout(gtx) - }) - }), - layout.Rigid(func(gtx C) D { - return layout.UniformInset(values.MarginPadding5).Layout(gtx, func(gtx C) D { - pg.confirm.Background, pg.confirm.Color = color.NRGBA{}, pg.theme.Color.Primary - return pg.confirm.Layout(gtx) - }) - }), - ) - }) - }, - } - - return pg.currencyModal.Layout(gtx, w, 1050) -} - func (pg *settingsPage) mainSection(gtx layout.Context, title string, body layout.Widget) layout.Dimensions { return layout.Inset{Bottom: values.MarginPadding10}.Layout(gtx, func(gtx C) D { return pg.theme.Card().Layout(gtx, func(gtx C) D { @@ -405,13 +401,13 @@ func (pg *settingsPage) handle(common pageCommon, win *Window) { go func() { common.modalReceiver <- &modalLoad{ template: ChangeStartupPasswordTemplate, - title: "Change startup password", + title: values.String(values.StrChangeStartupPassword), confirm: func(oldPass, newPass string) { pg.wal.ChangeStartupPassphrase(oldPass, newPass, pg.errorReceiver) }, - confirmText: "Change", + confirmText: values.String(values.StrChange), cancel: common.closeModal, - cancelText: "Cancel", + cancelText: values.String(values.StrCancel), } }() break @@ -422,13 +418,13 @@ func (pg *settingsPage) handle(common pageCommon, win *Window) { go func() { common.modalReceiver <- &modalLoad{ template: SetStartupPasswordTemplate, - title: "Create a startup password", + title: values.String(values.StrCreateStartupPassword), confirm: func(pass string) { pg.wal.SetStartupPassphrase(pass, pg.errorReceiver) }, - confirmText: "Create", + confirmText: values.String(values.StrCreate), cancel: common.closeModal, - cancelText: "Cancel", + cancelText: values.String(values.StrCancel), } }() return @@ -436,13 +432,13 @@ func (pg *settingsPage) handle(common pageCommon, win *Window) { go func() { common.modalReceiver <- &modalLoad{ template: RemoveStartupPasswordTemplate, - title: "Confirm to turn off startup password", + title: values.String(values.StrConfirmRemoveStartupPass), confirm: func(pass string) { pg.wal.RemoveStartupPassphrase(pass, pg.errorReceiver) }, - confirmText: "Confirm", + confirmText: values.String(values.StrConfirm), cancel: common.closeModal, - cancelText: "Cancel", + cancelText: values.String(values.StrCancel), } }() } @@ -453,16 +449,16 @@ func (pg *settingsPage) handle(common pageCommon, win *Window) { go func() { common.modalReceiver <- &modalLoad{ template: ConnectToSpecificPeerTemplate, - title: "Connect to specific peer", + title: values.String(values.StrConnectToSpecificPeer), confirm: func(ipAddress string) { if ipAddress != "" { pg.wal.SaveConfigValueForKey(specificPeerKey, ipAddress) common.closeModal() } }, - confirmText: "Connect", + confirmText: values.String(values.StrConfirm), cancel: common.closeModal, - cancelText: "Cancel", + cancelText: values.String(values.StrCancel), } }() return @@ -473,16 +469,16 @@ func (pg *settingsPage) handle(common pageCommon, win *Window) { go func() { common.modalReceiver <- &modalLoad{ template: ChangeSpecificPeerTemplate, - title: "Change specific peer", + title: values.String(values.StrChangeSpecificPeer), confirm: func(ipAddress string) { if ipAddress != "" { pg.wal.SaveConfigValueForKey(specificPeerKey, ipAddress) common.closeModal() } }, - confirmText: "Done", + confirmText: values.String(values.StrConfirm), cancel: common.closeModal, - cancelText: "Cancel", + cancelText: values.String(values.StrCancel), } }() break @@ -493,16 +489,16 @@ func (pg *settingsPage) handle(common pageCommon, win *Window) { go func() { common.modalReceiver <- &modalLoad{ template: UserAgentTemplate, - title: "Change user agent", + title: values.String(values.StrChangeUserAgent), confirm: func(agent string) { if agent != "" { pg.wal.SaveConfigValueForKey(userAgentKey, agent) common.closeModal() } }, - confirmText: "Done", + confirmText: values.String(values.StrGeneral), cancel: common.closeModal, - cancelText: "Cancel", + cancelText: values.String(values.StrCancel), } }() break @@ -513,16 +509,16 @@ func (pg *settingsPage) handle(common pageCommon, win *Window) { go func() { common.modalReceiver <- &modalLoad{ template: UserAgentTemplate, - title: "Set up user agent", + title: values.String(values.StrChangeUserAgent), confirm: func(agent string) { if agent != "" { pg.wal.SaveConfigValueForKey(userAgentKey, agent) common.closeModal() } }, - confirmText: "Set up", + confirmText: values.String(values.StrConfirm), cancel: common.closeModal, - cancelText: "Cancel", + cancelText: values.String(values.StrCancel), } }() return @@ -530,32 +526,9 @@ func (pg *settingsPage) handle(common pageCommon, win *Window) { pg.wal.RemoveUserConfigValueForKey(userAgentKey) } - for pg.currencyConversion.Clicked() { - pg.isCurrencyModalOpen = true - } - - if pg.radioButtonsGroup.Changed() { - if pg.radioButtonsGroup.Value == "None" { - pg.initialValue = USDExchangeValue - } else { - pg.initialValue = "None" - } - pg.wal.SaveConfigValueForKey(dcrlibwallet.CurrencyConversionConfigKey, pg.radioButtonsGroup.Value) - } - - for pg.cancel.Button.Clicked() { - pg.wal.SaveConfigValueForKey(dcrlibwallet.CurrencyConversionConfigKey, pg.initialValue) - pg.isCurrencyModalOpen = false - } - - if pg.confirm.Button.Clicked() { - pg.initialValue = pg.radioButtonsGroup.Value - pg.isCurrencyModalOpen = false - } - select { case err := <-pg.errorReceiver: - if err.Error() == "invalid_passphrase" { + if err.Error() == dcrlibwallet.ErrInvalidPassphrase { e := "Password is incorrect" common.notify(e, false) return @@ -605,10 +578,4 @@ func (pg *settingsPage) updateSettingOptions() { pg.agentLabel.Text = pg.agentValue pg.userAgent.Value = true } - - pg.currencyValue = pg.wal.ReadStringConfigValueForKey(dcrlibwallet.CurrencyConversionConfigKey) - if pg.currencyValue == "" { - pg.currencyValue = "None" - } - pg.radioButtonsGroup.Value = pg.currencyValue } diff --git a/ui/transaction_details_page.go b/ui/transaction_details_page.go index 9c6c2918b..68e9c8300 100644 --- a/ui/transaction_details_page.go +++ b/ui/transaction_details_page.go @@ -192,7 +192,7 @@ func (pg *transactionDetailsPage) txnBalanceAndStatus(gtx layout.Context, common }) }), layout.Rigid(func(gtx C) D { - txt := common.theme.Body1(fmt.Sprintf("%d Confirmations", (*pg.txnInfo).Confirmations)) + txt := common.theme.Body1(values.StringF(values.StrNConfirmations, (*pg.txnInfo).Confirmations)) txt.Color = common.theme.Color.Gray return txt.Layout(gtx) }), @@ -210,28 +210,28 @@ func (pg *transactionDetailsPage) txnTypeAndID(gtx layout.Context) layout.Dimens m := values.MarginPadding10 return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx C) D { - return pg.txnInfoSection(gtx, "From", transaction.WalletName, transaction.AccountName, true, false) + return pg.txnInfoSection(gtx, values.String(values.StrFrom), transaction.WalletName, transaction.AccountName, true, false) }), layout.Rigid(func(gtx C) D { return layout.Inset{Bottom: m, Top: m}.Layout(gtx, func(gtx C) D { - return pg.txnInfoSection(gtx, "Fee", "", dcrutil.Amount(transaction.Txn.Fee).String(), false, false) + return pg.txnInfoSection(gtx, values.String(values.StrFee), "", dcrutil.Amount(transaction.Txn.Fee).String(), false, false) }) }), layout.Rigid(func(gtx C) D { if transaction.Txn.BlockHeight != -1 { - return pg.txnInfoSection(gtx, "Included in block", "", fmt.Sprintf("%d", transaction.Txn.BlockHeight), false, false) + return pg.txnInfoSection(gtx, values.String(values.StrIncludedInBlock), "", fmt.Sprintf("%d", transaction.Txn.BlockHeight), false, false) } return layout.Dimensions{} }), layout.Rigid(func(gtx C) D { return layout.Inset{Bottom: m, Top: m}.Layout(gtx, func(gtx C) D { - return pg.txnInfoSection(gtx, "Type", "", transaction.Txn.Type, false, false) + return pg.txnInfoSection(gtx, values.String(values.StrType), "", transaction.Txn.Type, false, false) }) }), layout.Rigid(func(gtx C) D { trimmedHash := transaction.Txn.Hash[:24] + "..." + transaction.Txn.Hash[len(transaction.Txn.Hash)-24:] return layout.Inset{Bottom: m}.Layout(gtx, func(gtx C) D { - return pg.txnInfoSection(gtx, "Transaction ID", "", trimmedHash, false, true) + return pg.txnInfoSection(gtx, values.String(values.StrTransactionID), "", trimmedHash, false, true) }) }), ) @@ -298,7 +298,7 @@ func (pg *transactionDetailsPage) txnInputs(gtx layout.Context) layout.Dimension } collapsibleHeader := func(gtx C) D { - t := pg.theme.Body1(fmt.Sprintf("%d Inputs consumed", len(transaction.Txn.Inputs))) + t := pg.theme.Body1(values.StringF(values.StrXInputsConsumed, len(transaction.Txn.Inputs))) t.Color = pg.theme.Color.Gray return t.Layout(gtx) } @@ -321,7 +321,7 @@ func (pg *transactionDetailsPage) txnOutputs(gtx layout.Context, common *pageCom transaction := *pg.txnInfo collapsibleHeader := func(gtx C) D { - t := common.theme.Body1(fmt.Sprintf("%d Outputs created", len(transaction.Txn.Outputs))) + t := common.theme.Body1(values.StringF(values.StrXOutputCreated, len(transaction.Txn.Outputs))) t.Color = common.theme.Color.Gray return t.Layout(gtx) } @@ -403,7 +403,7 @@ func (pg *transactionDetailsPage) viewTxn(gtx layout.Context, common *pageCommon return pg.pageSections(gtx, func(gtx C) D { return layout.Flex{Spacing: layout.SpaceBetween}.Layout(gtx, layout.Rigid(func(gtx C) D { - return pg.theme.Body1("View on dcrdata").Layout(gtx) + return pg.theme.Body1(values.String(values.StrViewOnDcrdata)).Layout(gtx) }), layout.Rigid(func(gtx C) D { redirect := common.icons.redirectIcon diff --git a/ui/transactions_page.go b/ui/transactions_page.go index eb29741e5..d6a9bbc4d 100644 --- a/ui/transactions_page.go +++ b/ui/transactions_page.go @@ -53,22 +53,23 @@ func (win *Window) TransactionsPage(common pageCommon) layout.Widget { theme: common.theme, } - pg.orderDropDown = common.theme.DropDown([]decredmaterial.DropDownItem{{Text: "Newest"}, {Text: "Oldest"}}, 1) + pg.orderDropDown = common.theme.DropDown([]decredmaterial.DropDownItem{{Text: values.String(values.StrNewest)}, + {Text: values.String(values.StrOldest)}}, 1) pg.txTypeDropDown = common.theme.DropDown([]decredmaterial.DropDownItem{ { - Text: "All", + Text: values.String(values.StrAll), }, { - Text: "Sent", + Text: values.String(values.StrSent), }, { - Text: "Received", + Text: values.String(values.StrReceived), }, { - Text: "Yourself", + Text: values.String(values.StrYourself), }, { - Text: "Staking", + Text: values.String(values.StrStaking), }, }, 1) @@ -117,7 +118,7 @@ func (pg *transactionsPage) Layout(gtx layout.Context, common pageCommon) layout // return "No transactions yet" text if there are no transactions if len(wallTxs) == 0 { gtx.Constraints.Min.X = gtx.Constraints.Max.X - txt := common.theme.Body1("No transactions yet") + txt := common.theme.Body1(values.String(values.StrNoTransactionsYet)) txt.Color = common.theme.Color.Gray2 return txt.Layout(gtx) } diff --git a/ui/values/arrays.go b/ui/values/arrays.go new file mode 100644 index 000000000..60a55c62d --- /dev/null +++ b/ui/values/arrays.go @@ -0,0 +1,14 @@ +package values + +import "github.com/planetdecred/godcr/ui/values/localizable" + +var ( + ArrLanguages map[string]string + ArrExchangeCurrencies map[string]string +) + +func init() { + ArrLanguages = make(map[string]string) + ArrLanguages[localizable.ENGLISH] = StrEnglish + ArrLanguages[localizable.FRENCH] = StrFrench +} diff --git a/ui/values/localizable/cmd/main.go b/ui/values/localizable/cmd/main.go new file mode 100644 index 000000000..e95993e61 --- /dev/null +++ b/ui/values/localizable/cmd/main.go @@ -0,0 +1,80 @@ +package main + +import ( + "bufio" + "fmt" + "io/ioutil" + "os" + "regexp" + "strings" + + "github.com/planetdecred/godcr/ui/values/localizable" +) + +var rex = regexp.MustCompile(`(?m)("(?:\\.|[^"\\])*")\s*=\s*("(?:\\.|[^"\\])*")`) // "key"="value" +const commentPrefix = "/" + +func main() { + if len(os.Args) < 2 { + fmt.Println("Invalid arguments") + return + } + + readIntoMap := func(m map[string]string, localizableStrings string) { + scanner := bufio.NewScanner(strings.NewReader(localizableStrings)) + for scanner.Scan() { + line := scanner.Text() + line = strings.TrimSpace(line) + if line == "" || strings.HasPrefix(line, commentPrefix) { + continue + } + + matches := rex.FindAllStringSubmatch(line, -1) + if len(matches) == 0 { + continue + } + + kv := matches[0] + key := trimQuotes(kv[1]) + value := trimQuotes(kv[2]) + + m[key] = value + } + } + + en := make(map[string]string) + readIntoMap(en, localizable.EN) + + fileBuff, err := ioutil.ReadFile(os.Args[1]) + if err != nil { + fmt.Println("Error reading file:", err) + return + } + + translation := make(map[string]string) + readIntoMap(translation, string(fileBuff)) + + var sb strings.Builder + for k := range en { + translationValue, ok := translation[k] + if ok { + sb.WriteString("\"" + k + "\" = \"") + sb.WriteString(translationValue) + sb.WriteString("\";\n") + } + } + + err = ioutil.WriteFile("translated.txt", []byte(sb.String()), 0644) + if err != nil { + fmt.Println("Error writing file:", err) + } +} + +func trimQuotes(s string) string { + if len(s) >= 2 { + if s[0] == '"' && s[len(s)-1] == '"' { + return s[1 : len(s)-1] + } + } + return s +} diff --git a/ui/values/localizable/en.go b/ui/values/localizable/en.go new file mode 100644 index 000000000..7a055f80a --- /dev/null +++ b/ui/values/localizable/en.go @@ -0,0 +1,119 @@ +package localizable + +const ENGLISH = "en" + +// one string per line, no multiline +// semicolon is not compulsory +const EN = ` +"appName" = "godcr"; +"appTitle" = "godcr (%s)"; +"recentTransactions" = "Recent Transactions"; +"seeAll" = "See all"; +"send" = "Send"; +"receive" = "Receive"; +"online" = "Online"; +"offline" = "Offline"; +"showDetails" = "Show details"; +"hideDetails" = "Hide details"; +"connectedPeersCount" = "Connected peers count"; +"noConnectedPeer" = "No connected peers."; +"disconnect" = "Disconnect"; +"reconnect" = "Reconnect"; +"currentTotalBalance" = "Current Total Balance"; +"walletStatus" = "Wallet Status"; +"blockHeaderFetched" = "Block header fetched"; +"noTransactionsYet" = "No transactions yet."; +"fetchingBlockHeaders" = "Fetching block headers · %v%%"; +"syncSteps" = "Step %d/3"; +"blockHeaderFetchedCount" = "%d of %d"; +"connectedTo" = "Connected to"; +"synced" = "Synced"; +"syncingState" = "Syncing..."; +"walletNotSynced" = "Not Synced"; +"cancel" = "Cancel"; +"resumeAccountDiscoveryTitle" = "Unlock to resume restoration"; +"unlock" = "Unlock"; +"syncingProgress" = "Syncing progress"; +"noWalletLoaded" = "No wallet loaded"; +"lastBlockHeight" = "Last Block Height"; +"ago" = "ago"; +"newest" = "Newest"; +"oldest" = "Oldest"; +"all" = "All"; +"transferred" = "Transferred"; +"sent" = "Sent"; +"received" = "Received"; +"yourself" = "Yourself"; +"staking" = "Staking"; +"nConfirmations" = "%d Confirmations"; +"from" = "From"; +"to" = "To"; +"fee" = "Fee"; +"includedInBlock" = "Included in block"; +"type" = "Type"; +"transactionId" = "Transaction ID"; +"xInputsConsumed" = "%d Inputs consumed"; +"xOutputCreated" = "%d Outputs created"; +"viewOnDcrdata" = "View on dcrdata"; +"watchOnlyWallets" = "Watch-only wallets"; +"signMessage" = "Sign message"; +"verifyMessage" = "Verify message"; +"viewProperty" = "View property"; +"stakeShuffle" = "StakeShuffle"; +"rename" = "Rename"; +"renameWalletSheetTitle" = "Rename wallet"; +"settings" = "Settings"; +"createANewWallet" = "Create a new wallet" +"importExistingWallet" = "Import an existing wallet"; +"importWatchingOnlyWallet" = "Import a watch-only wallet"; +"create" = "Create"; +"watchOnlyWalletImported" = "Watch only wallet imported"; +"addNewAccount" = "Add new account"; +"notBackedUp" = "Not backed up"; +"labelSpendable" = "Spendable"; +"backupSeedPhrase" = "Back up seed phrase"; +"verifySeedInfo" = "Verify your seed phrase backup so you can recover your funds when needed."; +"createNewAccount" = "Create new account"; +"invalidPassphrase" = "Passphrase entered was not valid."; +"Import" = "Import"; +"spendingPassword" = "Spending password"; +"changeSpendingPass" = "Change spending password"; +"notifications" = "Notifications"; +"beepForNewBlocks" = "Beep for new blocks"; +"debug" = "Debug"; +"rescanBlockchain" = "Rescan blockchain"; +"dangerZone" = "Danger zone"; +"removeWallet" = "Remove wallet from device"; +"change" = "Change"; +"notConnected" = "Not connected to decred network"; +"rescanProgressNotification" = "Check progress in Overview!"; +"rescan" = "Rescan"; +"confirmToRemove" = "Confirm to remove"; +"remove" = "Remove"; +"confirm" = "Confirm"; +"general" = "General"; +"unconfirmedFunds" = "Spend Unconfirmed Funds"; +"currencyConversion" = "Currency conversion"; +"language" = "Language"; +"security" = "Security"; +"startupPassword" = "Startup password"; +"changeStartupPassword" = "Change startup password"; +"connection" = "Connection"; +"connectToSpecificPeer" = "Connect to specific peer"; +"changeSpecificPeer" = "Change specific peer"; +"CustomUserAgent" = "Custom user agent"; +"userAgentSummary" = "For exchange rate fetching"; +"changeUserAgent" = "Change user agent"; +"createStartupPassword" = "Create a startup password"; +"confirmRemoveStartupPass" = "Confirm to turn off startup password"; +"userAgentDialogTitle" = "Set up user agent"; +"overview" = "Overview"; +"transactions" = "Transactions"; +"wallets" = "Wallets"; +"tickets" = "Tickets"; +"more" = "More"; +"english" = "English"; +"french" = "French"; +"usdBittrex" = "USD (Bittrex)"; +"none" = "None"; +` diff --git a/ui/values/localizable/fr.go b/ui/values/localizable/fr.go new file mode 100644 index 000000000..86dcc358d --- /dev/null +++ b/ui/values/localizable/fr.go @@ -0,0 +1,89 @@ +package localizable + +const FRENCH = "fr" + +const FR = ` +"watchOnlyWallets" = "Porte-monnaies en lecture seule"; +"changeStartupPassword" = "Changer le mot de passe de démarrage"; +"transactions" = "Transactions"; +"unconfirmedFunds" = "Dépenser des fonds non-confirmés"; +"notBackedUp" = "Non sauvegardé"; +"Import" = "Importer"; +"overview" = "Vue d\'ensemble"; +"from" = "De"; +"addNewAccount" = "Ajouter un nouveau compte"; +"changeSpendingPass" = "Changer le mot de passe de dépense"; +"rescanBlockchain" = "Rescanner blockchain"; +"xInputsConsumed" = "%d entrées consommées"; +"userAgentDialogTitle" = "Configurer user agent"; +"verifySeedInfo" = "Vérifier la sauvegarde de votre phrase de récupération afin de pouvoir récupérer vos fonds."; +"createNewAccount" = "Créer un nouveau compte"; +"createANewWallet" = "Créer un nouveau porte-monnaie"; +"seeAll" = "Voir tout"; +"resumeAccountDiscoveryTitle" = "Déverouiller pour reprendre la restauration"; +"offline" = "Offline"; +"showDetails" = "Montrer les détails"; +"walletStatus" = "Statut de porte-monnaie"; +"wallets" = "Porte-monnaies"; +"settings" = "Paramètres"; +"labelSpendable" = "Disponible"; +"remove" = "Supprimer"; +"synced" = "Synchronisé"; +"more" = "Plus"; +"sent" = "Envoyé"; +"connection" = "Connexion"; +"cancel" = "Annuler"; +"backupSeedPhrase" = "Sauvegarder la phrase de récupération"; +"fetchingBlockHeaders" = "Récupération des entêtes de blocs · %v%%"; +"blockHeaderFetchedCount" = "%d de %d"; +"currencyConversion" = "Conversion de devise"; +"renameWalletSheetTitle" = "Renommer le porte-monnaie"; +"send" = "Envoyer"; +"transferred" = "Transféré"; +"type" = "Type"; +"to" = "À"; +"importWatchingOnlyWallet" = "Importer un porte-monnaie en observation seule"; +"create" = "Créer"; +"hideDetails" = "Masquer les détails"; +"rescanProgressNotification" = "Suivre l\'avancement dans la vue d\'ensemble!"; +"general" = "Général"; +"reconnect" = "Reconnecter"; +"importExistingWallet" = "Importer un porte-monnaie existant"; +"syncingProgress" = "Synchronisation en cours"; +"confirm" = "Confirmer"; +"startupPassword" = "Mot de passe de démarrage"; +"connectedPeersCount" = "Compteur de pairs connectés"; +"blockHeaderFetched" = "Entête de bloc récupéré"; +"includedInBlock" = "Inclus dans le bloc"; +"online" = "Online"; +"unlock" = "Déverouiller"; +"xOutputCreated" = "%d sorties créées"; +"invalidPassphrase" = "La phrase secrète entrée n\'est pas valide."; +"confirmToRemove" = "Confirmer la suppression"; +"syncingState" = "Synchronisation…"; +"verifyMessage" = "Vérifier le message"; +"spendingPassword" = "Mot de passe de dépense"; +"change" = "Échanger"; +"currentTotalBalance" = "Solde Total Actuel"; +"signMessage" = "Signer le message"; +"notifications" = "Notifications"; +"disconnect" = "Déconnecter"; +"receive" = "Recevoir"; +"fee" = "Frais"; +"userAgentSummary" = "Pour rafraîchir les taux de change"; +"rename" = "Renommer"; +"noTransactionsYet" = "Aucune transaction pour le moment."; +"createStartupPassword" = "Créer un mot de passe de démarrage"; +"transactionId" = "Transaction ID"; +"debug" = "Debug"; +"notConnected" = "Pas de connexion au réseau Decred"; +"received" = "Reçu"; +"removeWallet" = "Supprimer le porte-monnaie de cet appareil"; +"recentTransactions" = "Transactions récentes"; +"ago" = "avant"; +"viewOnDcrdata" = "Voir sur dcrdata"; +"beepForNewBlocks" = "Beep pour les nouveaux blocs"; +"connectToSpecificPeer" = "Se connecter à un pair spécifique"; +"english" = "Anglais"; +"french" = "Français"; +` diff --git a/ui/values/localizable/zh.go b/ui/values/localizable/zh.go new file mode 100644 index 000000000..96330af45 --- /dev/null +++ b/ui/values/localizable/zh.go @@ -0,0 +1,28 @@ +package localizable + +const CHINESE = "zh" + +const ZH = ` +"connectedPeersCount" = "已连接的节点数量"; +"walletStatus" = "钱包状态"; +"offline" = "离线"; +"hideDetails" = "隐藏详情"; +"fetchingBlockHeaders" = "正在获取区块头 · %v%%"; +"unlock" = "解锁"; +"online" = "在线"; +"recentTransactions" = "最近的交易"; +"receive" = "接收"; +"reconnect" = "重连"; +"syncingProgress" = "同步进度"; +"syncingState" = "同步中…"; +"cancel" = "取消"; +"resumeAccountDiscoveryTitle" = "解锁以恢复"; +"blockHeaderFetched" = "区块头已获取"; +"noTransactionsYet" = "没有交易"; +"showDetails" = "查看详情"; +"currentTotalBalance" = "当前总金额"; +"synced" = "已同步"; +"send" = "发送"; +"disconnect" = "断开连接"; +"blockHeaderFetchedCount" = "%d of %d"; +` diff --git a/ui/values/strings.go b/ui/values/strings.go new file mode 100644 index 000000000..78552dddc --- /dev/null +++ b/ui/values/strings.go @@ -0,0 +1,225 @@ +package values + +import ( + "bufio" + "fmt" + "regexp" + "strings" + + "github.com/planetdecred/godcr/ui/values/localizable" +) + +const ( + DefaultLangauge = localizable.ENGLISH + commentPrefix = "/" +) + +var rex = regexp.MustCompile(`(?m)("(?:\\.|[^"\\])*")\s*=\s*("(?:\\.|[^"\\])*")`) // "key"="value" +var Languages = []string{localizable.ENGLISH, localizable.CHINESE, localizable.FRENCH} +var UserLanguages = []string{DefaultLangauge} // order of preference + +var languageStrings map[string]map[string]string + +func init() { + + readIntoMap := func(m map[string]string, localizableStrings string) { + scanner := bufio.NewScanner(strings.NewReader(localizableStrings)) + for scanner.Scan() { + line := scanner.Text() + line = strings.TrimSpace(line) + if line == "" || strings.HasPrefix(line, commentPrefix) { + continue + } + + matches := rex.FindAllStringSubmatch(line, -1) + if len(matches) == 0 { + continue + } + + kv := matches[0] + key := trimQuotes(kv[1]) + value := trimQuotes(kv[2]) + + m[key] = value + } + } + + en := make(map[string]string) + zh := make(map[string]string) + fr := make(map[string]string) + languageStrings = make(map[string]map[string]string) + + readIntoMap(en, localizable.EN) + languageStrings[localizable.ENGLISH] = en + + readIntoMap(zh, localizable.ZH) + languageStrings[localizable.CHINESE] = zh + + readIntoMap(fr, localizable.FR) + languageStrings[localizable.FRENCH] = fr +} + +func hasLanguage(language string) bool { + for _, lang := range Languages { + if lang == language { + return true + } + } + + return false +} + +func SetUserLanguage(lang string) { + if hasLanguage(lang) { + languages := []string{lang} + if lang != DefaultLangauge { + languages = append(languages, DefaultLangauge) + } + + UserLanguages = languages + } +} + +func trimQuotes(s string) string { + if len(s) >= 2 { + if s[0] == '"' && s[len(s)-1] == '"' { + return s[1 : len(s)-1] + } + } + return s +} + +func String(key string) string { + for _, lang := range UserLanguages { + languageMap := languageStrings[lang] + str, ok := languageMap[key] + if ok { + return str + } + } + + return "" +} + +func StringF(key string, a ...interface{}) string { + str := String(key) + if str == "" { + return str + } + + return fmt.Sprintf(str, a...) +} + +const ( + StrAppName = "appName" + StrSend = "send" + StrReceive = "receive" + StrUnlock = "unlock" + StrWalletStatus = "walletStatus" + StrFetchingBlockHeaders = "fetchingBlockHeaders" + StrSyncingState = "syncingState" + StrResumeAccountDiscoveryTitle = "resumeAccountDiscoveryTitle" + StrHideDetails = "hideDetails" + StrSyncSteps = "syncSteps" + StrBlockHeaderFetchedCount = "blockHeaderFetchedCount" + StrConnectedTo = "connectedTo" + StrSynced = "synced" + StrNoWalletLoaded = "noWalletLoaded" + StrReconnect = "reconnect" + StrWalletNotSynced = "walletNotSynced" + StrDisconnect = "disconnect" + StrSyncingProgress = "syncingProgress" + StrBlockHeaderFetched = "blockHeaderFetched" + StrNoTransactionsYet = "noTransactionsYet" + StrCancel = "cancel" + StrAppTitle = "appTitle" + StrSeeAll = "seeAll" + StrOnline = "online" + StrConnectedPeersCount = "connectedPeersCount" + StrNoConnectedPeer = "noConnectedPeer" + StrCurrentTotalBalance = "currentTotalBalance" + StrRecentTransactions = "recentTransactions" + StrOffline = "offline" + StrShowDetails = "showDetails" + StrLastBlockHeight = "lastBlockHeight" + StrAgo = "ago" + StrNewest = "newest" + StrOldest = "oldest" + StrAll = "all" + StrTransferred = "transferred" + StrSent = "sent" + StrReceived = "received" + StrYourself = "yourself" + StrStaking = "staking" + StrNConfirmations = "nConfirmations" + StrFrom = "from" + StrTo = "to" + StrFee = "fee" + StrIncludedInBlock = "includedInBlock" + StrType = "type" + StrTransactionID = "transactionId" + StrXInputsConsumed = "xInputsConsumed" + StrXOutputCreated = "xOutputCreated" + StrViewOnDcrdata = "viewOnDcrdata" + StrViewProperty = "viewProperty" + StrAddNewAccount = "addNewAccount" + StrBackupSeedPhrase = "backupSeedPhrase" + StrCreateNewAccount = "createNewAccount" + StrInvalidPassphrase = "invalidPassphrase" + StrCreate = "create" + StrNotBackedUp = "notBackedUp" + StrLabelSpendable = "labelSpendable" + StrSignMessage = "signMessage" + StrStakeShuffle = "stakeShuffle" + StrRenameWalletSheetTitle = "renameWalletSheetTitle" + StrSettings = "settings" + StrImportWatchingOnlyWallet = "importWatchingOnlyWallet" + StrVerifySeedInfo = "verifySeedInfo" + StrVerifyMessage = "verifyMessage" + StrRename = "rename" + StrCreateANewWallet = "createANewWallet" + StrImportExistingWallet = "importExistingWallet" + StrWatchOnlyWallets = "watchOnlyWallets" + StrWatchOnlyWalletImported = "watchOnlyWalletImported" + StrImport = "Import" + StrRescanProgressNotification = "rescanProgressNotification" + StrRemove = "remove" + StrConfirm = "confirm" + StrSpendingPassword = "spendingPassword" + StrDangerZone = "dangerZone" + StrNotConnected = "notConnected" + StrConfirmToRemove = "confirmToRemove" + StrChangeSpendingPass = "changeSpendingPass" + StrDebug = "debug" + StrBeepForNewBlocks = "beepForNewBlocks" + StrRemoveWallet = "removeWallet" + StrChange = "change" + StrRescan = "rescan" + StrNotifications = "notifications" + StrRescanBlockchain = "rescanBlockchain" + StrStartupPassword = "startupPassword" + StrChangeSpecificPeer = "changeSpecificPeer" + StrLanguage = "language" + StrConnection = "connection" + StrCustomUserAgent = "CustomUserAgent" + StrConfirmRemoveStartupPass = "confirmRemoveStartupPass" + StrUserAgentDialogTitle = "userAgentDialogTitle" + StrSecurity = "security" + StrUnconfirmedFunds = "unconfirmedFunds" + StrChangeStartupPassword = "changeStartupPassword" + StrConnectToSpecificPeer = "connectToSpecificPeer" + StrUserAgentSummary = "userAgentSummary" + StrGeneral = "general" + StrChangeUserAgent = "changeUserAgent" + StrCreateStartupPassword = "createStartupPassword" + StrCurrencyConversion = "currencyConversion" + StrTransactions = "transactions" + StrWallets = "wallets" + StrTickets = "tickets" + StrMore = "more" + StrOverview = "overview" + StrEnglish = "english" + StrFrench = "french" + StrUsdBittrex = "usdBittrex" + StrNone = "none" +) diff --git a/ui/wallet_page.go b/ui/wallet_page.go index 84e9c6a75..7a882215c 100644 --- a/ui/wallet_page.go +++ b/ui/wallet_page.go @@ -20,7 +20,7 @@ import ( "github.com/planetdecred/godcr/wallet" ) -const PageWallet = "Wallet" +const PageWallet = "Wallets" type menuItem struct { text string @@ -91,7 +91,7 @@ func (win *Window) WalletPage(common pageCommon) layout.Widget { pg.collapsibles = make(map[int]collapsible) pg.watchOnlyWalletMoreButtons = make(map[int]decredmaterial.IconButton) - pg.watchOnlyWalletLabel = pg.theme.Body1("Watch-only Wallets") + pg.watchOnlyWalletLabel = pg.theme.Body1(values.String(values.StrWatchOnlyWallets)) pg.watchOnlyWalletLabel.Color = pg.theme.Color.Gray pg.iconButton = decredmaterial.IconButton{ @@ -126,14 +126,14 @@ func (win *Window) WalletPage(common pageCommon) layout.Widget { func (pg *walletPage) initializeWalletMenu() { pg.optionsMenu = []menuItem{ { - text: "Sign message", + text: values.String(values.StrSignMessage), button: new(widget.Clickable), action: func(common pageCommon) { common.changePage(PageSignMessage) }, }, { - text: "Verify message", + text: values.String(values.StrVerifyMessage), button: new(widget.Clickable), separate: true, action: func(common pageCommon) { @@ -143,7 +143,7 @@ func (pg *walletPage) initializeWalletMenu() { }, }, { - text: "View property", + text: values.String(values.StrViewProperty), button: new(widget.Clickable), separate: true, action: func(common pageCommon) { @@ -151,7 +151,7 @@ func (pg *walletPage) initializeWalletMenu() { }, }, { - text: "StakeShuffle", + text: values.String(values.StrStakeShuffle), button: new(widget.Clickable), separate: true, action: func(common pageCommon) { @@ -159,26 +159,26 @@ func (pg *walletPage) initializeWalletMenu() { }, }, { - text: "Rename", + text: values.String(values.StrRename), button: new(widget.Clickable), action: func(common pageCommon) { go func() { common.modalReceiver <- &modalLoad{ template: RenameWalletTemplate, - title: "Rename wallet", + title: values.String(values.StrRenameWalletSheetTitle), confirm: func(name string) { id := common.info.Wallets[*common.selectedWallet].ID common.wallet.RenameWallet(id, name, pg.errorReceiver) }, - confirmText: "Rename", + confirmText: values.String(values.StrRename), cancel: common.closeModal, - cancelText: "Cancel", + cancelText: values.String(values.StrCancel), } }() }, }, { - text: "Settings", + text: values.String(values.StrSettings), button: new(widget.Clickable), action: func(common pageCommon) { common.changePage(PageWalletSettings) @@ -188,19 +188,19 @@ func (pg *walletPage) initializeWalletMenu() { pg.addWalletMenu = []menuItem{ { - text: "Create a new wallet", + text: values.String(values.StrCreateANewWallet), button: new(widget.Clickable), action: pg.showAddWalletModal, }, { - text: "Import an existing wallet", + text: values.String(values.StrImportExistingWallet), button: new(widget.Clickable), action: func(common pageCommon) { common.changePage(PageCreateRestore) }, }, { - text: "Import a watch only wallet", + text: values.String(values.StrImportWatchingOnlyWallet), button: new(widget.Clickable), action: pg.showImportWatchOnlyWalletModal, }, @@ -208,27 +208,27 @@ func (pg *walletPage) initializeWalletMenu() { pg.watchOnlyWalletMenu = []menuItem{ { - text: "Settings", + text: values.String(values.StrSettings), button: new(widget.Clickable), action: func(common pageCommon) { common.changePage(PageWalletSettings) }, }, { - text: "Rename", + text: values.String(values.StrRename), button: new(widget.Clickable), action: func(common pageCommon) { go func() { common.modalReceiver <- &modalLoad{ template: RenameWalletTemplate, - title: "Rename wallet", + title: values.String(values.StrRenameWalletSheetTitle), confirm: func(name string) { id := common.info.Wallets[*common.selectedWallet].ID common.wallet.RenameWallet(id, name, pg.errorReceiver) }, - confirmText: "Rename", + confirmText: values.String(values.StrRename), cancel: common.closeModal, - cancelText: "Cancel", + cancelText: values.String(values.StrCancel), } }() }, @@ -240,13 +240,13 @@ func (pg *walletPage) showAddWalletModal(common pageCommon) { go func() { common.modalReceiver <- &modalLoad{ template: CreateWalletTemplate, - title: "Create new wallet", + title: values.String(values.StrCreate), confirm: func(name string, passphrase string) { pg.wallet.CreateWallet(name, passphrase, pg.errorReceiver) }, - confirmText: "Create", + confirmText: values.String(values.StrCreate), cancel: common.closeModal, - cancelText: "Cancel", + cancelText: values.String(values.StrCancel), } }() } @@ -255,7 +255,7 @@ func (pg *walletPage) showImportWatchOnlyWalletModal(common pageCommon) { go func() { common.modalReceiver <- &modalLoad{ template: ImportWatchOnlyWalletTemplate, - title: "Import watch-only wallet", + title: values.String(values.StrImportWatchingOnlyWallet), confirm: func(name, extendedPubKey string) { err := pg.wallet.ImportWatchOnlyWallet(name, extendedPubKey) if err != nil { @@ -263,12 +263,12 @@ func (pg *walletPage) showImportWatchOnlyWalletModal(common pageCommon) { } else { common.closeModal() pg.wallet.GetMultiWalletInfo() - common.notify("Watch only wallet imported", true) + common.notify(values.String(values.StrWatchOnlyWalletImported), true) } }, - confirmText: "Import", + confirmText: values.String(values.StrImport), cancel: common.closeModal, - cancelText: "Cancel", + cancelText: values.String(values.StrCancel), } }() } @@ -284,7 +284,7 @@ func (pg *walletPage) Layout(gtx layout.Context, common pageCommon) layout.Dimen return common.Layout(gtx, func(gtx C) D { return common.UniformPadding(gtx, func(gtx C) D { return layout.Center.Layout(gtx, func(gtx C) D { - return common.theme.H3("No wallets loaded").Layout(gtx) + return common.theme.H3(values.String(values.StrNoWalletLoaded)).Layout(gtx) }) }) }) @@ -461,7 +461,7 @@ func (pg *walletPage) walletSection(gtx layout.Context, common pageCommon) layou }) }), layout.Rigid(func(gtx C) D { - txt := pg.theme.H6("Add new account") + txt := pg.theme.H6(values.String(values.StrAddNewAccount)) txt.Color = common.theme.Color.Gray return txt.Layout(gtx) }), @@ -605,7 +605,7 @@ func (pg *walletPage) layoutCollapsibleHeader(gtx layout.Context, walletInfo wal }), layout.Rigid(func(gtx C) D { if len(walletInfo.Seed) > 0 { - txt := pg.theme.Caption("Not backed up") + txt := pg.theme.Caption(values.String(values.StrNotBackedUp)) txt.Color = pg.theme.Color.Danger return txt.Layout(gtx) } @@ -653,7 +653,7 @@ func (pg *walletPage) tableLayout(gtx layout.Context, leftLabel, rightLabel decr layout.Rigid(func(gtx C) D { if isIcon { if seed > 0 { - txt := pg.theme.Caption("Not backed up") + txt := pg.theme.Caption(values.String(values.StrNotBackedUp)) txt.Color = pg.theme.Color.Danger inset := layout.Inset{ Bottom: values.MarginPadding5, @@ -726,7 +726,7 @@ func (pg *walletPage) walletAccountsLayout(gtx layout.Context, name, totalBal, s Right: values.MarginPadding10, } return inset.Layout(gtx, func(gtx C) D { - spendibleLabel := pg.theme.Body2("Spendable") + spendibleLabel := pg.theme.Body2(values.String(values.StrLabelSpendable)) spendibleLabel.Color = pg.theme.Color.Gray spendibleBalLabel := pg.theme.Body2(spendableBal) spendibleBalLabel.Color = pg.theme.Color.Gray @@ -761,12 +761,12 @@ func (pg *walletPage) backupSeedNotification(gtx layout.Context, common pageComm return inset.Layout(gtx, func(gtx C) D { return layout.Flex{Axis: layout.Vertical}.Layout(gtx, layout.Rigid(func(gtx C) D { - txt := pg.theme.Body2("Back up seed phrase") + txt := pg.theme.Body2(values.String(values.StrBackupSeedPhrase)) txt.Color = textColour return txt.Layout(gtx) }), layout.Rigid(func(gtx C) D { - txt := pg.theme.Caption("Verify your seed phrase so you can recover your funds when needed.") + txt := pg.theme.Caption(values.String(values.StrVerifySeedInfo)) txt.Color = textColour return txt.Layout(gtx) }), @@ -873,7 +873,7 @@ func (pg *walletPage) Handle(common pageCommon) { go func() { common.modalReceiver <- &modalLoad{ template: CreateAccountTemplate, - title: "Create new account", + title: values.String(values.StrCreateNewAccount), confirm: func(name string, passphrase string) { pg.wallet.AddAccount(walletID, name, []byte(passphrase), pg.errorReceiver, func(acct *dcrlibwallet.Account) { walletAccount := walletAccount{ @@ -885,9 +885,9 @@ func (pg *walletPage) Handle(common pageCommon) { common.addAccount(walletAccount) }) }, - confirmText: "Create", + confirmText: values.String(values.StrCreate), cancel: common.closeModal, - cancelText: "Cancel", + cancelText: values.String(values.StrCancel), } }() break @@ -936,8 +936,8 @@ func (pg *walletPage) Handle(common pageCommon) { select { case err := <-pg.errorReceiver: - if err.Error() == "invalid_passphrase" { - e := "Password is incorrect" + if err.Error() == dcrlibwallet.ErrInvalidPassphrase { + e := values.String(values.StrInvalidPassphrase) common.notify(e, false) return } diff --git a/ui/wallet_settings_page.go b/ui/wallet_settings_page.go index 8c434a4a2..409c9a62a 100644 --- a/ui/wallet_settings_page.go +++ b/ui/wallet_settings_page.go @@ -57,7 +57,7 @@ func (pg *walletSettingsPage) Layout(gtx layout.Context, common pageCommon) layo body := func(gtx C) D { page := SubPage{ - title: "Settings", + title: values.String(values.StrSettings), walletName: common.info.Wallets[*common.selectedWallet].Name, back: func() { common.changePage(PageWallet) @@ -86,9 +86,9 @@ func (pg *walletSettingsPage) Layout(gtx layout.Context, common pageCommon) layo func (pg *walletSettingsPage) changePassphrase() layout.Widget { return func(gtx C) D { - return pg.pageSections(gtx, "Spending password", pg.changePass, func(gtx C) D { + return pg.pageSections(gtx, values.String(values.StrSpendingPassword), pg.changePass, func(gtx C) D { return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, - layout.Rigid(pg.bottomSectionLabel("Change spending password")), + layout.Rigid(pg.bottomSectionLabel(values.String(values.StrChangeSpendingPass))), layout.Flexed(1, func(gtx C) D { return layout.E.Layout(gtx, func(gtx C) D { return pg.chevronRightIcon.Layout(gtx, values.MarginPadding20) @@ -101,9 +101,9 @@ func (pg *walletSettingsPage) changePassphrase() layout.Widget { func (pg *walletSettingsPage) notification() layout.Widget { return func(gtx C) D { - return pg.pageSections(gtx, "Notification", nil, func(gtx C) D { + return pg.pageSections(gtx, values.String(values.StrNotifications), nil, func(gtx C) D { return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, - layout.Rigid(pg.bottomSectionLabel("Beep for new blocks")), + layout.Rigid(pg.bottomSectionLabel(values.String(values.StrBeepForNewBlocks))), layout.Flexed(1, func(gtx C) D { return layout.E.Layout(gtx, func(gtx C) D { return pg.theme.Switch(pg.notificationW).Layout(gtx) @@ -116,9 +116,9 @@ func (pg *walletSettingsPage) notification() layout.Widget { func (pg *walletSettingsPage) debug() layout.Widget { return func(gtx C) D { - return pg.pageSections(gtx, "Debug", pg.rescan, func(gtx C) D { + return pg.pageSections(gtx, values.String(values.StrDebug), pg.rescan, func(gtx C) D { return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, - layout.Rigid(pg.bottomSectionLabel("Rescan blockchain")), + layout.Rigid(pg.bottomSectionLabel(values.String(values.StrRescanBlockchain))), layout.Flexed(1, func(gtx C) D { return layout.E.Layout(gtx, func(gtx C) D { return pg.chevronRightIcon.Layout(gtx, values.MarginPadding20) @@ -131,9 +131,9 @@ func (pg *walletSettingsPage) debug() layout.Widget { func (pg *walletSettingsPage) dangerZone() layout.Widget { return func(gtx C) D { - return pg.pageSections(gtx, "Danger zone", pg.deleteWallet, func(gtx C) D { + return pg.pageSections(gtx, values.String(values.StrDangerZone), pg.deleteWallet, func(gtx C) D { return layout.Flex{Axis: layout.Horizontal}.Layout(gtx, - layout.Rigid(pg.bottomSectionLabel("Remove wallet from device")), + layout.Rigid(pg.bottomSectionLabel(values.String(values.StrRemoveWallet))), layout.Flexed(1, func(gtx C) D { return layout.E.Layout(gtx, func(gtx C) D { return pg.chevronRightIcon.Layout(gtx, values.MarginPadding20) @@ -190,13 +190,13 @@ func (pg *walletSettingsPage) handle(common pageCommon) { go func() { common.modalReceiver <- &modalLoad{ template: ChangePasswordTemplate, - title: "Change spending password", + title: values.String(values.StrChangeSpendingPass), confirm: func(oldPass, newPass string) { pg.wal.ChangeWalletPassphrase(walletID, oldPass, newPass, pg.errorReceiver) }, - confirmText: "Change", + confirmText: values.String(values.StrChange), cancel: common.closeModal, - cancelText: "Cancel", + cancelText: values.String(values.StrCancel), } }() break @@ -207,26 +207,26 @@ func (pg *walletSettingsPage) handle(common pageCommon) { go func() { common.modalReceiver <- &modalLoad{ template: RescanWalletTemplate, - title: "Rescan blockchain", + title: values.String(values.StrRescanBlockchain), confirm: func() { err := pg.wal.RescanBlocks(walletID) if err != nil { - if err.Error() == "not_connected" { - common.notify("Not connected to decred network", false) + if err.Error() == dcrlibwallet.ErrNotConnected { + common.notify(values.String(values.StrNotConnected), false) return } common.notify(err.Error(), false) return } - msg := "Rescan initiated (check in overview)" + msg := values.String(values.StrRescanProgressNotification) common.notify(msg, true) go func() { common.modalReceiver <- &modalLoad{} }() }, - confirmText: "Rescan", + confirmText: values.String(values.StrRescan), cancel: common.closeModal, - cancelText: "Cancel", + cancelText: values.String(values.StrCancel), } }() break @@ -240,26 +240,26 @@ func (pg *walletSettingsPage) handle(common pageCommon) { go func() { common.modalReceiver <- &modalLoad{ template: ConfirmRemoveTemplate, - title: "Remove wallet from device", + title: values.String(values.StrRemoveWallet), confirm: func() { walletID := pg.walletInfo.Wallets[*common.selectedWallet].ID go func() { common.modalReceiver <- &modalLoad{ template: PasswordTemplate, - title: "Confirm to remove", + title: values.String(values.StrConfirmToRemove), confirm: func(pass string) { pg.wal.DeleteWallet(walletID, []byte(pass), pg.errorReceiver) pg.resetSelectedWallet(common) }, - confirmText: "Confirm", + confirmText: values.String(values.StrConfirm), cancel: common.closeModal, - cancelText: "Cancel", + cancelText: values.String(values.StrCancel), } }() }, - confirmText: "Remove", + confirmText: values.String(values.StrRemove), cancel: common.closeModal, - cancelText: "Cancel", + cancelText: values.String(values.StrCancel), } }() break @@ -267,8 +267,8 @@ func (pg *walletSettingsPage) handle(common pageCommon) { select { case err := <-pg.errorReceiver: - if err.Error() == "invalid_passphrase" { - e := "Password is incorrect" + if err.Error() == dcrlibwallet.ErrInvalidPassphrase { + e := values.String(values.StrInvalidPassphrase) common.notify(e, false) } default: diff --git a/ui/window.go b/ui/window.go index 1e3ec7336..08892e32b 100644 --- a/ui/window.go +++ b/ui/window.go @@ -2,7 +2,6 @@ package ui import ( "errors" - "fmt" "image" "time" @@ -81,7 +80,7 @@ func CreateWindow(wal *wallet.Wallet, decredIcons map[string]image.Image, collec } else { netType = wal.Net } - win.window = app.NewWindow(app.Size(values.AppWidth, values.AppHeight), app.Title(fmt.Sprintf("%s (%s)", "godcr", netType))) + win.window = app.NewWindow(app.Size(values.AppWidth, values.AppHeight), app.Title(values.StringF(values.StrAppTitle, netType))) theme := decredmaterial.NewTheme(collection, decredIcons, false) if theme == nil { return nil, errors.New("Unexpected error while loading theme")