#!/usr/bin/env python3 # -*- coding: utf-8 -*- """Bake deploy/apps/lang/*.lang into a compiled-in fallback table. Boards that can reach the Lua Store download their language as a file. Boards that cannot (the V4: ~14 KB of free internal heap, so the net worker and the store are unreliable there) still need translations, and before this they were stuck on English. This generates src/ui-touch/i18n_builtin.h from the SAME .lang files the store serves, so the two can never drift: translators edit deploy/apps/lang/*.lang, and both paths follow. Rows are emitted sorted by key so TR() can binary-search them, and the table lives in .rodata — flash, not RAM. Run from the repo root (a PlatformIO pre-build step does this automatically): python3 scripts/build/gen-lang-builtin.py """ import glob, os, sys ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) LANGDIR = os.path.join(ROOT, 'deploy/apps/lang') OUT = os.path.join(ROOT, 'src/ui-touch/i18n_builtin.h') # Index order must match the UiLang enum in i18n.h. CODES = ["en", "hu", "nl", "de", "fr", "es", "it", "ru", "uk", "bg", "sr", "el", "pt-br", "ro"] def c_escape(s): out = [] for ch in s: if ch == '\\': out.append('\\\\') elif ch == '"': out.append('\\"') elif ch == '\n': out.append('\\n') elif ch == '\t': out.append('\\t') elif ch == '\r': out.append('\\r') elif ord(ch) < 0x20: out.append('\\x%02x' % ord(ch)) else: out.append(ch) return ''.join(out) def read_lang(path): """key -> value, with the file's own \\n/\\t/\\\\ escapes decoded.""" rows = {} for line in open(path, encoding='utf-8'): line = line.rstrip('\n').rstrip('\r') if '\t' not in line: # header/comment lines carry no tab continue k, v = line.split('\t', 1) def un(t): o, i = [], 0 while i < len(t): if t[i] == '\\' and i + 1 < len(t): n = t[i + 1] o.append('\n' if n == 'n' else '\t' if n == 't' else n) i += 2 else: o.append(t[i]); i += 1 return ''.join(o) k, v = un(k), un(v) if k and v: rows[k] = v return rows def main(): langs = {} for code in CODES[1:]: # English is the key itself p = os.path.join(LANGDIR, code + '.lang') if os.path.exists(p): langs[code] = read_lang(p) if not langs: sys.exit('no .lang files found in ' + LANGDIR) w = ['// Generated by scripts/build/gen-lang-builtin.py — DO NOT EDIT.', '// Source: deploy/apps/lang/*.lang (the same files the Lua Store serves).', '// Compiled-in fallback for boards that cannot rely on downloading a language.', '#pragma once', '#include "i18n.h"', ''] for code in CODES[1:]: rows = langs.get(code, {}) sym = 'kBuiltin_' + code.replace('-', '_') w.append('static const I18nPair %s[] = {' % sym) for k in sorted(rows): # sorted: TR() binary-searches w.append(' { "%s", "%s" },' % (c_escape(k), c_escape(rows[k]))) w.append('};') w.append('') w.append('static const I18nPair* const kBuiltinLang[LANG_COUNT] = {') w.append(' nullptr,') # LANG_EN: the key is the English for code in CODES[1:]: sym = 'kBuiltin_' + code.replace('-', '_') w.append(' %s,' % (sym if langs.get(code) else 'nullptr')) w.append('};') w.append('static const int kBuiltinLangCount[LANG_COUNT] = {') w.append(' 0,') for code in CODES[1:]: sym = 'kBuiltin_' + code.replace('-', '_') w.append(' %s,' % (('(int)(sizeof(%s)/sizeof(%s[0]))' % (sym, sym)) if langs.get(code) else '0')) w.append('};') w.append('') open(OUT, 'w', encoding='utf-8').write('\n'.join(w)) total = sum(len(r) for r in langs.values()) print('%s: %d languages, %d rows, %d bytes' % (os.path.relpath(OUT, ROOT), len(langs), total, os.path.getsize(OUT))) if __name__ == '__main__': main()