Repository: gohugoio/hugo (language: go)

Issue:
Inconsistent page data in custom output format
### Description

I've noticed that some page data is missing when attempting to access it in a custom output format. I've only observed this issue when attempting to access a translated page's properties. 

Given the following config:

```toml
baseURL = 'http://example.org/'
title = 'My New Hugo Site'

languageCode = "en"
defaultContentLanguage = "en"


[languages]
  [languages.en]
    title = "Repro"
    languageName = "English"
    contentDir = "content/en"
    weight = 1

  [languages.zh_CN]
    title = "Repro"
    weight = 2
    languageName = "简体中文"
    contentDir = "content/zh_CN"

[outputFormats]
  [outputFormats.metadata]
    baseName = "metadata"
    mediaType = "text/html"
    isPlainText = true
    notAlternative = true

[outputs]
    home = ["HTML", "metadata"]
```

... and the following templates:

_home.html_
```html
<h2>Translations</h2>
<ul>
{{ range .Site.Menus.main }}
  {{ $p := .Page }}
  {{ range $p.Translations}}
    <li>{{ .Title }}, {{ .Summary }}</li>
  {{ end }}
{{ end }}
</ul>
```

_metadata.home.html_
```html
<h2>Translations</h2>
<ul>
{{ range .Site.Menus.main }}
  {{ $p := .Page }}
  {{ range $p.Translations}}
    <li>{{ .Title }}, {{ .Summary }}</li>
  {{ end }}
{{ end }}
</ul>
```

will produce different results. In this case, only for the English generation of `metadata.home.html`, the `.Summary` property for the translated page is empty:

![image](https://user-images.githubusercontent.com/13353733/130441759-35d9dfe6-2fe1-4159-87e3-2f54a02cec52.png)

You can find a repro of this case in https://github.com/jamiebrynes7/hugo-repro-missing-data with the associated configuration and page data.

### What version of Hugo are you using (`hugo version`)?

<pre>
$ hugo version
hugo v0.87.0-B0C541E4+extended windows/amd64 BuildDate=2021-08-03T10:57:28Z VendorInfo=gohugoio
</pre>

### Does this issue reproduce with the latest release?

Yes



Discussion / hints:
I suspect that this behavior might be limited to the summary field, due to the unique way this field is handled. See my [recent comments](https://github.com/gohugoio/hugo/issues/8910#issuecomment-9031586000). 

Fix patch:
diff --git a/hugolib/page.go b/hugolib/page.go
index d2d96204408..286d210752c 100644
--- a/hugolib/page.go
+++ b/hugolib/page.go
@@ -971,7 +971,24 @@ func (p *pageState) shiftToOutputFormat(isRenderingSite bool, idx int) error {
 			}
 		}
 		p.pageOutput.initContentProvider(cp)
-		p.pageOutput.cp = cp
+	} else {
+		// We attempt to assign pageContentOutputs while preparing each site
+		// for rendering and before rendering each site. This lets us share
+		// content between page outputs to conserve resources. But if a template
+		// unexpectedly calls a method of a ContentProvider that is not yet
+		// initialized, we assign a LazyContentProvider that performs the
+		// initialization just in time.
+		if lcp, ok := (p.pageOutput.ContentProvider.(*page.LazyContentProvider)); ok {
+			lcp.Reset()
+		} else {
+			p.pageOutput.ContentProvider = page.NewLazyContentProvider(func() (page.ContentProvider, error) {
+				cp, err := newPageContentOutput(p, p.pageOutput)
+				if err != nil {
+					return nil, err
+				}
+				return cp, nil
+			})
+		}
 	}
 
 	return nil
