diff options
Diffstat (limited to 'src/i18n/gen.py')
-rwxr-xr-x | src/i18n/gen.py | 172 |
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()) |