diff --git a/hugolib/pages_language_merge_test.go b/hugolib/pages_language_merge_test.go index 3b55a6288..b7c78836c 100644 --- a/hugolib/pages_language_merge_test.go +++ b/hugolib/pages_language_merge_test.go @@ -17,6 +17,7 @@ import ( "fmt" "testing" + "github.com/gohugoio/hugo/resource" "github.com/stretchr/testify/require" ) @@ -35,16 +36,16 @@ func TestMergeLanguages(t *testing.T) { frSite := h.Sites[1] nnSite := h.Sites[2] - assert.Equal(30, len(enSite.RegularPages)) + assert.Equal(31, len(enSite.RegularPages)) assert.Equal(6, len(frSite.RegularPages)) - assert.Equal(11, len(nnSite.RegularPages)) + assert.Equal(12, len(nnSite.RegularPages)) for i := 0; i < 2; i++ { mergedNN := nnSite.RegularPages.MergeByLanguage(enSite.RegularPages) - assert.Equal(30, len(mergedNN)) - for i := 1; i <= 30; i++ { + assert.Equal(31, len(mergedNN)) + for i := 1; i <= 31; i++ { expectedLang := "en" - if i == 2 || i%3 == 0 { + if i == 2 || i%3 == 0 || i == 31 { expectedLang = "nn" } p := mergedNN[i-1] @@ -53,8 +54,8 @@ func TestMergeLanguages(t *testing.T) { } mergedFR := frSite.RegularPages.MergeByLanguage(enSite.RegularPages) - assert.Equal(30, len(mergedFR)) - for i := 1; i <= 30; i++ { + assert.Equal(31, len(mergedFR)) + for i := 1; i <= 31; i++ { expectedLang := "en" if i%5 == 0 { expectedLang = "fr" @@ -67,6 +68,17 @@ func TestMergeLanguages(t *testing.T) { assert.Equal(4, len(firstNN.Sites())) assert.Equal("en", firstNN.Sites().First().Language.Lang) + nnBundle := nnSite.getPage("page", "bundle") + enBundle := enSite.getPage("page", "bundle") + + assert.Equal(6, len(enBundle.Resources)) + assert.Equal(2, len(nnBundle.Resources)) + + var ri interface{} = nnBundle.Resources + + // This looks less ugly in the templates ... + mergedNNResources := ri.(resource.ResourcesLanguageMerger).MergeByLanguage(enBundle.Resources) + assert.Equal(6, len(mergedNNResources)) } func TestMergeLanguagesTemplate(t *testing.T) { @@ -79,10 +91,16 @@ func TestMergeLanguagesTemplate(t *testing.T) { {{ if eq .Lang "nn" }}: {{ $enSite := index .Sites 0 }} {{ $frSite := index .Sites 1 }} +{{ $nnBundle := .Site.GetPage "page" "bundle" }} +{{ $enBundle := $enSite.GetPage "page" "bundle" }} {{ .Scratch.Set "pages" ($pages | lang.Merge $frSite.RegularPages| lang.Merge $enSite.RegularPages) }} +{{ .Scratch.Set "pages2" (sort ($nnBundle.Resources | lang.Merge $enBundle.Resources) "Title") }} {{ end }} {{ $pages := .Scratch.Get "pages" }} -{{ range $i, $p := $pages }}{{ add $i 1 }}: {{ .Path }} {{ .Lang }} | {{ end }} +{{ $pages2 := .Scratch.Get "pages2" }} +Pages1: {{ range $i, $p := $pages }}{{ add $i 1 }}: {{ .Path }} {{ .Lang }} | {{ end }} +Pages2: {{ range $i, $p := $pages2 }}{{ add $i 1 }}: {{ .Title }} {{ .Lang }} | {{ end }} + `, "shortcodes/shortcode.html", "MyShort", "shortcodes/lingo.html", "MyLingo", @@ -91,7 +109,8 @@ func TestMergeLanguagesTemplate(t *testing.T) { b.CreateSites() b.Build(BuildCfg{}) - b.AssertFileContent("public/nn/index.html", "p1.md en | 2: p2.nn.md nn | 3: p3.nn.md nn | 4: p4.md en | 5: p5.fr.md fr | 6: p6.nn.md nn | 7: p7.md en | 8: p8.md en | 9: p9.nn.md nn | 10: p10.fr.md fr | 11: p11.md en | 12: p12.nn.md nn | 13: p13.md en | 14: p14.md en | 15: p15.nn.md nn") + b.AssertFileContent("public/nn/index.html", "Pages1: 1: p1.md en | 2: p2.nn.md nn | 3: p3.nn.md nn | 4: p4.md en | 5: p5.fr.md fr | 6: p6.nn.md nn | 7: p7.md en | 8: p8.md en | 9: p9.nn.md nn | 10: p10.fr.md fr | 11: p11.md en | 12: p12.nn.md nn | 13: p13.md en | 14: p14.md en | 15: p15.nn.md nn") + b.AssertFileContent("public/nn/index.html", "Pages2: 1: doc100 en | 2: doc101 nn | 3: doc102 nn | 4: doc103 en | 5: doc104 en | 6: doc105 en") } func newTestSiteForLanguageMerge(t testing.TB, count int) *sitesBuilder { @@ -126,6 +145,18 @@ date: "2018-02-28" } } + // See https://github.com/gohugoio/hugo/issues/4644 + // Add a bundles + j := 100 + contentPairs = append(contentPairs, []string{"bundle/index.md", fmt.Sprintf(contentTemplate, j, j)}...) + for i := 0; i < 6; i++ { + contentPairs = append(contentPairs, []string{fmt.Sprintf("bundle/pb%d.md", i), fmt.Sprintf(contentTemplate, i+j, i+j)}...) + } + contentPairs = append(contentPairs, []string{"bundle/index.nn.md", fmt.Sprintf(contentTemplate, j, j)}...) + for i := 1; i < 3; i++ { + contentPairs = append(contentPairs, []string{fmt.Sprintf("bundle/pb%d.nn.md", i), fmt.Sprintf(contentTemplate, i+j, i+j)}...) + } + builder.WithContent(contentPairs...) return builder } diff --git a/resource/resource.go b/resource/resource.go index 12e1160cf..e92706803 100644 --- a/resource/resource.go +++ b/resource/resource.go @@ -34,10 +34,11 @@ import ( ) var ( - _ Resource = (*genericResource)(nil) - _ metaAssigner = (*genericResource)(nil) - _ Source = (*genericResource)(nil) - _ Cloner = (*genericResource)(nil) + _ Resource = (*genericResource)(nil) + _ metaAssigner = (*genericResource)(nil) + _ Source = (*genericResource)(nil) + _ Cloner = (*genericResource)(nil) + _ ResourcesLanguageMerger = (*Resources)(nil) ) const DefaultResourceType = "unknown" @@ -97,6 +98,16 @@ type Resource interface { Content() (interface{}, error) } +type ResourcesLanguageMerger interface { + MergeByLanguage(other Resources) Resources + // Needed for integration with the tpl package. + MergeByLanguageInterface(other interface{}) (interface{}, error) +} + +type translatedResource interface { + TranslationKey() string +} + // Resources represents a slice of resources, which can be a mix of different types. // I.e. both pages and images etc. type Resources []Resource @@ -222,6 +233,36 @@ func getGlob(pattern string) (glob.Glob, error) { } +// MergeByLanguage adds missing translations in r1 from r2. +func (r1 Resources) MergeByLanguage(r2 Resources) Resources { + result := append(Resources(nil), r1...) + m := make(map[string]bool) + for _, r := range r1 { + if translated, ok := r.(translatedResource); ok { + m[translated.TranslationKey()] = true + } + } + + for _, r := range r2 { + if translated, ok := r.(translatedResource); ok { + if _, found := m[translated.TranslationKey()]; !found { + result = append(result, r) + } + } + } + return result +} + +// MergeByLanguageInterface is the generic version of MergeByLanguage. It +// is here just so it can be called from the tpl package. +func (r1 Resources) MergeByLanguageInterface(in interface{}) (interface{}, error) { + r2, ok := in.(Resources) + if !ok { + return nil, fmt.Errorf("%T cannot be merged by language", in) + } + return r1.MergeByLanguage(r2), nil +} + type Spec struct { *helpers.PathSpec