diff --git a/langs/language.go b/langs/language.go index 3bb131be124..0df2914a169 100644 --- a/langs/language.go +++ b/langs/language.go @@ -21,6 +21,7 @@ import ( "github.com/pkg/errors" + "github.com/gohugoio/hugo/common/htime" "github.com/gohugoio/hugo/common/maps" "github.com/gohugoio/hugo/config" "github.com/gohugoio/locales" @@ -77,7 +78,8 @@ type Language struct { // Used for date formatting etc. We don't want these exported to the // templates. // TODO(bep) do the same for some of the others. - translator locales.Translator + translator locales.Translator + timeFormatter htime.TimeFormatter location *time.Location @@ -113,9 +115,10 @@ func NewLanguage(lang string, cfg config.Provider) *Language { Lang: lang, ContentDir: cfg.GetString("contentDir"), Cfg: cfg, LocalCfg: localCfg, - Provider: compositeConfig, - params: params, - translator: translator, + Provider: compositeConfig, + params: params, + translator: translator, + timeFormatter: htime.NewTimeFormatter(translator), } if err := l.loadLocation(cfg.GetString("timeZone")); err != nil { @@ -260,6 +263,10 @@ func (l *Language) IsSet(key string) bool { // Internal access to unexported Language fields. // This construct is to prevent them from leaking to the templates. +func GetTimeFormatter(l *Language) htime.TimeFormatter { + return l.timeFormatter +} + func GetTranslator(l *Language) locales.Translator { return l.translator } diff --git a/resources/page/integration_test.go b/resources/page/integration_test.go new file mode 100644 index 00000000000..285b1434296 --- /dev/null +++ b/resources/page/integration_test.go @@ -0,0 +1,72 @@ +// Copyright 2021 The Hugo Authors. All rights reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package page_test + +import ( + "testing" + + "github.com/gohugoio/hugo/hugolib" +) + +func TestGroupByLocalizedDate(t *testing.T) { + + files := ` +-- config.toml -- +defaultContentLanguage = 'en' +defaultContentLanguageInSubdir = true +[languages] +[languages.en] +title = 'My blog' +weight = 1 +[languages.fr] +title = 'Mon blogue' +weight = 2 +[languages.nn] +title = 'Bloggen min' +weight = 3 +-- content/p1.md -- +--- +title: "Post 1" +date: "2020-01-01" +--- +-- content/p2.md -- +--- +title: "Post 2" +date: "2020-02-01" +--- +-- content/p1.fr.md -- +--- +title: "Post 1" +date: "2020-01-01" +--- +-- content/p2.fr.md -- +--- +title: "Post 2" +date: "2020-02-01" +--- +-- layouts/index.html -- +{{ range $k, $v := site.RegularPages.GroupByDate "January, 2006" }}{{ $k }}|{{ $v.Key }}|{{ $v.Pages }}{{ end }} + + ` + + b := hugolib.NewIntegrationTestBuilder( + hugolib.IntegrationTestConfig{ + T: t, + TxtarString: files, + NeedsOsFS: true, + }).Build() + + b.AssertFileContent("public/en/index.html", "0|February, 2020|Pages(1)1|January, 2020|Pages(1)") + b.AssertFileContent("public/fr/index.html", "0|février, 2020|Pages(1)1|janvier, 2020|Pages(1)") +} diff --git a/resources/page/pagegroup.go b/resources/page/pagegroup.go index 18c98e70e64..881a38d3d01 100644 --- a/resources/page/pagegroup.go +++ b/resources/page/pagegroup.go @@ -26,6 +26,7 @@ import ( "github.com/gohugoio/hugo/common/collections" "github.com/gohugoio/hugo/common/hreflect" "github.com/gohugoio/hugo/compare" + "github.com/gohugoio/hugo/langs" "github.com/gohugoio/hugo/resources/resource" ) @@ -219,7 +220,7 @@ func (p Pages) GroupByParam(key string, order ...string) (PagesGroup, error) { return r, nil } -func (p Pages) groupByDateField(sorter func(p Pages) Pages, formatter func(p Page) string, order ...string) (PagesGroup, error) { +func (p Pages) groupByDateField(format string, sorter func(p Pages) Pages, getDate func(p Page) time.Time, order ...string) (PagesGroup, error) { if len(p) < 1 { return nil, nil } @@ -234,16 +235,20 @@ func (p Pages) groupByDateField(sorter func(p Pages) Pages, formatter func(p Pag return nil, nil } - date := formatter(sp[0].(Page)) + firstPage := sp[0].(Page) + date := getDate(firstPage) + formatter := langs.GetTimeFormatter(firstPage.Language()) + formatted := formatter.Format(date, format) var r []PageGroup - r = append(r, PageGroup{Key: date, Pages: make(Pages, 0)}) + r = append(r, PageGroup{Key: formatted, Pages: make(Pages, 0)}) r[0].Pages = append(r[0].Pages, sp[0]) i := 0 for _, e := range sp[1:] { - date = formatter(e.(Page)) - if r[i].Key.(string) != date { - r = append(r, PageGroup{Key: date}) + date = getDate(e.(Page)) + formatted := formatter.Format(date, format) + if r[i].Key.(string) != formatted { + r = append(r, PageGroup{Key: formatted}) i++ } r[i].Pages = append(r[i].Pages, e) @@ -259,10 +264,10 @@ func (p Pages) GroupByDate(format string, order ...string) (PagesGroup, error) { sorter := func(p Pages) Pages { return p.ByDate() } - formatter := func(p Page) string { - return p.Date().Format(format) + getDate := func(p Page) time.Time { + return p.Date() } - return p.groupByDateField(sorter, formatter, order...) + return p.groupByDateField(format, sorter, getDate, order...) } // GroupByPublishDate groups by the given page's PublishDate value in @@ -273,10 +278,10 @@ func (p Pages) GroupByPublishDate(format string, order ...string) (PagesGroup, e sorter := func(p Pages) Pages { return p.ByPublishDate() } - formatter := func(p Page) string { - return p.PublishDate().Format(format) + getDate := func(p Page) time.Time { + return p.PublishDate() } - return p.groupByDateField(sorter, formatter, order...) + return p.groupByDateField(format, sorter, getDate, order...) } // GroupByExpiryDate groups by the given page's ExpireDate value in @@ -287,10 +292,10 @@ func (p Pages) GroupByExpiryDate(format string, order ...string) (PagesGroup, er sorter := func(p Pages) Pages { return p.ByExpiryDate() } - formatter := func(p Page) string { - return p.ExpiryDate().Format(format) + getDate := func(p Page) time.Time { + return p.ExpiryDate() } - return p.groupByDateField(sorter, formatter, order...) + return p.groupByDateField(format, sorter, getDate, order...) } // GroupByLastmod groups by the given page's Lastmod value in @@ -301,10 +306,10 @@ func (p Pages) GroupByLastmod(format string, order ...string) (PagesGroup, error sorter := func(p Pages) Pages { return p.ByLastmod() } - formatter := func(p Page) string { - return p.Lastmod().Format(format) + getDate := func(p Page) time.Time { + return p.Lastmod() } - return p.groupByDateField(sorter, formatter, order...) + return p.groupByDateField(format, sorter, getDate, order...) } // GroupByParamDate groups by a date set as a param on the page in @@ -340,10 +345,10 @@ func (p Pages) GroupByParamDate(key string, format string, order ...string) (Pag pageBy(pdate).Sort(r) return r } - formatter := func(p Page) string { - return dates[p].Format(format) + getDate := func(p Page) time.Time { + return dates[p] } - return p.groupByDateField(sorter, formatter, order...) + return p.groupByDateField(format, sorter, getDate, order...) } // ProbablyEq wraps compare.ProbablyEqer diff --git a/resources/page/testhelpers_test.go b/resources/page/testhelpers_test.go index cee1f99e52e..3c53d3fd0cb 100644 --- a/resources/page/testhelpers_test.go +++ b/resources/page/testhelpers_test.go @@ -322,7 +322,7 @@ func (p *testPage) Lang() string { } func (p *testPage) Language() *langs.Language { - panic("not implemented") + return langs.NewDefaultLanguage(config.New()) } func (p *testPage) LanguagePrefix() string { diff --git a/tpl/time/init.go b/tpl/time/init.go index a76348b7a7c..4bb2ddf67bf 100644 --- a/tpl/time/init.go +++ b/tpl/time/init.go @@ -28,7 +28,7 @@ func init() { if d.Language == nil { panic("Language must be set") } - ctx := New(langs.GetTranslator(d.Language), langs.GetLocation(d.Language)) + ctx := New(langs.GetTimeFormatter(d.Language), langs.GetLocation(d.Language)) ns := &internal.TemplateFuncsNamespace{ Name: name, diff --git a/tpl/time/time.go b/tpl/time/time.go index 66ea721faee..f82d63c4402 100644 --- a/tpl/time/time.go +++ b/tpl/time/time.go @@ -21,15 +21,13 @@ import ( "github.com/gohugoio/hugo/common/htime" - "github.com/gohugoio/locales" - "github.com/spf13/cast" ) // New returns a new instance of the time-namespaced template functions. -func New(translator locales.Translator, location *time.Location) *Namespace { +func New(timeFormatter htime.TimeFormatter, location *time.Location) *Namespace { return &Namespace{ - timeFormatter: htime.NewTimeFormatter(translator), + timeFormatter: timeFormatter, location: location, } } diff --git a/tpl/time/time_test.go b/tpl/time/time_test.go index f368ea43f7f..9001f6b6b4b 100644 --- a/tpl/time/time_test.go +++ b/tpl/time/time_test.go @@ -20,6 +20,7 @@ import ( qt "github.com/frankban/quicktest" + "github.com/gohugoio/hugo/common/htime" translators "github.com/gohugoio/localescompressed" ) @@ -27,7 +28,7 @@ func TestTimeLocation(t *testing.T) { t.Parallel() loc, _ := time.LoadLocation("America/Antigua") - ns := New(translators.GetTranslator("en"), loc) + ns := New(htime.NewTimeFormatter(translators.GetTranslator("en")), loc) for i, test := range []struct { name string @@ -86,7 +87,7 @@ func TestFormat(t *testing.T) { c.Run("UTC", func(c *qt.C) { c.Parallel() - ns := New(translators.GetTranslator("en"), time.UTC) + ns := New(htime.NewTimeFormatter(translators.GetTranslator("en")), time.UTC) for i, test := range []struct { layout string @@ -129,7 +130,7 @@ func TestFormat(t *testing.T) { loc, err := time.LoadLocation("America/Los_Angeles") c.Assert(err, qt.IsNil) - ns := New(translators.GetTranslator("en"), loc) + ns := New(htime.NewTimeFormatter(translators.GetTranslator("en")), loc) d, err := ns.Format(":time_full", "2020-03-09T11:00:00") @@ -143,7 +144,7 @@ func TestFormat(t *testing.T) { func TestDuration(t *testing.T) { t.Parallel() - ns := New(translators.GetTranslator("en"), time.UTC) + ns := New(htime.NewTimeFormatter(translators.GetTranslator("en")), time.UTC) for i, test := range []struct { unit any