diff --git a/resources/page/page_lazy_contentprovider.go b/resources/page/page_lazy_contentprovider.go
new file mode 100644
index 00000000000..a513a063a16
--- /dev/null
+++ b/resources/page/page_lazy_contentprovider.go
@@ -0,0 +1,99 @@
+// Copyright 2019 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
+
+import (
+	"html/template"
+
+	"github.com/gohugoio/hugo/lazy"
+)
+
+// LazyContentProvider initializes itself when read. Each method of the
+// ContentProvider interface initializes a content provider and shares it
+// with other methods.
+//
+// Used in cases where we cannot guarantee whether the content provider
+// will be needed. Must create via NewLazyContentProvider.
+type LazyContentProvider struct {
+	init *lazy.Init
+	cp   ContentProvider
+}
+
+// NewLazyContentProvider returns a LazyContentProvider initialized with
+// function f. The resulting LazyContentProvider calls f in order to
+// retrieve a ContentProvider
+func NewLazyContentProvider(f func() (ContentProvider, error)) *LazyContentProvider {
+	lcp := LazyContentProvider{
+		init: lazy.New(),
+		cp:   NopPage,
+	}
+	lcp.init.Add(func() (interface{}, error) {
+		cp, err := f()
+		if err != nil {
+			return nil, err
+		}
+		lcp.cp = cp
+		return nil, nil
+	})
+	return &lcp
+}
+
+func (lcp *LazyContentProvider) Reset() {
+	lcp.init.Reset()
+}
+
+func (lcp *LazyContentProvider) Content() (interface{}, error) {
+	lcp.init.Do()
+	return lcp.cp.Content()
+}
+
+func (lcp *LazyContentProvider) Plain() string {
+	lcp.init.Do()
+	return lcp.cp.Plain()
+}
+
+func (lcp *LazyContentProvider) PlainWords() []string {
+	lcp.init.Do()
+	return lcp.cp.PlainWords()
+}
+
+func (lcp *LazyContentProvider) Summary() template.HTML {
+	lcp.init.Do()
+	return lcp.cp.Summary()
+}
+
+func (lcp *LazyContentProvider) Truncated() bool {
+	lcp.init.Do()
+	return lcp.cp.Truncated()
+}
+
+func (lcp *LazyContentProvider) FuzzyWordCount() int {
+	lcp.init.Do()
+	return lcp.cp.FuzzyWordCount()
+}
+
+func (lcp *LazyContentProvider) WordCount() int {
+	lcp.init.Do()
+	return lcp.cp.WordCount()
+}
+
+func (lcp *LazyContentProvider) ReadingTime() int {
+	lcp.init.Do()
+	return lcp.cp.ReadingTime()
+}
+
+func (lcp *LazyContentProvider) Len() int {
+	lcp.init.Do()
+	return lcp.cp.Len()
+}


