more translations

This commit is contained in:
Drew Weymouth
2024-07-21 11:13:26 -07:00
parent 1096e4434b
commit 9910d54f8e
9 changed files with 121 additions and 60 deletions
+2 -1
View File
@@ -9,6 +9,7 @@ import (
"github.com/dweymouth/supersonic/ui/widgets"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/lang"
"fyne.io/fyne/v2/widget"
)
@@ -58,7 +59,7 @@ func (g *genrePageAdapter) Route() controller.Route {
func (g *genrePageAdapter) ActionButton() *widget.Button {
fn := func() { go g.pm.PlayRandomSongs(g.genre) }
return widget.NewButtonWithIcon("Play random", myTheme.ShuffleIcon, fn)
return widget.NewButtonWithIcon(lang.L("Play random"), myTheme.ShuffleIcon, fn)
}
func (a *genrePageAdapter) Iter(sortOrder string, filter mediaprovider.AlbumFilter) widgets.GridViewIterator {
+2 -2
View File
@@ -249,8 +249,8 @@ func NewRadioList(nowPlayingIDPtr *string) *RadioList {
a.playingIcon = container.NewCenter(widget.NewIcon(playIcon))
a.ExtendBaseWidget(a)
a.hdr = widgets.NewListHeader([]widgets.ListColumn{
{Text: "Name", Alignment: fyne.TextAlignLeading, CanToggleVisible: false},
{Text: "Home Page", Alignment: fyne.TextAlignLeading, CanToggleVisible: false}},
{Text: lang.L("Name"), Alignment: fyne.TextAlignLeading, CanToggleVisible: false},
{Text: lang.L("Home Page"), Alignment: fyne.TextAlignLeading, CanToggleVisible: false}},
a.columnsLayout)
a.hdr.DisableSorting = true
a.list = widgets.NewFocusList(
+19 -18
View File
@@ -25,6 +25,7 @@ import (
"fyne.io/fyne/v2/canvas"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/dialog"
"fyne.io/fyne/v2/lang"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
@@ -246,7 +247,7 @@ func (m *Controller) GetArtistTracks(artistID string) []*mediaprovider.Track {
}
func (m *Controller) PromptForFirstServer() {
d := dialogs.NewAddEditServerDialog("Connect to Server", false, nil, m.MainWindow.Canvas().Focus)
d := dialogs.NewAddEditServerDialog(lang.L("Connect to Server"), false, nil, m.MainWindow.Canvas().Focus)
pop := widget.NewModalPopUp(d, m.MainWindow.Canvas())
d.OnSubmit = func() {
d.DisableSubmit()
@@ -333,7 +334,7 @@ func (m *Controller) DoEditPlaylistWorkflow(playlist *mediaprovider.Playlist) {
}
dlg.OnDeletePlaylist = func() {
pop.Hide()
dialog.ShowCustomConfirm("Confirm Delete Playlist", "OK", "Cancel", layout.NewSpacer(), /*custom content*/
dialog.ShowCustomConfirm(lang.L("Confirm Delete Playlist"), lang.L("OK"), lang.L("Cancel"), layout.NewSpacer(), /*custom content*/
func(ok bool) {
if !ok {
pop.Show()
@@ -380,8 +381,8 @@ func (c *Controller) DoConnectToServerWorkflow(server *backend.ServerConfig) {
canceled := false
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
dlg := dialog.NewCustom("Connecting", "Cancel",
widget.NewLabel(fmt.Sprintf("Connecting to %s", server.Nickname)), c.MainWindow)
dlg := dialog.NewCustom(lang.L("Connecting"), lang.L("Cancel"),
widget.NewLabel(fmt.Sprintf(lang.L("Connecting to")+" %s", server.Nickname)), c.MainWindow)
dlg.SetOnClosed(func() {
canceled = true
cancel()
@@ -414,16 +415,16 @@ func (m *Controller) PromptForLoginAndConnect() {
pop := widget.NewModalPopUp(d, m.MainWindow.Canvas())
d.OnSubmit = func(server *backend.ServerConfig, password string) {
d.DisableSubmit()
d.SetInfoText("Testing connection...")
d.SetInfoText(lang.L("Testing connection") + "...")
go func() {
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
err := m.App.ServerManager.TestConnectionAndAuth(ctx, server.ServerConnection, password)
if err == backend.ErrUnreachable {
d.SetErrorText("Server unreachable")
d.SetErrorText(lang.L("Server unreachable"))
} else if err != nil {
d.SetErrorText("Authentication failed")
d.SetErrorText(lang.L("Authentication failed"))
} else {
pop.Hide()
m.trySetPasswordAndConnectToServer(server, password)
@@ -434,7 +435,7 @@ func (m *Controller) PromptForLoginAndConnect() {
}
d.OnEditServer = func(server *backend.ServerConfig) {
pop.Hide()
editD := dialogs.NewAddEditServerDialog("Edit server", true, server, m.MainWindow.Canvas().Focus)
editD := dialogs.NewAddEditServerDialog(lang.L("Edit server"), true, server, m.MainWindow.Canvas().Focus)
editPop := widget.NewModalPopUp(editD, m.MainWindow.Canvas())
editD.OnSubmit = func() {
d.DisableSubmit()
@@ -461,7 +462,7 @@ func (m *Controller) PromptForLoginAndConnect() {
}
d.OnNewServer = func() {
pop.Hide()
newD := dialogs.NewAddEditServerDialog("Add server", true, nil, m.MainWindow.Canvas().Focus)
newD := dialogs.NewAddEditServerDialog(lang.L("Add Server"), true, nil, m.MainWindow.Canvas().Focus)
newPop := widget.NewModalPopUp(newD, m.MainWindow.Canvas())
newD.OnSubmit = func() {
d.DisableSubmit()
@@ -491,8 +492,8 @@ func (m *Controller) PromptForLoginAndConnect() {
}
d.OnDeleteServer = func(server *backend.ServerConfig) {
pop.Hide()
dialog.ShowConfirm("Confirm delete server",
fmt.Sprintf("Are you sure you want to delete the server %q?", server.Nickname),
dialog.ShowConfirm(lang.L("Confirm Delete Server"),
fmt.Sprintf(lang.L("Are you sure you want to delete the server")+" %q?", server.Nickname),
func(ok bool) {
if ok {
m.App.ServerManager.DeleteServer(server.ID)
@@ -527,7 +528,7 @@ func (c *Controller) ShowSettingsDialog(themeUpdateCallbk func(), themeFiles map
devs, err := c.App.LocalPlayer.ListAudioDevices()
if err != nil {
log.Printf("error listing audio devices: %v", err)
devs = []mpv.AudioDevice{{Name: "auto", Description: "Autoselect device"}}
devs = []mpv.AudioDevice{{Name: "auto", Description: lang.L("Autoselect device")}}
}
curPlayer := c.App.PlaybackManager.CurrentPlayer()
@@ -634,7 +635,7 @@ func (c *Controller) tryConnectToServer(ctx context.Context, server *backend.Ser
}
func (c *Controller) testConnectionAndUpdateDialogText(dlg *dialogs.AddEditServerDialog) bool {
dlg.SetInfoText("Testing connection...")
dlg.SetInfoText(lang.L("Testing connection") + "...")
conn := backend.ServerConnection{
ServerType: dlg.ServerType,
Hostname: dlg.Host,
@@ -646,10 +647,10 @@ func (c *Controller) testConnectionAndUpdateDialogText(dlg *dialogs.AddEditServe
defer cancel()
err := c.App.ServerManager.TestConnectionAndAuth(ctx, conn, dlg.Password)
if err == backend.ErrUnreachable {
dlg.SetErrorText("Could not reach server (wrong hostname?)")
dlg.SetErrorText(lang.L("Could not reach server") + fmt.Sprintf(" (%s?)", lang.L("wrong URL")))
return false
} else if err != nil {
dlg.SetErrorText("Authentication failed (wrong username/password)")
dlg.SetErrorText(lang.L("Authentication failed") + fmt.Sprintf(" (%s)", lang.L("wrong username/password")))
return false
}
return true
@@ -697,7 +698,7 @@ func (c *Controller) ShowShareDialog(id string) {
}
hyperlink := widget.NewHyperlink(shareUrl.String(), shareUrl)
dlg := dialog.NewCustom("Share content", "OK",
dlg := dialog.NewCustom(lang.L("Share content"), lang.L("OK"),
container.NewHBox(
hyperlink,
widget.NewButtonWithIcon("", theme.ContentCopyIcon(), func() {
@@ -787,7 +788,7 @@ func (c *Controller) downloadTrack(track *mediaprovider.Track, filePath string)
}
log.Printf("Saved song %s to: %s\n", track.Title, filePath)
c.sendNotification(fmt.Sprintf("Download completed: %s", track.Title), fmt.Sprintf("Saved at: %s", filePath))
c.sendNotification(fmt.Sprintf(lang.L("Download completed")+": %s", track.Title), fmt.Sprintf(lang.L("Saved at")+": %s", filePath))
}
func (c *Controller) downloadTracks(tracks []*mediaprovider.Track, filePath, downloadName string) {
@@ -825,7 +826,7 @@ func (c *Controller) downloadTracks(tracks []*mediaprovider.Track, filePath, dow
log.Printf("Saved song %s to: %s\n", track.Title, filePath)
}
c.sendNotification(fmt.Sprintf("Download completed: %s", downloadName), fmt.Sprintf("Saved at: %s", filePath))
c.sendNotification(fmt.Sprintf(lang.L("Download completed")+": %s", downloadName), fmt.Sprintf("Saved at: %s", filePath))
}
func (c *Controller) sendNotification(title, content string) {
+2 -1
View File
@@ -4,6 +4,7 @@ import (
"log"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/lang"
"github.com/dweymouth/supersonic/backend/mediaprovider"
"github.com/dweymouth/supersonic/ui/util"
)
@@ -15,7 +16,7 @@ type QuickSearch struct {
func NewQuickSearch(mp mediaprovider.MediaProvider, im util.ImageFetcher) *QuickSearch {
q := &QuickSearch{mp: mp}
q.SearchDialog = NewSearchDialog(im, "Quick Search", "Close", q.onSearched)
q.SearchDialog = NewSearchDialog(im, lang.L("Search Everywhere"), lang.L("Close"), q.onSearched)
return q
}
+25 -24
View File
@@ -12,6 +12,7 @@ import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/lang"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
@@ -56,24 +57,24 @@ func SecondsToTimeString(s float64) string {
var str string
if days > 0 {
daysStr := "days"
daysStr := lang.L("days")
if days == 1 {
daysStr = "day"
daysStr = lang.L("day")
}
str = fmt.Sprintf("%d %s ", days, daysStr)
}
if hr > 0 {
hrStr := "hrs"
hrStr := lang.L("hrs")
if hr == 1 {
hrStr = "hr"
hrStr = lang.L("hr")
}
str += fmt.Sprintf("%d %s ", hr, hrStr)
}
if min > 0 {
str += fmt.Sprintf("%d min ", min)
str += fmt.Sprintf("%d %s ", min, lang.L("min"))
}
if sec > 0 {
str += fmt.Sprintf("%d sec ", sec)
str += fmt.Sprintf("%d %s ", sec, lang.L("sec"))
}
return str[:len(str)-1]
}
@@ -158,45 +159,45 @@ func PlaintextFromHTMLString(s string) string {
}
func DisplayReleaseType(releaseTypes mediaprovider.ReleaseTypes) string {
baseType := "Album"
baseType := lang.L("Album")
switch {
case releaseTypes&mediaprovider.ReleaseTypeAudiobook > 0:
baseType = "Audiobook"
baseType = lang.L("Audiobook")
case releaseTypes&mediaprovider.ReleaseTypeAudioDrama > 0:
baseType = "Audio Drama"
baseType = lang.L("Audio Drama")
case releaseTypes&mediaprovider.ReleaseTypeBroadcast > 0:
baseType = "Broadcast"
baseType = lang.L("Broadcast")
case releaseTypes&mediaprovider.ReleaseTypeDJMix > 0:
baseType = "DJ-Mix"
baseType = lang.L("DJ-Mix")
case releaseTypes&mediaprovider.ReleaseTypeEP > 0:
baseType = "EP"
baseType = lang.L("EP")
case releaseTypes&mediaprovider.ReleaseTypeFieldRecording > 0:
baseType = "Field Recording"
baseType = lang.L("Field Recording")
case releaseTypes&mediaprovider.ReleaseTypeInterview > 0:
baseType = "Interview"
baseType = lang.L("Interview")
case releaseTypes&mediaprovider.ReleaseTypeMixtape > 0:
baseType = "Mixtape"
baseType = lang.L("Mixtape")
case releaseTypes&mediaprovider.ReleaseTypeSingle > 0:
baseType = "Single"
baseType = lang.L("Single")
case releaseTypes&mediaprovider.ReleaseTypeSoundtrack > 0:
baseType = "Soundtrack"
baseType = lang.L("Soundtrack")
}
var modifiers []string
if releaseTypes&mediaprovider.ReleaseTypeLive > 0 {
modifiers = append(modifiers, "Live")
modifiers = append(modifiers, lang.L("Live"))
}
if releaseTypes&mediaprovider.ReleaseTypeDemo > 0 {
modifiers = append(modifiers, "Demo")
modifiers = append(modifiers, lang.L("Demo"))
}
if releaseTypes&mediaprovider.ReleaseTypeRemix > 0 {
modifiers = append(modifiers, "Remix")
modifiers = append(modifiers, lang.L("Remix"))
}
if releaseTypes&mediaprovider.ReleaseTypeSpokenWord > 0 {
modifiers = append(modifiers, "Spoken Word")
modifiers = append(modifiers, lang.L("Spoken Word"))
}
if releaseTypes&mediaprovider.ReleaseTypeCompilation > 0 {
modifiers = append(modifiers, "Compilation")
modifiers = append(modifiers, lang.L("Compilation"))
}
modifiers = append(modifiers, baseType)
@@ -205,7 +206,7 @@ func DisplayReleaseType(releaseTypes mediaprovider.ReleaseTypes) string {
func NewRatingSubmenu(onSetRating func(int)) *fyne.MenuItem {
newRatingMenuItem := func(rating int) *fyne.MenuItem {
label := "(none)"
label := lang.L("(none)")
if rating > 0 {
label = strconv.Itoa(rating)
}
@@ -213,7 +214,7 @@ func NewRatingSubmenu(onSetRating func(int)) *fyne.MenuItem {
onSetRating(rating)
})
}
ratingMenu := fyne.NewMenuItem("Set rating", nil)
ratingMenu := fyne.NewMenuItem(lang.L("Set rating"), nil)
ratingMenu.Icon = theme.NewThemedResource(res.ResStarOutlineSvg)
ratingMenu.ChildMenu = fyne.NewMenu("", []*fyne.MenuItem{
newRatingMenuItem(0),
+12 -11
View File
@@ -11,6 +11,7 @@ import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/lang"
"fyne.io/fyne/v2/layout"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
@@ -153,7 +154,7 @@ func NewAlbumFilterPopup(filter *AlbumFilterButton) *AlbumFilterPopup {
}
// setup is favorite/not favorite filters
a.isFavorite = widget.NewCheck("Is favorite", func(fav bool) {
a.isFavorite = widget.NewCheck(lang.L("Is favorite"), func(fav bool) {
filterOptions := a.filterBtn.filter.Options()
if fav {
a.isNotFavorite.SetChecked(false)
@@ -163,7 +164,7 @@ func NewAlbumFilterPopup(filter *AlbumFilterButton) *AlbumFilterPopup {
debounceOnChanged()
})
a.isFavorite.Hidden = a.filterBtn.FavoriteDisabled
a.isNotFavorite = widget.NewCheck("Is not favorite", func(fav bool) {
a.isNotFavorite = widget.NewCheck(lang.L("Is not favorite"), func(fav bool) {
filterOptions := a.filterBtn.filter.Options()
if fav {
a.isFavorite.SetChecked(false)
@@ -184,11 +185,11 @@ func NewAlbumFilterPopup(filter *AlbumFilterButton) *AlbumFilterPopup {
a.genreFilter.Hidden = a.filterBtn.GenreDisabled
// setup container
title := widget.NewLabel("Album filters")
title := widget.NewLabel(lang.L("Album filters"))
title.TextStyle.Bold = true
a.container = container.NewVBox(
container.NewHBox(layout.NewSpacer(), title, layout.NewSpacer()),
container.NewHBox(widget.NewLabel("Year from"), minYear, widget.NewLabel("to"), maxYear),
container.NewHBox(widget.NewLabel(lang.L("Year from")), minYear, widget.NewLabel("to"), maxYear),
container.NewHBox(a.isFavorite, a.isNotFavorite),
a.genreFilter,
)
@@ -281,7 +282,7 @@ func NewGenreFilterSubsection(onChanged func([]string), initialSelectedGenres []
},
)
g.filterText = widget.NewEntry()
g.filterText.SetPlaceHolder("Filter genres")
g.filterText.SetPlaceHolder(lang.L("Filter genres"))
i := NewTappableIcon(theme.ContentClearIcon())
i.NoPointerCursor = true
i.OnTapped = func() { g.filterText.SetText("") }
@@ -290,18 +291,18 @@ func NewGenreFilterSubsection(onChanged func([]string), initialSelectedGenres []
g.filterText.OnChanged = func(_ string) {
debouncer()
}
g.allBtn = widget.NewButton("All", func() { g.selectAllOrNoneInView(false) })
g.noneBtn = widget.NewButton("None", func() { g.selectAllOrNoneInView(true) })
g.allBtn = widget.NewButton(lang.L("All"), func() { g.selectAllOrNoneInView(false) })
g.noneBtn = widget.NewButton(lang.L("None"), func() { g.selectAllOrNoneInView(true) })
g.genresTitleLine = widget.NewRichText(
&widget.TextSegment{
Text: "Genres ",
Text: lang.L("Genres") + " ",
Style: widget.RichTextStyle{
Inline: true,
TextStyle: fyne.TextStyle{Bold: true},
},
},
&widget.TextSegment{Text: "(none selected)"},
&widget.TextSegment{Text: fmt.Sprintf("(%s)", lang.L("none selected"))},
)
g.updateNumSelectedText()
@@ -378,11 +379,11 @@ func (g *GenreFilterSubsection) invokeOnChanged() {
}
func (g *GenreFilterSubsection) updateNumSelectedText() {
numText := "none"
numText := lang.L("none")
if l := len(g.selectedGenres); l > 0 {
numText = strconv.Itoa(l)
}
t := fmt.Sprintf("(%s selected)", numText)
t := fmt.Sprintf("(%s %s)", numText, lang.L("selected"))
g.genresTitleLine.Segments[1].(*widget.TextSegment).Text = t
g.genresTitleLine.Refresh()
}
+2 -1
View File
@@ -3,6 +3,7 @@ package widgets
import (
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/container"
"fyne.io/fyne/v2/lang"
"fyne.io/fyne/v2/widget"
fynelyrics "github.com/dweymouth/fyne-lyrics"
"github.com/dweymouth/supersonic/backend/mediaprovider"
@@ -29,7 +30,7 @@ type LyricsViewer struct {
func NewLyricsViewer() *LyricsViewer {
l := &LyricsViewer{
noLyricsMsg: container.NewCenter(NewInfoMessage(
"Lyrics not available", "")),
lang.L("Lyrics not available"), "")),
isEmpty: true,
}
l.ExtendBaseWidget(l)
+2 -1
View File
@@ -4,6 +4,7 @@ import (
"time"
"fyne.io/fyne/v2"
"fyne.io/fyne/v2/lang"
"fyne.io/fyne/v2/theme"
"fyne.io/fyne/v2/widget"
"github.com/dweymouth/supersonic/ui/util"
@@ -27,7 +28,7 @@ func NewSearchEntry() *SearchEntry {
// For use only by extending widgets
func (sf *SearchEntry) Init() {
sf.PlaceHolder = "Search"
sf.PlaceHolder = lang.L("Search")
sf.ActionItem = NewClearTextButton(func() {
sf.SetText("")
})