Fix and refactor code to manage localization more efficiently.
- Added `StringsYML` field in `App` struct to store content of `strings.yml`.
- Modified `NewApp` to read `strings.yml` during app initialization.
- Refactored to use a new `GetLocalizedData` function to get language-specific data.
- Adopted the `GetLocalizedData` function for language data.
- Added dynamic localization using `Lang` map for dashboard title.- commit
- d517deef2cea2fcf3f945c21114f85c25f51729a
- author
- bnema · · 2023-10-04 07:31 UTC
- parent
- 306cd6f
- signature
- unsigned
10 files · +185 -126
M internal/app/app.go +17 -0
| @@ -3,6 +3,7 @@ package app | |||
| 3 | 3 | import ( | |
| 4 | 4 | "database/sql" | |
| 5 | 5 | "fmt" | |
| 6 | + | "io" | |
| 6 | 7 | "io/fs" | |
| 7 | 8 | "log" | |
| 8 | 9 | "os" | |
| @@ -22,6 +23,7 @@ var ( | |||
| 22 | 23 | type App struct { | |
| 23 | 24 | TemplateFS fs.FS | |
| 24 | 25 | PublicFS fs.FS | |
| 26 | + | StringsYML []byte // strings.yml contains the strings for the current language | |
| 25 | 27 | DBDir string | |
| 26 | 28 | DBFilename string | |
| 27 | 29 | DBPath string | |
| @@ -97,10 +99,25 @@ func NewApp() *App { | |||
| 97 | 99 | log.Fatalf("Failed to load configuration: %v", err) | |
| 98 | 100 | } | |
| 99 | 101 | ||
| 102 | + | // Open the strings.yml file containing the strings for the current language | |
| 103 | + | file, err := templates.TemplateFS.Open("strings.yml") | |
| 104 | + | if err != nil { | |
| 105 | + | log.Fatalf("Failed to open strings.yml: %v", err) | |
| 106 | + | } | |
| 107 | + | ||
| 108 | + | // Read the file content into a byte slice | |
| 109 | + | bytes, err := io.ReadAll(file) | |
| 110 | + | if err != nil { | |
| 111 | + | log.Fatalf("Failed to read strings.yml: %v", err) | |
| 112 | + | } | |
| 113 | + | ||
| 114 | + | file.Close() | |
| 115 | + | ||
| 100 | 116 | // Initialize App | |
| 101 | 117 | a := &App{ | |
| 102 | 118 | TemplateFS: templates.TemplateFS, | |
| 103 | 119 | PublicFS: webui.PublicFS, | |
| 120 | + | StringsYML: bytes, | |
| 104 | 121 | DBDir: DBDir, | |
| 105 | 122 | DBFilename: DBFilename, | |
| 106 | 123 | Config: config, | |
| @@ -3,6 +3,7 @@ package app | |||||
| 3 | import ( | 3 | import ( | ||
| 4 | "database/sql" | 4 | "database/sql" | ||
| 5 | "fmt" | 5 | "fmt" | ||
| 6 | + | "io" | |||
| 6 | "io/fs" | 7 | "io/fs" | ||
| 7 | "log" | 8 | "log" | ||
| 8 | "os" | 9 | "os" | ||
| @@ -22,6 +23,7 @@ var ( | |||||
| 22 | type App struct { | 23 | type App struct { | ||
| 23 | TemplateFS fs.FS | 24 | TemplateFS fs.FS | ||
| 24 | PublicFS fs.FS | 25 | PublicFS fs.FS | ||
| 26 | + | StringsYML []byte // strings.yml contains the strings for the current language | |||
| 25 | DBDir string | 27 | DBDir string | ||
| 26 | DBFilename string | 28 | DBFilename string | ||
| 27 | DBPath string | 29 | DBPath string | ||
| @@ -97,10 +99,25 @@ func NewApp() *App { | |||||
| 97 | log.Fatalf("Failed to load configuration: %v", err) | 99 | log.Fatalf("Failed to load configuration: %v", err) | ||
| 98 | } | 100 | } | ||
| 99 | 101 | ||||
| 102 | + | // Open the strings.yml file containing the strings for the current language | |||
| 103 | + | file, err := templates.TemplateFS.Open("strings.yml") | |||
| 104 | + | if err != nil { | |||
| 105 | + | log.Fatalf("Failed to open strings.yml: %v", err) | |||
| 106 | + | } | |||
| 107 | + | ||||
| 108 | + | // Read the file content into a byte slice | |||
| 109 | + | bytes, err := io.ReadAll(file) | |||
| 110 | + | if err != nil { | |||
| 111 | + | log.Fatalf("Failed to read strings.yml: %v", err) | |||
| 112 | + | } | |||
| 113 | + | ||||
| 114 | + | file.Close() | |||
| 115 | + | ||||
| 100 | // Initialize App | 116 | // Initialize App | ||
| 101 | a := &App{ | 117 | a := &App{ | ||
| 102 | TemplateFS: templates.TemplateFS, | 118 | TemplateFS: templates.TemplateFS, | ||
| 103 | PublicFS: webui.PublicFS, | 119 | PublicFS: webui.PublicFS, | ||
| 120 | + | StringsYML: bytes, | |||
| 104 | DBDir: DBDir, | 121 | DBDir: DBDir, | ||
| 105 | DBFilename: DBFilename, | 122 | DBFilename: DBFilename, | ||
| 106 | Config: config, | 123 | Config: config, | ||
M internal/httpserve/handler/adminroutes.go +19 -20
| @@ -2,12 +2,10 @@ package handler | |||
| 2 | 2 | ||
| 3 | 3 | import ( | |
| 4 | 4 | "fmt" | |
| 5 | - | "net/http" | |
| 6 | 5 | ||
| 7 | 6 | "github.com/bnema/gordon/internal/app" | |
| 8 | 7 | "github.com/bnema/gordon/internal/httpserve/middleware" | |
| 9 | 8 | "github.com/bnema/gordon/internal/templates/render" | |
| 10 | - | "github.com/bnema/gordon/internal/webui" | |
| 11 | 9 | "github.com/labstack/echo/v4" | |
| 12 | 10 | ) | |
| 13 | 11 | ||
| @@ -16,27 +14,34 @@ var ( | |||
| 16 | 14 | fragmentsPath = "html/fragments" | |
| 17 | 15 | ) | |
| 18 | 16 | ||
| 17 | + | func GetLocalizedData(c echo.Context, a *app.App) (map[string]interface{}, error) { | |
| 18 | + | lang := c.Get(middleware.LangKey) | |
| 19 | + | ||
| 20 | + | if lang == nil { | |
| 21 | + | return nil, fmt.Errorf("LangKey not found in context") | |
| 22 | + | } | |
| 23 | + | ||
| 24 | + | yamlData, err := render.GetLocalization(lang.(string), a) | |
| 25 | + | if err != nil { | |
| 26 | + | return nil, fmt.Errorf("failed to get localization: %w", err) | |
| 27 | + | } | |
| 28 | + | return map[string]interface{}{ | |
| 29 | + | "Lang": yamlData, | |
| 30 | + | }, nil | |
| 31 | + | } | |
| 32 | + | ||
| 19 | 33 | // Handle the admin route to display index.gohtml from the templateFS with the data from strings.yml | |
| 20 | 34 | func AdminRoute(c echo.Context, a *app.App) error { | |
| 21 | - | lang := c.Get(middleware.LangKey).(string) | |
| 22 | - | yamlData := webui.StringsYamlData{} | |
| 23 | - | err := webui.ReadStringsDataFromYAML(lang, a.TemplateFS, "strings.yml", &yamlData) | |
| 35 | + | data, err := GetLocalizedData(c, a) | |
| 24 | 36 | if err != nil { | |
| 25 | 37 | return err | |
| 26 | 38 | } | |
| 27 | - | ||
| 28 | 39 | // Navigate inside the fs.FS to get the template | |
| 29 | 40 | rendererData, err := render.GetHTMLRenderer(mainPath, "index.gohtml", a.TemplateFS, a, fragmentsPath) | |
| 30 | 41 | if err != nil { | |
| 31 | 42 | return err | |
| 32 | 43 | } | |
| 33 | 44 | ||
| 34 | - | // Create a data map to pass to the renderer | |
| 35 | - | data := map[string]interface{}{ | |
| 36 | - | "CurrentLang": yamlData.CurrentLang, | |
| 37 | - | // "BuildVersion" will be automatically added in the renderer | |
| 38 | - | } | |
| 39 | - | ||
| 40 | 45 | renderedHTML, err := rendererData.Render(data, a) | |
| 41 | 46 | if err != nil { | |
| 42 | 47 | return err | |
| @@ -46,12 +51,9 @@ func AdminRoute(c echo.Context, a *app.App) error { | |||
| 46 | 51 | } | |
| 47 | 52 | ||
| 48 | 53 | func AdminManagerRoute(c echo.Context, a *app.App) error { | |
| 49 | - | lang := c.Get(middleware.LangKey).(string) | |
| 50 | - | yamlData := webui.StringsYamlData{} | |
| 51 | - | err := webui.ReadStringsDataFromYAML(lang, a.TemplateFS, "strings.yml", &yamlData) | |
| 54 | + | data, err := GetLocalizedData(c, a) | |
| 52 | 55 | if err != nil { | |
| 53 | - | fmt.Printf("Error reading YAML: %v\n", err) | |
| 54 | - | return echo.NewHTTPError(http.StatusInternalServerError, "Failed to load language data") | |
| 56 | + | return err | |
| 55 | 57 | } | |
| 56 | 58 | ||
| 57 | 59 | // Navigate inside the fs.FS to get the template | |
| @@ -59,9 +61,6 @@ func AdminManagerRoute(c echo.Context, a *app.App) error { | |||
| 59 | 61 | if err != nil { | |
| 60 | 62 | return err | |
| 61 | 63 | } | |
| 62 | - | data := map[string]interface{}{ | |
| 63 | - | "CurrentLang": yamlData.CurrentLang, | |
| 64 | - | } | |
| 65 | 64 | ||
| 66 | 65 | renderedHTML, err := rendererData.Render(data, a) | |
| 67 | 66 | if err != nil { | |
| @@ -2,12 +2,10 @@ package handler | |||||
| 2 | 2 | ||||
| 3 | import ( | 3 | import ( | ||
| 4 | "fmt" | 4 | "fmt" | ||
| 5 | - | "net/http" | |||
| 6 | 5 | ||||
| 7 | "github.com/bnema/gordon/internal/app" | 6 | "github.com/bnema/gordon/internal/app" | ||
| 8 | "github.com/bnema/gordon/internal/httpserve/middleware" | 7 | "github.com/bnema/gordon/internal/httpserve/middleware" | ||
| 9 | "github.com/bnema/gordon/internal/templates/render" | 8 | "github.com/bnema/gordon/internal/templates/render" | ||
| 10 | - | "github.com/bnema/gordon/internal/webui" | |||
| 11 | "github.com/labstack/echo/v4" | 9 | "github.com/labstack/echo/v4" | ||
| 12 | ) | 10 | ) | ||
| 13 | 11 | ||||
| @@ -16,27 +14,34 @@ var ( | |||||
| 16 | fragmentsPath = "html/fragments" | 14 | fragmentsPath = "html/fragments" | ||
| 17 | ) | 15 | ) | ||
| 18 | 16 | ||||
| 17 | + | func GetLocalizedData(c echo.Context, a *app.App) (map[string]interface{}, error) { | |||
| 18 | + | lang := c.Get(middleware.LangKey) | |||
| 19 | + | ||||
| 20 | + | if lang == nil { | |||
| 21 | + | return nil, fmt.Errorf("LangKey not found in context") | |||
| 22 | + | } | |||
| 23 | + | ||||
| 24 | + | yamlData, err := render.GetLocalization(lang.(string), a) | |||
| 25 | + | if err != nil { | |||
| 26 | + | return nil, fmt.Errorf("failed to get localization: %w", err) | |||
| 27 | + | } | |||
| 28 | + | return map[string]interface{}{ | |||
| 29 | + | "Lang": yamlData, | |||
| 30 | + | }, nil | |||
| 31 | + | } | |||
| 32 | + | ||||
| 19 | // Handle the admin route to display index.gohtml from the templateFS with the data from strings.yml | 33 | // Handle the admin route to display index.gohtml from the templateFS with the data from strings.yml | ||
| 20 | func AdminRoute(c echo.Context, a *app.App) error { | 34 | func AdminRoute(c echo.Context, a *app.App) error { | ||
| 21 | - | lang := c.Get(middleware.LangKey).(string) | 35 | + | data, err := GetLocalizedData(c, a) |
| 22 | - | yamlData := webui.StringsYamlData{} | |||
| 23 | - | err := webui.ReadStringsDataFromYAML(lang, a.TemplateFS, "strings.yml", &yamlData) | |||
| 24 | if err != nil { | 36 | if err != nil { | ||
| 25 | return err | 37 | return err | ||
| 26 | } | 38 | } | ||
| 27 | - | ||||
| 28 | // Navigate inside the fs.FS to get the template | 39 | // Navigate inside the fs.FS to get the template | ||
| 29 | rendererData, err := render.GetHTMLRenderer(mainPath, "index.gohtml", a.TemplateFS, a, fragmentsPath) | 40 | rendererData, err := render.GetHTMLRenderer(mainPath, "index.gohtml", a.TemplateFS, a, fragmentsPath) | ||
| 30 | if err != nil { | 41 | if err != nil { | ||
| 31 | return err | 42 | return err | ||
| 32 | } | 43 | } | ||
| 33 | 44 | ||||
| 34 | - | // Create a data map to pass to the renderer | |||
| 35 | - | data := map[string]interface{}{ | |||
| 36 | - | "CurrentLang": yamlData.CurrentLang, | |||
| 37 | - | // "BuildVersion" will be automatically added in the renderer | |||
| 38 | - | } | |||
| 39 | - | ||||
| 40 | renderedHTML, err := rendererData.Render(data, a) | 45 | renderedHTML, err := rendererData.Render(data, a) | ||
| 41 | if err != nil { | 46 | if err != nil { | ||
| 42 | return err | 47 | return err | ||
| @@ -46,12 +51,9 @@ func AdminRoute(c echo.Context, a *app.App) error { | |||||
| 46 | } | 51 | } | ||
| 47 | 52 | ||||
| 48 | func AdminManagerRoute(c echo.Context, a *app.App) error { | 53 | func AdminManagerRoute(c echo.Context, a *app.App) error { | ||
| 49 | - | lang := c.Get(middleware.LangKey).(string) | 54 | + | data, err := GetLocalizedData(c, a) |
| 50 | - | yamlData := webui.StringsYamlData{} | |||
| 51 | - | err := webui.ReadStringsDataFromYAML(lang, a.TemplateFS, "strings.yml", &yamlData) | |||
| 52 | if err != nil { | 55 | if err != nil { | ||
| 53 | - | fmt.Printf("Error reading YAML: %v\n", err) | 56 | + | return err |
| 54 | - | return echo.NewHTTPError(http.StatusInternalServerError, "Failed to load language data") | |||
| 55 | } | 57 | } | ||
| 56 | 58 | ||||
| 57 | // Navigate inside the fs.FS to get the template | 59 | // Navigate inside the fs.FS to get the template | ||
| @@ -59,9 +61,6 @@ func AdminManagerRoute(c echo.Context, a *app.App) error { | |||||
| 59 | if err != nil { | 61 | if err != nil { | ||
| 60 | return err | 62 | return err | ||
| 61 | } | 63 | } | ||
| 62 | - | data := map[string]interface{}{ | |||
| 63 | - | "CurrentLang": yamlData.CurrentLang, | |||
| 64 | - | } | |||
| 65 | 64 | ||||
| 66 | renderedHTML, err := rendererData.Render(data, a) | 65 | renderedHTML, err := rendererData.Render(data, a) | ||
| 67 | if err != nil { | 66 | if err != nil { | ||
M internal/httpserve/handler/oauth.go +2 -11
| @@ -8,9 +8,7 @@ import ( | |||
| 8 | 8 | "strings" | |
| 9 | 9 | ||
| 10 | 10 | "github.com/bnema/gordon/internal/app" | |
| 11 | - | "github.com/bnema/gordon/internal/httpserve/middleware" | |
| 12 | 11 | "github.com/bnema/gordon/internal/templates/render" | |
| 13 | - | "github.com/bnema/gordon/internal/webui" | |
| 14 | 12 | _ "github.com/joho/godotenv/autoload" | |
| 15 | 13 | "github.com/labstack/echo-contrib/session" | |
| 16 | 14 | "github.com/labstack/echo/v4" | |
| @@ -19,12 +17,11 @@ import ( | |||
| 19 | 17 | ||
| 20 | 18 | // RenderLoginPage renders the login.html template | |
| 21 | 19 | func RenderLoginPage(c echo.Context, a *app.App) error { | |
| 22 | - | lang := c.Get(middleware.LangKey).(string) | |
| 23 | - | yamlData := webui.StringsYamlData{} | |
| 24 | - | err := webui.ReadStringsDataFromYAML(lang, a.TemplateFS, "strings.yml", &yamlData) | |
| 20 | + | data, err := GetLocalizedData(c, a) | |
| 25 | 21 | if err != nil { | |
| 26 | 22 | return err | |
| 27 | 23 | } | |
| 24 | + | ||
| 28 | 25 | // Navigate inside the fs.FS to get the template | |
| 29 | 26 | path := "html/login" | |
| 30 | 27 | rendererData, err := render.GetHTMLRenderer(path, "index.gohtml", a.TemplateFS, a) | |
| @@ -32,12 +29,6 @@ func RenderLoginPage(c echo.Context, a *app.App) error { | |||
| 32 | 29 | return err | |
| 33 | 30 | } | |
| 34 | 31 | ||
| 35 | - | // Create a data map to pass to the renderer | |
| 36 | - | data := map[string]interface{}{ | |
| 37 | - | "CurrentLang": yamlData.CurrentLang, | |
| 38 | - | // "BuildVersion" will be automatically added in the renderer | |
| 39 | - | } | |
| 40 | - | ||
| 41 | 32 | renderedHTML, err := rendererData.Render(data, a) | |
| 42 | 33 | if err != nil { | |
| 43 | 34 | return err | |
| @@ -8,9 +8,7 @@ import ( | |||||
| 8 | "strings" | 8 | "strings" | ||
| 9 | 9 | ||||
| 10 | "github.com/bnema/gordon/internal/app" | 10 | "github.com/bnema/gordon/internal/app" | ||
| 11 | - | "github.com/bnema/gordon/internal/httpserve/middleware" | |||
| 12 | "github.com/bnema/gordon/internal/templates/render" | 11 | "github.com/bnema/gordon/internal/templates/render" | ||
| 13 | - | "github.com/bnema/gordon/internal/webui" | |||
| 14 | _ "github.com/joho/godotenv/autoload" | 12 | _ "github.com/joho/godotenv/autoload" | ||
| 15 | "github.com/labstack/echo-contrib/session" | 13 | "github.com/labstack/echo-contrib/session" | ||
| 16 | "github.com/labstack/echo/v4" | 14 | "github.com/labstack/echo/v4" | ||
| @@ -19,12 +17,11 @@ import ( | |||||
| 19 | 17 | ||||
| 20 | // RenderLoginPage renders the login.html template | 18 | // RenderLoginPage renders the login.html template | ||
| 21 | func RenderLoginPage(c echo.Context, a *app.App) error { | 19 | func RenderLoginPage(c echo.Context, a *app.App) error { | ||
| 22 | - | lang := c.Get(middleware.LangKey).(string) | 20 | + | data, err := GetLocalizedData(c, a) |
| 23 | - | yamlData := webui.StringsYamlData{} | |||
| 24 | - | err := webui.ReadStringsDataFromYAML(lang, a.TemplateFS, "strings.yml", &yamlData) | |||
| 25 | if err != nil { | 21 | if err != nil { | ||
| 26 | return err | 22 | return err | ||
| 27 | } | 23 | } | ||
| 24 | + | ||||
| 28 | // Navigate inside the fs.FS to get the template | 25 | // Navigate inside the fs.FS to get the template | ||
| 29 | path := "html/login" | 26 | path := "html/login" | ||
| 30 | rendererData, err := render.GetHTMLRenderer(path, "index.gohtml", a.TemplateFS, a) | 27 | rendererData, err := render.GetHTMLRenderer(path, "index.gohtml", a.TemplateFS, a) | ||
| @@ -32,12 +29,6 @@ func RenderLoginPage(c echo.Context, a *app.App) error { | |||||
| 32 | return err | 29 | return err | ||
| 33 | } | 30 | } | ||
| 34 | 31 | ||||
| 35 | - | // Create a data map to pass to the renderer | |||
| 36 | - | data := map[string]interface{}{ | |||
| 37 | - | "CurrentLang": yamlData.CurrentLang, | |||
| 38 | - | // "BuildVersion" will be automatically added in the renderer | |||
| 39 | - | } | |||
| 40 | - | ||||
| 41 | renderedHTML, err := rendererData.Render(data, a) | 32 | renderedHTML, err := rendererData.Render(data, a) | ||
| 42 | if err != nil { | 33 | if err != nil { | ||
| 43 | return err | 34 | return err | ||
M internal/httpserve/router.go +4 -0
| @@ -38,12 +38,14 @@ func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo { | |||
| 38 | 38 | return e | |
| 39 | 39 | } | |
| 40 | 40 | ||
| 41 | + | // bindStaticRoute bind static path | |
| 41 | 42 | func bindStaticRoute(e *echo.Echo, a *app.App, path string) { | |
| 42 | 43 | e.GET(path, func(c echo.Context) error { | |
| 43 | 44 | return handler.StaticRoute(c, a) | |
| 44 | 45 | }) | |
| 45 | 46 | } | |
| 46 | 47 | ||
| 48 | + | // bindAdminRoute binds all admin routes | |
| 47 | 49 | func bindAdminRoute(e *echo.Echo, a *app.App, adminPath string) { | |
| 48 | 50 | e.GET(adminPath, func(c echo.Context) error { | |
| 49 | 51 | return handler.AdminRoute(c, a) | |
| @@ -54,6 +56,7 @@ func bindAdminRoute(e *echo.Echo, a *app.App, adminPath string) { | |||
| 54 | 56 | }) | |
| 55 | 57 | } | |
| 56 | 58 | ||
| 59 | + | // bindLoginRoute binds all login routes | |
| 57 | 60 | func bindLoginRoute(e *echo.Echo, a *app.App, adminPath string) { | |
| 58 | 61 | e.GET(adminPath+"/login", func(c echo.Context) error { | |
| 59 | 62 | return handler.RenderLoginPage(c, a) | |
| @@ -69,6 +72,7 @@ func bindLoginRoute(e *echo.Echo, a *app.App, adminPath string) { | |||
| 69 | 72 | }) | |
| 70 | 73 | } | |
| 71 | 74 | ||
| 75 | + | // bindHTMXEndpoints binds all HTMX endpoints | |
| 72 | 76 | func bindHTMXEndpoints(e *echo.Echo, a *app.App) { | |
| 73 | 77 | // Create a group for /htmx endpoints | |
| 74 | 78 | htmxGroup := e.Group("/htmx") | |
| @@ -38,12 +38,14 @@ func RegisterRoutes(e *echo.Echo, a *app.App) *echo.Echo { | |||||
| 38 | return e | 38 | return e | ||
| 39 | } | 39 | } | ||
| 40 | 40 | ||||
| 41 | + | // bindStaticRoute bind static path | |||
| 41 | func bindStaticRoute(e *echo.Echo, a *app.App, path string) { | 42 | func bindStaticRoute(e *echo.Echo, a *app.App, path string) { | ||
| 42 | e.GET(path, func(c echo.Context) error { | 43 | e.GET(path, func(c echo.Context) error { | ||
| 43 | return handler.StaticRoute(c, a) | 44 | return handler.StaticRoute(c, a) | ||
| 44 | }) | 45 | }) | ||
| 45 | } | 46 | } | ||
| 46 | 47 | ||||
| 48 | + | // bindAdminRoute binds all admin routes | |||
| 47 | func bindAdminRoute(e *echo.Echo, a *app.App, adminPath string) { | 49 | func bindAdminRoute(e *echo.Echo, a *app.App, adminPath string) { | ||
| 48 | e.GET(adminPath, func(c echo.Context) error { | 50 | e.GET(adminPath, func(c echo.Context) error { | ||
| 49 | return handler.AdminRoute(c, a) | 51 | return handler.AdminRoute(c, a) | ||
| @@ -54,6 +56,7 @@ func bindAdminRoute(e *echo.Echo, a *app.App, adminPath string) { | |||||
| 54 | }) | 56 | }) | ||
| 55 | } | 57 | } | ||
| 56 | 58 | ||||
| 59 | + | // bindLoginRoute binds all login routes | |||
| 57 | func bindLoginRoute(e *echo.Echo, a *app.App, adminPath string) { | 60 | func bindLoginRoute(e *echo.Echo, a *app.App, adminPath string) { | ||
| 58 | e.GET(adminPath+"/login", func(c echo.Context) error { | 61 | e.GET(adminPath+"/login", func(c echo.Context) error { | ||
| 59 | return handler.RenderLoginPage(c, a) | 62 | return handler.RenderLoginPage(c, a) | ||
| @@ -69,6 +72,7 @@ func bindLoginRoute(e *echo.Echo, a *app.App, adminPath string) { | |||||
| 69 | }) | 72 | }) | ||
| 70 | } | 73 | } | ||
| 71 | 74 | ||||
| 75 | + | // bindHTMXEndpoints binds all HTMX endpoints | |||
| 72 | func bindHTMXEndpoints(e *echo.Echo, a *app.App) { | 76 | func bindHTMXEndpoints(e *echo.Echo, a *app.App) { | ||
| 73 | // Create a group for /htmx endpoints | 77 | // Create a group for /htmx endpoints | ||
| 74 | htmxGroup := e.Group("/htmx") | 78 | htmxGroup := e.Group("/htmx") | ||
M internal/templates/models/html/admin/index.gohtml +33 -31
| @@ -1,40 +1,42 @@ | |||
| 1 | 1 | <!DOCTYPE html> | |
| 2 | 2 | <html lang="en"> | |
| 3 | + | {{template "header.gohtml" .}} | |
| 3 | 4 | ||
| 4 | - | {{template "header.gohtml" .}} | |
| 5 | - | ||
| 6 | - | <body class="bg-zinc-200 flex flex-col md:flex-row p-4 md:p-8 p-0"> | |
| 7 | - | ||
| 8 | - | {{template "leftmenu.gohtml" .}} | |
| 9 | - | <div class="flex-1 flex flex-col md:pt-10 space-y-4 p-4 md:ml-4"> | |
| 10 | - | <div class="hidden md:flex mb-4 justify-end"> | |
| 11 | - | <div class="w-72 h-10 bg-gray-300 rounded-lg"> | |
| 12 | - | <div class="flex justify-between items-center h-full px-2"> | |
| 13 | - | <input type="text" class="w-full h-full bg-transparent outline-none" placeholder="Search" /> | |
| 14 | - | <i class="iconoir-search"></i> | |
| 5 | + | <body class="bg-zinc-200 flex flex-col md:flex-row p-4 md:p-8 p-0"> | |
| 6 | + | {{template "leftmenu.gohtml" .}} | |
| 7 | + | <div class="flex-1 flex flex-col md:pt-10 space-y-4 p-4 md:ml-4"> | |
| 8 | + | <div class="hidden md:flex mb-4 justify-end"> | |
| 9 | + | <div class="w-72 h-10 bg-gray-300 rounded-lg"> | |
| 10 | + | <div class="flex justify-between items-center h-full px-2"> | |
| 11 | + | <input | |
| 12 | + | type="text" | |
| 13 | + | class="w-full h-full bg-transparent outline-none" | |
| 14 | + | placeholder="Search" | |
| 15 | + | /> | |
| 16 | + | <i class="iconoir-search"></i> | |
| 17 | + | </div> | |
| 15 | 18 | </div> | |
| 16 | 19 | </div> | |
| 17 | - | </div> | |
| 18 | - | <div class="flex flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-4 flex-grow"> | |
| 19 | - | <div class="flex-grow bg-white p-4 rounded-md shadow mb-4"> | |
| 20 | - | <h2>Dashboard</h2> | |
| 21 | - | </div> | |
| 22 | - | <div class="w-full md:w-2/6 flex flex-col space-y-4"> | |
| 23 | - | <div class="bg-white p-4 rounded-md shadow mb-4"> | |
| 24 | - | <div class="w-full h-44 rounded-sm bg-gray-300"></div> | |
| 20 | + | <div | |
| 21 | + | class="flex flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-4 flex-grow" | |
| 22 | + | > | |
| 23 | + | <div class="flex-grow bg-white p-4 rounded-md shadow mb-4"> | |
| 24 | + | <h2>{{index .Lang "dashboard" "content" "title"}}</h2> | |
| 25 | 25 | </div> | |
| 26 | - | <div class="bg-white p-4 rounded-md shadow mb-4"> | |
| 27 | - | <div class="w-full h-44 rounded-sm bg-gray-300"></div> | |
| 28 | - | </div> | |
| 29 | - | <div class="bg-white p-4 rounded-md shadow mb-4"> | |
| 30 | - | <div class="w-full h-44 rounded-sm bg-gray-300"></div> | |
| 26 | + | <div class="w-full md:w-2/6 flex flex-col space-y-4"> | |
| 27 | + | <div class="bg-white p-4 rounded-md shadow mb-4"> | |
| 28 | + | <div class="w-full h-44 rounded-sm bg-gray-300"></div> | |
| 29 | + | </div> | |
| 30 | + | <div class="bg-white p-4 rounded-md shadow mb-4"> | |
| 31 | + | <div class="w-full h-44 rounded-sm bg-gray-300"></div> | |
| 32 | + | </div> | |
| 33 | + | <div class="bg-white p-4 rounded-md shadow mb-4"> | |
| 34 | + | <div class="w-full h-44 rounded-sm bg-gray-300"></div> | |
| 35 | + | </div> | |
| 31 | 36 | </div> | |
| 32 | 37 | </div> | |
| 33 | 38 | </div> | |
| 34 | - | </div> | |
| 35 | - | <!-- footer --> | |
| 36 | - | {{template "footer.gohtml" .}} | |
| 37 | - | ||
| 38 | - | </body> | |
| 39 | - | ||
| 40 | - | </html>⏎̸ | |
| 39 | + | <!-- footer --> | |
| 40 | + | {{template "footer.gohtml" .}} | |
| 41 | + | </body> | |
| 42 | + | </html> | |
| @@ -1,40 +1,42 @@ | |||||
| 1 | <!DOCTYPE html> | 1 | <!DOCTYPE html> | ||
| 2 | <html lang="en"> | 2 | <html lang="en"> | ||
| 3 | + | {{template "header.gohtml" .}} | |||
| 3 | 4 | ||||
| 4 | - | {{template "header.gohtml" .}} | 5 | + | <body class="bg-zinc-200 flex flex-col md:flex-row p-4 md:p-8 p-0"> |
| 5 | - | 6 | + | {{template "leftmenu.gohtml" .}} | |
| 6 | - | <body class="bg-zinc-200 flex flex-col md:flex-row p-4 md:p-8 p-0"> | 7 | + | <div class="flex-1 flex flex-col md:pt-10 space-y-4 p-4 md:ml-4"> |
| 7 | - | 8 | + | <div class="hidden md:flex mb-4 justify-end"> | |
| 8 | - | {{template "leftmenu.gohtml" .}} | 9 | + | <div class="w-72 h-10 bg-gray-300 rounded-lg"> |
| 9 | - | <div class="flex-1 flex flex-col md:pt-10 space-y-4 p-4 md:ml-4"> | 10 | + | <div class="flex justify-between items-center h-full px-2"> |
| 10 | - | <div class="hidden md:flex mb-4 justify-end"> | 11 | + | <input |
| 11 | - | <div class="w-72 h-10 bg-gray-300 rounded-lg"> | 12 | + | type="text" |
| 12 | - | <div class="flex justify-between items-center h-full px-2"> | 13 | + | class="w-full h-full bg-transparent outline-none" |
| 13 | - | <input type="text" class="w-full h-full bg-transparent outline-none" placeholder="Search" /> | 14 | + | placeholder="Search" |
| 14 | - | <i class="iconoir-search"></i> | 15 | + | /> |
| 16 | + | <i class="iconoir-search"></i> | |||
| 17 | + | </div> | |||
| 15 | </div> | 18 | </div> | ||
| 16 | </div> | 19 | </div> | ||
| 17 | - | </div> | 20 | + | <div |
| 18 | - | <div class="flex flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-4 flex-grow"> | 21 | + | class="flex flex-col md:flex-row space-y-4 md:space-y-0 md:space-x-4 flex-grow" |
| 19 | - | <div class="flex-grow bg-white p-4 rounded-md shadow mb-4"> | 22 | + | > |
| 20 | - | <h2>Dashboard</h2> | 23 | + | <div class="flex-grow bg-white p-4 rounded-md shadow mb-4"> |
| 21 | - | </div> | 24 | + | <h2>{{index .Lang "dashboard" "content" "title"}}</h2> |
| 22 | - | <div class="w-full md:w-2/6 flex flex-col space-y-4"> | |||
| 23 | - | <div class="bg-white p-4 rounded-md shadow mb-4"> | |||
| 24 | - | <div class="w-full h-44 rounded-sm bg-gray-300"></div> | |||
| 25 | </div> | 25 | </div> | ||
| 26 | - | <div class="bg-white p-4 rounded-md shadow mb-4"> | 26 | + | <div class="w-full md:w-2/6 flex flex-col space-y-4"> |
| 27 | - | <div class="w-full h-44 rounded-sm bg-gray-300"></div> | 27 | + | <div class="bg-white p-4 rounded-md shadow mb-4"> |
| 28 | - | </div> | 28 | + | <div class="w-full h-44 rounded-sm bg-gray-300"></div> |
| 29 | - | <div class="bg-white p-4 rounded-md shadow mb-4"> | 29 | + | </div> |
| 30 | - | <div class="w-full h-44 rounded-sm bg-gray-300"></div> | 30 | + | <div class="bg-white p-4 rounded-md shadow mb-4"> |
| 31 | + | <div class="w-full h-44 rounded-sm bg-gray-300"></div> | |||
| 32 | + | </div> | |||
| 33 | + | <div class="bg-white p-4 rounded-md shadow mb-4"> | |||
| 34 | + | <div class="w-full h-44 rounded-sm bg-gray-300"></div> | |||
| 35 | + | </div> | |||
| 31 | </div> | 36 | </div> | ||
| 32 | </div> | 37 | </div> | ||
| 33 | </div> | 38 | </div> | ||
| 34 | - | </div> | 39 | + | <!-- footer --> |
| 35 | - | <!-- footer --> | 40 | + | {{template "footer.gohtml" .}} |
| 36 | - | {{template "footer.gohtml" .}} | 41 | + | </body> |
| 37 | - | 42 | + | </html> | |
| 38 | - | </body> | |||
| 39 | - | ||||
| 40 | - | </html>⏎̸ | |||
M internal/templates/models/html/admin/manager.gohtml +1 -1
| @@ -9,7 +9,7 @@ | |||
| 9 | 9 | <div class="flex-1 flex flex-col space-y-4 p-4 md:ml-4"> | |
| 10 | 10 | <div class="flex flex-col md:flex-row w-full flex-grow md:mt-24"> | |
| 11 | 11 | <div class="flex-grow bg-white p-4 rounded-md shadow mb-4"> | |
| 12 | - | <h1 class="text-lg md:text-2xl font-bold">Container Engine Manager</h1> | |
| 12 | + | <h1 class="text-lg md:text-2xl font-bold">{{index .Lang "manager" "content" "title"}}</h1> | |
| 13 | 13 | <!-- List all the containers --> | |
| 14 | 14 | <div | |
| 15 | 15 | id="container-manager" | |
| @@ -9,7 +9,7 @@ | |||||
| 9 | <div class="flex-1 flex flex-col space-y-4 p-4 md:ml-4"> | 9 | <div class="flex-1 flex flex-col space-y-4 p-4 md:ml-4"> | ||
| 10 | <div class="flex flex-col md:flex-row w-full flex-grow md:mt-24"> | 10 | <div class="flex flex-col md:flex-row w-full flex-grow md:mt-24"> | ||
| 11 | <div class="flex-grow bg-white p-4 rounded-md shadow mb-4"> | 11 | <div class="flex-grow bg-white p-4 rounded-md shadow mb-4"> | ||
| 12 | - | <h1 class="text-lg md:text-2xl font-bold">Container Engine Manager</h1> | 12 | + | <h1 class="text-lg md:text-2xl font-bold">{{index .Lang "manager" "content" "title"}}</h1> |
| 13 | <!-- List all the containers --> | 13 | <!-- List all the containers --> | ||
| 14 | <div | 14 | <div | ||
| 15 | id="container-manager" | 15 | id="container-manager" | ||
M internal/templates/models/strings.yml +48 -16
| @@ -1,23 +1,55 @@ | |||
| 1 | 1 | en: | |
| 2 | - | header: | |
| 2 | + | common: | |
| 3 | 3 | meta: | |
| 4 | - | title: Gordon - Admin Dashboard | |
| 5 | - | description: Admin dashboard for Gordon | |
| 6 | - | keywords: gordon, admin, dashboard | |
| 7 | - | body: | |
| 4 | + | description: Admin interface for Gordon | |
| 5 | + | keywords: gordon, admin, interface | |
| 8 | 6 | header: | |
| 9 | - | helloworld: Hello World! | |
| 10 | - | div: | |
| 11 | - | hello: Hello component loaded from a template! | |
| 7 | + | someKey: Hello World! | |
| 8 | + | navmenu: | |
| 9 | + | dashboard: Dashboard | |
| 10 | + | manager: Manager | |
| 11 | + | settings: Settings | |
| 12 | + | logout: Logout | |
| 13 | + | dashboard: | |
| 14 | + | meta: | |
| 15 | + | title: Gordon - Admin Dashboard | |
| 16 | + | content: | |
| 17 | + | title: Dashboard | |
| 18 | + | manager: | |
| 19 | + | meta: | |
| 20 | + | title: Gordon - Container Engine Manager | |
| 21 | + | content: | |
| 22 | + | title: Container Engine Manager | |
| 23 | + | settings: | |
| 24 | + | meta: | |
| 25 | + | title: Gordon - Settings | |
| 26 | + | content: | |
| 27 | + | title: Settings | |
| 12 | 28 | ||
| 13 | 29 | fr: | |
| 14 | - | header: | |
| 30 | + | common: | |
| 15 | 31 | meta: | |
| 16 | - | title: Gordon - Tableau de bord administrateur | |
| 17 | - | description: Tableau de bord administrateur pour Gordon | |
| 18 | - | keywords: gordon, admin, dashboard | |
| 19 | - | body: | |
| 32 | + | description: Interface d'administration pour Gordon | |
| 33 | + | keywords: gordon, admin, interface | |
| 20 | 34 | header: | |
| 21 | - | helloworld: Bonjour le monde! | |
| 22 | - | div: | |
| 23 | - | hello: Composant de salut chargé à partir d'un modèle! | |
| 35 | + | someKey: Bonjour le monde! | |
| 36 | + | navmenu: | |
| 37 | + | dashboard: Tableau de bord | |
| 38 | + | manager: Gestionnaire | |
| 39 | + | settings: Paramètres | |
| 40 | + | logout: Déconnexion | |
| 41 | + | dashboard: | |
| 42 | + | meta: | |
| 43 | + | title: Gordon - Tableau de bord | |
| 44 | + | content: | |
| 45 | + | title: Tableau de bord | |
| 46 | + | manager: | |
| 47 | + | meta: | |
| 48 | + | title: Gordon - Gestionnaire de moteur de conteneur | |
| 49 | + | content: | |
| 50 | + | title: Gestionnaire de moteur de conteneur | |
| 51 | + | settings: | |
| 52 | + | meta: | |
| 53 | + | title: Gordon - Paramètres | |
| 54 | + | content: | |
| 55 | + | title: Paramètres | |
| @@ -1,23 +1,55 @@ | |||||
| 1 | en: | 1 | en: | ||
| 2 | - | header: | 2 | + | common: |
| 3 | meta: | 3 | meta: | ||
| 4 | - | title: Gordon - Admin Dashboard | 4 | + | description: Admin interface for Gordon |
| 5 | - | description: Admin dashboard for Gordon | 5 | + | keywords: gordon, admin, interface |
| 6 | - | keywords: gordon, admin, dashboard | |||
| 7 | - | body: | |||
| 8 | header: | 6 | header: | ||
| 9 | - | helloworld: Hello World! | 7 | + | someKey: Hello World! |
| 10 | - | div: | 8 | + | navmenu: |
| 11 | - | hello: Hello component loaded from a template! | 9 | + | dashboard: Dashboard |
| 10 | + | manager: Manager | |||
| 11 | + | settings: Settings | |||
| 12 | + | logout: Logout | |||
| 13 | + | dashboard: | |||
| 14 | + | meta: | |||
| 15 | + | title: Gordon - Admin Dashboard | |||
| 16 | + | content: | |||
| 17 | + | title: Dashboard | |||
| 18 | + | manager: | |||
| 19 | + | meta: | |||
| 20 | + | title: Gordon - Container Engine Manager | |||
| 21 | + | content: | |||
| 22 | + | title: Container Engine Manager | |||
| 23 | + | settings: | |||
| 24 | + | meta: | |||
| 25 | + | title: Gordon - Settings | |||
| 26 | + | content: | |||
| 27 | + | title: Settings | |||
| 12 | 28 | ||||
| 13 | fr: | 29 | fr: | ||
| 14 | - | header: | 30 | + | common: |
| 15 | meta: | 31 | meta: | ||
| 16 | - | title: Gordon - Tableau de bord administrateur | 32 | + | description: Interface d'administration pour Gordon |
| 17 | - | description: Tableau de bord administrateur pour Gordon | 33 | + | keywords: gordon, admin, interface |
| 18 | - | keywords: gordon, admin, dashboard | |||
| 19 | - | body: | |||
| 20 | header: | 34 | header: | ||
| 21 | - | helloworld: Bonjour le monde! | 35 | + | someKey: Bonjour le monde! |
| 22 | - | div: | 36 | + | navmenu: |
| 23 | - | hello: Composant de salut chargé à partir d'un modèle! | 37 | + | dashboard: Tableau de bord |
| 38 | + | manager: Gestionnaire | |||
| 39 | + | settings: Paramètres | |||
| 40 | + | logout: Déconnexion | |||
| 41 | + | dashboard: | |||
| 42 | + | meta: | |||
| 43 | + | title: Gordon - Tableau de bord | |||
| 44 | + | content: | |||
| 45 | + | title: Tableau de bord | |||
| 46 | + | manager: | |||
| 47 | + | meta: | |||
| 48 | + | title: Gordon - Gestionnaire de moteur de conteneur | |||
| 49 | + | content: | |||
| 50 | + | title: Gestionnaire de moteur de conteneur | |||
| 51 | + | settings: | |||
| 52 | + | meta: | |||
| 53 | + | title: Gordon - Paramètres | |||
| 54 | + | content: | |||
| 55 | + | title: Paramètres | |||
A internal/templates/render/localization.go +60 -0
| @@ -0,0 +1,60 @@ | |||
| 1 | + | package render | |
| 2 | + | ||
| 3 | + | import ( | |
| 4 | + | "fmt" | |
| 5 | + | ||
| 6 | + | "github.com/bnema/gordon/internal/app" | |
| 7 | + | "gopkg.in/yaml.v3" | |
| 8 | + | ) | |
| 9 | + | ||
| 10 | + | type Localizations map[string]map[string]map[string]interface{} | |
| 11 | + | ||
| 12 | + | // GetLocalization function returns the localization for the given language | |
| 13 | + | func GetLocalization(lang string, a *app.App) (map[string]interface{}, error) { | |
| 14 | + | // 1. Check if the language is supported | |
| 15 | + | supportedLanguages := []string{"en", "fr"} // Add more languages here | |
| 16 | + | if !contains(supportedLanguages, lang) { | |
| 17 | + | return nil, fmt.Errorf("language %s is not supported", lang) | |
| 18 | + | } | |
| 19 | + | ||
| 20 | + | fmt.Println("Language:", lang) | |
| 21 | + | ||
| 22 | + | // 2. Get the localizations from the strings.yml file | |
| 23 | + | localizations, err := getLocalizations(a) | |
| 24 | + | if err != nil { | |
| 25 | + | return nil, fmt.Errorf("failed to get localizations: %w", err) | |
| 26 | + | } | |
| 27 | + | ||
| 28 | + | // 3. Return the localizations for the given language | |
| 29 | + | if localization, ok := localizations[lang]; ok { | |
| 30 | + | result := make(map[string]interface{}) | |
| 31 | + | for k, v := range localization { | |
| 32 | + | result[k] = v | |
| 33 | + | } | |
| 34 | + | return result, nil | |
| 35 | + | } | |
| 36 | + | ||
| 37 | + | return nil, fmt.Errorf("language %s is not available", lang) | |
| 38 | + | } | |
| 39 | + | ||
| 40 | + | // getLocalizations function returns the localizations from the strings.yml file | |
| 41 | + | func getLocalizations(a *app.App) (Localizations, error) { | |
| 42 | + | var loc Localizations | |
| 43 | + | ||
| 44 | + | err := yaml.Unmarshal(a.StringsYML, &loc) | |
| 45 | + | if err != nil { | |
| 46 | + | return loc, fmt.Errorf("failed to unmarshal strings.yml: %w", err) | |
| 47 | + | } | |
| 48 | + | ||
| 49 | + | return loc, nil | |
| 50 | + | } | |
| 51 | + | ||
| 52 | + | // Helper function to check if a slice contains a string | |
| 53 | + | func contains(slice []string, str string) bool { | |
| 54 | + | for _, v := range slice { | |
| 55 | + | if v == str { | |
| 56 | + | return true | |
| 57 | + | } | |
| 58 | + | } | |
| 59 | + | return false | |
| 60 | + | } | |
| @@ -0,0 +1,60 @@ | |||||
| 1 | + | package render | |||
| 2 | + | ||||
| 3 | + | import ( | |||
| 4 | + | "fmt" | |||
| 5 | + | ||||
| 6 | + | "github.com/bnema/gordon/internal/app" | |||
| 7 | + | "gopkg.in/yaml.v3" | |||
| 8 | + | ) | |||
| 9 | + | ||||
| 10 | + | type Localizations map[string]map[string]map[string]interface{} | |||
| 11 | + | ||||
| 12 | + | // GetLocalization function returns the localization for the given language | |||
| 13 | + | func GetLocalization(lang string, a *app.App) (map[string]interface{}, error) { | |||
| 14 | + | // 1. Check if the language is supported | |||
| 15 | + | supportedLanguages := []string{"en", "fr"} // Add more languages here | |||
| 16 | + | if !contains(supportedLanguages, lang) { | |||
| 17 | + | return nil, fmt.Errorf("language %s is not supported", lang) | |||
| 18 | + | } | |||
| 19 | + | ||||
| 20 | + | fmt.Println("Language:", lang) | |||
| 21 | + | ||||
| 22 | + | // 2. Get the localizations from the strings.yml file | |||
| 23 | + | localizations, err := getLocalizations(a) | |||
| 24 | + | if err != nil { | |||
| 25 | + | return nil, fmt.Errorf("failed to get localizations: %w", err) | |||
| 26 | + | } | |||
| 27 | + | ||||
| 28 | + | // 3. Return the localizations for the given language | |||
| 29 | + | if localization, ok := localizations[lang]; ok { | |||
| 30 | + | result := make(map[string]interface{}) | |||
| 31 | + | for k, v := range localization { | |||
| 32 | + | result[k] = v | |||
| 33 | + | } | |||
| 34 | + | return result, nil | |||
| 35 | + | } | |||
| 36 | + | ||||
| 37 | + | return nil, fmt.Errorf("language %s is not available", lang) | |||
| 38 | + | } | |||
| 39 | + | ||||
| 40 | + | // getLocalizations function returns the localizations from the strings.yml file | |||
| 41 | + | func getLocalizations(a *app.App) (Localizations, error) { | |||
| 42 | + | var loc Localizations | |||
| 43 | + | ||||
| 44 | + | err := yaml.Unmarshal(a.StringsYML, &loc) | |||
| 45 | + | if err != nil { | |||
| 46 | + | return loc, fmt.Errorf("failed to unmarshal strings.yml: %w", err) | |||
| 47 | + | } | |||
| 48 | + | ||||
| 49 | + | return loc, nil | |||
| 50 | + | } | |||
| 51 | + | ||||
| 52 | + | // Helper function to check if a slice contains a string | |||
| 53 | + | func contains(slice []string, str string) bool { | |||
| 54 | + | for _, v := range slice { | |||
| 55 | + | if v == str { | |||
| 56 | + | return true | |||
| 57 | + | } | |||
| 58 | + | } | |||
| 59 | + | return false | |||
| 60 | + | } | |||
D internal/webui/strings.go +0 -46
| @@ -1,46 +0,0 @@ | |||
| 1 | - | package webui | |
| 2 | - | ||
| 3 | - | import ( | |
| 4 | - | "fmt" | |
| 5 | - | "io/fs" | |
| 6 | - | ||
| 7 | - | "github.com/bnema/gordon/pkg/parser" | |
| 8 | - | ) | |
| 9 | - | ||
| 10 | - | type LangData map[string]interface{} | |
| 11 | - | ||
| 12 | - | type StringsModel struct { | |
| 13 | - | Header string `yaml:"header"` | |
| 14 | - | Subheader string `yaml:"subheader"` | |
| 15 | - | } | |
| 16 | - | type StringsYamlData struct { | |
| 17 | - | StringsModel StringsModel `yaml:"strings_model"` | |
| 18 | - | EN LangData `yaml:"en"` | |
| 19 | - | FR LangData `yaml:"fr"` | |
| 20 | - | CurrentLang LangData | |
| 21 | - | } | |
| 22 | - | ||
| 23 | - | // ReadStringsDataFromYAML reads the strings.yaml file and unmarshals it into the out interface | |
| 24 | - | func ReadStringsDataFromYAML(lang string, fs fs.FS, filename string, out interface{}) error { | |
| 25 | - | ||
| 26 | - | err := parser.ParseYAMLFile(fs, filename, out) | |
| 27 | - | if err != nil { | |
| 28 | - | return err | |
| 29 | - | } | |
| 30 | - | ||
| 31 | - | // If the type assertion succeeds, we set the appropriate language data | |
| 32 | - | if data, ok := out.(*StringsYamlData); ok { | |
| 33 | - | switch lang { | |
| 34 | - | case "en": | |
| 35 | - | data.CurrentLang = data.EN | |
| 36 | - | case "fr": | |
| 37 | - | data.CurrentLang = data.FR | |
| 38 | - | default: // default to English if no match | |
| 39 | - | data.CurrentLang = data.EN | |
| 40 | - | } | |
| 41 | - | } else { | |
| 42 | - | return fmt.Errorf("failed to assert type of out interface: %w", err) | |
| 43 | - | } | |
| 44 | - | ||
| 45 | - | return nil | |
| 46 | - | } | |
| @@ -1,46 +0,0 @@ | |||||
| 1 | - | package webui | |||
| 2 | - | ||||
| 3 | - | import ( | |||
| 4 | - | "fmt" | |||
| 5 | - | "io/fs" | |||
| 6 | - | ||||
| 7 | - | "github.com/bnema/gordon/pkg/parser" | |||
| 8 | - | ) | |||
| 9 | - | ||||
| 10 | - | type LangData map[string]interface{} | |||
| 11 | - | ||||
| 12 | - | type StringsModel struct { | |||
| 13 | - | Header string `yaml:"header"` | |||
| 14 | - | Subheader string `yaml:"subheader"` | |||
| 15 | - | } | |||
| 16 | - | type StringsYamlData struct { | |||
| 17 | - | StringsModel StringsModel `yaml:"strings_model"` | |||
| 18 | - | EN LangData `yaml:"en"` | |||
| 19 | - | FR LangData `yaml:"fr"` | |||
| 20 | - | CurrentLang LangData | |||
| 21 | - | } | |||
| 22 | - | ||||
| 23 | - | // ReadStringsDataFromYAML reads the strings.yaml file and unmarshals it into the out interface | |||
| 24 | - | func ReadStringsDataFromYAML(lang string, fs fs.FS, filename string, out interface{}) error { | |||
| 25 | - | ||||
| 26 | - | err := parser.ParseYAMLFile(fs, filename, out) | |||
| 27 | - | if err != nil { | |||
| 28 | - | return err | |||
| 29 | - | } | |||
| 30 | - | ||||
| 31 | - | // If the type assertion succeeds, we set the appropriate language data | |||
| 32 | - | if data, ok := out.(*StringsYamlData); ok { | |||
| 33 | - | switch lang { | |||
| 34 | - | case "en": | |||
| 35 | - | data.CurrentLang = data.EN | |||
| 36 | - | case "fr": | |||
| 37 | - | data.CurrentLang = data.FR | |||
| 38 | - | default: // default to English if no match | |||
| 39 | - | data.CurrentLang = data.EN | |||
| 40 | - | } | |||
| 41 | - | } else { | |||
| 42 | - | return fmt.Errorf("failed to assert type of out interface: %w", err) | |||
| 43 | - | } | |||
| 44 | - | ||||
| 45 | - | return nil | |||
| 46 | - | } | |||
M pkg/htmx/htmlfragments.go +1 -1
| @@ -6,7 +6,7 @@ import ( | |||
| 6 | 6 | ||
| 7 | 7 | "github.com/PuerkitoBio/goquery" | |
| 8 | 8 | "github.com/bnema/gordon/internal/app" | |
| 9 | - | "github.com/bnema/gordon/internal/gotemplate/render" | |
| 9 | + | "github.com/bnema/gordon/internal/templates/render" | |
| 10 | 10 | ) | |
| 11 | 11 | ||
| 12 | 12 | // GetHTMLFragmentByID returns the HTML fragment with the specified id | |
| @@ -6,7 +6,7 @@ import ( | |||||
| 6 | 6 | ||||
| 7 | "github.com/PuerkitoBio/goquery" | 7 | "github.com/PuerkitoBio/goquery" | ||
| 8 | "github.com/bnema/gordon/internal/app" | 8 | "github.com/bnema/gordon/internal/app" | ||
| 9 | - | "github.com/bnema/gordon/internal/gotemplate/render" | 9 | + | "github.com/bnema/gordon/internal/templates/render" |
| 10 | ) | 10 | ) | ||
| 11 | 11 | ||||
| 12 | // GetHTMLFragmentByID returns the HTML fragment with the specified id | 12 | // GetHTMLFragmentByID returns the HTML fragment with the specified id | ||