Test patch:
diff --git a/hugolib/page_test.go b/hugolib/page_test.go
index 7d55787c8e3..fc01bbf25eb 100644
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -36,6 +36,7 @@ import (
 	"github.com/gohugoio/hugo/resources/page"
 	"github.com/gohugoio/hugo/resources/resource"
 	"github.com/spf13/afero"
+	"github.com/spf13/jwalterweatherman"
 
 	qt "github.com/frankban/quicktest"
 	"github.com/gohugoio/hugo/deps"
@@ -767,6 +768,184 @@ Here is the last report for commits in the year 2016. It covers hrev50718-hrev50
 `)
 }
 
+// Issue 8919
+func TestContentProviderWithCustomOutputFormat(t *testing.T) {
+	b := newTestSitesBuilder(t)
+	b.WithLogger(loggers.NewBasicLoggerForWriter(jwalterweatherman.LevelDebug, os.Stderr))
+	b.WithConfigFile("toml", `baseURL = 'http://example.org/'
+title = 'My New Hugo Site'
+
+timeout = 600000 # ten minutes in case we want to pause and debug
+
+defaultContentLanguage = "en"
+
+[languages]
+	[languages.en]
+	title = "Repro"
+	languageName = "English"
+	contentDir = "content/en"
+
+	[languages.zh_CN]
+	title = "Repro"
+	languageName = "简体中文"
+	contentDir = "content/zh_CN"
+
+[outputFormats]
+	[outputFormats.metadata]
+	baseName = "metadata"
+	mediaType = "text/html"
+	isPlainText = true
+	notAlternative = true
+
+[outputs]
+	home = ["HTML", "metadata"]`)
+
+	b.WithTemplates("home.metadata.html", `<h2>Translations metadata</h2>
+<ul>
+{{ $p := .Page }}
+{{ range $p.Translations}}
+<li>Title: {{ .Title }}, {{ .Summary }}</li>
+<li>Content: {{ .Content }}</li>
+<li>Plain: {{ .Plain }}</li>
+<li>PlainWords: {{ .PlainWords }}</li>
+<li>Summary: {{ .Summary }}</li>
+<li>Truncated: {{ .Truncated }}</li>
+<li>FuzzyWordCount: {{ .FuzzyWordCount }}</li>
+<li>ReadingTime: {{ .ReadingTime }}</li>
+<li>Len: {{ .Len }}</li>
+{{ end }}
+</ul>`)
+
+	b.WithTemplates("_default/baseof.html", `<html>
+
+<body>
+	{{ block "main" . }}{{ end }}
+</body>
+
+</html>`)
+
+	b.WithTemplates("_default/home.html", `{{ define "main" }}
+<h2>Translations</h2>
+<ul>
+{{ $p := .Page }}
+{{ range $p.Translations}}
+<li>Title: {{ .Title }}, {{ .Summary }}</li>
+<li>Content: {{ .Content }}</li>
+<li>Plain: {{ .Plain }}</li>
+<li>PlainWords: {{ .PlainWords }}</li>
+<li>Summary: {{ .Summary }}</li>
+<li>Truncated: {{ .Truncated }}</li>
+<li>FuzzyWordCount: {{ .FuzzyWordCount }}</li>
+<li>ReadingTime: {{ .ReadingTime }}</li>
+<li>Len: {{ .Len }}</li>
+{{ end }}
+</ul>
+{{ end }}`)
+
+	b.WithContent("en/_index.md", `---
+title: Title (en)
+summary: Summary (en)
+---
+
+Here is some content.
+`)
+
+	b.WithContent("zh_CN/_index.md", `---
+title: Title (zh)
+summary: Summary (zh)
+---
+
+这是一些内容
+`)
+
+	b.Build(BuildCfg{})
+
+	b.AssertFileContent("public/index.html", `<html>
+	
+<body>
+	
+<h2>Translations</h2>
+<ul>
+
+	
+<li>Title: Title (zh), Summary (zh)</li>
+<li>Content: <p>这是一些内容</p>
+</li>
+<li>Plain: 这是一些内容
+</li>
+<li>PlainWords: [这是一些内容]</li>
+<li>Summary: Summary (zh)</li>
+<li>Truncated: false</li>
+<li>FuzzyWordCount: 100</li>
+<li>ReadingTime: 1</li>
+<li>Len: 26</li>	
+
+</ul>
+
+</body>
+
+</html>`)
+	b.AssertFileContent("public/metadata.html", `<h2>Translations metadata</h2>
+<ul>
+
+	
+<li>Title: Title (zh), Summary (zh)</li>
+<li>Content: <p>这是一些内容</p>
+</li>
+<li>Plain: 这是一些内容
+</li>
+<li>PlainWords: [这是一些内容]</li>
+<li>Summary: Summary (zh)</li>
+<li>Truncated: false</li>
+<li>FuzzyWordCount: 100</li>
+<li>ReadingTime: 1</li>
+<li>Len: 26</li>	
+
+</ul>`)
+	b.AssertFileContent("public/zh_cn/index.html", `<html>
+
+<body>
+	
+<h2>Translations</h2>
+<ul>
+
+
+<li>Title: Title (en), Summary (en)</li>
+<li>Content: <p>Here is some content.</p>
+</li>
+<li>Plain: Here is some content.
+</li>
+<li>PlainWords: [Here is some content.]</li>
+<li>Summary: Summary (en)</li>
+<li>Truncated: false</li>
+<li>FuzzyWordCount: 100</li>
+<li>ReadingTime: 1</li>
+<li>Len: 29</li>	
+
+</ul>
+
+</body>
+
+</html>`)
+	b.AssertFileContent("public/zh_cn/metadata.html", `<h2>Translations metadata</h2>
+<ul>
+
+	
+<li>Title: Title (en), Summary (en)</li>
+<li>Content: <p>Here is some content.</p>
+</li>
+<li>Plain: Here is some content.
+</li>
+<li>PlainWords: [Here is some content.]</li>
+<li>Summary: Summary (en)</li>
+<li>Truncated: false</li>
+<li>FuzzyWordCount: 100</li>
+<li>ReadingTime: 1</li>
+<li>Len: 29</li>	
+
+</ul>`)
+}
+
 func TestPageWithDate(t *testing.T) {
 	t.Parallel()
 	cfg, fs := newTestCfg()