aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/i18n/gen.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/i18n/gen.py')
-rwxr-xr-xsrc/i18n/gen.py172
1 files changed, 172 insertions, 0 deletions
diff --git a/src/i18n/gen.py b/src/i18n/gen.py
new file mode 100755
index 0000000..9271cc0
--- /dev/null
+++ b/src/i18n/gen.py
@@ -0,0 +1,172 @@
+#!/usr/bin/env python3
+
+import concurrent.futures
+import dataclasses
+import json
+import os
+import re
+import subprocess
+import sys
+import urllib.request
+from dataclasses import dataclass
+from typing import Any, TextIO
+
+
+FILENAME = "locales.gen.go"
+
+class Rune(int):
+ pass
+
+
+@dataclass
+class Locale:
+ bcp: str
+ eurozonep: bool
+ enabledp: bool
+ territory: str | None = dataclasses.field(default=None)
+ name: str = dataclasses.field(init=False)
+ date_format: str = dataclasses.field(init=False)
+ group_separator: Rune = dataclasses.field(init=False)
+ decimal_separator: Rune = dataclasses.field(init=False)
+ monetary_formats: tuple[str, str] = dataclasses.field(init=False)
+ percent_format: str = dataclasses.field(init=False)
+
+
+LOCALES = (
+ Locale(bcp="ca", eurozonep=True, enabledp=False),
+ Locale(bcp="de", eurozonep=True, enabledp=False),
+ Locale(bcp="el", eurozonep=True, enabledp=False),
+ Locale(bcp="en", eurozonep=True, enabledp=True, territory="GB"),
+ Locale(bcp="es", eurozonep=True, enabledp=False),
+ Locale(bcp="et", eurozonep=True, enabledp=False),
+ Locale(bcp="fi", eurozonep=True, enabledp=False),
+ Locale(bcp="fr", eurozonep=True, enabledp=False),
+ Locale(bcp="ga", eurozonep=True, enabledp=False),
+ Locale(bcp="hr", eurozonep=True, enabledp=False),
+ Locale(bcp="it", eurozonep=True, enabledp=False),
+ Locale(bcp="lb", eurozonep=True, enabledp=False),
+ Locale(bcp="lt", eurozonep=True, enabledp=False),
+ Locale(bcp="lv", eurozonep=True, enabledp=False),
+ Locale(bcp="mt", eurozonep=True, enabledp=False),
+ Locale(bcp="nl", eurozonep=True, enabledp=True),
+ Locale(bcp="pt", eurozonep=True, enabledp=False, territory="PT"),
+ Locale(bcp="sk", eurozonep=True, enabledp=False),
+ Locale(bcp="sl", eurozonep=True, enabledp=False),
+ Locale(bcp="sv", eurozonep=True, enabledp=True),
+ Locale(bcp="tr", eurozonep=True, enabledp=False),
+ Locale(bcp="bg", eurozonep=False, enabledp=False),
+ Locale(bcp="ro", eurozonep=False, enabledp=False),
+ Locale(bcp="uk", eurozonep=False, enabledp=False),
+)
+
+BASELINK = "https://raw.githubusercontent.com/unicode-org/cldr-json/refs/heads/main/cldr-json/%s/main/%%s/%s"
+NUMBERS_LINK, DATES_LINK, LANGUAGES_LINK = (
+ BASELINK % ("cldr-numbers-full", "numbers.json"),
+ BASELINK % ("cldr-dates-full", "ca-gregorian.json"),
+ BASELINK % ("cldr-localenames-full", "languages.json"),
+)
+
+
+def main() -> int:
+ rv = 0
+ nprocs = os.cpu_count()
+ with concurrent.futures.ThreadPoolExecutor(max_workers=nprocs) as executor:
+ for x in [executor.submit(write_locale, l) for l in LOCALES]:
+ try:
+ x.result()
+ except Exception as e:
+ print(f"gen.py: {e}", file=sys.stderr)
+ rv = 1
+
+ with open(FILENAME, "w") as f:
+ f.write("""// Code generated by gen.py. DO NOT EDIT.
+
+package i18n
+
+import "github.com/leonelquinteros/gotext"
+
+type LocaleInfo struct {
+ Bcp, Name string
+ Eurozonep, Enabledp bool
+ DateFormat string
+ GroupSeparator, DecimalSeparator rune
+ MonetaryFormats [2]string
+ PercentFormat string
+}
+
+var locales = [...]LocaleInfo{
+""")
+ for x in LOCALES:
+ f.write("{\n")
+ for k, v in x.__dict__.items():
+ if not v or k == "territory":
+ continue
+ if k == "name":
+ f.write('Name: gotext.GetC(%s, "Language Name"),\n' % val_to_go(v))
+ else:
+ f.write("%s: %s,\n" % (pascal(k), val_to_go(v)))
+ f.write("},\n")
+ f.write("}")
+
+ subprocess.run(["gofmt", "-w", FILENAME])
+ return rv
+
+
+def write_locale(l: Locale) -> None:
+ bcp = '%s-%s' % (l.bcp, l.territory) if l.territory else l.bcp
+ jn, jd, jl = map(json.load, (
+ urllib.request.urlopen(NUMBERS_LINK % bcp),
+ urllib.request.urlopen(DATES_LINK % bcp),
+ urllib.request.urlopen(LANGUAGES_LINK % bcp),
+ ))
+ name = jl["main"][bcp]["localeDisplayNames"]["languages"][l.bcp]
+ l.name = name.capitalize()
+ syms = jn["main"][bcp]["numbers"]["symbols-numberSystem-latn"]
+ l.group_separator = Rune(ord(syms["group"]))
+ l.decimal_separator = Rune(ord(syms["decimal"]))
+
+ fmt = jn["main"][bcp]["numbers"]["percentFormats-numberSystem-latn"]["standard"]
+ l.percent_format = numfmt_subst(fmt)
+
+ fmt = jd["main"][bcp]["dates"]["calendars"]["gregorian"]["dateFormats"]["short"]
+ l.date_format = (
+ fmt
+ .replace("yy", "06")
+ .replace("MM", "01")
+ .replace("dd", "02")
+ .replace("y", "2006")
+ .replace("M", "1")
+ .replace("d", "2")
+ .replace("'", "")
+ )
+
+ fmt = jn["main"][bcp]["numbers"]["currencyFormats-numberSystem-latn"]["standard"]
+ parts = list(map(numfmt_subst, fmt.replace("¤", "€").split(";")))
+ if len(parts) == 1:
+ parts.append('-' + parts[0])
+ l.monetary_formats = tuple(parts)
+
+
+def numfmt_subst(s: str) -> str:
+ return re.sub(r"[0#,.]+", "123", s)
+
+
+def pascal(s: str) -> str:
+ return ''.join(map(str.capitalize, s.split('_')))
+
+
+def val_to_go(x: Any) -> str:
+ match x:
+ case bool():
+ return "true" if x else "false"
+ case Rune():
+ return "'%s'" % chr(x)
+ case str():
+ return '"%s"' % x
+ case (str(), str()):
+ return "[2]string{%s, %s}" % (val_to_go(x[0]), val_to_go(x[1]))
+
+
+if __name__ == "__main__":
+ os.chdir(os.path.dirname(sys.argv[0]))
+ sys.exit(main())