2
0

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
This commit is contained in:
2026-03-07 13:09:46 -05:00
parent a2edcdabe7
commit 5788123e00
17 changed files with 844 additions and 15 deletions

View File

@@ -441,6 +441,7 @@ func prepareMigrationTasks() []*migration {
newMigration(364, "Add view_count to blog_post", v1_26.AddViewCountToBlogPost),
newMigration(365, "Add public_app_integration to repository", v1_26.AddPublicAppIntegrationToRepository),
newMigration(366, "Add page experiment tables for A/B testing", v1_26.AddPageExperimentTables),
newMigration(367, "Add pages translation table for multi-language support", v1_26.AddPagesTranslationTable),
}
return preparedMigrations
}

View File

@@ -0,0 +1,24 @@
// Copyright 2026 MarketAlly. All rights reserved.
// SPDX-License-Identifier: MIT
package v1_26
import (
"code.gitcaddy.com/server/v3/modules/timeutil"
"xorm.io/xorm"
)
func AddPagesTranslationTable(x *xorm.Engine) error {
type Translation struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX NOT NULL"`
Lang string `xorm:"VARCHAR(10) NOT NULL"`
ConfigJSON string `xorm:"TEXT"`
AutoGenerated bool `xorm:"NOT NULL DEFAULT false"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}
return x.Sync(new(Translation))
}

View File

@@ -0,0 +1,70 @@
// Copyright 2026 MarketAlly. All rights reserved.
// SPDX-License-Identifier: MIT
package pages
import (
"context"
"code.gitcaddy.com/server/v3/models/db"
"code.gitcaddy.com/server/v3/modules/timeutil"
)
func init() {
db.RegisterModel(new(Translation))
}
// Translation stores a language-specific translation overlay for a landing page.
type Translation struct {
ID int64 `xorm:"pk autoincr"`
RepoID int64 `xorm:"INDEX NOT NULL"`
Lang string `xorm:"VARCHAR(10) NOT NULL"`
ConfigJSON string `xorm:"TEXT"`
AutoGenerated bool `xorm:"NOT NULL DEFAULT false"`
CreatedUnix timeutil.TimeStamp `xorm:"created"`
UpdatedUnix timeutil.TimeStamp `xorm:"updated"`
}
// TableName returns the table name for Translation.
func (t *Translation) TableName() string {
return "pages_translation"
}
// GetTranslationsByRepoID returns all translations for a repository.
func GetTranslationsByRepoID(ctx context.Context, repoID int64) ([]*Translation, error) {
translations := make([]*Translation, 0, 10)
return translations, db.GetEngine(ctx).Where("repo_id = ?", repoID).
Asc("lang").Find(&translations)
}
// GetTranslation returns a specific language translation for a repository.
func GetTranslation(ctx context.Context, repoID int64, lang string) (*Translation, error) {
t := new(Translation)
has, err := db.GetEngine(ctx).Where("repo_id = ? AND lang = ?", repoID, lang).Get(t)
if err != nil {
return nil, err
}
if !has {
return nil, nil
}
return t, nil
}
// CreateTranslation creates a new translation.
func CreateTranslation(ctx context.Context, t *Translation) error {
_, err := db.GetEngine(ctx).Insert(t)
return err
}
// UpdateTranslation updates an existing translation.
func UpdateTranslation(ctx context.Context, t *Translation) error {
_, err := db.GetEngine(ctx).ID(t.ID).Cols("config_json", "auto_generated").Update(t)
return err
}
// DeleteTranslation deletes a translation by repo ID and language.
func DeleteTranslation(ctx context.Context, repoID int64, lang string) error {
_, err := db.GetEngine(ctx).Where("repo_id = ? AND lang = ?", repoID, lang).
Delete(new(Translation))
return err
}