#!/usr/bin/env python3 """Generates watch/src/c/i18n_tables.auto.c from engine/i18n/*.lang. Compiles each non-English .lang file into a `static const char *const` keys[]/values[] pair for i18n_load_table() (see engine/src/i18n.h's own doc comment on why the watch needs a zero-copy, compiled-in table instead of i18n_load()'s file-based parsing). English is deliberately skipped: the fallback text already baked into tarot_data.c/astro.c/ narrative.c/guidance.c *is* English, so shipping a redundant English catalog would roughly double this app's translation-data footprint (which counts against the same ~64KB whole-app budget as everything else - see watch/README.md) for zero behavioural benefit. This is a generated file (like message_keys.auto.c, resource_ids.auto.c elsewhere in a Pebble project) - not meant to be hand-edited or committed; watch/wscript regenerates it on every build. Parsing rules mirror engine/src/i18n.c's i18n_load() exactly: '#'-prefixed and blank lines are skipped, the first '=' splits key/value, and both sides are trimmed of surrounding spaces/tabs only (not quotes - values are raw text, same as the desktop file loader). """ import glob import os import sys SCRIPT_DIR = os.path.dirname(os.path.abspath(__file__)) I18N_DIR = os.path.normpath(os.path.join(SCRIPT_DIR, "..", "..", "engine", "i18n")) OUT_PATH = os.path.normpath(os.path.join(SCRIPT_DIR, "..", "src", "c", "i18n_tables.auto.c")) def parse_lang_file(path): entries = [] with open(path, "r", encoding="utf-8") as f: for raw_line in f: line = raw_line.rstrip("\r\n") stripped = line.strip(" \t") if not stripped or stripped.startswith("#"): continue if "=" not in line: continue key, value = line.split("=", 1) key = key.strip(" \t") value = value.strip(" \t") if not key: continue entries.append((key, value)) return entries def c_string_literal(s): escaped = s.replace("\\", "\\\\").replace('"', '\\"') return '"' + escaped + '"' def main(): lang_files = sorted(glob.glob(os.path.join(I18N_DIR, "*.lang"))) languages = [] for path in lang_files: code = os.path.splitext(os.path.basename(path))[0] if code == "en": continue # see module doc comment - English is never a compiled table languages.append((code, parse_lang_file(path))) lines = [] lines.append("/* Generated by watch/scripts/gen_i18n_tables.py from engine/i18n's .lang") lines.append(" * files - do not edit by hand, and do not commit (see .gitignore). */") lines.append("") lines.append('#include "i18n_tables.h"') lines.append("") lines.append('#include "../../../engine/src/i18n.h"') lines.append("") lines.append("#include ") lines.append("#include ") lines.append("") for code, entries in languages: lines.append(f"static const char *const k_keys_{code}[] = {{") for key, _ in entries: lines.append(f" {c_string_literal(key)},") lines.append("};") lines.append(f"static const char *const k_values_{code}[] = {{") for _, value in entries: lines.append(f" {c_string_literal(value)},") lines.append("};") lines.append("") lines.append("void watch_i18n_load(const char *lang_code) {") for code, entries in languages: lines.append(f' if (lang_code && strcmp(lang_code, "{code}") == 0) {{') lines.append(f" i18n_load_table(k_keys_{code}, k_values_{code}, {len(entries)});") lines.append(" return;") lines.append(" }") lines.append(" i18n_load_table(NULL, NULL, 0); /* \"en\" or unrecognized: use built-in English */") lines.append("}") lines.append("") os.makedirs(os.path.dirname(OUT_PATH), exist_ok=True) with open(OUT_PATH, "w", encoding="utf-8") as f: f.write("\n".join(lines)) print(f"wrote {OUT_PATH} ({', '.join(code for code, _ in languages)})", file=sys.stderr) if __name__ == "__main__": main()