This commit is contained in:
Tomas Dvorak
2026-01-26 08:13:18 +01:00
parent aa036b6550
commit dfc079288f
505 changed files with 95755 additions and 5712 deletions
+385
View File
@@ -0,0 +1,385 @@
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"})
}