22// colour and logo. The name and accent live in the settings table (so they ride
33// along in the SQLite backup); a custom logo is a file under <dataDir>/branding/
44// (which the data-dir tar backup already captures). When nothing is configured
5- // the built-in РосПанель defaults apply.
5+ // the built-in RosPanel defaults apply.
66package branding
77
88import (
@@ -20,12 +20,14 @@ import (
2020 "regexp"
2121 "strconv"
2222 "strings"
23+
24+ "github.com/AppsGanin/rospanel/internal/model"
2325)
2426
2527//go:embed default-logo.svg
2628var defaultLogoSVG []byte
2729
28- // DefaultLogo returns the built-in РосПанель logo (SVG).
30+ // DefaultLogo returns the built-in RosPanel logo (SVG).
2931func DefaultLogo () []byte { return defaultLogoSVG }
3032
3133const (
@@ -47,7 +49,7 @@ type Theme struct {
4749 Surface string `json:"surface"` // cards / inputs / panels
4850}
4951
50- // DefaultTheme is the stock РосПанель palette (Госуслуги -style blue on a soft
52+ // DefaultTheme is the stock RosPanel palette (Gosuslugi -style blue on a soft
5153// blue page with white surfaces).
5254func DefaultTheme () Theme {
5355 return Theme {
@@ -100,27 +102,30 @@ func pick(v, fallback string) string {
100102// NormalizeTheme validates each provided colour and returns the JSON to persist.
101103// Empty fields are dropped (⇒ default applies); a non-empty non-hex field errors.
102104func NormalizeTheme (t Theme ) (string , error ) {
103- clean := func (name , v string ) (string , error ) {
105+ // Which colour failed is part of the message, and it is a word — so each field
106+ // gets its own code rather than one code with the field name glued in: an
107+ // argument travels verbatim and would arrive at an English panel in Russian.
108+ clean := func (code , fallback , v string ) (string , error ) {
104109 v = strings .ToLower (strings .TrimSpace (v ))
105110 if v == "" || accentRe .MatchString (v ) {
106111 return v , nil
107112 }
108- return "" , fmt . Errorf ( "цвет «%s» должен быть в формате #RRGGBB" , name )
113+ return "" , model . FieldErr ( code , fallback )
109114 }
110115 var err error
111- if t .Accent , err = clean ("акцент" , t .Accent ); err != nil {
116+ if t .Accent , err = clean ("err.badColorAccent" , "цвет « акцент» должен быть в формате #RRGGBB " , t .Accent ); err != nil {
112117 return "" , err
113118 }
114- if t .Text , err = clean ("текст" , t .Text ); err != nil {
119+ if t .Text , err = clean ("err.badColorText" , "цвет « текст» должен быть в формате #RRGGBB " , t .Text ); err != nil {
115120 return "" , err
116121 }
117- if t .Muted , err = clean ("приглушённый текст" , t .Muted ); err != nil {
122+ if t .Muted , err = clean ("err.badColorMuted" , "цвет « приглушённый текст» должен быть в формате #RRGGBB " , t .Muted ); err != nil {
118123 return "" , err
119124 }
120- if t .Bg , err = clean ("фон" , t .Bg ); err != nil {
125+ if t .Bg , err = clean ("err.badColorBg" , "цвет « фон» должен быть в формате #RRGGBB " , t .Bg ); err != nil {
121126 return "" , err
122127 }
123- if t .Surface , err = clean ("поверхность" , t .Surface ); err != nil {
128+ if t .Surface , err = clean ("err.badColorSurface" , "цвет « поверхность» должен быть в формате #RRGGBB " , t .Surface ); err != nil {
124129 return "" , err
125130 }
126131 if t == (Theme {}) {
@@ -229,17 +234,17 @@ func SaveLogo(dataDir string, r io.Reader) error {
229234 return err
230235 }
231236 if len (b ) == 0 {
232- return fmt . Errorf ( "пустой файл" )
237+ return model . FieldErr ( "err.emptyFile" , "пустой файл" )
233238 }
234239 if len (b ) > MaxLogoBytes {
235- return fmt . Errorf ( " логотип больше %d КБ" , MaxLogoBytes >> 10 )
240+ return model . FieldErr ( "err.logoTooBig" , " логотип больше {{kb}} КБ" , map [ string ] any { "kb" : MaxLogoBytes >> 10 } )
236241 }
237242 cfg , format , err := image .DecodeConfig (bytes .NewReader (b ))
238243 if err != nil || (format != "png" && format != "jpeg" ) {
239- return fmt . Errorf ( "нужен PNG или JPEG" )
244+ return model . FieldErr ( "err.needPngJpeg" , "нужен PNG или JPEG" )
240245 }
241246 if cfg .Width > maxLogoDim || cfg .Height > maxLogoDim {
242- return fmt . Errorf ( " изображение больше %d×%d пикселей" , maxLogoDim , maxLogoDim )
247+ return model . FieldErr ( "err.imageTooLarge" , " изображение больше {{dim}}×{{dim}} пикселей" , map [ string ] any { "dim" : maxLogoDim } )
243248 }
244249 if err := os .MkdirAll (brandingDir (dataDir ), 0o700 ); err != nil {
245250 return err
0 commit comments