- classes/schema builds one @graph per page (WebPage, article, profile, service, BreadcrumbList, VideoObject, WebSite, publisher) linked by @id - breadcrumb trail through the primary parent chain, hreflang alternates with x-default, html_lang and og:locale in the resolve answer - migration 202610280004 adds the publisher, local business, home crumb and pagination settings columns
120 lines
3.5 KiB
Go
120 lines
3.5 KiB
Go
package classes
|
|
|
|
import (
|
|
"strings"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
)
|
|
|
|
// OGLocalesConfigKey maps locale codes to og:locale values (D-55); unset
|
|
// codes fall back to DefaultOGLocales, else <code>_<CODE>.
|
|
const OGLocalesConfigKey = "golem15.seo.og_locales"
|
|
|
|
// SearchURLConfigKey is the site search address with the
|
|
// {search_term_string} placeholder (P33-25); empty means no SearchAction.
|
|
const SearchURLConfigKey = "golem15.seo.search_url"
|
|
|
|
// DefaultOGLocales are the og:locale values of the common locales.
|
|
var DefaultOGLocales = map[string]string{"pl": "pl_PL", "en": "en_US", "de": "de_DE"}
|
|
|
|
// Alternate is one hreflang alternate of a page (D-55): a language tag or
|
|
// x-default and the absolute URL.
|
|
type Alternate struct {
|
|
Hreflang string `json:"hreflang"`
|
|
URL string `json:"url"`
|
|
}
|
|
|
|
// LangTag turns a locale code into its language tag: pl stays pl, pt_BR
|
|
// and pt-br become pt-BR (the region is kept when the locale has one).
|
|
func LangTag(locale string) string {
|
|
locale = strings.TrimSpace(strings.ReplaceAll(locale, "_", "-"))
|
|
lang, region, ok := strings.Cut(locale, "-")
|
|
if !ok || region == "" {
|
|
return strings.ToLower(lang)
|
|
}
|
|
return strings.ToLower(lang) + "-" + strings.ToUpper(region)
|
|
}
|
|
|
|
// OGLocale is the og:locale value of locale: the configured value, else
|
|
// the default table, else the language with its region (pt_BR) or the
|
|
// language doubled (fr_FR).
|
|
func (s *Service) OGLocale(locale string) string {
|
|
tag := LangTag(locale)
|
|
if tag == "" {
|
|
return ""
|
|
}
|
|
if cfg := s.Config(); cfg != nil {
|
|
for _, key := range []string{locale, tag, strings.ToLower(tag)} {
|
|
if v := strings.TrimSpace(cfg.String(OGLocalesConfigKey + "." + key)); v != "" {
|
|
return v
|
|
}
|
|
}
|
|
}
|
|
if v, ok := DefaultOGLocales[tag]; ok {
|
|
return v
|
|
}
|
|
lang, region, ok := strings.Cut(tag, "-")
|
|
if ok {
|
|
return lang + "_" + region
|
|
}
|
|
return lang + "_" + strings.ToUpper(lang)
|
|
}
|
|
|
|
// translatedLocales lists the locales of rec with a public address, in the
|
|
// record's order.
|
|
func translatedLocales(rec *pact.ContentRecord) []pact.ContentLocale {
|
|
if rec == nil {
|
|
return nil
|
|
}
|
|
var out []pact.ContentLocale
|
|
seen := map[string]bool{}
|
|
for _, l := range rec.Locales {
|
|
if strings.TrimSpace(l.URL) == "" || l.Locale == "" || seen[l.Locale] {
|
|
continue
|
|
}
|
|
seen[l.Locale] = true
|
|
out = append(out, l)
|
|
}
|
|
return out
|
|
}
|
|
|
|
// Alternates lists the hreflang alternates of a record (D-55 to D-57):
|
|
// each translated locale with its absolute URL, the page itself included,
|
|
// then x-default on the default locale's URL. A record translated into one
|
|
// locale only has none; a locale without a translation is absent.
|
|
func Alternates(site string, rec *pact.ContentRecord, defaultLocale string) []Alternate {
|
|
locales := translatedLocales(rec)
|
|
if len(locales) < 2 {
|
|
return []Alternate{}
|
|
}
|
|
out := make([]Alternate, 0, len(locales)+1)
|
|
xDefault := ""
|
|
for _, l := range locales {
|
|
u := AbsoluteURL(site, l.URL)
|
|
out = append(out, Alternate{Hreflang: LangTag(l.Locale), URL: u})
|
|
if l.Locale == defaultLocale {
|
|
xDefault = u
|
|
}
|
|
}
|
|
if xDefault != "" {
|
|
out = append(out, Alternate{Hreflang: "x-default", URL: xDefault})
|
|
}
|
|
return out
|
|
}
|
|
|
|
// OGLocaleAlternates are the og:locale:alternate values of a record: one
|
|
// per other translated locale.
|
|
func (s *Service) OGLocaleAlternates(rec *pact.ContentRecord, locale string) []string {
|
|
out := []string{}
|
|
locales := translatedLocales(rec)
|
|
if len(locales) < 2 {
|
|
return out
|
|
}
|
|
for _, l := range locales {
|
|
if l.Locale != locale {
|
|
out = append(out, s.OGLocale(l.Locale))
|
|
}
|
|
}
|
|
return out
|
|
}
|