package controllers import ( "net/http" "strconv" "fotbal-club/internal/models" "fotbal-club/pkg/database" "github.com/gin-gonic/gin" "gorm.io/gorm" ) // I18nController handles internationalization endpoints type I18nController struct { db *gorm.DB } // NewI18nController creates a new i18n controller func NewI18nController() *I18nController { return &I18nController{ db: database.GetDB(), } } // GetLanguages returns all active languages func (ctrl *I18nController) GetLanguages(c *gin.Context) { languages, err := models.GetActiveLanguages(ctrl.db) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch languages"}) return } c.JSON(http.StatusOK, gin.H{ "languages": languages, }) } // GetTranslations returns translations for a specific language func (ctrl *I18nController) GetTranslations(c *gin.Context) { languageCode := c.Param("language") context := c.Query("context") // optional context filter var translations []models.Translation query := ctrl.db.Where("language_code = ?", languageCode) if context != "" { query = query.Where("context = ?", context) } err := query.Order("key ASC").Find(&translations).Error if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch translations"}) return } // Convert to key-value map for easier frontend consumption result := make(map[string]string) for _, t := range translations { result[t.Key] = t.Value } c.JSON(http.StatusOK, gin.H{ "translations": result, "language": languageCode, "context": context, }) } // SetUserLanguage sets user's preferred language func (ctrl *I18nController) SetUserLanguage(c *gin.Context) { userID, exists := c.Get("user_id") if !exists { c.JSON(http.StatusUnauthorized, gin.H{"error": "Unauthorized"}) return } var req struct { LanguageCode string `json:"language_code" binding:"required"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // Validate language var lang models.Language err := ctrl.db.Where("code = ? AND is_active = ?", req.LanguageCode, true).First(&lang).Error if err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid language code"}) return } // Update or create user preference var pref models.UserLanguagePreference err = ctrl.db.Where("user_id = ?", userID).First(&pref).Error if err == gorm.ErrRecordNotFound { // Create new preference pref = models.UserLanguagePreference{ UserID: userID.(uint), LanguageCode: req.LanguageCode, } err = ctrl.db.Create(&pref).Error } else { // Update existing preference pref.LanguageCode = req.LanguageCode err = ctrl.db.Save(&pref).Error } if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save language preference"}) return } // Set cookie c.SetCookie("lang", req.LanguageCode, 365*24*60*60, "/", "", false, true) c.JSON(http.StatusOK, gin.H{ "message": "Language preference saved", "language": req.LanguageCode, }) } // AdminGetAllLanguages returns all languages (including inactive) for admin func (ctrl *I18nController) AdminGetAllLanguages(c *gin.Context) { var languages []models.Language err := ctrl.db.Order("sort_order ASC, name ASC").Find(&languages).Error if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch languages"}) return } c.JSON(http.StatusOK, gin.H{"languages": languages}) } // AdminCreateLanguage creates a new language func (ctrl *I18nController) AdminCreateLanguage(c *gin.Context) { var req struct { ID string `json:"id" binding:"required"` Name string `json:"name" binding:"required"` NativeName string `json:"native_name" binding:"required"` Code string `json:"code" binding:"required"` IsDefault bool `json:"is_default"` IsActive bool `json:"is_default"` SortOrder int `json:"sort_order"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } // If setting as default, unset other defaults if req.IsDefault { ctrl.db.Model(&models.Language{}).Where("is_default = ?", true).Update("is_default", false) } language := models.Language{ ID: req.ID, Name: req.Name, NativeName: req.NativeName, Code: req.Code, IsDefault: req.IsDefault, IsActive: req.IsActive, SortOrder: req.SortOrder, } err := ctrl.db.Create(&language).Error if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create language"}) return } c.JSON(http.StatusCreated, gin.H{"language": language}) } // AdminUpdateLanguage updates a language func (ctrl *I18nController) AdminUpdateLanguage(c *gin.Context) { id := c.Param("id") var req struct { Name string `json:"name"` NativeName string `json:"native_name"` Code string `json:"code"` IsDefault bool `json:"is_default"` IsActive bool `json:"is_active"` SortOrder int `json:"sort_order"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } var language models.Language err := ctrl.db.Where("id = ?", id).First(&language).Error if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Language not found"}) return } // If setting as default, unset other defaults if req.IsDefault && !language.IsDefault { ctrl.db.Model(&models.Language{}).Where("is_default = ?", true).Update("is_default", false) } // Update fields if req.Name != "" { language.Name = req.Name } if req.NativeName != "" { language.NativeName = req.NativeName } if req.Code != "" { language.Code = req.Code } language.IsDefault = req.IsDefault language.IsActive = req.IsActive if req.SortOrder != 0 { language.SortOrder = req.SortOrder } err = ctrl.db.Save(&language).Error if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update language"}) return } c.JSON(http.StatusOK, gin.H{"language": language}) } // AdminDeleteLanguage deletes a language func (ctrl *I18nController) AdminDeleteLanguage(c *gin.Context) { id := c.Param("id") var language models.Language err := ctrl.db.Where("id = ?", id).First(&language).Error if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Language not found"}) return } // Don't allow deletion of default language if language.IsDefault { c.JSON(http.StatusBadRequest, gin.H{"error": "Cannot delete default language"}) return } err = ctrl.db.Delete(&language).Error if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete language"}) return } c.JSON(http.StatusOK, gin.H{"message": "Language deleted"}) } // AdminGetTranslations returns translations for admin func (ctrl *I18nController) AdminGetTranslations(c *gin.Context) { languageCode := c.Query("language") context := c.Query("context") page := c.DefaultQuery("page", "1") limit := c.DefaultQuery("limit", "100") pageInt, _ := strconv.Atoi(page) limitInt, _ := strconv.Atoi(limit) offset := (pageInt - 1) * limitInt var translations []models.Translation var total int64 query := ctrl.db.Model(&models.Translation{}) if languageCode != "" { query = query.Where("language_code = ?", languageCode) } if context != "" { query = query.Where("context = ?", context) } // Count total query.Count(&total) // Get paginated results err := query.Preload("Language").Order("key ASC, language_code ASC"). Limit(limitInt).Offset(offset).Find(&translations).Error if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to fetch translations"}) return } c.JSON(http.StatusOK, gin.H{ "translations": translations, "total": total, "page": pageInt, "limit": limitInt, }) } // AdminCreateTranslation creates a new translation func (ctrl *I18nController) AdminCreateTranslation(c *gin.Context) { var req struct { Key string `json:"key" binding:"required"` LanguageCode string `json:"language_code" binding:"required"` Value string `json:"value" binding:"required"` Context string `json:"context"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } translation := models.Translation{ Key: req.Key, LanguageCode: req.LanguageCode, Value: req.Value, Context: req.Context, } err := ctrl.db.Create(&translation).Error if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to create translation"}) return } c.JSON(http.StatusCreated, gin.H{"translation": translation}) } // AdminUpdateTranslation updates a translation func (ctrl *I18nController) AdminUpdateTranslation(c *gin.Context) { id := c.Param("id") var req struct { Key string `json:"key"` Value string `json:"value"` Context string `json:"context"` } if err := c.ShouldBindJSON(&req); err != nil { c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) return } var translation models.Translation err := ctrl.db.Where("id = ?", id).First(&translation).Error if err != nil { c.JSON(http.StatusNotFound, gin.H{"error": "Translation not found"}) return } if req.Key != "" { translation.Key = req.Key } if req.Value != "" { translation.Value = req.Value } if req.Context != "" { translation.Context = req.Context } err = ctrl.db.Save(&translation).Error if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to update translation"}) return } c.JSON(http.StatusOK, gin.H{"translation": translation}) } // AdminDeleteTranslation deletes a translation func (ctrl *I18nController) AdminDeleteTranslation(c *gin.Context) { id := c.Param("id") err := ctrl.db.Delete(&models.Translation{}, id).Error if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to delete translation"}) return } c.JSON(http.StatusOK, gin.H{"message": "Translation deleted"}) }