Repository: gohugoio/hugo (language: go)

Issue:
hugolib: Deprecate .Site.MultiLingual in favor of hugo.IsMultiLingual
This would be symmetrical with:

```text
hugo.IsMultiHost
hugo.IsServer
hugo.IsDevelopment
```

We deprecated `Site.IsServer` and `Site.IsDevelopment` in v0.120.0.


Discussion / hints:

Fix patch:
diff --git a/common/hugo/hugo.go b/common/hugo/hugo.go
index 4ead647b8f5..a3d1a7bfcac 100644
--- a/common/hugo/hugo.go
+++ b/common/hugo/hugo.go
@@ -116,12 +116,18 @@ func (i HugoInfo) IsMultiHost() bool {
 	return i.conf.IsMultihost()
 }
 
+// IsMultiLingual reports whether there are two or more configured languages.
+func (i HugoInfo) IsMultiLingual() bool {
+	return i.conf.IsMultiLingual()
+}
+
 // ConfigProvider represents the config options that are relevant for HugoInfo.
 type ConfigProvider interface {
 	Environment() string
 	Running() bool
 	WorkingDir() string
 	IsMultihost() bool
+	IsMultiLingual() bool
 }
 
 // NewInfo creates a new Hugo Info object.
diff --git a/hugolib/site_new.go b/hugolib/site_new.go
index 3bcc307ad4b..5a58119585b 100644
--- a/hugolib/site_new.go
+++ b/hugolib/site_new.go
@@ -361,8 +361,7 @@ func newHugoSites(cfg deps.DepsCfg, d *deps.Deps, pageTrees *pageTrees, sites []
 	return h, nil
 }
 
