mirror of
				https://github.com/taigrr/wtf
				synced 2025-01-18 04:03:14 -08:00 
			
		
		
		
	Add support for user-configuration language tag specification. (#1038)
Adds a new top-level configuration key called "language": ```yaml wtf: langauge: "ja-JP" ``` Users can now define which BCP 47 language tag to use to format any text or numbers that currently support localization. Defaults to "en-CA". Acceptible values: any BCP 47 language tag recognized by the Go "language" package. Good luck to you figuring out what that cannonical list is. After a morning of trying to suss it out, I have no idea. Signed-off-by: Chris Cummer <chriscummer@me.com>
This commit is contained in:
		
							parent
							
								
									fd794707cd
								
							
						
					
					
						commit
						d7da659b8b
					
				| @ -5,6 +5,12 @@ import ( | |||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/olebedev/config" | 	"github.com/olebedev/config" | ||||||
|  | 	"golang.org/x/text/language" | ||||||
|  | 	"golang.org/x/text/message" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	defaultLanguageTag = "en-CA" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type Module struct { | 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 { | type Common struct { | ||||||
| 	Module | 	Module | ||||||
| 	PositionSettings `help:"Defines where in the grid this module’s widget will be displayed."` | 	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"` | 	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"` | 	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"` | 	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"` | 	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"` | 	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 | // 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() | 	baseColors := NewDefaultColorTheme() | ||||||
| 
 | 
 | ||||||
| 	colorsConfig, err := globalSettings.Get("wtf.colors") | 	colorsConfig, err := globalConfig.Get("wtf.colors") | ||||||
| 	if err != nil && strings.Contains(err.Error(), "Nonexistent map") { | 	if err != nil && strings.Contains(err.Error(), "Nonexistent map") { | ||||||
| 		// Create a default colors config to fill in for the missing one | 		// 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: | 		// 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, | 		Config:          moduleConfig, | ||||||
| 		Enabled:         moduleConfig.UBool("enabled", false), | 		Enabled:         moduleConfig.UBool("enabled", false), | ||||||
| 		Focusable:       moduleConfig.UBool("focusable", defaultFocusable), | 		Focusable:       moduleConfig.UBool("focusable", defaultFocusable), | ||||||
|  | 		LanguageTag:     globalConfig.UString("wtf.language", defaultLanguageTag), | ||||||
| 		RefreshInterval: moduleConfig.UInt("refreshInterval", 300), | 		RefreshInterval: moduleConfig.UInt("refreshInterval", 300), | ||||||
| 		Title:           moduleConfig.UString("title", defaultTitle), | 		Title:           moduleConfig.UString("title", defaultTitle), | ||||||
| 
 | 
 | ||||||
| @ -100,11 +109,10 @@ func NewCommonSettingsFromModule(name, defaultTitle string, defaultFocusable boo | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	sigilsPath := "wtf.sigils" | 	sigilsPath := "wtf.sigils" | ||||||
| 
 | 	common.Sigils.Checkbox.Checked = globalConfig.UString(sigilsPath+".checkbox.checked", "x") | ||||||
| 	common.Sigils.Checkbox.Checked = globalSettings.UString(sigilsPath+".checkbox.checked", "x") | 	common.Sigils.Checkbox.Unchecked = globalConfig.UString(sigilsPath+".checkbox.unchecked", " ") | ||||||
| 	common.Sigils.Checkbox.Unchecked = globalSettings.UString(sigilsPath+".checkbox.unchecked", " ") | 	common.Sigils.Paging.Normal = globalConfig.UString(sigilsPath+".paging.normal", globalConfig.UString("wtf.paging.pageSigil", "*")) | ||||||
| 	common.Sigils.Paging.Normal = globalSettings.UString(sigilsPath+".paging.normal", globalSettings.UString("wtf.paging.pageSigil", "*")) | 	common.Sigils.Paging.Selected = globalConfig.UString(sigilsPath+".paging.select", globalConfig.UString("wtf.paging.selectedSigil", "_")) | ||||||
| 	common.Sigils.Paging.Selected = globalSettings.UString(sigilsPath+".paging.select", globalSettings.UString("wtf.paging.selectedSigil", "_")) |  | ||||||
| 
 | 
 | ||||||
| 	return &common | 	return &common | ||||||
| } | } | ||||||
| @ -141,6 +149,20 @@ func (common *Common) FocusChar() string { | |||||||
| 	return fmt.Sprint(common.focusChar) | 	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 { | func (common *Common) RowColor(idx int) string { | ||||||
| 	if idx%2 == 0 { | 	if idx%2 == 0 { | ||||||
| 		return common.Colors.RowTheme.EvenForeground | 		return common.Colors.RowTheme.EvenForeground | ||||||
|  | |||||||
| @ -4,8 +4,6 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| 
 | 
 | ||||||
| 	ghb "github.com/google/go-github/v32/github" | 	ghb "github.com/google/go-github/v32/github" | ||||||
| 	"golang.org/x/text/language" |  | ||||||
| 	"golang.org/x/text/message" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func (widget *Widget) display() { | func (widget *Widget) display() { | ||||||
| @ -124,13 +122,16 @@ func (widget *Widget) displayMyReviewRequests(repo *Repo, username string) strin | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (widget *Widget) displayStats(repo *Repo) string { | 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( | 	str := fmt.Sprintf( | ||||||
| 		" PRs: %s  Issues: %s  Stars: %s\n", | 		" PRs: %s  Issues: %s  Stars: %s\n", | ||||||
| 		prntr.Sprintf("%d", repo.PullRequestCount()), | 		locPrinter.Sprintf("%d", repo.PullRequestCount()), | ||||||
| 		prntr.Sprintf("%d", repo.IssueCount()), | 		locPrinter.Sprintf("%d", repo.IssueCount()), | ||||||
| 		prntr.Sprintf("%d", repo.StarCount()), | 		locPrinter.Sprintf("%d", repo.StarCount()), | ||||||
| 	) | 	) | ||||||
| 
 | 
 | ||||||
| 	return str | 	return str | ||||||
|  | |||||||
| @ -105,12 +105,14 @@ func (widget *Widget) content() (string, string, bool) { | |||||||
| 	} | 	} | ||||||
| 	var str string | 	var str string | ||||||
| 
 | 
 | ||||||
|  | 	locPrinter, _ := widget.settings.LocalizedPrinter() | ||||||
|  | 
 | ||||||
| 	for idx, stream := range widget.topStreams { | 	for idx, stream := range widget.topStreams { | ||||||
| 		row := fmt.Sprintf( | 		row := fmt.Sprintf( | ||||||
| 			"[%s]%2d. [red]%s [white]%s", | 			"[%s]%2d. [red]%s [white]%s", | ||||||
| 			widget.RowColor(idx), | 			widget.RowColor(idx), | ||||||
| 			idx+1, | 			idx+1, | ||||||
| 			utils.PrettyNumber(float64(stream.ViewerCount)), | 			utils.PrettyNumber(locPrinter, float64(stream.ViewerCount)), | ||||||
| 			stream.Streamer, | 			stream.Streamer, | ||||||
| 		) | 		) | ||||||
| 		str += utils.HighlightableHelper(widget.View, row, idx, len(stream.Streamer)) | 		str += utils.HighlightableHelper(widget.View, row, idx, len(stream.Streamer)) | ||||||
|  | |||||||
| @ -5,7 +5,6 @@ import ( | |||||||
| 	"math" | 	"math" | ||||||
| 	"strings" | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"golang.org/x/text/language" |  | ||||||
| 	"golang.org/x/text/message" | 	"golang.org/x/text/message" | ||||||
| 
 | 
 | ||||||
| 	"github.com/rivo/tview" | 	"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 | // PrettyNumber formats number as string with 1000 delimiters and, if necessary, rounds it to 2 decimals | ||||||
| func PrettyNumber(number float64) string { | func PrettyNumber(prtr *message.Printer, number float64) string { | ||||||
| 	p := message.NewPrinter(language.English) |  | ||||||
| 
 |  | ||||||
| 	if number == math.Trunc(number) { | 	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) | ||||||
| } | } | ||||||
|  | |||||||
| @ -5,6 +5,8 @@ import ( | |||||||
| 
 | 
 | ||||||
| 	"github.com/rivo/tview" | 	"github.com/rivo/tview" | ||||||
| 	"github.com/stretchr/testify/assert" | 	"github.com/stretchr/testify/assert" | ||||||
|  | 	"golang.org/x/text/language" | ||||||
|  | 	"golang.org/x/text/message" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func Test_CenterText(t *testing.T) { | func Test_CenterText(t *testing.T) { | ||||||
| @ -45,15 +47,17 @@ func Test_Truncate(t *testing.T) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func Test_PrettyNumber(t *testing.T) { | func Test_PrettyNumber(t *testing.T) { | ||||||
| 	assert.Equal(t, "1,000,000", PrettyNumber(1000000)) | 	locPrinter := message.NewPrinter(language.English) | ||||||
| 	assert.Equal(t, "1,000,000.99", PrettyNumber(1000000.99)) | 
 | ||||||
| 	assert.Equal(t, "1,000,000", PrettyNumber(1000000.00)) | 	assert.Equal(t, "1,000,000", PrettyNumber(locPrinter, 1000000)) | ||||||
| 	assert.Equal(t, "100,000", PrettyNumber(100000)) | 	assert.Equal(t, "1,000,000.99", PrettyNumber(locPrinter, 1000000.99)) | ||||||
| 	assert.Equal(t, "100,000.01", PrettyNumber(100000.009)) | 	assert.Equal(t, "1,000,000", PrettyNumber(locPrinter, 1000000.00)) | ||||||
| 	assert.Equal(t, "10,000", PrettyNumber(10000)) | 	assert.Equal(t, "100,000", PrettyNumber(locPrinter, 100000)) | ||||||
| 	assert.Equal(t, "1,000", PrettyNumber(1000)) | 	assert.Equal(t, "100,000.01", PrettyNumber(locPrinter, 100000.009)) | ||||||
| 	assert.Equal(t, "1,000", PrettyNumber(1000)) | 	assert.Equal(t, "10,000", PrettyNumber(locPrinter, 10000)) | ||||||
| 	assert.Equal(t, "100", PrettyNumber(100)) | 	assert.Equal(t, "1,000", PrettyNumber(locPrinter, 1000)) | ||||||
| 	assert.Equal(t, "0", PrettyNumber(0)) | 	assert.Equal(t, "1,000", PrettyNumber(locPrinter, 1000)) | ||||||
| 	assert.Equal(t, "0.10", PrettyNumber(0.1)) | 	assert.Equal(t, "100", PrettyNumber(locPrinter, 100)) | ||||||
|  | 	assert.Equal(t, "0", PrettyNumber(locPrinter, 0)) | ||||||
|  | 	assert.Equal(t, "0.10", PrettyNumber(locPrinter, 0.1)) | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user