bazaar/scripts/countries.py
Alexander Vanhee 3476a3a4b2
Improve world map loading mechanisms (#1315)
* Improve world map loading mechanisms

Makes it so the locale data for the world map gets automatically generated at build time and changes the output to GVariant for improved loading speed over JSON.

This seems to mostly stop the lag spike when opening the graph dialog

* Add checker
2026-03-22 11:09:30 -07:00

142 lines
4.3 KiB
Python
Executable file

#!/usr/bin/env python3
import struct
import json
import sys
from pathlib import Path
try:
from babel import Locale
except ImportError:
print("Error: babel library not found.")
print("Install it with: pip install babel")
sys.exit(1)
try:
import gi
gi.require_version('GLib', '2.0')
from gi.repository import GLib
except ImportError:
print("Error: PyGObject not found.")
sys.exit(1)
verbose = False
def log(msg):
if verbose:
print(msg)
def read_linguas(linguas_path):
languages = []
with open(linguas_path, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if line and not line.startswith("#"):
languages.append(line)
return languages
def normalize_locale_code(lang_code):
locale_map = {"zh_CN": "zh_Hans_CN", "zh_TW": "zh_Hant_TW", "fa_IR": "fa_IR"}
return locale_map.get(lang_code, lang_code)
def get_country_translation(country_code, lang_code):
try:
normalized = normalize_locale_code(lang_code)
if "_" in normalized:
parts = normalized.split("_")
if len(parts) == 3:
locale = Locale(parts[0], script=parts[1], territory=parts[2])
else:
lang, territory = parts[0], parts[1]
locale = Locale(lang, territory=territory)
else:
locale = Locale(normalized)
territory_name = locale.territories.get(country_code.upper())
return territory_name if territory_name else None
except Exception as e:
log(f"Warning: Could not get translation for {country_code} in {lang_code}: {e}")
return None
def build_variant(json_path, linguas_path):
languages = read_linguas(linguas_path)
log(f"Found {len(languages)} languages: {', '.join(languages)}")
with open(json_path, "r", encoding="utf-8") as f:
data = json.load(f)
builder = GLib.VariantBuilder(GLib.VariantType.new("a(ssa{ss}aaa(dd))"))
for feature in data.get("features", []):
country_name = feature.get("N", "")
country_code = feature.get("I", "")
if not country_code:
continue
log(f"Processing: {country_name} ({country_code})")
translations = {}
for lang in languages:
translation = get_country_translation(country_code, lang)
translations[lang] = translation if translation else country_name
coords_raw = feature.get("C", [])
poly_builder = GLib.VariantBuilder(GLib.VariantType.new("aaa(dd)"))
for polygon in coords_raw:
ring_builder = GLib.VariantBuilder(GLib.VariantType.new("aa(dd)"))
for ring in polygon:
point_builder = GLib.VariantBuilder(GLib.VariantType.new("a(dd)"))
for point in ring:
point_builder.add_value(GLib.Variant("(dd)", (float(point[0]), float(point[1]))))
ring_builder.add_value(point_builder.end())
poly_builder.add_value(ring_builder.end())
trans_variant = GLib.Variant("a{ss}", translations)
coords_variant = poly_builder.end()
entry = GLib.Variant.new_tuple(
GLib.Variant("s", country_name),
GLib.Variant("s", country_code),
trans_variant,
coords_variant,
)
builder.add_value(entry)
return builder.end()
def main():
global verbose
linguas_path = "../po/LINGUAS"
json_path = "countries.json.in"
output_path = "../src/countries.gvariant"
if "--verbose" in sys.argv:
verbose = True
sys.argv.remove("--verbose")
if len(sys.argv) > 1:
json_path = sys.argv[1]
if len(sys.argv) > 2:
linguas_path = sys.argv[2]
if len(sys.argv) > 3:
output_path = sys.argv[3]
if not Path(linguas_path).exists():
print(f"Error: LINGUAS file not found at {linguas_path}")
sys.exit(1)
if not Path(json_path).exists():
print(f"Error: JSON file not found at {json_path}")
sys.exit(1)
variant = build_variant(json_path, linguas_path)
data = variant.get_data_as_bytes()
with open(output_path, "wb") as f:
f.write(data.get_data())
print(f"GVariant saved to: {output_path} ({GLib.format_size(data.get_size())})")
if __name__ == "__main__":
main()