diff options
author | Bjørn Erik Pedersen <[email protected]> | 2022-04-10 20:30:52 +0200 |
---|---|---|
committer | Bjørn Erik Pedersen <[email protected]> | 2022-04-12 13:24:16 +0200 |
commit | 627eed1d620910f494056330733db6c6187b8fe9 (patch) | |
tree | a136a398e8f85b10d3de7b1781825453886435ed /langs | |
parent | 82ba634ed90186c756189a79b637559b28dd363e (diff) | |
download | hugo-627eed1d620910f494056330733db6c6187b8fe9.tar.gz hugo-627eed1d620910f494056330733db6c6187b8fe9.zip |
Make string sorting (e.g. ByTitle, ByLinkTitle and ByParam) language aware
Fixes #2180
Diffstat (limited to 'langs')
-rw-r--r-- | langs/language.go | 39 | ||||
-rw-r--r-- | langs/language_test.go | 59 |
2 files changed, 96 insertions, 2 deletions
diff --git a/langs/language.go b/langs/language.go index 0df2914a1..244f6a743 100644 --- a/langs/language.go +++ b/langs/language.go @@ -19,6 +19,9 @@ import ( "sync" "time" + "golang.org/x/text/collate" + "golang.org/x/text/language" + "github.com/pkg/errors" "github.com/gohugoio/hugo/common/htime" @@ -80,8 +83,9 @@ type Language struct { // TODO(bep) do the same for some of the others. translator locales.Translator timeFormatter htime.TimeFormatter - - location *time.Location + tag language.Tag + collator *Collator + location *time.Location // Error during initialization. Will fail the buld. initErr error @@ -111,6 +115,18 @@ func NewLanguage(lang string, cfg config.Provider) *Language { } } + var coll *Collator + tag, err := language.Parse(lang) + if err == nil { + coll = &Collator{ + c: collate.New(tag), + } + } else { + coll = &Collator{ + c: collate.New(language.English), + } + } + l := &Language{ Lang: lang, ContentDir: cfg.GetString("contentDir"), @@ -119,6 +135,8 @@ func NewLanguage(lang string, cfg config.Provider) *Language { params: params, translator: translator, timeFormatter: htime.NewTimeFormatter(translator), + tag: tag, + collator: coll, } if err := l.loadLocation(cfg.GetString("timeZone")); err != nil { @@ -275,6 +293,10 @@ func GetLocation(l *Language) *time.Location { return l.location } +func GetCollator(l *Language) *Collator { + return l.collator +} + func (l *Language) loadLocation(tzStr string) error { location, err := time.LoadLocation(tzStr) if err != nil { @@ -284,3 +306,16 @@ func (l *Language) loadLocation(tzStr string) error { return nil } + +type Collator struct { + sync.Mutex + c *collate.Collator +} + +// CompareStrings compares a and b. +// It returns -1 if a < b, 1 if a > b and 0 if a == b. +// Note that the Collator is not thread safe, so you may want +// to aquire a lock on it before calling this method. +func (c *Collator) CompareStrings(a, b string) int { + return c.c.CompareString(a, b) +} diff --git a/langs/language_test.go b/langs/language_test.go index e6ef94824..264e813a0 100644 --- a/langs/language_test.go +++ b/langs/language_test.go @@ -14,10 +14,13 @@ package langs import ( + "sync" "testing" qt "github.com/frankban/quicktest" "github.com/gohugoio/hugo/config" + "golang.org/x/text/collate" + "golang.org/x/text/language" ) func TestGetGlobalOnlySetting(t *testing.T) { @@ -47,3 +50,59 @@ func TestLanguageParams(t *testing.T) { c.Assert(lang.Params()["p1"], qt.Equals, "p1p") c.Assert(lang.Get("p1"), qt.Equals, "p1cfg") } + +func TestCollator(t *testing.T) { + + c := qt.New(t) + + var wg sync.WaitGroup + + coll := &Collator{c: collate.New(language.English, collate.Loose)} + + for i := 0; i < 10; i++ { + wg.Add(1) + go func() { + coll.Lock() + defer coll.Unlock() + defer wg.Done() + for j := 0; j < 10; j++ { + k := coll.CompareStrings("abc", "def") + c.Assert(k, qt.Equals, -1) + } + }() + } + wg.Wait() + +} + +func BenchmarkCollator(b *testing.B) { + s := []string{"foo", "bar", "éntre", "baz", "qux", "quux", "corge", "grault", "garply", "waldo", "fred", "plugh", "xyzzy", "thud"} + + doWork := func(coll *Collator) { + for i := 0; i < len(s); i++ { + for j := i + 1; j < len(s); j++ { + _ = coll.CompareStrings(s[i], s[j]) + } + } + } + + b.Run("Single", func(b *testing.B) { + coll := &Collator{c: collate.New(language.English, collate.Loose)} + for i := 0; i < b.N; i++ { + doWork(coll) + } + }) + + b.Run("Para", func(b *testing.B) { + b.RunParallel(func(pb *testing.PB) { + coll := &Collator{c: collate.New(language.English, collate.Loose)} + + for pb.Next() { + coll.Lock() + doWork(coll) + coll.Unlock() + } + }) + }) + +} |