-// Returns true if we're running in a server.
-// Deprecated: use hugo.IsServer instead
+// Deprecated: Use hugo.IsServer instead.
 func (s *Site) IsServer() bool {
 	hugo.Deprecate(".Site.IsServer", "Use hugo.IsServer instead.", "v0.120.0")
 	return s.conf.Internal.Running
@@ -382,8 +381,9 @@ func (s *Site) Copyright() string {
 	return s.conf.Copyright
 }
 
+// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
 func (s *Site) RSSLink() template.URL {
-	hugo.Deprecate("Site.RSSLink", "Use the Output Format's Permalink method instead, e.g. .OutputFormats.Get \"RSS\".Permalink", "v0.114.0")
+	hugo.Deprecate(".Site.RSSLink", "Use the Output Format's Permalink method instead, e.g. .OutputFormats.Get \"RSS\".Permalink", "v0.114.0")
 	rssOutputFormat := s.home.OutputFormats().Get("rss")
 	return template.URL(rssOutputFormat.Permalink())
 }
@@ -431,9 +431,9 @@ func (s *Site) BaseURL() string {
 	return s.conf.C.BaseURL.WithPath
 }
 
-// Returns the last modification date of the content.
-// Deprecated: Use .Lastmod instead.
+// Deprecated: Use .Site.Lastmod instead.
 func (s *Site) LastChange() time.Time {
+	hugo.Deprecate(".Site.LastChange", "Use .Site.Lastmod instead.", "v0.123.0")
 	return s.lastmod
 }
 
@@ -459,13 +459,13 @@ func (s *Site) Social() map[string]string {
 	return s.conf.Social
 }
 
-// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead
+// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead.
 func (s *Site) DisqusShortname() string {
 	hugo.Deprecate(".Site.DisqusShortname", "Use .Site.Config.Services.Disqus.Shortname instead.", "v0.120.0")
 	return s.Config().Services.Disqus.Shortname
 }
 
-// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead
+// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead.
 func (s *Site) GoogleAnalytics() string {
 	hugo.Deprecate(".Site.GoogleAnalytics", "Use .Site.Config.Services.GoogleAnalytics.ID instead.", "v0.120.0")
 	return s.Config().Services.GoogleAnalytics.ID
@@ -484,7 +484,9 @@ func (s *Site) BuildDrafts() bool {
 	return s.conf.BuildDrafts
 }
 
+// Deprecated: Use hugo.IsMultiLingual instead.
 func (s *Site) IsMultiLingual() bool {
+	hugo.Deprecate(".Site.IsMultiLingual", "Use hugo.IsMultiLingual instead.", "v0.124.0")
 	return s.h.isMultiLingual()
 }
 
diff --git a/resources/page/site.go b/resources/page/site.go
index 132ab9fe856..d9f3d967b83 100644
--- a/resources/page/site.go
+++ b/resources/page/site.go
@@ -54,8 +54,7 @@ type Site interface {
 	// A shortcut to the home
 	Home() Page
 
-	// Returns true if we're running in a server.
-	// Deprecated: use hugo.IsServer instead
+	// Deprecated: Use hugo.IsServer instead.
 	IsServer() bool
 
 	// Returns the server port.
@@ -64,7 +63,6 @@ type Site interface {
 	// Returns the configured title for this Site.
 	Title() string
 
-	// Returns the configured language code for this Site.
 	// Deprecated: Use .Language.LanguageCode instead.
 	LanguageCode() string
 
@@ -86,7 +84,6 @@ type Site interface {
 	// Returns a taxonomy map.
 	Taxonomies() TaxonomyList
 
-	// Returns the last modification date of the content.
 	// Deprecated: Use .Lastmod instead.
 	LastChange() time.Time
 
@@ -129,13 +126,13 @@ type Site interface {
 	// BuildDrafts is deprecated and will be removed in a future release.
 	BuildDrafts() bool
 
-	// IsMultiLingual reports whether this site is configured with more than one language.
+	// Deprecated: Use hugo.IsMultiLingual instead.
 	IsMultiLingual() bool
 
 	// LanguagePrefix returns the language prefix for this site.
 	LanguagePrefix() string
 
-	// Deprecated. Use site.Home.OutputFormats.Get "rss" instead.
+	// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
 	RSSLink() template.URL
 }
 
@@ -180,7 +177,7 @@ func (s *siteWrapper) Authors() AuthorList {
 	return AuthorList{}
 }
 
-// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead
+// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead.
 func (s *siteWrapper) GoogleAnalytics() string {
 	return s.s.GoogleAnalytics()
 }
@@ -217,7 +214,7 @@ func (s *siteWrapper) Home() Page {
 	return s.s.Home()
 }
 
-// Deprecated: use hugo.IsServer instead
+// Deprecated: Use hugo.IsServer instead.
 func (s *siteWrapper) IsServer() bool {
 	return s.s.IsServer()
 }
@@ -262,9 +259,9 @@ func (s *siteWrapper) Taxonomies() TaxonomyList {
 	return s.s.Taxonomies()
 }
 
+// Deprecated: Use .Site.Lastmod instead.
 func (s *siteWrapper) LastChange() time.Time {
-	hugo.Deprecate(".Site.LastChange", "Use .Site.Lastmod instead.", "v0.123.0")
-	return s.s.Lastmod()
+	return s.s.LastChange()
 }
 
 func (s *siteWrapper) Lastmod() time.Time {
@@ -295,11 +292,12 @@ func (s *siteWrapper) BuildDrafts() bool {
 	return s.s.BuildDrafts()
 }
 
+// Deprecated: Use hugo.IsMultiLingual instead.
 func (s *siteWrapper) IsMultiLingual() bool {
 	return s.s.IsMultiLingual()
 }
 
-// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead
+// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead.
 func (s *siteWrapper) DisqusShortname() string {
 	return s.s.DisqusShortname()
 }
@@ -308,6 +306,7 @@ func (s *siteWrapper) LanguagePrefix() string {
 	return s.s.LanguagePrefix()
 }
 
+// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
 func (s *siteWrapper) RSSLink() template.URL {
 	return s.s.RSSLink()
 }
@@ -342,6 +341,7 @@ func (t testSite) ServerPort() int {
 	return 1313
 }
 
+// Deprecated: Use .Site.Lastmod instead.
 func (testSite) LastChange() (t time.Time) {
 	return
 }
@@ -386,7 +386,7 @@ func (t testSite) Languages() langs.Languages {
 	return nil
 }
 
-// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead
+// Deprecated: Use .Site.Config.Services.GoogleAnalytics.ID instead.
 func (t testSite) GoogleAnalytics() string {
 	return ""
 }
@@ -395,7 +395,7 @@ func (t testSite) MainSections() []string {
 	return nil
 }
 
-// Deprecated: use hugo.IsServer instead
+// Deprecated: Use hugo.IsServer instead.
 func (t testSite) IsServer() bool {
 	return false
 }
@@ -444,7 +444,7 @@ func (s testSite) Config() SiteConfig {
 	return SiteConfig{}
 }
 
-// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead
+// Deprecated: Use .Site.Config.Services.Disqus.Shortname instead.
 func (testSite) DisqusShortname() string {
 	return ""
 }
@@ -453,6 +453,7 @@ func (s testSite) BuildDrafts() bool {
 	return false
 }
 
+// Deprecated: Use hugo.IsMultiLingual instead.
 func (s testSite) IsMultiLingual() bool {
 	return false
 }
@@ -461,6 +462,7 @@ func (s testSite) Param(key any) (any, error) {
 	return nil, nil
 }
 
+// Deprecated: Use .Site.Home.OutputFormats.Get "rss" instead.
 func (s testSite) RSSLink() template.URL {
 	return ""
 }
diff --git a/source/fileInfo.go b/source/fileInfo.go
index 1d222699435..44d08e62080 100644
--- a/source/fileInfo.go
+++ b/source/fileInfo.go
@@ -61,7 +61,7 @@ func (fi *File) Extension() string {
 func (fi *File) Ext() string { return fi.p().Ext() }
 
 // Lang returns a file's language (e.g. "sv").
-// Deprecated: use .Page.Language.Lang instead.
+// Deprecated: Use .Page.Language.Lang instead.
 func (fi *File) Lang() string {
 	hugo.Deprecate(".Page.File.Lang", "Use .Page.Language.Lang instead.", "v0.123.0")
 	return fi.fim.Meta().Lang


Test patch:
diff --git a/common/hugo/hugo_integration_test.go b/common/hugo/hugo_integration_test.go
new file mode 100644
index 00000000000..9fcfb95cf6d
--- /dev/null
+++ b/common/hugo/hugo_integration_test.go
@@ -0,0 +1,77 @@
+// Copyright 2024 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 hugo_test
+
+import (
+	"strings"
+	"testing"
+
+	"github.com/gohugoio/hugo/hugolib"
+)
+
+func TestIsMultiLingualAndIsMultiHost(t *testing.T) {
+	t.Parallel()
+
+	files := `
+-- hugo.toml --
+disableKinds = ['page','rss','section','sitemap','taxonomy','term']
+defaultContentLanguageInSubdir = true
+[languages.de]
+baseURL = 'https://de.example.org/'
+[languages.en]
+baseURL = 'https://en.example.org/'
+-- content/_index.md --
+---
+title: home
+---
+-- layouts/index.html --
+multilingual={{ hugo.IsMultiLingual }}
+multihost={{ hugo.IsMultiHost }}
+  `
+
+	b := hugolib.Test(t, files)
+
+	b.AssertFileContent("public/de/index.html",
+		"multilingual=true",
+		"multihost=true",
+	)
+	b.AssertFileContent("public/en/index.html",
+		"multilingual=true",
+		"multihost=true",
+	)
+
+	files = strings.ReplaceAll(files, "baseURL = 'https://de.example.org/'", "")
+	files = strings.ReplaceAll(files, "baseURL = 'https://en.example.org/'", "")
+
+	b = hugolib.Test(t, files)
+
+	b.AssertFileContent("public/de/index.html",
+		"multilingual=true",
+		"multihost=false",
+	)
+	b.AssertFileContent("public/en/index.html",
+		"multilingual=true",
+		"multihost=false",
+	)
+
+	files = strings.ReplaceAll(files, "[languages.de]", "")
+	files = strings.ReplaceAll(files, "[languages.en]", "")
+
+	b = hugolib.Test(t, files)
+
+	b.AssertFileContent("public/en/index.html",
+		"multilingual=false",
+		"multihost=false",
+	)
+}
diff --git a/common/hugo/hugo_test.go b/common/hugo/hugo_test.go
index e76d80000ac..e1d9071586a 100644
--- a/common/hugo/hugo_test.go
+++ b/common/hugo/hugo_test.go
@@ -65,10 +65,11 @@ func TestDeprecationLogLevelFromVersion(t *testing.T) {
 }
 
 type testConfig struct {
-	environment string
-	running     bool
-	workingDir  string
-	multihost   bool
+	environment  string
+	running      bool
+	workingDir   string
+	multihost    bool
+	multilingual bool
 }
 
 func (c testConfig) Environment() string {
@@ -86,3 +87,7 @@ func (c testConfig) WorkingDir() string {
 func (c testConfig) IsMultihost() bool {
 	return c.multihost
 }
+
+func (c testConfig) IsMultiLingual() bool {
+	return c.multilingual
+}
diff --git a/hugolib/page_test.go b/hugolib/page_test.go
index 9a904ee46fe..b6236ca5fbe 100644
--- a/hugolib/page_test.go
+++ b/hugolib/page_test.go
@@ -578,7 +578,7 @@ date: 2012-01-12
 	b.Assert(s.getPageOldVersion("/with-index-no-date").Date().IsZero(), qt.Equals, true)
 	checkDate(s.getPageOldVersion("/with-index-date"), 2018)
 
-	b.Assert(s.Site().LastChange().Year(), qt.Equals, 2018)
+	b.Assert(s.Site().Lastmod().Year(), qt.Equals, 2018)
 }
 
 func TestCreateNewPage(t *testing.T) {
@@ -773,7 +773,7 @@ func TestSummaryManualSplit(t *testing.T) {
 title: Simple
 ---
 This is **summary**.
-<!--more--> 
+<!--more-->
 This is **content**.
 -- layouts/_default/single.html --
 Summary: {{ .Summary }}|Truncated: {{ .Truncated }}|
diff --git a/resources/page/page_matcher_test.go b/resources/page/page_matcher_test.go
index 21f6891d76c..3ab68e8af39 100644
--- a/resources/page/page_matcher_test.go
+++ b/resources/page/page_matcher_test.go
@@ -157,10 +157,11 @@ func TestDecodeCascadeConfig(t *testing.T) {
 }
 
 type testConfig struct {
-	environment string
-	running     bool
-	workingDir  string
-	multihost   bool
+	environment  string
+	running      bool
+	workingDir   string
+	multihost    bool
+	multilingual bool
 }
 
 func (c testConfig) Environment() string {
@@ -179,6 +180,10 @@ func (c testConfig) IsMultihost() bool {
 	return c.multihost
 }
 
+func (c testConfig) IsMultiLingual() bool {
+	return c.multilingual
+}
+
 func TestIsGlobWithExtension(t *testing.T) {
 	c := qt.New(t)
 
diff --git a/testscripts/commands/hugo_is_multihost.txt b/testscripts/commands/hugo_is_multihost.txt
deleted file mode 100644
index a1147cd738c..00000000000
--- a/testscripts/commands/hugo_is_multihost.txt
+++ /dev/null
@@ -1,13 +0,0 @@
-hugo
-
-stdout 'IsMultiHost: true'
-
--- hugo.toml --
-title = "Hugo IsMultiHost Test"
-[languages.en]
-baseURL = "https://example.org"
-[languages.zh]
-baseURL = "https://zh.example.org"
-
--- layouts/index.html --
-{{ warnf "IsMultiHost: %v" hugo.IsMultiHost }}