From f583f98628b7029913028d0520b040e202146db9 Mon Sep 17 00:00:00 2001 From: Equationzhao Date: Wed, 26 Feb 2025 13:25:43 +0800 Subject: [PATCH] feat: (WIP) add locale support for time formatting and implement GetLocale function --- internal/cli/g.go | 1 + internal/cli/view.go | 19 +++++++-- internal/util/util.go | 27 ++++++++++++ internal/util/util_test.go | 84 +++++++++++++++++++++++++++++++++++++- 4 files changed, 126 insertions(+), 5 deletions(-) diff --git a/internal/cli/g.go b/internal/cli/g.go index d161e0d..ec86f07 100644 --- a/internal/cli/g.go +++ b/internal/cli/g.go @@ -42,6 +42,7 @@ var ( r = render.NewRenderer(&theme.DefaultAll) p = display.NewFitTerminal() timeFormat = "Jan 02 15:04" + locale = "en_US" // ReturnCode - Exit status: // 0 if OK, // 1 if minor problems (e.g., cannot access subdirectory), diff --git a/internal/cli/view.go b/internal/cli/view.go index 64e132a..096c8a1 100644 --- a/internal/cli/view.go +++ b/internal/cli/view.go @@ -10,6 +10,7 @@ import ( contents "github.com/Equationzhao/g/internal/content" "github.com/Equationzhao/g/internal/display" "github.com/Equationzhao/g/internal/filter" + "github.com/Equationzhao/g/internal/util" "github.com/gabriel-vasile/mimetype" "github.com/urfave/cli/v2" ) @@ -166,14 +167,14 @@ var viewFlag = []cli.Flag{ &cli.StringFlag{ Name: "time-style", Usage: `time/date format with -l, - valid timestamp styles are default, iso, long-iso, full-iso, locale, + valid timestamp styles are default, iso, long-iso, full-iso, custom +FORMAT like date(1). (default: +%b %d %H:%M ,like Jan 02 15:04)`, EnvVars: []string{"TIME_STYLE"}, Action: func(context *cli.Context, s string) error { _ = context.Set("time", "1") /* - The TIME_STYLE argument can be full-iso, long-iso, iso, locale, or +FORMAT. + The TIME_STYLE argument can be full-iso, long-iso, iso, or +FORMAT. FORMAT is interpreted like in date(1). Also, the TIME_STYLE environment variable sets the default style to use. */ @@ -187,8 +188,6 @@ var viewFlag = []cli.Flag{ timeFormat = "2006-01-02 15:04:05.000000000 -0700" case "long-iso": timeFormat = "2006-01-02 15:04" - case "locale": - timeFormat = "Jan 02 15:04" case "iso": timeFormat = "01-02 15:04" case "default": @@ -201,6 +200,18 @@ var viewFlag = []cli.Flag{ }, Category: "VIEW", }, + &cli.BoolFlag{ + Name: "locale", + Usage: "show time in locale format", + DisableDefaultText: true, + Action: func(context *cli.Context, b bool) error { + if b { + locale = util.GetLocale() + } + return nil + }, + Category: "VIEW", + }, &cli.BoolFlag{ Name: "full-time", Usage: "like -all/l --time-style=full-iso", diff --git a/internal/util/util.go b/internal/util/util.go index 2a80d75..89ad032 100644 --- a/internal/util/util.go +++ b/internal/util/util.go @@ -2,6 +2,7 @@ package util import ( "fmt" + "os" "path/filepath" "strconv" "strings" @@ -65,3 +66,29 @@ func SplitNumberAndUnit(input string) (float64, string) { return number, unit } + +// GetLocale returns the language code from the system locale +// It checks LC_ALL, LC_MESSAGES, and LANG environment variables in that order +// If none are set, it returns "en_US" as default +func GetLocale() string { + locale := "" + + // Check environment variables in order of priority + for _, envVar := range []string{"LC_ALL", "LC_MESSAGES", "LANG"} { + if val := os.Getenv(envVar); val != "" { + locale = val + break + } + } + + if locale == "" { + return "en_US" // default locale + } + + // Extract just the language code part (e.g., "en_US" from "en_US.UTF-8") + if i := strings.IndexRune(locale, '.'); i > 0 { + locale = locale[:i] + } + + return locale +} diff --git a/internal/util/util_test.go b/internal/util/util_test.go index 791f48c..f1860a2 100644 --- a/internal/util/util_test.go +++ b/internal/util/util_test.go @@ -1,6 +1,9 @@ package util -import "testing" +import ( + "os" + "testing" +) func TestSplitNumberAndUnit(t *testing.T) { type args struct { @@ -151,3 +154,82 @@ func TestEscape(t *testing.T) { }) } } + +func TestGetLocale(t *testing.T) { + tests := []struct { + name string + envVars map[string]string + want string + saveEnvs map[string]string + }{ + { + name: "No env vars set", + envVars: map[string]string{ + "LC_ALL": "", + "LC_MESSAGES": "", + "LANG": "", + }, + want: "en_US", + }, + { + name: "Only LANG set", + envVars: map[string]string{ + "LC_ALL": "", + "LC_MESSAGES": "", + "LANG": "fr_FR.UTF-8", + }, + want: "fr_FR", + }, + { + name: "LC_MESSAGES takes precedence over LANG", + envVars: map[string]string{ + "LC_ALL": "", + "LC_MESSAGES": "de_DE.UTF-8", + "LANG": "fr_FR.UTF-8", + }, + want: "de_DE", + }, + { + name: "LC_ALL takes precedence over all", + envVars: map[string]string{ + "LC_ALL": "ja_JP.UTF-8", + "LC_MESSAGES": "de_DE.UTF-8", + "LANG": "fr_FR.UTF-8", + }, + want: "ja_JP", + }, + { + name: "No UTF-8 suffix", + envVars: map[string]string{ + "LC_ALL": "ko_KR", + }, + want: "ko_KR", + }, + } + + // Save original env vars to restore later + saveEnvs := make(map[string]string) + for _, envVar := range []string{"LC_ALL", "LC_MESSAGES", "LANG"} { + saveEnvs[envVar] = os.Getenv(envVar) + } + + // Restore env vars after test completes + defer func() { + for k, v := range saveEnvs { + os.Setenv(k, v) + } + }() + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + // Set environment variables for this test + for k, v := range tt.envVars { + os.Setenv(k, v) + } + + if got := GetLocale(); got != tt.want { + t.Errorf("GetLocale() = %v, want %v", got, tt.want) + } + }) + } +}