Introduces watch/, a real Pebble watchapp (built via pebble build) that
build / build (push) Successful in 30s
build / build (push) Successful in 30s
shows the daily tarot/astrology reading on-device: persistence, reading computation, and UI screens, driven by the existing engine/interpreter code linked in unchanged where possible. Required engine-side changes to make that linking work: - A compact low-precision ephemeris (lowprec_ephemeris.c) replacing the vendored ~127KB Astronomy Engine, which doesn't fit the watch's ~64KB app budget. - Hand-rolled sqrt/atan2/sin/cos replacements for that ephemeris and astro.c's Ascendant calculation - Pebble's statically-linked libm hard-faults on real hardware under this app's -fPIE link for all four. - narrative.c/guidance.c refactored from FILE*/fprintf onto snprintf- based buffers, since Pebble's SDK blocks fprintf at compile time.
This commit is contained in:
@@ -0,0 +1,105 @@
|
||||
#!/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 <stddef.h>")
|
||||
lines.append("#include <string.h>")
|
||||
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()
|
||||
Reference in New Issue
Block a user