2
0
Files
gitcaddy-server/modules/pages/config.go
logikonline 5788123e00 feat(pages): add multi-language support for landing pages
Implement internationalization system for landing pages:
- Database model for storing language-specific translations
- Language configuration with default and enabled languages
- Language switcher in navigation across all templates
- Translation management UI in settings
- Support for 15 languages including English, Spanish, German, French, Japanese, Chinese
- Auto-detection and manual language selection
- AI-powered translation generation capability
2026-03-07 13:09:46 -05:00

370 lines
11 KiB
Go

// Copyright 2026 MarketAlly. All rights reserved.
// SPDX-License-Identifier: MIT
package pages
import (
"crypto/sha256"
"encoding/hex"
"slices"
"gopkg.in/yaml.v3"
)
// LandingConfig represents the parsed .gitea/landing.yaml configuration
type LandingConfig struct {
Enabled bool `yaml:"enabled"`
PublicLanding bool `yaml:"public_landing"`
Template string `yaml:"template"` // open-source-hero, minimalist-docs, saas-conversion, bold-marketing
// Custom domain (optional)
Domain string `yaml:"domain,omitempty"`
// Brand configuration
Brand BrandConfig `yaml:"brand,omitempty"`
// Hero section
Hero HeroConfig `yaml:"hero,omitempty"`
// Stats/metrics
Stats []StatConfig `yaml:"stats,omitempty"`
// Value propositions
ValueProps []ValuePropConfig `yaml:"value_props,omitempty"`
// Features
Features []FeatureConfig `yaml:"features,omitempty"`
// Social proof
SocialProof SocialProofConfig `yaml:"social_proof,omitempty"`
// Pricing (for saas-conversion template)
Pricing PricingConfig `yaml:"pricing,omitempty"`
// CTA section
CTASection CTASectionConfig `yaml:"cta_section,omitempty"`
// Blog section
Blog BlogSectionConfig `yaml:"blog,omitempty"`
// Navigation visibility
Navigation NavigationConfig `yaml:"navigation,omitempty"`
// Footer
Footer FooterConfig `yaml:"footer,omitempty"`
// Theme customization
Theme ThemeConfig `yaml:"theme,omitempty"`
// SEO & Social
SEO SEOConfig `yaml:"seo,omitempty"`
// Analytics
Analytics AnalyticsConfig `yaml:"analytics,omitempty"`
// Advanced settings
Advanced AdvancedConfig `yaml:"advanced,omitempty"`
// A/B testing experiments
Experiments ExperimentConfig `yaml:"experiments,omitempty"`
// Multi-language support
I18n I18nConfig `yaml:"i18n,omitempty"`
}
// BrandConfig represents brand/identity settings
type BrandConfig struct {
Name string `yaml:"name,omitempty"`
LogoURL string `yaml:"logo_url,omitempty"`
LogoSource string `yaml:"logo_source,omitempty"` // "url" (default), "repo", or "org" — selects avatar source
Tagline string `yaml:"tagline,omitempty"`
FaviconURL string `yaml:"favicon_url,omitempty"`
}
// HeroConfig represents hero section settings
type HeroConfig struct {
Headline string `yaml:"headline,omitempty"`
Subheadline string `yaml:"subheadline,omitempty"`
PrimaryCTA CTAButton `yaml:"primary_cta,omitempty"`
SecondaryCTA CTAButton `yaml:"secondary_cta,omitempty"`
ImageURL string `yaml:"image_url,omitempty"`
CodeExample string `yaml:"code_example,omitempty"`
VideoURL string `yaml:"video_url,omitempty"`
}
// CTAButton represents a call-to-action button
type CTAButton struct {
Label string `yaml:"label,omitempty"`
URL string `yaml:"url,omitempty"`
Variant string `yaml:"variant,omitempty"` // primary, secondary, outline, text
}
// StatConfig represents a single stat/metric
type StatConfig struct {
Value string `yaml:"value,omitempty"`
Label string `yaml:"label,omitempty"`
}
// ValuePropConfig represents a value proposition
type ValuePropConfig struct {
Title string `yaml:"title,omitempty"`
Description string `yaml:"description,omitempty"`
Icon string `yaml:"icon,omitempty"`
}
// FeatureConfig represents a single feature item
type FeatureConfig struct {
Title string `yaml:"title,omitempty"`
Description string `yaml:"description,omitempty"`
Icon string `yaml:"icon,omitempty"`
ImageURL string `yaml:"image_url,omitempty"`
}
// SocialProofConfig represents social proof section
type SocialProofConfig struct {
Logos []string `yaml:"logos,omitempty"`
Testimonial TestimonialConfig `yaml:"testimonial,omitempty"`
Testimonials []TestimonialConfig `yaml:"testimonials,omitempty"`
}
// TestimonialConfig represents a testimonial
type TestimonialConfig struct {
Quote string `yaml:"quote,omitempty"`
Author string `yaml:"author,omitempty"`
Role string `yaml:"role,omitempty"`
Avatar string `yaml:"avatar,omitempty"`
}
// PricingConfig represents pricing section
type PricingConfig struct {
Headline string `yaml:"headline,omitempty"`
Subheadline string `yaml:"subheadline,omitempty"`
Plans []PricingPlanConfig `yaml:"plans,omitempty"`
}
// PricingPlanConfig represents a pricing plan
type PricingPlanConfig struct {
Name string `yaml:"name,omitempty"`
Price string `yaml:"price,omitempty"`
Period string `yaml:"period,omitempty"`
Features []string `yaml:"features,omitempty"`
CTA string `yaml:"cta,omitempty"`
Featured bool `yaml:"featured,omitempty"`
}
// CTASectionConfig represents the final CTA section
type CTASectionConfig struct {
Headline string `yaml:"headline,omitempty"`
Subheadline string `yaml:"subheadline,omitempty"`
Button CTAButton `yaml:"button,omitempty"`
}
// BlogSectionConfig represents blog section settings on the landing page
type BlogSectionConfig struct {
Enabled bool `yaml:"enabled,omitempty"`
Headline string `yaml:"headline,omitempty"`
Subheadline string `yaml:"subheadline,omitempty"`
MaxPosts int `yaml:"max_posts,omitempty"` // default 3
ShowExcerpt bool `yaml:"show_excerpt,omitempty"` // show subtitle as excerpt
CTAButton CTAButton `yaml:"cta_button,omitempty"` // "View All Posts" link
}
// NavigationConfig controls which built-in navigation links appear in the header and footer
type NavigationConfig struct {
ShowDocs bool `yaml:"show_docs,omitempty"`
ShowAPI bool `yaml:"show_api,omitempty"`
ShowRepository bool `yaml:"show_repository,omitempty"`
ShowReleases bool `yaml:"show_releases,omitempty"`
ShowIssues bool `yaml:"show_issues,omitempty"`
}
// FooterConfig represents footer settings
type FooterConfig struct {
Links []FooterLink `yaml:"links,omitempty"`
Social []SocialLink `yaml:"social,omitempty"`
Copyright string `yaml:"copyright,omitempty"`
ShowPoweredBy bool `yaml:"show_powered_by,omitempty"`
}
// FooterLink represents a single footer link
type FooterLink struct {
Label string `yaml:"label,omitempty"`
URL string `yaml:"url,omitempty"`
}
// SocialLink represents a social media link
type SocialLink struct {
Platform string `yaml:"platform,omitempty"` // twitter, github, discord, linkedin, youtube
URL string `yaml:"url,omitempty"`
}
// ThemeConfig represents theme customization
type ThemeConfig struct {
PrimaryColor string `yaml:"primary_color,omitempty"`
AccentColor string `yaml:"accent_color,omitempty"`
Mode string `yaml:"mode,omitempty"` // light, dark, auto
}
// SEOConfig represents SEO and social sharing settings
type SEOConfig struct {
Title string `yaml:"title,omitempty"`
Description string `yaml:"description,omitempty"`
Keywords []string `yaml:"keywords,omitempty"`
OGImage string `yaml:"og_image,omitempty"`
TwitterCard string `yaml:"twitter_card,omitempty"`
TwitterSite string `yaml:"twitter_site,omitempty"`
}
// AnalyticsConfig represents analytics settings
type AnalyticsConfig struct {
Plausible string `yaml:"plausible,omitempty"`
Umami UmamiConfig `yaml:"umami,omitempty"`
GoogleAnalytics string `yaml:"google_analytics,omitempty"`
}
// UmamiConfig represents Umami analytics settings
type UmamiConfig struct {
WebsiteID string `yaml:"website_id,omitempty"`
URL string `yaml:"url,omitempty"`
}
// ExperimentConfig represents A/B testing experiment settings
type ExperimentConfig struct {
Enabled bool `yaml:"enabled,omitempty"`
AutoOptimize bool `yaml:"auto_optimize,omitempty"`
MinImpressions int `yaml:"min_impressions,omitempty"`
ApprovalRequired bool `yaml:"approval_required,omitempty"`
}
// I18nConfig represents multi-language settings for the landing page
type I18nConfig struct {
DefaultLang string `yaml:"default_lang,omitempty" json:"default_lang,omitempty"`
Languages []string `yaml:"languages,omitempty" json:"languages,omitempty"`
}
// LanguageDisplayNames returns a map of language codes to display names
func LanguageDisplayNames() map[string]string {
return map[string]string{
"en": "English",
"es": "Espanol",
"de": "Deutsch",
"fr": "Francais",
"ja": "Japanese",
"zh": "Chinese",
"pt": "Portugues",
"ru": "Russian",
"ko": "Korean",
"it": "Italiano",
"hi": "Hindi",
"ar": "Arabic",
"nl": "Nederlands",
"pl": "Polski",
"tr": "Turkish",
}
}
// AdvancedConfig represents advanced settings
type AdvancedConfig struct {
CustomCSS string `yaml:"custom_css,omitempty"`
CustomHead string `yaml:"custom_head,omitempty"`
Redirects map[string]string `yaml:"redirects,omitempty"`
PublicReleases bool `yaml:"public_releases,omitempty"`
}
// ParseLandingConfig parses a landing.yaml file content
func ParseLandingConfig(content []byte) (*LandingConfig, error) {
config := &LandingConfig{
Enabled: true,
Template: "open-source-hero",
}
if err := yaml.Unmarshal(content, config); err != nil {
return nil, err
}
// Apply defaults
if config.Template == "" {
config.Template = "open-source-hero"
}
if config.Theme.Mode == "" {
config.Theme.Mode = "auto"
}
return config, nil
}
// HashConfig returns a SHA256 hash of the config content for change detection
func HashConfig(content []byte) string {
hash := sha256.Sum256(content)
return hex.EncodeToString(hash[:])
}
// DefaultConfig returns a default landing page configuration
func DefaultConfig() *LandingConfig {
return &LandingConfig{
Enabled: true,
Template: "open-source-hero",
Hero: HeroConfig{
Headline: "Build something amazing",
Subheadline: "A powerful toolkit for developers who want to ship fast.",
PrimaryCTA: CTAButton{
Label: "Get Started",
URL: "#",
},
SecondaryCTA: CTAButton{
Label: "View on GitHub",
URL: "#",
},
},
Stats: []StatConfig{
{Value: "10k+", Label: "Downloads"},
{Value: "100+", Label: "Contributors"},
{Value: "MIT", Label: "License"},
},
ValueProps: []ValuePropConfig{
{Title: "Fast", Description: "Optimized for performance out of the box.", Icon: "zap"},
{Title: "Flexible", Description: "Adapts to your workflow, not the other way around.", Icon: "gear"},
{Title: "Open Source", Description: "Free forever. Community driven.", Icon: "heart"},
},
Navigation: NavigationConfig{
ShowDocs: true,
ShowRepository: true,
ShowReleases: true,
},
CTASection: CTASectionConfig{
Headline: "Ready to get started?",
Subheadline: "Join thousands of developers already using this project.",
Button: CTAButton{
Label: "Get Started Free",
URL: "#",
},
},
Footer: FooterConfig{
ShowPoweredBy: true,
},
Theme: ThemeConfig{
Mode: "auto",
},
}
}
// ValidTemplates returns the list of valid template names
func ValidTemplates() []string {
return []string{"open-source-hero", "minimalist-docs", "saas-conversion", "bold-marketing"}
}
// IsValidTemplate checks if a template name is valid
func IsValidTemplate(name string) bool {
return slices.Contains(ValidTemplates(), name)
}
// TemplateDisplayNames returns a map of template names to display names
func TemplateDisplayNames() map[string]string {
return map[string]string{
"open-source-hero": "Open Source Hero",
"minimalist-docs": "Minimalist Docs",
"saas-conversion": "SaaS Conversion",
"bold-marketing": "Bold Marketing",
}
}