Forking what is left of ZeroNet and hopefully adding an AT Proto Frontend/Proxy
at main 135 lines 4.5 kB view raw
1import os 2import json 3import logging 4import inspect 5import re 6import html 7import string 8 9from Config import config 10 11translates = [] 12 13 14class EscapeProxy(dict): 15 # Automatically escape the accessed string values 16 def __getitem__(self, key): 17 val = dict.__getitem__(self, key) 18 if type(val) in (str, str): 19 return html.escape(val) 20 elif type(val) is dict: 21 return EscapeProxy(val) 22 elif type(val) is list: 23 return EscapeProxy(enumerate(val)) # Convert lists to dict 24 else: 25 return val 26 27 28class Translate(dict): 29 def __init__(self, lang_dir=None, lang=None): 30 if not lang_dir: 31 lang_dir = os.path.dirname(__file__) + "/languages/" 32 if not lang: 33 lang = config.language 34 self.lang = lang 35 self.lang_dir = lang_dir 36 self.setLanguage(lang) 37 self.formatter = string.Formatter() 38 39 if config.debug: 40 # Auto reload FileRequest on change 41 from Debug import DebugReloader 42 DebugReloader.watcher.addCallback(self.load) 43 44 translates.append(self) 45 46 def setLanguage(self, lang): 47 self.lang = re.sub("[^a-z-]", "", lang) 48 self.lang_file = self.lang_dir + "%s.json" % lang 49 self.load() 50 51 def __repr__(self): 52 return "<translate %s>" % self.lang 53 54 def load(self): 55 if self.lang == "en": 56 data = {} 57 dict.__init__(self, data) 58 self.clear() 59 elif os.path.isfile(self.lang_file): 60 try: 61 data = json.load(open(self.lang_file, encoding="utf8")) 62 logging.debug("Loaded translate file: %s (%s entries)" % (self.lang_file, len(data))) 63 except Exception as err: 64 logging.error("Error loading translate file %s: %s" % (self.lang_file, err)) 65 data = {} 66 dict.__init__(self, data) 67 else: 68 data = {} 69 dict.__init__(self, data) 70 self.clear() 71 logging.debug("Translate file not exists: %s" % self.lang_file) 72 73 def format(self, s, kwargs, nested=False): 74 kwargs["_"] = self 75 if nested: 76 back = self.formatter.vformat(s, [], kwargs) # PY3 TODO: Change to format_map 77 return self.formatter.vformat(back, [], kwargs) 78 else: 79 return self.formatter.vformat(s, [], kwargs) 80 81 def formatLocals(self, s, nested=False): 82 kwargs = inspect.currentframe().f_back.f_locals 83 return self.format(s, kwargs, nested=nested) 84 85 def __call__(self, s, kwargs=None, nested=False, escape=True): 86 if not kwargs: 87 kwargs = inspect.currentframe().f_back.f_locals 88 if escape: 89 kwargs = EscapeProxy(kwargs) 90 return self.format(s, kwargs, nested=nested) 91 92 def __missing__(self, key): 93 return key 94 95 def pluralize(self, value, single, multi): 96 if value > 1: 97 return self[multi].format(value) 98 else: 99 return self[single].format(value) 100 101 def translateData(self, data, translate_table=None, mode="js"): 102 if not translate_table: 103 translate_table = self 104 105 patterns = [] 106 for key, val in list(translate_table.items()): 107 if key.startswith("_("): # Problematic string: only match if called between _(" ") function 108 key = key.replace("_(", "").replace(")", "").replace(", ", '", "') 109 translate_table[key] = "|" + val 110 patterns.append(re.escape(key)) 111 112 def replacer(match): 113 target = translate_table[match.group(1)] 114 if mode == "js": 115 if target and target[0] == "|": # Strict string match 116 if match.string[match.start() - 2] == "_": # Only if the match if called between _(" ") function 117 return '"' + target[1:] + '"' 118 else: 119 return '"' + match.group(1) + '"' 120 return '"' + target + '"' 121 else: 122 return match.group(0)[0] + target + match.group(0)[-1] 123 124 if mode == "html": 125 pattern = '[">](' + "|".join(patterns) + ')["<]' 126 else: 127 pattern = '"(' + "|".join(patterns) + ')"' 128 data = re.sub(pattern, replacer, data) 129 130 if mode == "html": 131 data = data.replace("lang={lang}", "lang=%s" % self.lang) # lang get parameter to .js file to avoid cache 132 133 return data 134 135translate = Translate()