ZeroNet/src/Translate/Translate.py

136 lines
4.4 KiB
Python

import os
import json
import logging
import inspect
import re
import html
import string
from Config import config
translates = []
class EscapeProxy(dict):
# Automatically escape the accessed string values
def __getitem__(self, key):
val = dict.__getitem__(self, key)
if type(val) in (str, str):
return html.escape(val)
elif type(val) is dict:
return EscapeProxy(val)
elif type(val) is list:
return EscapeProxy(enumerate(val)) # Convert lists to dict
else:
return val
class Translate(dict):
def __init__(self, lang_dir=None, lang=None):
if not lang_dir:
lang_dir = os.path.dirname(__file__) + "/languages/"
if not lang:
lang = config.language
self.lang = lang
self.lang_dir = lang_dir
self.setLanguage(lang)
self.formatter = string.Formatter()
if config.debug:
# Auto reload FileRequest on change
from Debug import DebugReloader
DebugReloader.watcher.addCallback(self.load)
translates.append(self)
def setLanguage(self, lang):
self.lang = re.sub("[^a-z-]", "", lang)
self.lang_file = self.lang_dir + "%s.json" % lang
self.load()
def __repr__(self):
return "<translate %s>" % self.lang
def load(self):
if self.lang == "en":
data = {}
dict.__init__(self, data)
self.clear()
elif os.path.isfile(self.lang_file):
try:
data = json.load(open(self.lang_file, encoding="utf8"))
logging.debug("Loaded translate file: %s (%s entries)" % (self.lang_file, len(data)))
except Exception as err:
logging.error("Error loading translate file %s: %s" % (self.lang_file, err))
data = {}
dict.__init__(self, data)
else:
data = {}
dict.__init__(self, data)
self.clear()
logging.debug("Translate file not exists: %s" % self.lang_file)
def format(self, s, kwargs, nested=False):
kwargs["_"] = self
if nested:
back = self.formatter.vformat(s, [], kwargs) # PY3 TODO: Change to format_map
return self.formatter.vformat(back, [], kwargs)
else:
return self.formatter.vformat(s, [], kwargs)
def formatLocals(self, s, nested=False):
kwargs = inspect.currentframe().f_back.f_locals
return self.format(s, kwargs, nested=nested)
def __call__(self, s, kwargs=None, nested=False, escape=True):
if not kwargs:
kwargs = inspect.currentframe().f_back.f_locals
if escape:
kwargs = EscapeProxy(kwargs)
return self.format(s, kwargs, nested=nested)
def __missing__(self, key):
return key
def pluralize(self, value, single, multi):
if value > 1:
return self[multi].format(value)
else:
return self[single].format(value)
def translateData(self, data, translate_table=None, mode="js"):
if not translate_table:
translate_table = self
patterns = []
for key, val in list(translate_table.items()):
if key.startswith("_("): # Problematic string: only match if called between _(" ") function
key = key.replace("_(", "").replace(")", "").replace(", ", '", "')
translate_table[key] = "|" + val
patterns.append(re.escape(key))
def replacer(match):
target = translate_table[match.group(1)]
if mode == "js":
if target and target[0] == "|": # Strict string match
if match.string[match.start() - 2] == "_": # Only if the match if called between _(" ") function
return '"' + target[1:] + '"'
else:
return '"' + match.group(1) + '"'
return '"' + target + '"'
else:
return match.group(0)[0] + target + match.group(0)[-1]
if mode == "html":
pattern = '[">](' + "|".join(patterns) + ')["<]'
else:
pattern = '"(' + "|".join(patterns) + ')"'
data = re.sub(pattern, replacer, data)
if mode == "html":
data = data.replace("lang={lang}", "lang=%s" % self.lang) # lang get parameter to .js file to avoid cache
return data
translate = Translate()