diff --git a/cfg/common_settings.go b/cfg/common_settings.go index bbe7cbd9..0e932ede 100644 --- a/cfg/common_settings.go +++ b/cfg/common_settings.go @@ -5,6 +5,12 @@ import ( "strings" "github.com/olebedev/config" + "golang.org/x/text/language" + "golang.org/x/text/message" +) + +const ( + defaultLanguageTag = "en-CA" ) type Module struct { @@ -23,6 +29,7 @@ type Sigils struct { } } +// Common defines a set of common configuration settings applicable to all modules type Common struct { Module PositionSettings `help:"Defines where in the grid this module’s widget will be displayed."` @@ -36,6 +43,7 @@ type Common struct { Bordered bool `help:"Whether or not the module should be displayed with a border." values:"true, false" optional:"true" default:"true"` Enabled bool `help:"Whether or not this module is executed and if its data displayed onscreen." values:"true, false" optional:"true" default:"false"` Focusable bool `help:"Whether or not this module is focusable." values:"true, false" optional:"true" default:"false"` + LanguageTag string `help:"The BCP 47 langauge tag to localize text to." values:"Any supported BCP 47 language tag." optional:"true" default:"en-CA"` RefreshInterval int `help:"How often, in seconds, this module will update its data." values:"A positive integer, 0..n." optional:"true"` Title string `help:"The title string to show when displaying this module" optional:"true"` @@ -43,10 +51,10 @@ type Common struct { } // NewCommonSettingsFromModule returns a common settings configuration tailed to the given module -func NewCommonSettingsFromModule(name, defaultTitle string, defaultFocusable bool, moduleConfig *config.Config, globalSettings *config.Config) *Common { +func NewCommonSettingsFromModule(name, defaultTitle string, defaultFocusable bool, moduleConfig *config.Config, globalConfig *config.Config) *Common { baseColors := NewDefaultColorTheme() - colorsConfig, err := globalSettings.Get("wtf.colors") + colorsConfig, err := globalConfig.Get("wtf.colors") if err != nil && strings.Contains(err.Error(), "Nonexistent map") { // Create a default colors config to fill in for the missing one // This comes into play when the configuration file does not contain a `colors:` key, i.e: @@ -93,6 +101,7 @@ func NewCommonSettingsFromModule(name, defaultTitle string, defaultFocusable boo Config: moduleConfig, Enabled: moduleConfig.UBool("enabled", false), Focusable: moduleConfig.UBool("focusable", defaultFocusable), + LanguageTag: globalConfig.UString("wtf.language", defaultLanguageTag), RefreshInterval: moduleConfig.UInt("refreshInterval", 300), Title: moduleConfig.UString("title", defaultTitle), @@ -100,11 +109,10 @@ func NewCommonSettingsFromModule(name, defaultTitle string, defaultFocusable boo } sigilsPath := "wtf.sigils" - - common.Sigils.Checkbox.Checked = globalSettings.UString(sigilsPath+".checkbox.checked", "x") - common.Sigils.Checkbox.Unchecked = globalSettings.UString(sigilsPath+".checkbox.unchecked", " ") - common.Sigils.Paging.Normal = globalSettings.UString(sigilsPath+".paging.normal", globalSettings.UString("wtf.paging.pageSigil", "*")) - common.Sigils.Paging.Selected = globalSettings.UString(sigilsPath+".paging.select", globalSettings.UString("wtf.paging.selectedSigil", "_")) + common.Sigils.Checkbox.Checked = globalConfig.UString(sigilsPath+".checkbox.checked", "x") + common.Sigils.Checkbox.Unchecked = globalConfig.UString(sigilsPath+".checkbox.unchecked", " ") + common.Sigils.Paging.Normal = globalConfig.UString(sigilsPath+".paging.normal", globalConfig.UString("wtf.paging.pageSigil", "*")) + common.Sigils.Paging.Selected = globalConfig.UString(sigilsPath+".paging.select", globalConfig.UString("wtf.paging.selectedSigil", "_")) return &common } @@ -141,6 +149,20 @@ func (common *Common) FocusChar() string { return fmt.Sprint(common.focusChar) } +// LocalizedPrinter returns a message.Printer instance localized to the BCP 47 language +// configuration value defined in 'wtf.language' config. If none exists, it defaults to +// 'en-CA'. Use this to format numbers, etc. +func (common *Common) LocalizedPrinter() (*message.Printer, error) { + langTag, err := language.Parse(common.LanguageTag) + if err != nil { + return nil, err + } + + prntr := message.NewPrinter(langTag) + + return prntr, nil +} + func (common *Common) RowColor(idx int) string { if idx%2 == 0 { return common.Colors.RowTheme.EvenForeground diff --git a/modules/github/display.go b/modules/github/display.go index 06d098a2..eab995c1 100644 --- a/modules/github/display.go +++ b/modules/github/display.go @@ -4,8 +4,6 @@ import ( "fmt" ghb "github.com/google/go-github/v32/github" - "golang.org/x/text/language" - "golang.org/x/text/message" ) func (widget *Widget) display() { @@ -124,13 +122,16 @@ func (widget *Widget) displayMyReviewRequests(repo *Repo, username string) strin } func (widget *Widget) displayStats(repo *Repo) string { - prntr := message.NewPrinter(language.English) + locPrinter, err := widget.settings.LocalizedPrinter() + if err != nil { + return err.Error() + } str := fmt.Sprintf( " PRs: %s Issues: %s Stars: %s\n", - prntr.Sprintf("%d", repo.PullRequestCount()), - prntr.Sprintf("%d", repo.IssueCount()), - prntr.Sprintf("%d", repo.StarCount()), + locPrinter.Sprintf("%d", repo.PullRequestCount()), + locPrinter.Sprintf("%d", repo.IssueCount()), + locPrinter.Sprintf("%d", repo.StarCount()), ) return str diff --git a/modules/twitch/widget.go b/modules/twitch/widget.go index 54000ff1..d5984d72 100644 --- a/modules/twitch/widget.go +++ b/modules/twitch/widget.go @@ -105,12 +105,14 @@ func (widget *Widget) content() (string, string, bool) { } var str string + locPrinter, _ := widget.settings.LocalizedPrinter() + for idx, stream := range widget.topStreams { row := fmt.Sprintf( "[%s]%2d. [red]%s [white]%s", widget.RowColor(idx), idx+1, - utils.PrettyNumber(float64(stream.ViewerCount)), + utils.PrettyNumber(locPrinter, float64(stream.ViewerCount)), stream.Streamer, ) str += utils.HighlightableHelper(widget.View, row, idx, len(stream.Streamer)) diff --git a/utils/text.go b/utils/text.go index 53d9669e..00913bbb 100644 --- a/utils/text.go +++ b/utils/text.go @@ -5,7 +5,6 @@ import ( "math" "strings" - "golang.org/x/text/language" "golang.org/x/text/message" "github.com/rivo/tview" @@ -78,12 +77,10 @@ func Truncate(src string, maxLen int, withEllipse bool) string { } // PrettyNumber formats number as string with 1000 delimiters and, if necessary, rounds it to 2 decimals -func PrettyNumber(number float64) string { - p := message.NewPrinter(language.English) - +func PrettyNumber(prtr *message.Printer, number float64) string { if number == math.Trunc(number) { - return p.Sprintf("%.0f", number) + return prtr.Sprintf("%.0f", number) } - return p.Sprintf("%.2f", number) + return prtr.Sprintf("%.2f", number) } diff --git a/utils/text_test.go b/utils/text_test.go index 4ed65924..da877d38 100644 --- a/utils/text_test.go +++ b/utils/text_test.go @@ -5,6 +5,8 @@ import ( "github.com/rivo/tview" "github.com/stretchr/testify/assert" + "golang.org/x/text/language" + "golang.org/x/text/message" ) func Test_CenterText(t *testing.T) { @@ -45,15 +47,17 @@ func Test_Truncate(t *testing.T) { } func Test_PrettyNumber(t *testing.T) { - assert.Equal(t, "1,000,000", PrettyNumber(1000000)) - assert.Equal(t, "1,000,000.99", PrettyNumber(1000000.99)) - assert.Equal(t, "1,000,000", PrettyNumber(1000000.00)) - assert.Equal(t, "100,000", PrettyNumber(100000)) - assert.Equal(t, "100,000.01", PrettyNumber(100000.009)) - assert.Equal(t, "10,000", PrettyNumber(10000)) - assert.Equal(t, "1,000", PrettyNumber(1000)) - assert.Equal(t, "1,000", PrettyNumber(1000)) - assert.Equal(t, "100", PrettyNumber(100)) - assert.Equal(t, "0", PrettyNumber(0)) - assert.Equal(t, "0.10", PrettyNumber(0.1)) + locPrinter := message.NewPrinter(language.English) + + assert.Equal(t, "1,000,000", PrettyNumber(locPrinter, 1000000)) + assert.Equal(t, "1,000,000.99", PrettyNumber(locPrinter, 1000000.99)) + assert.Equal(t, "1,000,000", PrettyNumber(locPrinter, 1000000.00)) + assert.Equal(t, "100,000", PrettyNumber(locPrinter, 100000)) + assert.Equal(t, "100,000.01", PrettyNumber(locPrinter, 100000.009)) + assert.Equal(t, "10,000", PrettyNumber(locPrinter, 10000)) + assert.Equal(t, "1,000", PrettyNumber(locPrinter, 1000)) + assert.Equal(t, "1,000", PrettyNumber(locPrinter, 1000)) + assert.Equal(t, "100", PrettyNumber(locPrinter, 100)) + assert.Equal(t, "0", PrettyNumber(locPrinter, 0)) + assert.Equal(t, "0.10", PrettyNumber(locPrinter, 0.1)) }