aboutsummaryrefslogtreecommitdiffhomepage
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/app.go16
-rw-r--r--src/email/email.go14
-rw-r--r--src/http.go6
-rw-r--r--src/i18n/i18n.go68
-rw-r--r--src/templates.go6
-rw-r--r--src/templates/coins-designs-ee.html.tmpl155
-rw-r--r--src/templates/language.html.tmpl5
7 files changed, 221 insertions, 49 deletions
diff --git a/src/app.go b/src/app.go
new file mode 100644
index 0000000..7b3d905
--- /dev/null
+++ b/src/app.go
@@ -0,0 +1,16 @@
+package app
+
+import (
+ "os"
+ "syscall"
+
+ "git.thomasvoss.com/euro-cash.eu/pkg/atexit"
+ . "git.thomasvoss.com/euro-cash.eu/pkg/try"
+)
+
+func Restart() {
+ path := Try2(os.Executable())
+ atexit.Exec()
+ Try(syscall.Exec(path, append([]string{path}, os.Args[1:]...),
+ os.Environ()))
+}
diff --git a/src/email/email.go b/src/email/email.go
index 33b30e0..a1f7f0b 100644
--- a/src/email/email.go
+++ b/src/email/email.go
@@ -6,6 +6,8 @@ import (
"crypto/tls"
"fmt"
"math/rand/v2"
+ "log"
+ "errors"
"net/smtp"
"strconv"
"time"
@@ -29,14 +31,20 @@ Message-ID: <%s>
%s`
-func ServerError(fault error) error {
+func Send(subject, body string) {
+ if err := send(subject, body); err != nil {
+ log.Print(err)
+ }
+}
+
+func send(subject, body string) error {
if Config.Disabled {
- return fault
+ return errors.New(body)
}
msgid := strconv.FormatInt(rand.Int64(), 10) + "@" + Config.Host
msg := fmt.Sprintf(emailTemplate, Config.FromAddr, Config.ToAddr,
- "Error Report", time.Now().Format(time.RFC1123Z), msgid, fault)
+ subject, time.Now().Format(time.RFC1123Z), msgid, body)
tlsConfig := &tls.Config{
InsecureSkipVerify: false,
diff --git a/src/http.go b/src/http.go
index b785bca..b0d5bcd 100644
--- a/src/http.go
+++ b/src/http.go
@@ -179,11 +179,7 @@ func setUserLanguage(w http.ResponseWriter, r *http.Request) {
func throwError(status int, err error, w http.ResponseWriter, r *http.Request) {
w.WriteHeader(status)
- go func() {
- if err := email.ServerError(err); err != nil {
- log.Println(err)
- }
- }()
+ go email.Send("Server Error", err.Error())
errorTmpl.Execute(w, struct {
Code int
Msg string
diff --git a/src/i18n/i18n.go b/src/i18n/i18n.go
index b996681..cf82630 100644
--- a/src/i18n/i18n.go
+++ b/src/i18n/i18n.go
@@ -72,7 +72,7 @@ var (
Name: "Ελληνικά",
DateFormat: "2/1/2006",
Eurozone: true,
- Enabled: true,
+ Enabled: false,
GroupSeparator: '.',
DecimalSeparator: ',',
MonetaryPre: [2]string{"", "-"},
@@ -212,7 +212,7 @@ var (
Name: "Nederlands",
DateFormat: "2-1-2006",
Eurozone: true,
- Enabled: true,
+ Enabled: false,
GroupSeparator: '.',
DecimalSeparator: ',',
MonetaryPre: [2]string{"€ ", "€ -"},
@@ -254,7 +254,7 @@ var (
Name: "Svenska",
DateFormat: "2006-01-02",
Eurozone: true,
- Enabled: false,
+ Enabled: true,
GroupSeparator: ' ',
DecimalSeparator: ',',
MonetaryPre: [2]string{"", "-"},
@@ -266,23 +266,13 @@ var (
Name: "Български",
DateFormat: "2.01.2006 г.",
Eurozone: false, /* TODO(2026): Set to true */
- Enabled: true,
+ Enabled: false,
GroupSeparator: ' ',
DecimalSeparator: ',',
MonetaryPre: [2]string{"", "-"},
MonetaryPost: " €",
},
{
- Bcp: "en-US",
- Name: "English (US)",
- DateFormat: "1/2/2006",
- Eurozone: false,
- Enabled: false,
- GroupSeparator: ',',
- DecimalSeparator: '.',
- MonetaryPre: [2]string{"€", "-€"},
- },
- {
Bcp: "ro",
Name: "Română",
DateFormat: "02.01.2006",
@@ -305,9 +295,6 @@ var (
MonetaryPost: " €",
},
}
- /* 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
)
@@ -317,7 +304,9 @@ func Init() {
if !li.Enabled {
continue
}
- Printers[li.Bcp] = Printer{li, gotext.NewLocale("po", li.Bcp)}
+ gl := gotext.NewLocale("po", li.Bcp)
+ gl.AddDomain("messages")
+ Printers[li.Bcp] = Printer{li, gl}
}
DefaultPrinter = Printers["en"]
@@ -340,10 +329,35 @@ func (l LocaleInfo) Language() string {
return l.Bcp[:2]
}
+func (p Printer) Itoa(n int) string {
+ var bob strings.Builder
+ writeInt(&bob, n, p.LocaleInfo)
+ return bob.String()
+}
+
+func (p Printer) Ftoa(n float64) string {
+ var bob strings.Builder
+ writeFloat(&bob, n, p.LocaleInfo)
+ return bob.String()
+}
+
+func (p Printer) Mitoa(n int) string {
+ var bob strings.Builder
+ sprintfm(p.LocaleInfo, &bob, n)
+ return bob.String()
+}
+
+func (p Printer) Mftoa(n float64) string {
+ var bob strings.Builder
+ sprintfm(p.LocaleInfo, &bob, n)
+ return bob.String()
+}
+
func (p Printer) Sprintf(format string, args ...map[string]any) string {
var bob strings.Builder
vars := map[string]any{
"-": "a",
+ "Null": "",
}
for _, arg := range args {
maps.Copy(vars, arg)
@@ -410,9 +424,9 @@ func sprintfGeneric(li LocaleInfo, bob *strings.Builder, v any) error {
case time.Time:
htmlesc(bob, v.(time.Time).Format(li.DateFormat))
case int:
- writeInt(bob, v.(int), li.GroupSeparator)
+ writeInt(bob, v.(int), li)
case float64:
- writeFloat(bob, v.(float64), li.GroupSeparator, li.DecimalSeparator)
+ writeFloat(bob, v.(float64), li)
case string:
htmlesc(bob, v.(string))
default:
@@ -474,12 +488,12 @@ func sprintfm(li LocaleInfo, bob *strings.Builder, v any) error {
case int:
n := v.(int)
htmlesc(bob, li.MonetaryPre[btoi(n >= 0)])
- writeInt(bob, abs(n), li.GroupSeparator)
+ writeInt(bob, abs(n), li)
htmlesc(bob, li.MonetaryPost)
case float64:
n := v.(float64)
htmlesc(bob, li.MonetaryPre[btoi(n >= 0)])
- writeFloat(bob, abs(n), li.GroupSeparator, li.DecimalSeparator)
+ writeFloat(bob, abs(n), li)
htmlesc(bob, li.MonetaryPost)
default:
return errors.New("TODO")
@@ -496,7 +510,7 @@ func sprintfr(li LocaleInfo, bob *strings.Builder, v any) error {
return nil
}
-func writeInt(bob *strings.Builder, num int, sep rune) {
+func writeInt(bob *strings.Builder, num int, li LocaleInfo) {
s := fmt.Sprintf("%d", num)
if s[0] == '-' {
bob.WriteByte('-')
@@ -511,13 +525,13 @@ func writeInt(bob *strings.Builder, num int, sep rune) {
c++
bob.WriteByte(s[i])
if c == 3 && i+1 < n {
- bob.WriteRune(sep)
+ bob.WriteRune(li.GroupSeparator)
c = 0
}
}
}
-func writeFloat(bob *strings.Builder, num float64, tsep, dsep rune) {
+func writeFloat(bob *strings.Builder, num float64, li LocaleInfo) {
s := fmt.Sprintf("%.2f", num)
if s[0] == '-' {
bob.WriteByte('-')
@@ -533,12 +547,12 @@ func writeFloat(bob *strings.Builder, num float64, tsep, dsep rune) {
c++
bob.WriteByte(s[i])
if c == 3 && i+1 < n {
- bob.WriteRune(tsep)
+ bob.WriteRune(li.GroupSeparator)
c = 0
}
}
- bob.WriteRune(dsep)
+ bob.WriteRune(li.DecimalSeparator)
bob.WriteString(s[n+1:])
}
diff --git a/src/templates.go b/src/templates.go
index 20fcf79..353c755 100644
--- a/src/templates.go
+++ b/src/templates.go
@@ -46,6 +46,12 @@ func BuildTemplates(dir fs.FS, debugp bool) {
buildAndSetTemplate(dir, name)
if debugp {
go watch.FileFS(dir, name, func() {
+ defer func() {
+ if p := recover(); p != nil {
+ log.Print(p)
+ }
+ }()
+
buildAndSetTemplate(dir, name)
log.Printf("Template ‘%s’ updated\n", name)
})
diff --git a/src/templates/coins-designs-ee.html.tmpl b/src/templates/coins-designs-ee.html.tmpl
index 0624931..a6a62e9 100644
--- a/src/templates/coins-designs-ee.html.tmpl
+++ b/src/templates/coins-designs-ee.html.tmpl
@@ -1,18 +1,155 @@
{{ define "content" }}
<header>
{{ template "navbar" . }}
- <h1>{{ .Get "German Euro Coin Designs" }}</h1>
+ <h1>{{ .Get "Estonian Euro Coin Designs" }}</h1>
</header>
<main>
- <p>
- {{ .Get "The Estonian euro coins all feature the same design across all eight denominations. The country’s outline is displayed above the text “EESTI”, the country’s name in its own language." }}
- </p>
- <div class="design-container">
- <img
+ <div class="design-container">
+ <img
alt="{{ .Get `Estonian €1 coin` }}"
src="/designs/ee-100-1.avif"
>
- </div>
-</main>
-{{ end }}
+ </div>
+ <p>
+ {{ .Get "The Estonian euro coins feature the same design across all eight denominations. The country’s outline is prominently displayed above the country’s name in Estonian (‘{EstonianStart:r}EESTI{EstonianEnd:E}’)."
+ (map "EstonianStart" `<span lang="et"><em>` "EstonianEnd" "em,span") }}
+ </p>
+ <p>
+ {{ .Get "The design of the Estonian euro coins was chosen as part of a {EVLink:L}Eurovision{-:E}-style public televote where it competed and won against 9 other designs."
+ (map "EVLink" "https://en.wikipedia.org/wiki/Eurovision_Song_Contest" ) }}
+ </p>
+ <p>
+ {{ .Get "In June 2024 a public design competition was announced with a deadline for the 19th of October. In total 134 designs were submitted by the deadline and 10 designs were selected by a jury. These 10 designs were then voted on in a public vote over the course of one week. In total 45,453 people voted and the current design won with a total of 12,482 votes (27.46%)." }}
+ </p>
+ <p>
+ {{ .Get "The winner of the contest was awarded 50,000 KR (€3,196) while the other finalists were each awarded 20,000 KR (€1,278)." }}
+ </p>
+
+ <!-- TODO: Add images of the other designs: https://web.archive.org/web/20070704210956/http://www.eestipank.info/pub/et/majandus/euroopaliit/euro/kavand/_1kava.html -->
+ <!-- Good description of one of the designs: http://www.worldofcoins.eu/forum/index.php/topic,21902.15.html?PHPSESSID=pc4qnnd3ng4etv8fp75u41erb1 -->
+ <table>
+ <thead>
+ <tr>
+ <th data-numeric style="width: 1%">{{ .Get "Position" }}</th>
+ <th>{{ .Get "Name" }}<br>{{ .Get "Translation" }}</th>
+ <th>{{ .Get "Author(s)" }}</th>
+ <th data-numeric>{{ .Get "Votes" }}</th>
+ <th data-numeric>{{ .Get "Votes (%)" }}</th>
+ </tr>
+ </thead>
+ <tbody>
+ {{ $args := (map "Break" "<br>") }}
+ <tr>
+ <td data-numeric>1</td>
+ {{/* TRANSLATORS: This is a place name. If you’re translating for a latin-script language, translate this to ‘{Null}’, otherwise transliterate this into your respective alphabet. */}}
+ <td>Hara 2{{ .Get "{Break:r}Hara 2" $args }}</td>
+ <td>{{ .Get "Lembit Lõhmus" }}</td>
+ <td data-numeric>{{ .Printer.Itoa 12482 }}</td>
+ <td data-numeric>{{ .Printer.Ftoa 27.46 }}</td>
+ </tr>
+ <tr>
+ <td data-numeric>2</td>
+ <td>
+ <span lang="et">Järjepidevus</span>
+ {{/* TRANSLATORS: Estonian translators should replace the entire translation with ‘{Null}’ */}}
+ {{ .Get "{Break:r}Consistency" $args }}
+ </td>
+ <td>{{ .Get "Tiit Jürna" }}</td>
+ <td data-numeric>{{ .Printer.Itoa 7477 }}</td>
+ <td data-numeric>{{ .Printer.Ftoa 16.45 }}</td>
+ </tr>
+ <tr>
+ <td data-numeric>3</td>
+ <td>
+ <span lang="la">In corpore</span>
+ {{ .Get "{Break:r}In the Body" $args }}
+ </td>
+ <td>{{ .Get "Jaan Meristo" }}</td>
+ <td data-numeric>{{ .Printer.Itoa 7284 }}</td>
+ <td data-numeric>{{ .Printer.Ftoa 16.03 }}</td>
+ </tr>
+ <tr>
+ <td data-numeric>4</td>
+ <td>
+ Tomson 5791
+ {{/* TRANSLATORS: This name. If you’re translating for a latin-script language, translate this to ‘{Null}’, otherwise transliterate this into your respective alphabet. */}}
+ {{ .Get "{Break:r}Tomson 5791" $args }}
+ </td>
+ <td>{{ .Get "Taavi Torim" }}</td>
+ <td data-numeric>{{ .Printer.Itoa 6219 }}</td>
+ <td data-numeric>{{ .Printer.Ftoa 13.68 }}</td>
+ </tr>
+ <tr>
+ <td data-numeric>5</td>
+ <td>
+ <span lang="et">Eesti keel</span>
+ {{/* TRANSLATORS: Estonian translators should replace the entire translation with ‘{Null}’ */}}
+ {{ .Get "{Break:r}Estonian" $args }}
+ </td>
+ <td>{{ .Get "Jaak Peep, Villem Valme" }}</td>
+ <td data-numeric>{{ .Printer.Itoa 5997 }}</td>
+ <td data-numeric>{{ .Printer.Ftoa 13.19 }}</td>
+ </tr>
+ <tr>
+ <td data-numeric>6</td>
+ <td>261948</td>
+ <td>{{ .Get "Mai Järmut, Villu Järmut" }}</td>
+ <td data-numeric>{{ .Printer.Itoa 3036 }}</td>
+ <td data-numeric>{{ .Printer.Ftoa 6.68 }}</td>
+ </tr>
+ <tr>
+ <td data-numeric>7</td>
+ <td>
+ <span lang="et">Linnutee</span>
+ {{/* TRANSLATORS: Estonian translators should replace the entire translation with ‘{Null}’ */}}
+ {{ .Get "{Break:r}Bird Road" $args }}
+ </td>
+ <td>{{ .Get "Tiit Jürna" }}</td>
+ <td data-numeric>{{ .Printer.Itoa 1323 }}</td>
+ <td data-numeric>{{ .Printer.Ftoa 2.91 }}</td>
+ </tr>
+ <tr>
+ <td data-numeric>8</td>
+ <td>
+ <span lang="et">Leopardid-2</span>
+ {{/* TRANSLATORS: Estonian translators should replace the entire translation with ‘{Null}’ */}}
+ {{ .Get "{Break:r}Leopards-2" $args }}
+ </td>
+ <td>{{ .Get "Jaarno Ester" }}</td>
+ <td data-numeric>{{ .Printer.Itoa 759 }}</td>
+ <td data-numeric>{{ .Printer.Ftoa 1.67 }}</td>
+ </tr>
+ <tr>
+ <td data-numeric>9</td>
+ <td>
+ Nova
+ {{/* TRANSLATORS: This name. If you’re translating for a latin-script language, translate this to ‘{Null}’, otherwise transliterate this into your respective alphabet. */}}
+ {{ .Get "{Break:r}Nova" $args }}
+ </td>
+ <td>{{ .Get "Rene Haljasmäe" }}</td>
+ <td data-numeric>{{ .Printer.Itoa 498 }}</td>
+ <td data-numeric>{{ .Printer.Ftoa 1.1 }}</td>
+ </tr>
+ <tr>
+ <td data-numeric>10</td>
+ <td>
+ <span lang="et">Lill rukkis</span>
+ {{/* TRANSLATORS: Estonian translators should replace the entire translation with ‘{Null}’ */}}
+ {{ .Get "{Break:r}A Flower in the Rye" $args }}
+ </td>
+ <td>{{ .Get "Margus Kadarik" }}</td>
+ <td data-numeric>{{ .Printer.Itoa 378 }}</td>
+ <td data-numeric>{{ .Printer.Ftoa 0.83 }}</td>
+ </tr>
+ </tbody>
+ <tfoot>
+ <tr>
+ <th colspan="3">{{ .Get "Total" }}</th>
+ <th data-numeric>{{ .Printer.Itoa 45453 }}</th>
+ <th data-numeric>{{ .Printer.Ftoa 100.0 }}</th>
+ </tr>
+ </tfoot>
+ </table>
+</main>
+{{ end }} \ No newline at end of file
diff --git a/src/templates/language.html.tmpl b/src/templates/language.html.tmpl
index f4b240a..13a8e85 100644
--- a/src/templates/language.html.tmpl
+++ b/src/templates/language.html.tmpl
@@ -7,11 +7,6 @@
<p>
{{ .Get "Select your preferred language to use on the site." }}
</p>
- <p>
- If you are an American user, it’s suggested that you select
- American English instead of British English. This will ensure that
- dates will be formatted with the month before the day.
- </p>
<hr />
<h2>{{ .Get "Eurozone Languages" }}</h2>
{{ template "langgrid" true }}