#!/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())