aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/i18n
diff options
context:
space:
mode:
Diffstat (limited to 'src/i18n')
-rw-r--r--src/i18n/i18n.go196
1 files changed, 101 insertions, 95 deletions
diff --git a/src/i18n/i18n.go b/src/i18n/i18n.go
index 62d836f..3bc490f 100644
--- a/src/i18n/i18n.go
+++ b/src/i18n/i18n.go
@@ -3,6 +3,7 @@ package i18n
import (
"errors"
"fmt"
+ "maps"
"strings"
"time"
"unicode/utf8"
@@ -264,19 +265,11 @@ func Locales() []LocaleInfo {
}
func (p Printer) Get(fmt string, args ...map[string]any) string {
- /* TODO: Warning if you pass more than 1 arg? */
- var m map[string]any
- if len(args) == 0 {
- m = make(map[string]any)
- } else {
- m = args[0]
- }
-
- return p.Sprintf(p.inner.Get(fmt), m)
+ return p.Sprintf(p.inner.Get(fmt), args...)
}
-func (p Printer) GetN(fmtS, fmtP string, n int, args map[string]any) string {
- return p.Sprintf(p.inner.GetN(fmtS, fmtP, n), args)
+func (p Printer) GetN(fmtS, fmtP string, n int, args ...map[string]any) string {
+ return p.Sprintf(p.inner.GetN(fmtS, fmtP, n), args...)
}
/* Transform ‘en-US’ to ‘en’ */
@@ -284,17 +277,22 @@ func (l LocaleInfo) Language() string {
return l.Bcp[:2]
}
-func (p Printer) Sprintf(format string, args map[string]any) string {
+func (p Printer) Sprintf(format string, args ...map[string]any) string {
var bob strings.Builder
- args["-"] = ""
+ vars := map[string]any{
+ "-": "",
+ }
+ for _, arg := range args {
+ maps.Copy(vars, arg)
+ }
for {
- i := strings.IndexByte(format, '%')
+ i := strings.IndexByte(format, '{')
if i == -1 {
- bob.WriteString(format)
+ htmlesc(&bob, format)
break
}
- bob.WriteString(format[:i])
+ htmlesc(&bob, format[:i])
format = format[i+1:]
if len(format) == 0 {
@@ -302,54 +300,43 @@ func (p Printer) Sprintf(format string, args map[string]any) string {
break
}
- b := format[0]
- format = format[1:]
-
- switch b {
- case '%':
- bob.WriteByte(b)
- case '(':
- i = strings.IndexRune(format, ')')
- if i == -1 {
- /* TODO: Handle error: unterminated %( */
- return "unterminated %("
- }
-
- parts := strings.Split(format[:i], ":")
- format = format[i+1:]
-
- var flag rune
- switch len(parts) {
- case 1:
- flag = -1
- case 2:
- f, n := utf8.DecodeRune([]byte(parts[1]))
- if n != len(parts[1]) {
- /* TODO: Handle error: flag too long or empty */
- return "flag too long or empty"
- }
- flag = f
- default:
- /* TODO: Handle error: too many colons */
- return "too many colons"
- }
+ i = strings.IndexRune(format, '}')
+ if i == -1 {
+ /* TODO: Handle error: unterminated { */
+ return "unterminated {"
+ }
- h, ok := handlers[flag]
- if !ok {
- /* TODO: Handle error: no such handler */
- return "no such handler"
- }
+ parts := strings.Split(format[:i], ":")
+ format = format[i+1:]
- v, ok := args[parts[0]]
- if !ok {
- /* TODO: Handle error: no such key */
- return "no such key"
+ var flag rune
+ switch len(parts) {
+ case 1:
+ flag = -1
+ case 2:
+ f, n := utf8.DecodeRune([]byte(parts[1]))
+ if n != len(parts[1]) {
+ /* TODO: Handle error: flag too long or empty */
+ return "flag too long or empty"
}
- h(p.LocaleInfo, &bob, v)
+ flag = f
default:
- /* TODO: Handle error: invalid escape */
- bob.WriteByte(b)
+ /* TODO: Handle error: too many colons */
+ return "too many colons"
}
+
+ h, ok := handlers[flag]
+ if !ok {
+ /* TODO: Handle error: no such handler */
+ return "no such handler"
+ }
+
+ v, ok := vars[parts[0]]
+ if !ok {
+ /* TODO: Handle error: no such key */
+ return "no such key"
+ }
+ h(p.LocaleInfo, &bob, v)
}
return bob.String()
@@ -358,15 +345,15 @@ func (p Printer) Sprintf(format string, args map[string]any) string {
func sprintfGeneric(li LocaleInfo, bob *strings.Builder, v any) error {
switch v.(type) {
case time.Time:
- bob.WriteString(v.(time.Time).Format(li.DateFormat))
+ htmlesc(bob, v.(time.Time).Format(li.DateFormat))
case int:
writeInt(bob, v.(int), li.ThousandsSeparator)
case float64:
writeFloat(bob, v.(float64), li.ThousandsSeparator, li.DecimalSeparator)
case string:
- bob.WriteString(v.(string))
+ htmlesc(bob, v.(string))
default:
- bob.WriteString(fmt.Sprint(v))
+ htmlesc(bob, fmt.Sprint(v))
}
return nil
}
@@ -376,16 +363,16 @@ func sprintfe(li LocaleInfo, bob *strings.Builder, v any) error {
if !ok {
return errors.New("TODO")
}
- bob.WriteString("\u2068<a href=\"mailto:")
- attrEscape(bob, s)
- bob.WriteString("\">\u2069")
- bob.WriteString(s)
- bob.WriteString("\u2068</a>\u2069")
+ bob.WriteString("<a href=\"mailto:")
+ htmlesc(bob, s)
+ bob.WriteString("\">")
+ htmlesc(bob, s)
+ bob.WriteString("</a>")
return nil
}
func sprintfE(li LocaleInfo, bob *strings.Builder, _ any) error {
- bob.WriteString("\u2068</a>\u2069")
+ bob.WriteString("</a>")
return nil
}
@@ -394,9 +381,9 @@ func sprintfl(li LocaleInfo, bob *strings.Builder, v any) error {
if !ok {
return errors.New("TODO")
}
- bob.WriteString("\u2068<a href=\"")
- attrEscape(bob, s)
- bob.WriteString("\">\u2069")
+ bob.WriteString("<a href=\"")
+ htmlesc(bob, s)
+ bob.WriteString("\">")
return nil
}
@@ -405,9 +392,9 @@ func sprintfL(li LocaleInfo, bob *strings.Builder, v any) error {
if !ok {
return errors.New("TODO")
}
- bob.WriteString("\u2068<a href=\"")
- attrEscape(bob, s)
- bob.WriteString("\" target=\"_blank\">\u2069")
+ bob.WriteString("<a href=\"")
+ htmlesc(bob, s)
+ bob.WriteString("\" target=\"_blank\">")
return nil
}
@@ -416,15 +403,15 @@ func sprintfm(li LocaleInfo, bob *strings.Builder, v any) error {
case int:
n := v.(int)
i := btoi(n >= 0)
- bob.WriteString(li.MonetaryPre[i])
+ htmlesc(bob, li.MonetaryPre[i])
writeInt(bob, abs(n), li.ThousandsSeparator)
- bob.WriteString(li.MonetaryPost[i])
+ htmlesc(bob, li.MonetaryPost[i])
case float64:
n := v.(float64)
i := btoi(n >= 0)
- bob.WriteString(li.MonetaryPre[i])
+ htmlesc(bob, li.MonetaryPre[i])
writeFloat(bob, abs(n), li.ThousandsSeparator, li.DecimalSeparator)
- bob.WriteString(li.MonetaryPost[i])
+ htmlesc(bob, li.MonetaryPost[i])
default:
return errors.New("TODO")
}
@@ -436,27 +423,10 @@ func sprintfr(li LocaleInfo, bob *strings.Builder, v any) error {
if !ok {
return errors.New("TODO")
}
- bob.WriteRune('\u2068')
bob.WriteString(s)
- bob.WriteRune('\u2069')
return nil
}
-func attrEscape(bob *strings.Builder, s string) {
- for _, r := range s {
- switch r {
- case '<':
- bob.WriteString("&lt;")
- case '&':
- bob.WriteString("&amp;")
- case '"':
- bob.WriteString("&quot;")
- default:
- bob.WriteRune(r)
- }
- }
-}
-
func writeInt(bob *strings.Builder, num int, sep rune) {
s := fmt.Sprintf("%d", num)
if s[0] == '-' {
@@ -516,3 +486,39 @@ func btoi(b bool) int {
}
return 1
}
+
+func htmlesc(bob *strings.Builder, s string) {
+ for _, r := range s {
+ switch r {
+ case '<':
+ bob.WriteString("&lt;")
+ case '>':
+ bob.WriteString("&gt;")
+ case '&':
+ bob.WriteString("&amp;")
+ case '"':
+ bob.WriteString("&#34;")
+ case '\'':
+ bob.WriteString("&#39;")
+ default:
+ bob.WriteRune(r)
+ }
+ }
+}
+
+func htmlescByte(bob *strings.Builder, b byte) {
+ switch b {
+ case '<':
+ bob.WriteString("&lt;")
+ case '>':
+ bob.WriteString("&gt;")
+ case '&':
+ bob.WriteString("&amp;")
+ case '"':
+ bob.WriteString("&#34;")
+ case '\'':
+ bob.WriteString("&#39;")
+ default:
+ bob.WriteByte(b)
+ }
+}