mirror of
https://github.com/UpsilonNumworks/Upsilon.git
synced 2026-01-18 16:27:34 +01:00
182 lines
6.8 KiB
Python
182 lines
6.8 KiB
Python
#!/usr/bin/python
|
||
#coding=utf-8
|
||
|
||
import sys
|
||
import re
|
||
import unicodedata
|
||
from sets import Set
|
||
import argparse
|
||
|
||
ion_special_characters = {
|
||
u'Σ': "Ion::Charset::CapitalSigma",
|
||
u'λ': "Ion::Charset::SmallLambda",
|
||
u'μ': "Ion::Charset::SmallMu",
|
||
u'σ': "Ion::Charset::SmallSigma",
|
||
u'≤': "Ion::Charset::LessEqual",
|
||
u'≈': "Ion::Charset::AlmostEqual"
|
||
}
|
||
|
||
def ion_char(i18n_letter):
|
||
if i18n_letter == '\'':
|
||
return "'\\\''"
|
||
if ord(i18n_letter) < 128:
|
||
return "'" + i18n_letter + "'"
|
||
if i18n_letter in ion_special_characters:
|
||
return ion_special_characters[i18n_letter]
|
||
normalized = unicodedata.normalize("NFD", i18n_letter).encode('ascii', 'ignore')
|
||
#sys.stderr.write("Warning: Normalizing unicode character \"" + i18n_letter + "\" -> \"" + normalized + "\"\n")
|
||
return "'" + normalized + "'"
|
||
|
||
def source_definition(i18n_string):
|
||
ion_characters = []
|
||
letters = i18n_string.decode('utf8')
|
||
i = 0
|
||
while i < len(letters):
|
||
if letters[i] == '\\':
|
||
i = i+1
|
||
newChar = "'\\"+letters[i]+"'"
|
||
ion_characters.append(newChar)
|
||
else:
|
||
ion_characters.append(ion_char(letters[i]))
|
||
i = i+1
|
||
ion_characters.append("0")
|
||
return "{" + ", ".join(ion_characters) + "}"
|
||
|
||
def split_line(line):
|
||
match = re.match(r"^(\w+)\s*=\s*\"(.*)\"$", line)
|
||
if not match:
|
||
sys.stderr.write("Error: Invalid line \"" + line + "\"\n")
|
||
sys.exit(-1)
|
||
return (match.group(1), source_definition(match.group(2)))
|
||
|
||
def locale_from_filename(filename):
|
||
return re.match(r".*\.([a-z]+)\.i18n", filename).group(1)
|
||
|
||
def parse_files(files):
|
||
data = {}
|
||
messages = Set()
|
||
universal_messages = Set()
|
||
for path in files:
|
||
locale = locale_from_filename(path)
|
||
if locale not in data:
|
||
data[locale] = {}
|
||
with open(path, "r") as file:
|
||
for line in file:
|
||
name,definition = split_line(line)
|
||
if locale == "universal":
|
||
if name in messages:
|
||
sys.stderr.write("Error: Redefinition of message \"" + name + "\" as universal\n")
|
||
sys.exit(-1)
|
||
if name in universal_messages:
|
||
sys.stderr.write("Error: Redefinition of universal message \"" + name + "\"\n")
|
||
sys.exit(-1)
|
||
universal_messages.add(name)
|
||
else:
|
||
messages.add(name)
|
||
data[locale][name] = definition
|
||
return {"messages": sorted(messages), "universal_messages": sorted(universal_messages), "data": data}
|
||
|
||
def print_header(data, path, locales):
|
||
f = open(path, 'w')
|
||
f.write("#ifndef APPS_I18N_H\n")
|
||
f.write("#define APPS_I18N_H\n")
|
||
f.write("\n")
|
||
f.write("// This file is auto-generated by i18n.py\n")
|
||
f.write("\n")
|
||
f.write("#include <escher.h>\n")
|
||
f.write("\n")
|
||
f.write("namespace I18n {\n")
|
||
f.write("\n")
|
||
f.write("constexpr static int NumberOfLanguages = %d;\n" % len(locales))
|
||
f.write("\n")
|
||
f.write("enum class Message : uint16_t {\n")
|
||
for message in data["messages"]:
|
||
f.write(" " + message + ",\n")
|
||
f.write("\n")
|
||
f.write(" UniversalMessageMarker,\n")
|
||
f.write("\n")
|
||
for message in data["universal_messages"]:
|
||
f.write(" " + message + ",\n")
|
||
f.write("};\n")
|
||
f.write("\n")
|
||
f.write("enum class Language : uint16_t {\n")
|
||
f.write(" Default = 0,\n")
|
||
for locale in locales:
|
||
f.write(" " + locale.upper() + ",\n")
|
||
f.write("};\n")
|
||
f.write("\n")
|
||
f.write("constexpr const Message LanguageNames[NumberOfLanguages] = {\n");
|
||
for locale in locales:
|
||
f.write(" Message::Language" + locale.upper() + ",\n")
|
||
f.write("};\n")
|
||
f.write("\n")
|
||
f.write("}\n")
|
||
f.write("\n")
|
||
f.write("#endif\n")
|
||
f.close()
|
||
|
||
def print_implementation(data, path, locales):
|
||
f = open(path, 'w')
|
||
f.write("#include \"i18n.h\"\n")
|
||
f.write("#include \"global_preferences.h\"\n")
|
||
f.write("#include <assert.h>\n");
|
||
f.write("\n")
|
||
f.write("namespace I18n {\n")
|
||
f.write("\n")
|
||
for message in data["messages"]:
|
||
for locale in locales:
|
||
if not locale in data["data"]:
|
||
sys.stderr.write("Error: Undefined locale \"" + locale + "\"\n")
|
||
sys.exit(-1)
|
||
if not message in data["data"][locale]:
|
||
sys.stderr.write("Error: Undefined key \"" + message + "\" for locale \"" + locale + "\"\n")
|
||
sys.exit(-1)
|
||
f.write("constexpr static char " + locale + message + "[] = " + data["data"][locale][message] + ";\n")
|
||
f.write("\n")
|
||
f.write("constexpr static const char * messages[%d][%d] = {\n" % (len(data["messages"]), len(locales)))
|
||
for message in data["messages"]:
|
||
f.write(" {")
|
||
for locale in locales:
|
||
f.write(locale + message + ", ")
|
||
f.write("},\n")
|
||
f.write("};\n")
|
||
f.write("\n")
|
||
for message in data["universal_messages"]:
|
||
f.write("constexpr static char universal" + message + "[] = " + data["data"]["universal"][message] + ";\n")
|
||
f.write("\n")
|
||
f.write("constexpr static const char * universalMessages[%d] = {\n" % len(data["universal_messages"]))
|
||
for message in data["universal_messages"]:
|
||
f.write(" universal" + message + ",\n")
|
||
f.write("};\n")
|
||
f.write("\n")
|
||
f.write("const char * translate(Message m, Language l) {\n")
|
||
f.write(" int universalMessageOffset = (int)Message::UniversalMessageMarker+1;\n")
|
||
f.write(" if ((int)m >= universalMessageOffset) {\n")
|
||
f.write(" assert(universalMessages[(int)m - universalMessageOffset] != nullptr);\n")
|
||
f.write(" return universalMessages[(int)m - universalMessageOffset];\n")
|
||
f.write(" }")
|
||
f.write(" int languageIndex = (int)l;\n")
|
||
f.write(" if (l == Language::Default) {\n")
|
||
f.write(" languageIndex = (int) GlobalPreferences::sharedGlobalPreferences()->language();\n")
|
||
f.write(" }\n")
|
||
f.write(" assert(languageIndex > 0);\n")
|
||
f.write(" assert(((int)m*NumberOfLanguages+languageIndex-1)*sizeof(char *) < sizeof(messages));\n")
|
||
f.write(" return messages[(int)m][languageIndex-1];\n")
|
||
f.write("}\n")
|
||
f.write("\n")
|
||
f.write("}\n")
|
||
f.close()
|
||
|
||
parser = argparse.ArgumentParser(description="Process some i18n files.")
|
||
parser.add_argument('--header', help='the .h file to generate')
|
||
parser.add_argument('--implementation', help='the .cpp file to generate')
|
||
parser.add_argument('--locales', nargs='+', help='locale to actually generate')
|
||
parser.add_argument('--files', nargs='+', help='an i18n file')
|
||
|
||
args = parser.parse_args()
|
||
data = parse_files(args.files)
|
||
if args.header:
|
||
print_header(data, args.header, args.locales)
|
||
if args.implementation:
|
||
print_implementation(data, args.implementation, args.locales)
|