From 965ef8966ff54560904b7d993be2a4b420f20628 Mon Sep 17 00:00:00 2001 From: logikonline Date: Tue, 17 Mar 2026 23:34:29 -0400 Subject: [PATCH] feat(pages): add navigation label translation support Add translation support for navigation section labels (Value Props, Features, Pricing, Blog, Gallery, Compare, etc.). Adds TemplateDefaultLabels function that returns template-specific creative names (e.g., "Systems Analysis" for value props in Architecture Deep Dive). Auto-applies defaults when enabling pages or changing templates. Includes UI fields in languages settings and translation JSON serialization. Enables full localization of section headings. --- modules/pages/config.go | 62 ++++++++++++++++++ options/locale/locale_en-US.json | 2 + routers/web/repo/setting/pages.go | 67 ++++++++++++++++++++ services/pages/generate.go | 15 +++-- templates/repo/settings/pages_languages.tmpl | 49 +++++++++++++- 5 files changed, 186 insertions(+), 9 deletions(-) diff --git a/modules/pages/config.go b/modules/pages/config.go index 0cc47cf2f4..58896fd4da 100644 --- a/modules/pages/config.go +++ b/modules/pages/config.go @@ -469,3 +469,65 @@ func TemplateDisplayNames() map[string]string { "architecture-deep-dive": "Architecture Deep Dive", } } + +// TemplateDefaultLabels returns the template-specific default section labels. +// These are the creative names each template uses for its sections. +func TemplateDefaultLabels(template string) NavigationConfig { + switch template { + case "architecture-deep-dive": + return NavigationConfig{ + LabelValueProps: "Systems Analysis", + LabelFeatures: "Technical Specifications", + LabelPricing: "Resource Allocation", + LabelBlog: "Dispatches", + LabelGallery: "Visual Index", + LabelCompare: "Compare", + } + case "bold-marketing": + return NavigationConfig{ + LabelValueProps: "Why choose this", + LabelFeatures: "Capabilities", + LabelPricing: "Investment", + LabelBlog: "Blog", + LabelGallery: "Gallery", + LabelCompare: "Compare", + } + case "minimalist-docs": + return NavigationConfig{ + LabelValueProps: "Why choose this", + LabelFeatures: "Capabilities", + LabelPricing: "Investment", + LabelBlog: "Blog", + LabelGallery: "Gallery", + LabelCompare: "Compare", + } + case "open-source-hero": + return NavigationConfig{ + LabelValueProps: "Why choose us", + LabelFeatures: "Capabilities", + LabelPricing: "Pricing", + LabelBlog: "Blog", + LabelGallery: "Gallery", + LabelCompare: "Compare", + } + case "saas-conversion": + return NavigationConfig{ + LabelValueProps: "Why", + LabelFeatures: "Features", + LabelPricing: "Pricing", + LabelBlog: "Blog", + LabelGallery: "Gallery", + LabelCompare: "Compare", + } + default: + // developer-tool, documentation-first, visual-showcase, cli-terminal + return NavigationConfig{ + LabelValueProps: "Why choose us", + LabelFeatures: "Capabilities", + LabelPricing: "Pricing", + LabelBlog: "Blog", + LabelGallery: "Gallery", + LabelCompare: "Compare", + } + } +} diff --git a/options/locale/locale_en-US.json b/options/locale/locale_en-US.json index bd5b092033..a985868bbb 100644 --- a/options/locale/locale_en-US.json +++ b/options/locale/locale_en-US.json @@ -4614,6 +4614,8 @@ "repo.settings.pages.trans_section_comparison": "Comparison", "repo.settings.pages.trans_section_footer": "Footer", "repo.settings.pages.trans_section_seo": "SEO", + "repo.settings.pages.trans_section_navigation": "Navigation Labels", + "repo.settings.pages.trans_nav_label": "Label", "repo.settings.pages.trans_brand_name": "Brand Name", "repo.settings.pages.trans_brand_tagline": "Tagline", "repo.settings.pages.trans_stat_value": "Value", diff --git a/routers/web/repo/setting/pages.go b/routers/web/repo/setting/pages.go index e91ea5d187..9df7c1901e 100644 --- a/routers/web/repo/setting/pages.go +++ b/routers/web/repo/setting/pages.go @@ -87,6 +87,31 @@ func savePagesLandingConfig(ctx *context.Context, config *pages_module.LandingCo return repo_model.UpdatePagesConfig(ctx, dbConfig) } +// applyTemplateDefaultLabels populates Navigation label fields with +// template-specific defaults. Existing non-empty labels are preserved. +func applyTemplateDefaultLabels(config *pages_module.LandingConfig) { + defaults := pages_module.TemplateDefaultLabels(config.Template) + nav := &config.Navigation + if nav.LabelValueProps == "" { + nav.LabelValueProps = defaults.LabelValueProps + } + if nav.LabelFeatures == "" { + nav.LabelFeatures = defaults.LabelFeatures + } + if nav.LabelPricing == "" { + nav.LabelPricing = defaults.LabelPricing + } + if nav.LabelBlog == "" { + nav.LabelBlog = defaults.LabelBlog + } + if nav.LabelGallery == "" { + nav.LabelGallery = defaults.LabelGallery + } + if nav.LabelCompare == "" { + nav.LabelCompare = defaults.LabelCompare + } +} + // setCommonPagesData sets common data for all pages settings pages func setCommonPagesData(ctx *context.Context) { config := getPagesLandingConfig(ctx) @@ -141,6 +166,7 @@ func PagesPost(ctx *context.Context) { config := getPagesLandingConfig(ctx) config.Enabled = true config.Template = template + applyTemplateDefaultLabels(config) if err := savePagesLandingConfig(ctx, config); err != nil { ctx.ServerError("EnablePages", err) return @@ -161,6 +187,7 @@ func PagesPost(ctx *context.Context) { } config := getPagesLandingConfig(ctx) config.Template = template + applyTemplateDefaultLabels(config) if err := savePagesLandingConfig(ctx, config); err != nil { ctx.ServerError("UpdateTemplate", err) return @@ -766,6 +793,17 @@ type TranslationView struct { // SEO SEOTitle string SEODescription string + // Navigation labels + NavLabelValueProps string + NavLabelFeatures string + NavLabelPricing string + NavLabelBlog string + NavLabelGallery string + NavLabelCompare string + NavLabelDocs string + NavLabelReleases string + NavLabelAPI string + NavLabelIssues string } // overlayString extracts a string from a map @@ -938,6 +976,20 @@ func parseTranslationView(t *pages_model.Translation, config *pages_module.Landi view.SEODescription = overlayString(seo, "description") } + // Navigation labels + if nav, ok := overlay["navigation"].(map[string]any); ok { + view.NavLabelValueProps = overlayString(nav, "label_value_props") + view.NavLabelFeatures = overlayString(nav, "label_features") + view.NavLabelPricing = overlayString(nav, "label_pricing") + view.NavLabelBlog = overlayString(nav, "label_blog") + view.NavLabelGallery = overlayString(nav, "label_gallery") + view.NavLabelCompare = overlayString(nav, "label_compare") + view.NavLabelDocs = overlayString(nav, "label_docs") + view.NavLabelReleases = overlayString(nav, "label_releases") + view.NavLabelAPI = overlayString(nav, "label_api") + view.NavLabelIssues = overlayString(nav, "label_issues") + } + return view } @@ -1159,6 +1211,21 @@ func buildTranslationJSON(ctx *context.Context) string { overlay["seo"] = seo } + // Navigation labels + nav := map[string]any{} + for _, key := range []string{ + "label_value_props", "label_features", "label_pricing", + "label_blog", "label_gallery", "label_compare", + "label_docs", "label_releases", "label_api", "label_issues", + } { + if v := ctx.FormString("trans_nav_" + key); v != "" { + nav[key] = v + } + } + if len(nav) > 0 { + overlay["navigation"] = nav + } + if len(overlay) == 0 { return "" } diff --git a/services/pages/generate.go b/services/pages/generate.go index c45c2b6ce4..38047fd4f5 100644 --- a/services/pages/generate.go +++ b/services/pages/generate.go @@ -351,6 +351,9 @@ func buildTranslatableContent(config *pages_module.LandingConfig) string { } // Navigation labels (for translating nav items and section headers) + // Use template-specific defaults so AI translates the correct terms + // (e.g. "Systems Analysis" for architecture-deep-dive, not generic "Value Props") + defaults := pages_module.TemplateDefaultLabels(config.Template) labelOrDefault := func(label, def string) string { if label != "" { return label @@ -358,12 +361,12 @@ func buildTranslatableContent(config *pages_module.LandingConfig) string { return def } content["navigation"] = map[string]any{ - "label_value_props": labelOrDefault(config.Navigation.LabelValueProps, "Value Props"), - "label_features": labelOrDefault(config.Navigation.LabelFeatures, "Features"), - "label_pricing": labelOrDefault(config.Navigation.LabelPricing, "Pricing"), - "label_blog": labelOrDefault(config.Navigation.LabelBlog, "Blog"), - "label_gallery": labelOrDefault(config.Navigation.LabelGallery, "Gallery"), - "label_compare": labelOrDefault(config.Navigation.LabelCompare, "Compare"), + "label_value_props": labelOrDefault(config.Navigation.LabelValueProps, defaults.LabelValueProps), + "label_features": labelOrDefault(config.Navigation.LabelFeatures, defaults.LabelFeatures), + "label_pricing": labelOrDefault(config.Navigation.LabelPricing, defaults.LabelPricing), + "label_blog": labelOrDefault(config.Navigation.LabelBlog, defaults.LabelBlog), + "label_gallery": labelOrDefault(config.Navigation.LabelGallery, defaults.LabelGallery), + "label_compare": labelOrDefault(config.Navigation.LabelCompare, defaults.LabelCompare), "label_docs": labelOrDefault(config.Navigation.LabelDocs, "Docs"), "label_releases": labelOrDefault(config.Navigation.LabelReleases, "Releases"), "label_api": labelOrDefault(config.Navigation.LabelAPI, "API"), diff --git a/templates/repo/settings/pages_languages.tmpl b/templates/repo/settings/pages_languages.tmpl index 17250eb50c..9233fe5e8b 100644 --- a/templates/repo/settings/pages_languages.tmpl +++ b/templates/repo/settings/pages_languages.tmpl @@ -238,7 +238,7 @@
{{ctx.Locale.Tr "repo.settings.pages.trans_section_blog"}}
- +
@@ -257,7 +257,7 @@
{{ctx.Locale.Tr "repo.settings.pages.trans_section_gallery"}}
- +
@@ -270,7 +270,7 @@
{{ctx.Locale.Tr "repo.settings.pages.trans_section_comparison"}}
- +
@@ -312,6 +312,49 @@ {{end}} {{end}} + {{/* ── Navigation Labels ── */}} +
{{ctx.Locale.Tr "repo.settings.pages.trans_section_navigation"}}
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+
+
+ + +
+
+ + +
+
+