From 10f6c76b081a5cb38f85950f253baf7934ce7cfe Mon Sep 17 00:00:00 2001 From: Thomas Voss Date: Wed, 7 Aug 2024 15:25:20 +0200 Subject: Support date and currency formatting --- i18n/printer.go | 236 ++++++++++++++++++++++++++++++++++++++++++++++++ i18n/translations.go | 181 ------------------------------------- templates/root.templ | 9 +- templates/root_templ.go | 39 ++++---- 4 files changed, 263 insertions(+), 202 deletions(-) create mode 100644 i18n/printer.go delete mode 100644 i18n/translations.go diff --git a/i18n/printer.go b/i18n/printer.go new file mode 100644 index 0000000..2425f98 --- /dev/null +++ b/i18n/printer.go @@ -0,0 +1,236 @@ +package i18n + +import ( + "fmt" + "strings" + "time" + + "golang.org/x/text/language" + "golang.org/x/text/message" +) + +//go:generate gotext -srclang=en-GB update -out=catalog.go -lang=en-GB,nl-NL git.thomasvoss.com/euro-cash.eu + +type Printer struct { + Locale Locale + printer *message.Printer +} + +type Locale struct { + Code string + Name string + dateFmt string + moneyFmt string + Eurozone bool + Enabled bool +} + +var ( + Locales = [...]Locale{ + Locale{ + Code: "ca-AD", + Name: "català", + dateFmt: "2/1/2006", + Eurozone: true, + Enabled: false, + }, + Locale{ + Code: "de-DE", + Name: "Deutsch", + dateFmt: "2.1.2006", + Eurozone: true, + Enabled: false, + }, + Locale{ + Code: "el-GR", + Name: "ελληνικά", + dateFmt: "2/1/2006", + Eurozone: true, + Enabled: false, + }, + Locale{ + Code: "en-GB", + Name: "English", + dateFmt: "02/01/2006", + Eurozone: true, + Enabled: true, + }, + Locale{ + Code: "es-ES", + Name: "español", + dateFmt: "2/1/2006", + Eurozone: true, + Enabled: false, + }, + Locale{ + Code: "et-EE", + Name: "eesti", + dateFmt: "2.1.2006", + Eurozone: true, + Enabled: false, + }, + Locale{ + Code: "fi-FI", + Name: "suomi", + dateFmt: "2.1.2006", + Eurozone: true, + Enabled: false, + }, + Locale{ + Code: "fr-FR", + Name: "français", + dateFmt: "02/01/2006", + Eurozone: true, + Enabled: false, + }, + Locale{ + Code: "ga-IE", + Name: "Gaeilge", + dateFmt: "02/01/2006", + Eurozone: true, + Enabled: false, + }, + Locale{ + Code: "hr-HR", + Name: "hrvatski", + dateFmt: "02. 01. 2006.", + Eurozone: true, + Enabled: false, + }, + Locale{ + Code: "it-IT", + Name: "italiano", + dateFmt: "02/01/2006", + Eurozone: true, + Enabled: false, + }, + Locale{ + Code: "lt-LT", + Name: "lietuvių", + dateFmt: "2006-01-02", + Eurozone: true, + Enabled: false, + }, + Locale{ + Code: "lv-LV", + Name: "latviešu", + dateFmt: "2.01.2006.", + Eurozone: true, + Enabled: false, + }, + Locale{ + Code: "mt-MT", + Name: "Malti", + dateFmt: "2/1/2006", + Eurozone: true, + Enabled: false, + }, + Locale{ + Code: "nl-NL", + Name: "Nederlands", + dateFmt: "2-1-2006", + Eurozone: true, + Enabled: true, + }, + Locale{ + Code: "pt-PT", + Name: "português", + dateFmt: "02/01/2006", + Eurozone: true, + Enabled: false, + }, + Locale{ + Code: "sk-SK", + Name: "slovenčina", + dateFmt: "2. 1. 2006", + Eurozone: true, + Enabled: false, + }, + Locale{ + Code: "sl-SI", + Name: "slovenščina", + dateFmt: "2. 1. 2006", + Eurozone: true, + Enabled: false, + }, + + /* Non-Eurozone locales */ + Locale{ + Code: "bg-BG", + Name: "български", + dateFmt: "2.01.2006 г.", + Eurozone: false, + Enabled: false, + }, + Locale{ + Code: "en-US", + Name: "English (US)", + dateFmt: "1/2/2006", + Eurozone: false, + Enabled: false, + }, + Locale{ + Code: "ro-RO", + Name: "română", + dateFmt: "02.01.2006", + Eurozone: false, + Enabled: false, + }, + } + /* Map of language codes to printers. We do this instead of just + using language.MustParse() directly so that we can easily see if a + language is supported or not. */ + Printers map[string]Printer = make(map[string]Printer, len(Locales)) + DefaultPrinter Printer +) + +func InitPrinters() { + for _, loc := range Locales { + if loc.Enabled { + lang := language.MustParse(loc.Code) + Printers[strings.ToLower(loc.Code)] = Printer{ + Locale: loc, + printer: message.NewPrinter(lang), + } + } + } + DefaultPrinter = Printers["en-gb"] +} + +func (p Printer) T(fmt string, args ...any) string { + return p.printer.Sprintf(fmt, args...) +} + +func (p Printer) Date(d time.Time) string { + return d.Format(p.Locale.dateFmt) +} + +/* TODO: Try to use a decimal type here */ +func (p Printer) Money(val float64, round bool) string { + var valstr string + + /* Hack to avoid gotext writing these two ‘translations’ into the + translations file */ + f := p.printer.Sprintf + if round { + valstr = f("%d", int(val)) + } else { + valstr = f("%.2f", val) + } + + /* All Eurozone languages place the eurosign after the value except + for Dutch, English, Gaelic, and Maltese. Austrian German also + uses Dutch-style formatting, but we do not support that dialect. */ + switch p.Locale.Code { + case "en-GB", "en-US", "ga-IE", "mt-MT": + return fmt.Sprintf("€%s", valstr) + case "nl-NL": + return fmt.Sprintf("€ %s", valstr) + default: + return fmt.Sprintf("%s €", valstr) + } +} + +func (l Locale) Language() string { + return l.Code[:2] +} diff --git a/i18n/translations.go b/i18n/translations.go deleted file mode 100644 index 1d35f98..0000000 --- a/i18n/translations.go +++ /dev/null @@ -1,181 +0,0 @@ -package i18n - -import ( - "strings" - - "golang.org/x/text/language" - "golang.org/x/text/message" -) - -//go:generate gotext -srclang=en-GB update -out=catalog.go -lang=en-GB,nl-NL git.thomasvoss.com/euro-cash.eu - -type Printer struct { - Lang string - printer *message.Printer -} - -type Locale struct { - Code string - Name string - Eurozone bool - Enabled bool -} - -var ( - Locales = [...]Locale{ - Locale{ - Code: "ca-AD", - Name: "català", - Eurozone: true, - Enabled: false, - }, - Locale{ - Code: "de-DE", - Name: "Deutsch", - Eurozone: true, - Enabled: false, - }, - Locale{ - Code: "el-GR", - Name: "ελληνικά", - Eurozone: true, - Enabled: false, - }, - Locale{ - Code: "en-GB", - Name: "English", - Eurozone: true, - Enabled: true, - }, - Locale{ - Code: "es-ES", - Name: "español", - Eurozone: true, - Enabled: false, - }, - Locale{ - Code: "et-EE", - Name: "eesti", - Eurozone: true, - Enabled: false, - }, - Locale{ - Code: "fi-FI", - Name: "suomi", - Eurozone: true, - Enabled: false, - }, - Locale{ - Code: "fr-FR", - Name: "français", - Eurozone: true, - Enabled: false, - }, - Locale{ - Code: "ga-IE", - Name: "Gaeilge", - Eurozone: true, - Enabled: false, - }, - Locale{ - Code: "hr-HR", - Name: "hrvatski", - Eurozone: true, - Enabled: false, - }, - Locale{ - Code: "it-IT", - Name: "italiano", - Eurozone: true, - Enabled: false, - }, - Locale{ - Code: "lt-LT", - Name: "lietuvių", - Eurozone: true, - Enabled: false, - }, - Locale{ - Code: "lv-LV", - Name: "latviešu", - Eurozone: true, - Enabled: false, - }, - Locale{ - Code: "mt-MT", - Name: "Malti", - Eurozone: true, - Enabled: false, - }, - Locale{ - Code: "nl-NL", - Name: "Nederlands", - Eurozone: true, - Enabled: true, - }, - Locale{ - Code: "pt-PT", - Name: "português", - Eurozone: true, - Enabled: false, - }, - Locale{ - Code: "sk-SK", - Name: "slovenčina", - Eurozone: true, - Enabled: false, - }, - Locale{ - Code: "sl-SI", - Name: "slovenščina", - Eurozone: true, - Enabled: false, - }, - - /* Non-Eurozone locales */ - Locale{ - Code: "bg-BG", - Name: "български", - Eurozone: false, - Enabled: false, - }, - Locale{ - Code: "en-US", - Name: "English (US)", - Eurozone: false, - Enabled: false, - }, - Locale{ - Code: "ro-RO", - Name: "română", - Eurozone: false, - Enabled: false, - }, - } - /* Map of language codes to printers. We do this instead of just - using language.MustParse() directly so that we can easily see if a - language is supported or not. */ - Printers map[string]Printer = make(map[string]Printer, len(Locales)) - DefaultPrinter Printer -) - -func InitPrinters() { - for _, loc := range Locales { - if loc.Enabled { - lang := language.MustParse(loc.Code) - Printers[strings.ToLower(loc.Code)] = Printer{ - Lang: loc.Code, - printer: message.NewPrinter(lang), - } - } - } - DefaultPrinter = Printers["en-gb"] -} - -func (p Printer) T(fmt string, args ...any) string { - return p.printer.Sprintf(fmt, args...) -} - -func (l Locale) Language() string { - return l.Code[:2] -} diff --git a/templates/root.templ b/templates/root.templ index 0ea0ad1..d295abb 100644 --- a/templates/root.templ +++ b/templates/root.templ @@ -1,13 +1,16 @@ package templates -import "strings" -import "git.thomasvoss.com/euro-cash.eu/i18n" +import ( + "strings" + + "git.thomasvoss.com/euro-cash.eu/i18n" +) templ Root(head, body templ.Component) { {{ p := ctx.Value("printer").(i18n.Printer) }} - + diff --git a/templates/root_templ.go b/templates/root_templ.go index b7b62b6..30a881f 100644 --- a/templates/root_templ.go +++ b/templates/root_templ.go @@ -8,8 +8,11 @@ package templates import "github.com/a-h/templ" import templruntime "github.com/a-h/templ/runtime" -import "strings" -import "git.thomasvoss.com/euro-cash.eu/i18n" +import ( + "strings" + + "git.thomasvoss.com/euro-cash.eu/i18n" +) func Root(head, body templ.Component) templ.Component { return templruntime.GeneratedTemplate(func(templ_7745c5c3_Input templruntime.GeneratedComponentInput) (templ_7745c5c3_Err error) { @@ -35,9 +38,9 @@ func Root(head, body templ.Component) templ.Component { return templ_7745c5c3_Err } var templ_7745c5c3_Var2 string - templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(p.Lang) + templ_7745c5c3_Var2, templ_7745c5c3_Err = templ.JoinStringErrs(p.Locale.Code) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 10, Col: 20} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 13, Col: 27} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var2)) if templ_7745c5c3_Err != nil { @@ -70,7 +73,7 @@ func Root(head, body templ.Component) templ.Component { var templ_7745c5c3_Var3 string templ_7745c5c3_Var3, templ_7745c5c3_Err = templ.JoinStringErrs(p.T("Found a mistake or want to contribute missing information?")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 27, Col: 73} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 30, Col: 73} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { @@ -83,7 +86,7 @@ func Root(head, body templ.Component) templ.Component { var templ_7745c5c3_Var4 string templ_7745c5c3_Var4, templ_7745c5c3_Err = templ.JoinStringErrs(p.T("Feel free to contact us!")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 28, Col: 51} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 31, Col: 51} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var4)) if templ_7745c5c3_Err != nil { @@ -131,7 +134,7 @@ func Index() templ.Component { var templ_7745c5c3_Var6 string templ_7745c5c3_Var6, templ_7745c5c3_Err = templ.JoinStringErrs(p.T("The Euro Cash Compendium")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 41, Col: 39} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 44, Col: 39} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var6)) if templ_7745c5c3_Err != nil { @@ -144,7 +147,7 @@ func Index() templ.Component { var templ_7745c5c3_Var7 string templ_7745c5c3_Var7, templ_7745c5c3_Err = templ.JoinStringErrs(p.T("United in")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 43, Col: 21} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 46, Col: 21} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var7)) if templ_7745c5c3_Err != nil { @@ -157,7 +160,7 @@ func Index() templ.Component { var templ_7745c5c3_Var8 string templ_7745c5c3_Var8, templ_7745c5c3_Err = templ.JoinStringErrs(p.T("diversity")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 44, Col: 26} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 47, Col: 26} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var8)) if templ_7745c5c3_Err != nil { @@ -170,7 +173,7 @@ func Index() templ.Component { var templ_7745c5c3_Var9 string templ_7745c5c3_Var9, templ_7745c5c3_Err = templ.JoinStringErrs(p.T("cash")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 45, Col: 21} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 48, Col: 21} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var9)) if templ_7745c5c3_Err != nil { @@ -218,7 +221,7 @@ func SetLanguage() templ.Component { var templ_7745c5c3_Var11 string templ_7745c5c3_Var11, templ_7745c5c3_Err = templ.JoinStringErrs(p.T("Select Your Language")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 55, Col: 35} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 58, Col: 35} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var11)) if templ_7745c5c3_Err != nil { @@ -231,7 +234,7 @@ func SetLanguage() templ.Component { var templ_7745c5c3_Var12 string templ_7745c5c3_Var12, templ_7745c5c3_Err = templ.JoinStringErrs(p.T("Select your preferred language to use on the site.")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 60, Col: 62} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 63, Col: 62} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var12)) if templ_7745c5c3_Err != nil { @@ -244,7 +247,7 @@ func SetLanguage() templ.Component { var templ_7745c5c3_Var13 string templ_7745c5c3_Var13, templ_7745c5c3_Err = templ.JoinStringErrs(p.T("Eurozone Languages")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 70, Col: 33} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 73, Col: 33} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var13)) if templ_7745c5c3_Err != nil { @@ -265,7 +268,7 @@ func SetLanguage() templ.Component { var templ_7745c5c3_Var14 string templ_7745c5c3_Var14, templ_7745c5c3_Err = templ.JoinStringErrs(p.T("Other Languages")) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 73, Col: 30} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 76, Col: 30} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var14)) if templ_7745c5c3_Err != nil { @@ -318,7 +321,7 @@ func languageGrid(eurozone bool) templ.Component { var templ_7745c5c3_Var16 string templ_7745c5c3_Var16, templ_7745c5c3_Err = templ.JoinStringErrs(loc.Code) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 86, Col: 22} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 89, Col: 22} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) if templ_7745c5c3_Err != nil { @@ -341,7 +344,7 @@ func languageGrid(eurozone bool) templ.Component { var templ_7745c5c3_Var17 string templ_7745c5c3_Var17, templ_7745c5c3_Err = templ.JoinStringErrs(loc.Code) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 90, Col: 22} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 93, Col: 22} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var17)) if templ_7745c5c3_Err != nil { @@ -354,7 +357,7 @@ func languageGrid(eurozone bool) templ.Component { var templ_7745c5c3_Var18 string templ_7745c5c3_Var18, templ_7745c5c3_Err = templ.JoinStringErrs(strings.ToUpper(loc.Language())) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 91, Col: 50} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 94, Col: 50} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var18)) if templ_7745c5c3_Err != nil { @@ -367,7 +370,7 @@ func languageGrid(eurozone bool) templ.Component { var templ_7745c5c3_Var19 string templ_7745c5c3_Var19, templ_7745c5c3_Err = templ.JoinStringErrs(loc.Name) if templ_7745c5c3_Err != nil { - return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 93, Col: 17} + return templ.Error{Err: templ_7745c5c3_Err, FileName: `root.templ`, Line: 96, Col: 17} } _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var19)) if templ_7745c5c3_Err != nil { -- cgit v1.2.3